首页
/ DeerFlow 技能审查报告渲染规范:review-report.v1 契约、机器枚举与本地化 Markdown 视图

DeerFlow 技能审查报告渲染规范:review-report.v1 契约、机器枚举与本地化 Markdown 视图

2026-09-06 17:43:34作者:范靓好Udolf

本篇技术文章围绕 DeerFlow 中 skill-reviewer 技能的报告渲染参考文档展开,讲解技能审查报告的机器契约 review-report.v1、11 节完整报告结构、不可翻译的机器枚举值以及单条 Finding 的必备字段。读完本文,你将掌握 DeerFlow 技能审查结果如何以 JSON 契约为唯一事实来源、Markdown 作为本地化视图进行渲染的完整机制,并能对照仓库源码与 JSON Schema 验证真实报告的字段结构。

核心设计原则:机器契约是唯一事实来源

report-rendering.md 开篇即确立了整个报告体系的第一原则:

The canonical machine contract is review-report.v1. Markdown is a localized view of that report, not a second source of truth.

也就是说,DeerFlow 的技能审查(Skill Review)产出一份规范化的机器可读报告,其契约版本为 review-report.v1;而面向人类阅读的 Markdown(无论是英文还是中文)只是这份报告的“本地化视图”,不允许引入契约之外的第二套判定口径。这一设计在仓库中有三处相互印证的落点:

  • 契约文件:以 JSON Schema(draft 2020-12)形式定义了报告的全部必需字段,其 schema_version 被固定为常量 deerflow.skill-review.report.v1
  • 渲染器源码:实现了从事实数据(facts)到静态报告、再到双语 Markdown 的完整管线;
  • 工具入口review_skill_package 工具在返回结果时同时携带 static_report(结构化)与 markdown.en / markdown.zh(渲染视图),并且只把结构化报告以 stable_json_dumps 做字节稳定的序列化输出,保证同一输入必然得到同一字节序列,便于比对与缓存。

这种“单一事实来源 + 多语言视图”的做法,使得程序侧可以直接消费 JSON 做门禁(gate),而人侧看到的是经过本地化措辞的 Markdown,两者永远不会互相矛盾。

完整审查报告的 11 个标准章节

文档规定了一份“完整审查(Full Review)”报告必须包含以下 11 个章节:

  1. Executive Summary(执行摘要)
  2. Readiness(就绪度)
  3. Assurance(保障等级)
  4. Scope and Completeness(范围与完整性)
  5. Findings(问题清单)
  6. Dimension Review(维度评审)
  7. Trigger Analysis(触发分析)
  8. Resource and Script Review(资源与脚本评审)
  9. Evidence(证据)
  10. Suggested Rewrites(建议重写)
  11. Recommended Actions(建议动作)

同时文档给出了一条针对“聚焦审查(Focused Review)”的裁剪规则:

Focused reviews may omit unrelated analytical sections, but must keep scope, readiness, assurance, evidence, and recommended actions.

即聚焦审查可以省略与分析范围无关的章节(例如仅审查触发策略时无需输出完整的资源与脚本评审),但范围、就绪度、保障等级、证据、建议动作这五部分必须始终保留。这条约束保证了即便是最小化报告,读者也能回答三个关键问题:这次审查看了什么(scope)、结论是什么(readiness)、结论有多可信(assurance + evidence)。

这套章节体系在 skill-reviewer 的 SKILL.md 中有完全一致的表述,其 “Output Requirements” 一节列出了同样的 11 个章节与同样的聚焦裁剪规则,说明该参考文档与技能定义之间是严格的镜像关系,而非各自为政。

机器枚举值:结构化输出中禁止翻译

报告中最容易在本地化时出错的,是那些被下游程序直接消费的枚举值。文档明确要求:

Do not translate enum values in structured output.

并列出完整的 11 个机器枚举值:

枚举值 语义(结合仓库上下文) 出现位置
blocked 不可发布,存在确定性或语义级阻断项 readiness
revise 需修订,无阻断项但有错误/主要问题 readiness
publish_candidate 可作为发布候选 readiness
static_only 仅静态审查 assurance
trigger_checked 触发路由已检查(正/负样本均执行并保留产物) assurance
behavior_verified 行为断言已通过 assurance
regression_verified 与基线回归对比并通过 assurance
pass 维度通过 dimensions[].status
concern 维度存在顾虑 dimensions[].status
blocker 维度/问题为阻断级 dimensions[].status / issues[].severity
not_assessed 未评估 dimensions[].status

