OpenHuman Critic 对抗式代码评审 Agent 全解析:从 prompt 设计到只读沙箱实现
【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman
导读
本文以 OpenHuman 内置Critic(对抗式 QA 评审)子代理为研究对象,完整解读其系统提示词(prompt)、能力边界、评审清单、行为规则,并结合仓库源码剖析其agent.toml配置语义、工具调用链(read_diff/run_linter/run_tests/file_read)、系统提示词装配过程(prompt.rs)以及 Orchestrator 的委托路由机制。读完本文,你将掌握 OpenHuman 中"只读、对抗式、结论回流"的代码评审子代理是如何设计、配置与工作的,并可据此理解如何阅读或调优同类内置 Agent 的 prompt 与工具权限。
一、Critic 在 OpenHuman 中的地位与定位
OpenHuman 的 Agent 注册表位于 src/openhuman/agent/registry,其中 agents 目录 内置了 30 余个专职子代理,例如 planner、researcher、code_executor、vision_agent、crypto_agent 等。Critic 是其中之一,其完整定义由同一目录下的三个文件组成:
- agent.toml —— 结构化元数据(ID、温度、迭代上限、沙箱模式、工具白名单、委托名称等);
- prompt.md —— 本文的主角,即 Critic 的"原型提示词"(archetype body);
- prompt.rs —— 将原型提示词与运行时上下文拼接为最终系统提示词的构建器;
- prompt_tests.rs —— 对应的单元测试。
从 loader.rs 的加载流程看,每个内置 Agent 都遵循"agent.toml+prompt.md+prompt.rs"三件套布局:load_builtins遍历内置列表,将agent.toml解析为AgentDefinition,再把未显式设置的system_prompt替换为PromptSource::Inline(prompt.md 内容),并标记DefinitionSource::Builtin。
Critic 的角色定义在 prompt.md 第一行便直截了当:"你是 Critic 代理。你的工作是在问题进入生产环境之前发现它们。"它是一个对抗式(Adversarial)QA 评审者——不负责写代码,只负责找毛病、按优先级上报,并将结论交还给 Orchestrator 决策。
二、Capabilities:Critic 的四种核心能力
prompt.md 中列出的能力与agent.toml中[tools] named工具白名单一一对应,构成了 Critic 的完整工具面:
| 能力(prompt.md) | 对应工具 | 实现位置 |
|---|---|---|
| 读取 git diff 评审变更 | read_diff | read_diff.rs |
| 运行 linter(clippy / eslint)并解读结果 | run_linter | run_linter.rs |
| 运行测试套件并验证正确性 | run_tests | run_tests.rs |
| 读取项目文件获取上下文 | file_read | filesystem/mod.rs 中的文件读取工具 |
四个工具全部位于tools/impl/filesystem目录,其注释均明确标注"为 Critic 原型(Critic archetype)服务"。值得注意的是,这四个工具虽然运行外部进程(git、cargo clippy、eslint、cargo test、vitest),但在权限模型上都被声明为PermissionLevel::ReadOnly——这正是"评审者不写代码"原则在工具层的落地。
2.1 read_diff:结构化 diff 读取
read_diff.rs 内部实际执行git diff --stat -p,支持三个参数:
base(string):diff 的基准引用,如main、HEAD~3,缺省时对比未暂存(unstaged)改动;staged(boolean):仅显示已暂存改动(等价--cached),默认false;path_filter(string):将 diff 限制到指定路径或 glob。
实现细节上,若当前运行上下文携带 TinyAgents 的 workspace 描述符,则以描述符中的workspace.root作为工作目录,否则回退到工具构造时传入的workspace_dir;当 diff 为空时返回"No changes found.",命令失败时返回 stderr 错误信息。这意味着 Critic 既可以评审"正在编辑的未提交改动",也可以评审"某个分支或历史提交引入的变更"。
2.2 run_linter:双语言 linter 门禁
run_linter.rs 支持linter参数,枚举值为clippy(Rust)、eslint(TypeScript/JavaScript)与auto(根据项目文件自动探测,默认值),并可通过path参数限定检查范围。这与仓库实际使用的工具链完全吻合——Rust 侧使用cargo clippy(见 Cargo.toml 及 CI 脚本 scripts/ci/rust-coverage-changed.sh),前端使用 ESLint(见 eslint.config.js)。返回值包含警告与错误,供 Critic 结合 prompt 中的"Be specific"规则引用具体行号。
2.3 run_tests:测试套件验证
run_tests.rs 支持runner参数,枚举值为cargo_test(Rust)、vitest(TypeScript/JavaScript)与auto(默认自动探测),返回通过/失败及输出。这与仓库测试体系一致:Rust 侧遍布tests/下的*_e2e.rs集成测试与单元测试,前端则有 app/vitest.config.ts 支撑的 Vitest 测试(如 AppRoutes.guards.test.tsx)。
2.4 file_read:上下文只读探查
Critic 通过file_read读取项目文件以获得评审上下文,例如被评审文件的周边代码、SOUL.md 项目原则等。该能力与其余三个工具一样均为只读,配合sandbox_mode = "read_only",Critic 从工具层到沙箱层都被双重约束为"只能读、不能改"。
三、Review Checklist:五维评审清单
prompt.md 中 Critic 的评审清单按优先级从高到低分为五个维度,这既是 LLM 的推理框架,也是可被测试与运维复用的"评审规范":
- Security(安全)—— SQL 注入、XSS、命令注入、硬编码密钥、OWASP Top 10。OpenHuman 的 Rust 核心对安全尤为重视,仓库中 src/openhuman/security 目录(158 个 Rust 文件)与 SECURITY.md、docs/features/privacy-and-security.md 共同构成安全基线,Critic 的评审可对照这些文档进行合规性核查。
- Correctness(正确性)—— 边界条件、off-by-one 错误、null/None 处理、竞态条件。OpenHuman 核心是异步 Rust(大量
async/tokio),并发与竞态是真实风险点,例如 read_diff.rs 中tokio::process::Command的异步执行本身即是一个并发正确性的观察样本。 - Style(风格)—— 命名规范、代码组织、与既有模式的一致性。
- Tests(测试)—— 新路径是否被覆盖?既有测试是否仍然通过?这与
run_tests工具直接呼应,也与仓库 TEST-COVERAGE-MATRIX.md 的覆盖率治理目标一致。 - SOUL.md compliance(项目原则遵从)—— 代码是否与项目核心原则对齐。SOUL.md 是 OpenHuman 的产品人格与原则文件,位于 app/src/SOUL.md,同时存在 根目录 SOUL 相关内容 所描述的 IdentitySection 注入机制中(Orchestrator 注释说明 SOUL.md 作为产品人格由所有选择加入的 Agent 共享)。
该清单的优先级顺序(安全 > 正确性 > 风格)本身就是一种可执行的决策树,确保 LLM 在上下文预算有限时优先上报最严重的问题。
四、Rules:四条行为铁律
prompt.md 中的 Rules 部分是 Critic 输出质量的直接约束:
- Be specific(必须具体)—— 示例对比:"第 42 行:SQL 字符串拼接可注入"优于"代码可能有安全问题"。这要求 Critic 的结论必须落到文件、行号与具体模式上,与
read_diff返回带行号 hunk 的能力闭环。 - Prioritise(按优先级上报)—— 先报关键问题(安全 > 正确性 > 风格),与五维清单的顺序一致。
- Be constructive(建设性)—— 不仅要指出问题,还要给出修复建议。这与"对抗式但非敌对"的定位一致:Critic 的目标是让代码变好,而不是刷存在感。
- Read-only(只读)—— 只评审、永不修改代码;发现结果上报给 Orchestrator。这是 Critic 与 code_executor(写代码的执行代理)之间的根本分工边界。
五、agent.toml:配置语义逐项拆解
Critic 的 agent.toml 是该子代理的行为配置全集,逐项解读如下:
| 配置项 | 值 | 含义 |
|---|---|---|
id | "critic" | 内置 Agent 唯一标识,供 loader、Orchestrator 子代理白名单引用 |
display_name | "Critic" | 展示名 |
delegate_name | "review_code" | 由 Orchestrator 委托时合成的delegate_*工具名 |
when_to_use | "Adversarial reviewer — reviews diffs and code against project rules..." | 该文本会被用作委托工具的 LLM 可见描述,指导 Orchestrator 路由 |
temperature | 0.4 | 较低的采样温度,偏向稳定、可复现的评审输出 |
max_iterations | 5 | 单次委托的最大工具循环迭代次数,防止评审无限发散 |
max_result_chars | 8000 | 评审结论回流 Orchestrator 的最大字符数(约 2000 token),与 planner/researcher 等其他子代理上限一致,防止超长 diff 产生的无界评论污染 Orchestrator 上下文(注释引用 issue #4099) |
sandbox_mode | "read_only" | 沙箱只读模式,运行时层面禁止写操作 |
omit_identity | true | 跳过 IdentitySection(不注入 SOUL.md / ROLE.md 身份段) |
omit_memory_context | true | 不注入记忆上下文,保证评审基于当前工作区事实而非历史记忆 |
omit_safety_preamble | true | 跳过安全前言模板,提示词保持精简 |
[model] hint | "agentic" | 提示运行时选用具备工具调用(agentic)能力的模型 |
[tools] named | ["read_diff", "run_linter", "run_tests", "file_read"] | 工具白名单,仅暴露评审必需的四项能力 |
其中三个omit_*标志的选择非常讲究:Critic 需要的是"纯粹基于当前 diff 与代码事实的对抗式评审",因此刻意去掉身份注入、记忆注入与安全前言,避免 LLM 被历史偏好或冗长前言带偏;max_result_chars则从架构层面防止评审结论失控回流。
六、系统提示词装配:prompt.rs 的拼接机制
Critic 的最终系统提示词并非直接使用prompt.md,而是由 prompt.rs 在运行时动态装配:
- 通过
include_str!("prompt.md")将原型提示词编译进二进制,赋值给常量ARCHETYPE; - 依次追加四个动态段:
render_user_files(ctx)—— 用户文件注入段(对应 PROFILE.md / MEMORY.md,见 render_helpers_part_01.rs),为空时跳过;render_tools(ctx)—— 按当前工具白名单渲染的## Tools目录(render_helpers_part_01.rs);render_workspace(ctx)—— 工作目录与文件列表边界段(render_helpers_part_01.rs);- 每个段之间以空行分隔,最终返回完整的系统提示词。
prompt_tests.rs 中的build_returns_nonempty_body测试构造了最小化的PromptContext(空工具集、空可见工具名、PFormat 工具调用格式),断言build()返回非空正文——这验证了即使在没有工具、没有记忆等运行时上下文的极端情况下,Critic 的系统提示词骨架依然完整可生成。
七、委托链路:Orchestrator 如何调用 Critic
Critic 不会自主启动,它由 Orchestrator(Master Agent)按需委托。相关证据链:
- orchestrator/agent.toml 的
[subagents] allowlist将"critic"列入白名单。根据文件注释,collect_orchestrator_tools会在 Agent 构建期读取该白名单,为每个条目合成一个delegate_*委托工具:工具名取自目标的delegate_name(此处即review_code),工具描述取自目标的when_to_use。因此 Orchestrator 的 LLM 在函数调用层就能看到review_code这个一等公民工具。 - orchestrator/prompt.md 的委托路由表中明确写着 "Code review →
review_code",并在规则中说明:需要独立评审(independent review)时使用review_code或相关 worker;"评审 / 校验 / 批准 / 校对 X然后再定稿"这类结果门控工作必须同步执行——通过阻塞式delegate_*或spawn_async_subagent加blocking: true保持回合打开,直到子代理返回,从而杜绝"定稿后评审才完成"的空转。
委托时的结构化交接信封(prompt/objective/evidence/constraints/must_not_assume/expected_output/citation_requirement)由 Orchestrator 填充,Critic 作为子代理没有会话记忆,完全依赖信封中的任务描述工作——这与omit_memory_context = true的配置互为表里。
八、从源码看 Critic 的三大设计原则
综合 prompt.md、agent.toml 与工具实现,可以归纳 Critic 子代理的三大设计原则:
- 只读即安全边界:
sandbox_mode = "read_only"+ 四个PermissionLevel::ReadOnly工具 + prompt 中的 "Read-only" 规则,三层叠加确保评审行为零副作用。这与 src/openhuman/sandbox 模块的沙箱治理一脉相承。 - 结论必须可回流、有界回流:
max_result_chars = 8000约束评审结论体量,delegate_name = "review_code"保证结论沿委托链流回 Orchestrator 后被二次蒸馏(Orchestrator prompt 规定"每个委托回复都必须蒸馏"),避免原始工作笔记进入最终答复。 - 对抗性来自约束而非情绪:Critic 的"对抗"体现在评审清单(安全 > 正确性 > 风格)、具体行号引用与建设性修复建议上,是通过提示词工程构造的批判性推理框架,而不是无差别挑刺。
九、如何观察与验证 Critic 的行为
如果你想在自己的 OpenHuman 实例中观察 Critic 的实际运行:
- 触发方式:在对话中明确要求"review_code"语义的任务,例如"请评审我当前的改动"、"在定稿前先让评审代理检查这段代码",Orchestrator 会按路由表委托
review_code; - 代码级验证:阅读 prompt_tests.rs 与 loader_tests 可了解加载与装配的测试覆盖;
- 工具行为验证:三个工具的独立测试位于 read_diff_tests.rs、run_linter_tests.rs 以及 run_tests 对应的测试文件;
- 调优入口:如需调整 Critic 的评审强度或上下文行为,可修改 prompt.md(评审清单与规则)与 agent.toml(温度、迭代上限、结果回流上限、工具白名单)。
十、结语
Critic 是 OpenHuman"多智能体协作、专职分工"架构的一个典型样本:一份 25 行的原型提示词定义了角色、能力、评审清单与行为规则,一份 21 行的agent.toml从沙箱、温度、迭代、结果回流四个维度锁死行为边界,四只只读工具为其提供信息获取通道,而 Orchestrator 的委托机制与结构化交接信封负责把它安全地编排进整个工作流。理解 Critic,也就理解了 OpenHuman 中所有内置 Agent 的通用构建范式——prompt.md定"质"、agent.toml定"界"、工具白名单定"权"、委托链定"流"。
【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考