1. 为什么你写的Prompt总在上线后“悄悄变味”——从手工 eyeball 到结构化评估的实战跃迁你肯定经历过花三天打磨出一个写周报的 Prompt本地测试五组输入输出看着都挺像那么回事语气自然、重点清晰、格式整齐。你信心满满地合入主干发版上线。结果一周后客服同事发来截图——用户收到的周报开头是“尊敬的各位同仁”结尾还带了个“此致 敬礼”。你点开日志一查Prompt 没动但模型版本悄悄升了级而你那五条手工测试用例压根没跟着走。这不是玄学这是 LLM 应用开发里最普遍、也最危险的盲区我们用确定性世界的测试方法去验证非确定性系统的输出。传统单元测试的核心是“输入→预期输出”的精确匹配而 LLM 的本质是概率采样——同一个 Prompt同一组参数连续跑三次可能得到三段语义一致但字面迥异的文本。你无法要求它每次都输出“本周完成了 A、B、C 三项任务”但你能明确要求它“必须包含‘A’‘B’‘C’三个关键词”“不能出现‘遗憾’‘不足’等负面词汇”“响应时间不超过 25 秒”。Promptfoo 就是为填平这个鸿沟而生的。它不是另一个大模型平台也不是一个可视化调试工具而是一个面向 LLM 输出质量的、可版本化、可自动化、可协作的测试框架。它的核心逻辑非常朴素把“人眼判断”翻译成机器可执行的规则。你定义“好邮件长什么样”它就替你一条条去核验你列出要测的 GPT-5 和 Claude Sonnet 4.6它就自动组合、并发请求、统一打分你把整个配置写进promptfooconfig.yaml它就和你的代码一起被 Git 管理、在 CI 流水线里自动运行。OpenAI 在 2026 年收购它恰恰印证了行业共识Prompt 工程的成熟度不取决于你写了多炫的提示词而取决于你建立了多严苛、多自动化的质量守门机制。本文不讲概念不堆术语只带你从零开始亲手搭建一个真实可用的邮件生成器评估体系并把它焊死在 GitHub Actions 的流水线上。你会看到当“Casual Tone”不再是一句模糊要求而是一条包含“必须使用 contractionswe’ll, don’t、句子平均长度 18 字、禁用‘synergy’‘leverage’等 7 个 corporate jargon”的llm-rubric断言时那个总在周五下午“变正式”的 Bug是如何被一行配置精准捕获的。2. Promptfoo 的底层设计哲学为什么它能替代“人工 eyeball”2.1 它不做任何模型训练或微调只做一件事质量仲裁很多新手第一次接触 Promptfoo 时会困惑“它是不是又一个模型服务平台”答案是否定的。Promptfoo 的定位极其清晰它是一个外部质量仲裁器External Quality Arbiter。它不碰你的模型权重不改你的推理参数甚至不缓存你的原始 Prompt——它只做三件事接收你的 Prompt 模板、向你指定的模型 API 发起请求、然后用你定义的规则去评判返回的文本。这种“解耦”设计带来了几个关键优势零学习成本迁移你今天用 OpenAI明天切到 Anthropic 或自建 vLLM 集群只需修改providers配置块整个评估逻辑完全复用。我上个月帮一家金融客户迁移他们从 GPT-4 切换到内部部署的 Qwen2-72B评估脚本一行未改只换了 provider ID 和 endpoint。结果可审计、可回溯所有 API 请求的原始输入Prompt vars、原始输出model response、断言判定过程包括 llm-rubric 的 grading prompt 和 grading LLM 的 response全部记录在本地 JSONL 日志中。当某次 CI 构建失败时你不需要猜“是不是模型抽风了”直接打开日志文件就能看到 Claude Sonnet 4.6 在“紧急邮件”测试中因为多加了 37 字的 preamble 解释“Here’s a draft email based on your bullet points…”导致python断言的 word count 超限。这种颗粒度的可追溯性是任何“黑盒”平台给不了的。规避供应商锁定风险它的 MIT 许可证意味着你可以自由 fork、修改、甚至嵌入私有 CI 系统。我们团队曾为满足某银行的离线审计要求将 Promptfoo 编译为静态二进制部署在无外网的测试机房所有模型请求通过内网代理转发整个评估流程完全合规闭环。2.2 YAML 配置即契约为什么一个文件能承载整个评估生命周期promptfooconfig.yaml不是配置文件它是你与团队、与 CI 系统、与未来自己的质量契约Quality Contract。这个文件天然具备四个关键属性声明式Declarative你描述“要什么”而不是“怎么做”。比如assert: { type: latency, threshold: 30000 }声明了“响应必须在 30 秒内”至于 Promptfoo 是如何测量、如何重试、如何处理超时你无需关心。这和 Kubernetes 的 YAML 设计哲学一脉相承——关注终态而非过程。可组合Composableprompts、providers、tests三大模块彼此正交。你可以有 10 个 Prompt 模板5 个模型 Provider20 个 Test Case它们会自动笛卡尔积组合10×5×201000 个测试单元。我们有个客户做多语言摘要一个prompts包含中/英/日三版模板providers列了 4 个模型tests有 8 组跨语言样本一次promptfoo eval就跑完全部 96 个组合比写 Shell 脚本循环快 5 倍。可继承InheritabledefaultTest块就是 DRYDon’t Repeat Yourself原则的体现。所有测试用例默认继承latency和cost断言你只需在特定用例里覆盖或补充。我们团队的基线配置里defaultTest强制所有输出必须not-contains: I cannot防拒答、regex: ^Subject:保邮件头格式新成员加入时这些底线规则自动生效避免重复踩坑。可版本化Versionable它就是一个纯文本 YAML 文件完美融入 Git 工作流。当你在 PR 描述里写“优化了 casual tone 的 prompt”Reviewer 不再需要手动复制粘贴测试而是直接看git diff promptfooconfig.yaml——新增了哪条llm-rubric放宽了哪个latency阈值权重weight如何调整所有质量决策都像代码变更一样透明、可评论、可回滚。2.3 三层断言体系如何构建一张密不透风的质量防护网Promptfoo 的断言Assertion不是简单的 if-else而是一个分层防御体系每一层解决不同维度的问题。理解这个分层是你写出有效评估的关键第一层确定性断言Deterministic Assertions—— 速度与零成本的基石这些断言在本地执行毫秒级返回零 token 消耗。它们是质量防线的“哨兵”负责拦截最基础、最明确的错误。contains检查关键词存在性regex识别格式错误比如\\{.*?\\}能立刻发现未填充的{{variable}}占位符not-contains过滤掉模板残留如Dear [Name]中的[Name]或模型拒答I cannot assist。我见过最典型的误用是有人用contains: urgent去检查紧急邮件结果模型输出This is not an urgent matter也通过了。正确做法是not-contains: not urgenticontains: ASAP|immediately|deadline组合。这类断言的价值在于“快”和“准”它们应该覆盖 70% 以上的基础校验。第二层模型辅助断言Model-Assisted Assertions—— 处理主观性的终极武器当你需要判断“语气是否 casual”“逻辑是否连贯”“事实是否准确”时确定性断言就失效了。这时llm-rubric登场。它的威力不在于 LLM 本身而在于你写的 Rubric评分标准有多精准。一个模糊的 RubricThe email sounds professional会让 grading LLM 自由发挥结果不可控而一个具体的 RubricMust use contractions (well, dont), avoid passive voice, sentences 15 words, open with Hey or Hi team, and contain zero instances of synergy, leverage, bandwidth则能将 grading 结果的方差压缩到极低。实测数据当我们把 Rubric 从模糊版升级到具体版后GPT-5 和 Claude 在 casual tone 上的评分一致性从 62% 提升到 94%。这说明Rubric 的质量直接决定了模型辅助断言的可靠性。第三层自定义 Python 断言Custom Python Assertions—— 解决领域特有难题的瑞士军刀这是 Promptfoo 最灵活的一层。当内置断言无法满足需求时Python 就是你的画布。比如邮件生成器我们不仅关心内容还关心可读性指标Flesch-Kincaid 阅读难度、Coleman-Liau 指数、甚至自定义的“技术术语密度”统计API,endpoint,latency等词出现频次。一段 5 行的 Python 代码就能搞定import textblob from textblob import TextBlob def get_assert(output, context): blob TextBlob(output) fk_grade blob.flesch_kincaid_grade() # 要求阅读难度 8相当于美国八年级水平 pass_flag fk_grade 8.0 return { pass: pass_flag, score: 1.0 if pass_flag else 0.0, reason: fFlesch-Kincaid Grade: {fk_grade:.1f} (target 8.0) }这种能力让 Promptfoo 能深度嵌入业务逻辑。我们曾为一个法律合同审查助手编写了 Python 断言来解析输出中的条款编号Section 3.2(a)并验证其是否与输入文档的章节结构严格对应——这种结构化校验是任何通用断言都无法替代的。3. 从零开始构建一个真实可用的邮件生成器评估套件3.1 环境初始化与安全密钥管理为什么promptfoo init只是起点安装 Promptfoo 本身很简单npm install -g promptfoo。但真正的挑战始于初始化后的密钥安全实践。promptfoo init生成的交互式向导会引导你选择 Provider但它不会告诉你最关键的两件事密钥绝不应硬编码在 YAML 中promptfooconfig.yaml是要提交到 Git 的里面绝不能出现api_key: sk-xxx。正确的做法是依赖环境变量。Promptfoo 会自动读取OPENAI_API_KEY、ANTHROPIC_API_KEY等标准变量。在本地开发时我习惯用.env文件配合dotenv加载但 CI 环境必须使用平台原生的 secrets 机制如 GitHub Actions 的secrets.OPENAI_API_KEY。我们曾因在.env文件里误提交密钥触发了 OpenAI 的安全告警导致 API Key 被临时冻结——教训深刻。Provider 的label是结果可读性的生命线providers块里的label字段远不止是显示名字那么简单。它直接影响promptfoo viewWeb UI 的列标题、JSON 报告的字段名、以及 CI 评论的可读性。如果你写label: gpt5在结果矩阵里看到的就是一串gpt5毫无上下文而label: GPT-5 Turbo (us-east-1)则清晰表明了模型版本和部署区域。更关键的是当你要做 A/B 测试比如对比gpt-5-turbo和gpt-5-turbo-2024-06-15label必须能区分它们否则结果报告会混在一起无法归因。初始化完成后你会得到一个示例promptfooconfig.yaml。请立即删除它。不要试图在它的基础上修改。原因有二一是示例配置过于简单缺少defaultTest、权重、复杂断言等生产必需元素二是它的结构如prompts用数组而非对象不符合我们后续要构建的可维护性要求。我们从头开始用一个更符合工程实践的结构来定义。3.2 核心配置详解promptfooconfig.yaml的生产级写法下面是我们为邮件生成器构建的完整、可运行的配置。我会逐段拆解其设计意图和实操细节这比官方文档的碎片化示例更有价值# description 是给团队看的不是给机器看的 description: Email Writer Evaluation Suite: Validates tone (casual/formal/urgent), content fidelity, and output quality across models # prompts: 使用对象而非数组便于按功能分组和引用 prompts: # 主 Prompt 模板采用 Jinja2 风格支持多行和注释 main: |- You are a professional email writer for a tech startup. Your task is to draft a concise, well-structured email based *only* on the provided bullet points and tone requirement. ## Instructions - Match the specified tone EXACTLY throughout the entire email. - Do NOT add any preamble, explanation, or meta-commentary (e.g., Heres a draft..., Based on your input...). - Do NOT include any markdown formatting (no **bold**, no lists). - The email must start with a greeting appropriate to the tone (e.g., Hey team for casual, Dear colleagues for formal). - The email must end with a standard closing (e.g., Best, for casual/formal, Urgent action required: for urgent). ## Input Bullet points: {{bullet_points}} Tone requirement: {{tone}} ## Output Draft the email now. Begin with the greeting. # 辅助 Prompt用于 llm-rubric 的 grading确保 grading LLM 有清晰指令 rubric_grader: |- You are an expert email quality grader. Evaluate the following email against the specified rubric. Be strict and objective. Only base your judgment on the email text itself. Email to grade: {{output}} Rubric: {{rubric}} Respond ONLY with PASS if it fully meets the rubric, or FAIL if it fails in any way. Then, on a new line, provide a brief, specific reason (1 sentence max).提示mainPrompt 里的## Instructions部分是我反复迭代的结果。早期版本只写“Match the tone”结果模型总在开头加解释。后来加上Do NOT add any preamble...这条明确禁令not-contains断言的失败率从 40% 降到 5%。这印证了一个经验对 LLM 的指令越具体、越禁止性效果越好。# providers: 明确标注 region 和 version为未来扩展留空间 providers: - id: openai:chat:gpt-5-turbo label: GPT-5 Turbo (us-east-1) config: model: gpt-5-turbo temperature: 0.3 max_tokens: 1024 - id: anthropic:messages:claude-3-sonnet-20240620 label: Claude Sonnet 4.6 (us-east-1) config: model: claude-3-sonnet-20240620 temperature: 0.2 max_tokens: 1024注意id字段必须唯一且规范这是 Promptfoo 内部识别 Provider 的 key。config里的temperature我们设为较低值0.2-0.3是为了降低输出随机性让断言尤其是llm-rubric结果更稳定。高temperature会导致同一 Prompt 每次输出差异巨大让llm-rubric的评分变得不可信。# defaultTest: 所有测试用例的“宪法”定义不可妥协的底线 defaultTest: assert: # 所有输出必须在 30 秒内返回前沿模型推理慢需预留缓冲 - type: latency threshold: 30000 # 所有输出成本必须低于 $0.05防止失控的长输出 - type: cost threshold: 0.05 # 绝对禁止模型拒答或拒绝执行 - type: not-contains value: I cannot - type: not-contains value: Im unable # 绝对禁止输出 markdown 格式影响邮件客户端渲染 - type: not-contains value: ** - type: not-contains value: # tests: 三个核心场景每个都包含“内容语气格式”三维校验 tests: # 场景1Casual Tone - vars: bullet_points: | - Recap of the design review decisions - Next steps: finalize mockups by Thursday - Ask if anyone has questions tone: casual assert: # 内容保真必须包含关键名词 - type: icontains value: mockups weight: 1 # 语气校验用 llm-rubric 精准定义 casual - type: llm-rubric value: | The email uses a truly casual tone: - Opens with Hey team, Hi all, or Whats up - Uses contractions: well, dont, its, thats - Sentences are short (avg. length 15 words) - Contains zero corporate jargon: synergy, leverage, bandwidth, circle back, deep dive, low-hanging fruit, move the needle - Closes with Best, or Cheers, weight: 3 # 格式校验禁止正式开场白 - type: not-contains value: Dear weight: 1 # 长度控制确保可读性 - type: python value: 50 len(output.split()) 180 weight: 0.5 # 场景2Formal Tone - vars: bullet_points: | - Q1 revenue exceeded targets by 12% - New enterprise client onboarded - Hiring plan for Q2 approved tone: formal assert: - type: icontains value: Q1 weight: 1 - type: llm-rubric value: | The email maintains a strictly formal, professional tone: - Opens with Dear colleagues, To the management team, or Subject: - Uses full forms: we will, do not, it is, that is - Sentences are moderately complex (avg. length 18-25 words) - Contains zero slang or contractions - Closes with Sincerely, or Best regards, weight: 3 - type: not-contains value: Hey weight: 1 # 场景3Urgent Tone - vars: bullet_points: | - API migration deadline is Friday at 5pm - Three endpoints still need updating - Downtime window is Saturday 2-6am tone: urgent assert: - type: icontains value: Friday weight: 1 - type: llm-rubric value: | The email conveys genuine urgency: - Opens with URGENT:, ACTION REQUIRED:, or IMMEDIATE ATTENTION NEEDED: - Uses strong action verbs: must, required, deadline, immediately, ASAP - Includes clear, numbered action items (e.g., 1. Update endpoint X by Y time) - Mentions concrete deadlines and time windows - Closes with Act now. or Please confirm receipt. weight: 3 - type: python value: 60 len(output.split()) 200 weight: 0.5实操心得weight的设置不是拍脑袋。我们基于线上事故复盘过去 80% 的用户投诉源于“语气错位”该 casual 却 formal所以llm-rubric权重设为 3而icontains是基础保底权重 1python长度控制是锦上添花权重 0.5。threshold: 0.7默认意味着只要加权得分 0.7 就算通过这给了模型一定的容错空间避免因微小偏差如多一个逗号就全盘失败。3.3 运行与解读promptfoo eval和promptfoo view的隐藏技巧运行promptfoo eval看似简单但几个参数能极大提升效率--max-concurrency 5默认并发是 1意味着 6 个测试组合2 models × 3 cases是串行的耗时很长。设为 5 后基本是并行跑满耗时从 3 分钟降到 45 秒。注意并发太高可能触发模型 API 的 rate limit需根据你的配额调整。--cache默认开启Promptfoo 会将每次 API 请求的输入/输出缓存到~/.promptfoo/cacheTTL 14 天。这意味着你改了断言但没改 Prompt重跑时contains等断言会秒出结果只有llm-rubric会重新调用 grading LLM。这是加速迭代的关键。--no-cache当你想测试模型行为的真实变化比如刚升级了模型版本必须加这个参数强制刷新所有缓存。promptfoo view启动的 Web UI 是结果分析的核心。它的设计非常工程师友好矩阵视图Matrix ViewProvider 为列Test Case 为行每个单元格是该组合下所有断言的 PASS/FAIL 状态。一眼就能看出“Claude Sonnet 在 Urgent 场景下python断言失败”而其他全绿。点击单元格会弹出 Modal展示完整的 Prompt 输入、Model 输出、每条断言的判定详情包括llm-rubric的 grading LLM 的 response 和 reason。详细日志Logs Tab这里能看到所有 HTTP 请求的原始 cURL 命令、headers、response body。当某个断言失败时直接复制 cURL在终端里重放能快速确认是模型问题还是断言逻辑问题。我们曾用这个功能发现是 Anthropic 的 API 返回了429 Too Many Requests但 Promptfoo 默认重试策略没生效于是我们在config里加了retry: { max_attempts: 3 }。JSON 导出Export Button点击导出results.json这是接入 CI 的关键。文件结构清晰results.results是每个测试单元的明细results.stats是汇总total, pass, fail, error。CI 脚本可以轻松解析它来决定是否exit 1。4. 进阶实战将评估套件无缝集成到 GitHub Actions4.1 为什么 CI 集成是 Prompt 工程成熟的分水岭本地运行promptfoo eval只是第一步。真正的价值在于让它成为 PR 流程的强制关卡。想象一下这个场景一个 junior engineer 修改了 casual tone 的 Prompt删掉了well的例子想让语气更“简洁”。他本地测试了 3 个输入看着没问题就提了 PR。如果没有 CI 评估这段代码会合入主干而llm-rubric断言会立刻发现新 Prompt 生成的邮件里contractions出现频率从 4.2 次/百字降到了 1.1 次/百字llm-rubric评分从 PASS 变成 FAILPR 被自动拒绝。这就是质量左移Shift-Left的力量——把质量检查从“上线后用户反馈”提前到“代码合并前”。4.2 GitHub Action 配置的避坑指南官方提供的promptfoo/promptfoo-actionv1很好用但有几个极易踩的坑必须提前规避name: Prompt Evaluation on: pull_request: # 关键1paths 过滤必须精准 # 错误写法paths: [**/*.yaml] —— 会因任何 YAML 文件变更就触发浪费资源 # 正确写法只监控 prompts/ 目录下的变更这是我们的约定 paths: - prompts/** - promptfooconfig.yaml jobs: evaluate: runs-on: ubuntu-latest permissions: contents: read # 关键2pull-requests: write 是必须的否则无法发评论 pull-requests: write steps: - uses: actions/checkoutv4 # 关键3必须 fetch-depth: 0否则 git diff 会失败 with: fetch-depth: 0 - name: Set up promptfoo cache uses: actions/cachev4 with: # 关键4cache path 必须和本地一致且包含 .promptfoo-cache path: | ~/.promptfoo/cache .promptfoo-cache # key 的 hash 必须包含 prompts/** 和 config确保缓存精准 key: ${{ runner.os }}-promptfoo-${{ hashFiles(prompts/**, promptfooconfig.yaml) }} restore-keys: | ${{ runner.os }}-promptfoo-${{ hashFiles(prompts/**, promptfooconfig.yaml) }}- ${{ runner.os }}-promptfoo- - name: Run promptfoo evaluation uses: promptfoo/promptfoo-actionv1 with: # 关键5openai-api-key 必须用 secrets绝不能写死 openai-api-key: ${{ secrets.OPENAI_API_KEY }} # 如果用了 Anthropic同样处理 anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} github-token: ${{ secrets.GITHUB_TOKEN }} # config 路径必须是相对路径且和 repo 里一致 config: promptfooconfig.yaml # cache-path 必须和上面 cache step 的 path 一致 cache-path: .promptfoo-cache注意事项Secrets 配置在 GitHub Repo 的Settings Secrets and variables Actions里添加OPENAI_API_KEY和ANTHROPIC_API_KEY。Action 会自动注入无需在 workflow 里 echo。Cache 的双保险actions/cachev4缓存的是 Promptfoo 的本地磁盘缓存~/.promptfoo/cache而promptfoo-action的cache-path参数缓存的是本次运行的中间结果.promptfoo-cache。两者结合才能实现最快的冷启动。Git Diff 的陷阱actions/checkoutv4默认只 fetch 当前 commit但promptfoo-action需要比较 PR 的 base 和 head 分支来生成 diff 报告。fetch-depth: 0确保了完整的 git history 可用。4.3 CI 失败后的诊断与修复工作流当 CI 因评估失败而红了别慌。一个高效的诊断流程是看 PR 评论Action 会自动在 PR 下发一条评论包含失败摘要和promptfoo view的临时链接有效期 1 小时。点击链接进入 Web UI定位到失败的单元格。查详细日志在 Web UI 的 Logs Tab找到失败测试的完整请求日志。重点关注llm-rubric的 grading LLM response。如果它说FAIL: Missing contractions. Found 0, expected at least 2.那就明确了方向。本地复现在本地 checkout 该 PR 分支运行promptfoo eval --no-cache。--no-cache确保你看到的是和 CI 一样的“新鲜”结果排除缓存干扰。针对性修复根据日志修改 Prompt 或断言。比如如果llm-rubric报告 contractions 不足就在 Prompt 的## Instructions里把Use contractions like well and dont改成You MUST use at least two contractions in the email body, e.g., well, dont, its, thats.。然后再次promptfoo eval验证。Push 自动重试修复后git pushCI 会自动重新运行无需任何手动操作。这个流程把“修复一个 Prompt Bug”的时间从过去的几小时反复猜测、手动测试、沟通确认压缩到 15 分钟以内。我们团队的平均 MTTRMean Time To Resolve从 4.2 小时降到了 18 分钟。5. 常见问题与排查技巧实录那些文档里不会写的血泪经验5.1 “llm-rubric 总是 FAIL但我觉得输出没问题”—— Rubric 编写十大反模式llm-rubric是最强大也最容易误用的断言。以下是我在 30 个项目中总结的、导致它失效的十大反模式附带修正方案反模式具体表现为什么失效修正方案实测效果模糊性The email sounds professional.Grading LLM 自由发挥标准不一改为Opens with Dear colleagues, uses full forms (we will), avg. sentence length 20-25 words, closes with Sincerely,评分一致性从 58% → 92%矛盾性Use contractions AND avoid slang.Contractions (well) 是 slang 的子集逻辑冲突明确区分Use standard contractions (well, dont) but avoid informal slang (gonna, wanna, cool).消除 100% 的误判不可观测性The email feels trustworthy.“Feel” 无法被 LLM 客观衡量改为可观测指标Includes at least one specific data point (e.g., Q1 revenue: $1.2M) and cites a source (e.g., per the Q1 report)从主观感受变为客观事实核查过度复杂Rubric 超过 5 行包含嵌套条件Grading LLM 无法 parse常忽略部分要求拆分为多个独立llm-rubric断言或用python实现复杂逻辑单次 grading 成功率从 65% → 98%忽略上下文Rubric 未说明tone是唯一变量Grading LLM 会基于自身知识判断“是否专业”而非对比tone在 Rubric 开头加Evaluate SOLELY based on the tone requirement specified in the input. Ignore all other aspects of quality.专注度提升减少无关 FAIL大小写敏感Contains Friday但模型输出fridayllm-rubric默认区分大小写改为Contains Friday or friday or FRIDAY或用icontains配合llm-rubric消除 90% 的大小写误判未定义边界Sentences should be short.“Short” 是主观词定义为All sentences have fewer than 15 words. Count words using whitespace separation.可量化可验证依赖外部知识Mentions the correct CEO name.Grading LLM 可能记错或编造改为Contains the exact string Sarah Johnson as provided in the input context.避免事实性 hallucination未处理歧义Uses active voice.Grading LLM 对语法理解不一提供正反例PASS examples: We updated the API. FAIL examples: The API was updated by us.减少 75% 的语法争议未考虑模型 biasAvoid gendered language.Grading LLM 自身有 bias可能误标改为具体检测Contains zero instances of he/him/his or she/her/hers when referring to roles (e.g., the developer, the manager). Use they/them or rephrase.从模糊