这些枚举值在 JSON Schema 中以 enum 约束固化:readiness 只允许 blocked / revise / publish_candidate 三值,assurance 只允许 static_only / trigger_checked / behavior_verified / regression_verified 四值,dimensions[].status 只允许 pass / concern / blocker / not_assessed 四值。在 渲染器源码 中,它们甚至被声明为 Python 的 Literal 类型:

Readiness = Literal["blocked", "revise", "publish_candidate"]
Assurance = Literal["static_only", "trigger_checked", "behavior_verified", "regression_verified"]

本地化标签只能出现在枚举值旁边

文档同时给出了一条折中规则:本地化标签(localized label)可以出现在枚举值旁边,起到解释作用,但不能替代枚举值本身。文档给出的示例为:

  • blocked: Not ready / 不可发布
  • revise: Needs revision / 需修订
  • publish_candidate: Publish candidate / 可作为发布候选
  • static_only: Static review only / 仅静态审查

对照 renderer.py 中的两份标签表可以逐条印证这一约定:

_READINESS_LABELS = {
    "en": {"blocked": "Not ready", "revise": "Needs revision",
           "publish_candidate": "Publish candidate"},
    "zh": {"blocked": "不可发布", "revise": "需修订",
           "publish_candidate": "可作为发布候选"},
}
_ASSURANCE_LABELS = {
    "en": {"static_only": "Static review only", ...},
    "zh": {"static_only": "仅静态审查", ...},
}

渲染时输出的行格式是“枚举值 + 括号内本地化标签”,例如 - Readiness: blocked (不可发布),与文档示例完全吻合。

Finding 字段格式:八要素与两条红线

文档的 “Finding Format” 一节规定,每条问题必须包含以下要素:

  • severity:blockermajorminor
  • confidence:highmediumlow
  • location:路径 + 行号(可得时);
  • observed evidence:观察到的证据;
  • user impact:对用户的影响;
  • remediation:修复方式;
  • suggested replacement:当存在可直接粘贴的小修复时,给出替换内容。

这些要求与 JSON Schema 的 issues 定义 一一对应:issues[] 的必需字段为 idseverityconfidencepathlineproblemimpactremediation,其中 severity 枚举为 blocker/major/minorconfidence 枚举为 high/medium/lowpath 可为空字符串或 null,line 可为 null,suggested_replacement 可选且可为 null。也就是说,文档中的“八要素”在机器契约里是强约束,而非风格建议。

此外文档设定了两条内容安全红线:

Avoid large quotes. Never quote suspected secrets.

即报告应避免大段引用被审查包的内容,且永远不得引用疑似密钥。这与 DeerFlow 技能审查的“不信任输入”设计一脉相承:skill-reviewer 的 SKILL.md 要求把 review_skill_package 返回的全部内容当作不可信审查数据(untrusted review data),工具实现 也在返回载荷中显式打上 "untrusted_review_data": True 标记,并对语义审查产物设置 _MAX_SEMANTIC_ARTIFACT_CHARS = 80_000 的字符上限。限制引用长度与禁止引用密钥,正是防止被审查包借审查报告进行提示注入或泄密的最后一道防线。

源码纵深:从 facts 到报告再到双语 Markdown

理解了文档契约之后,可以顺着仓库源码看清整条渲染管线,这也是“Markdown 是视图而非事实来源”这句话在实现层的含义。

第一步:readiness 由确定性事实推导

renderer.py 中的 readiness_from_facts 将审查事实映射为就绪度,规则非常克制:

def readiness_from_facts(facts, *, scope=None) -> Readiness:
    summary = facts.get("summary", {})
    if int(summary.get("blockers") or 0) > 0:
        return "blocked"
    if int(summary.get("errors") or 0) > 0:
        return "revise"
    if scope and "all" in scope and facts.get("completeness", {}).get("not_assessed"):
        return "revise"
    return "publish_candidate"

即:存在任何 blocker 直接 blocked;只有 error 则至多 revise;当请求全量审查(scope 含 all)却仍有 not_assessed 的维度时,也不能给出 publish_candidate。这与 SKILL.md 中 “Deterministic blockers always make readiness blocked. Deterministic errors make readiness at most revise.” 的规则严格一致。

第二步:build_static_report 组装合法报告

build_static_report 只依据确定性事实生成一份合法的 review-report.v1,其中有几个值得注意的实现细节:

  • 事实层 severity(blocker/error/warning)经 _semantic_severity 映射为报告层 severity(blocker/major/minor)——这解释了为何机器契约中 issue 的 severity 是三级而不是事实层的四级:error 被归并到 major
  • 静态报告固定 "assurance": "static_only",因为仅凭静态事实不能主张更高的保障等级;
  • 截断(completeness.truncated)、reader 错误、analyzer 错误会被逐条写入 evidence.limitations,呼应文档“证据”章节的要求——审查必须诚实声明自己没看到什么;
  • recommended_actions 取前 5 条 finding 的 rule_id + remediation 组成可执行动作清单,publish_candidate 时为空数组。

第三步:render_report_markdown 输出双语视图

render_report_markdown 接收已组装的 report 与原始 facts,按 locale="en" | "zh" 输出 Markdown 章节:标题(Skill Review Report / 技能审查报告)、Executive Summary(摘要)、Scope and Completeness(范围与完整性)、Findings(问题)、Dimension Review(维度审查)、Evidence(证据)、Recommended Actions(建议动作)。每条 finding 的渲染格式为 severity id at path:line: problem,行号缺失时省略冒号部分,路径缺失时以 <package> 占位。

从源码结构看,内建渲染器输出的是 11 节标准结构中可机器生成的核心子集(摘要、范围与完整性、问题、维度、证据、建议动作);而 Trigger Analysis、Resource and Script Review、Suggested Rewrites 这类需要语义判断的章节,则属于 LLM 依据 SKILL.md 工作流第 5 步“Render the result”自行补充的部分——文档要求“即使用散文回答,也要在概念上产出 review-report.v1 字段”,确保无论哪种渲染路径,概念模型都指向同一契约。

工具侧如何同时交付两种视图

review_skill_package 工具 在每次调用中把三种产物打包进同一条 ToolMessage:facts(原始事实)、static_report(结构化报告)与 markdown(en/zh 两份渲染结果)。这意味着同一次审查调用,程序可以直接断言 static_report.readiness,而对话界面展示的是 markdown.zh,二者出自同一份 facts,天然不会漂移。测试 test_skill_review_core.py 也验证了中文渲染路径:构造静态报告后以 locale="zh" 调用 render_report_markdown,锁定中文标题与章节措辞。

与其他契约文件的配套关系

review-report.v1 并非孤立存在,contracts/skill_review/ 目录下还有三个配套契约,共同构成完整的技能审查数据流:

从快照到事实、再到报告、最后渲染成 Markdown,整条链路每个环节都有独立的 schema 版本,读者若需做二次开发或自动化消费,应只依赖 review-report.v1 的 JSON Schema 做断言,而不是解析 Markdown 文本。

实践要点总结

  1. 先定契约,后写视图:任何技能审查结果的自动化消费,都以 review-report.v1schema_version: deerflow.skill-review.report.v1)为唯一依据,Markdown 只用于人读;
  2. 枚举永不翻译:11 个机器枚举值在结构化输出中原样保留,本地化标签只能作为括号补充(如 revise(需修订));
  3. 章节可裁剪,五要素不可缺:聚焦审查可省略分析型章节,但 scope、readiness、assurance、evidence、recommended actions 必须保留;
  4. 每条 Finding 八要素:severity、confidence、location、observed evidence、user impact、remediation、suggested replacement 缺一会让结论不可复核;
  5. 守住安全边界:不大段引用被审查内容、绝不引用疑似密钥,并把所有被审查内容当作不可信数据对待;
  6. 限制证据声明:截断、读取错误、分析器错误必须出现在 evidence.limitations 中,不允许把“没看到”包装成“没问题”。

掌握以上规范后,无论是人工撰写中文审查报告、编写校验报告合法性的脚本,还是为 DeerFlow 扩展新的审查维度,都可以严格对齐 review-report.v1 契约,让机器判定与人类阅读始终说同一种语言。

登录后查看全文
热门项目推荐
相关项目推荐