章节导航 第 20 章 / 共 21 章
完整 Case Study:从工具定义到 Agent 执行的端到端流程
用一个具体的「智能代码审查」场景,走完 Tool 定义、MCP 接入、Agent 配置、Task 编排、LLM 消息流的完整生命周期
为什么需要这个 Case Study
前面章节分别讲了 Registry、Runtime、Agent、Tool、Protocol。但把它们串起来的是一个真实的执行流——从 YAML 配置到 LLM 拿到 tool schema,再到工具在 Runtime 里执行,最后返回给 Task 评分。
本章用一个完整的可运行案例走完这条链路。
场景设定:智能代码审查 Task
目标:给定一个 PR 的 diff,让 Agent 自动审查代码质量,输出审查意见。
需要的工具:
get_pr_diff— 获取 PR 的代码变更(自定义 Python 工具)run_linter— 在沙箱里跑 pylint/flake8(自定义 Python 工具)git_analyze— 通过 MCP Server 做 deeper 的 git 分析(MCP 工具)
Agent:main_agent(通用 tool-calling)
评分:用 LLM Judge 评估审查意见的质量
第一层:Tool 定义
自定义工具(Python,通过 @tool_registry 注册)
# extensions/tools/code_review_tools.py
from nexus.core import tool_registry
from nexus.core.tool_protocol import ToolContext, ToolResponse, ChatCompletionContentPartParam
@tool_registry.register(
"get_pr_diff",
meta={
"description": "获取指定 PR 的 diff 内容",
"tool_type": "runtime", # 在沙箱里执行
"parameters": {
"pr_number": {"type": "integer", "description": "PR 编号"},
"repo_path": {"type": "string", "description": "仓库路径,默认 /workspace/repo"}
}
}
)
async def get_pr_diff(params: dict, context: ToolContext) -> ToolResponse:
pr_number = params["pr_number"]
repo_path = params.get("repo_path", "/workspace/repo")
# 在 Runtime 沙箱里执行 git 命令
result = await context.runtime.run_command(
f"cd {repo_path} && git diff origin/main...HEAD --unified=3",
timeout=30
)
return ToolResponse(
output=[{"type": "text", "text": result.stdout}],
meta={"exit_code": result.exit_code}
)
@tool_registry.register(
"run_linter",
meta={
"description": "在代码上运行 linter(pylint/flake8)",
"tool_type": "runtime",
"parameters": {
"file_path": {"type": "string", "description": "要检查的文件路径"},
"linter": {"type": "string", "enum": ["pylint", "flake8"], "default": "flake8"}
}
}
)
async def run_linter(params: dict, context: ToolContext) -> ToolResponse:
file_path = params["file_path"]
linter = params.get("linter", "flake8")
result = await context.runtime.run_command(
f"{linter} {file_path}",
timeout=60
)
return ToolResponse(
output=[{"type": "text", "text": result.stdout or "No issues found."}],
meta={"exit_code": result.exit_code}
)
关键点:
tool_type: "runtime"→ 这个工具在 Runtime 沙箱里执行(不是 Host)context.runtime→ 工具通过 ToolContext 拿到 Runtime 引用,可以调用run_command、upload_file等- 返回
ToolResponse→ 统一格式,Agent 拿到的是output里的 content parts
MCP 工具(通过 MCP Server 接入)
假设我们有一个 git 分析的 MCP Server,提供 git_analyze 工具。
在 YAML 里配置 MCP Server:
# code_review.yaml
tools:
- "get_pr_diff" # 自定义 Python 工具
- "run_linter" # 自定义 Python 工具
- "mcp::git_analysis::git_analyze" # MCP 工具(格式:mcp::server_name::tool_name)
- "mcp::git_analysis::get_blame" # MCP 工具
mcp_servers:
git_analysis:
# 2026-10-08 更正:@modelcontextprotocol/server-git 已从 npm registry 下架
# (2026-10-08 探测 registry.npmjs.org/@modelcontextprotocol/server-git 返回 404,
# 同族对照 server-github / server-filesystem 均返回 200,说明是特定包下架)
# 官方 Git 参考实现现为 PyPI 的 mcp-server-git(2026-10-08 一手:PyPI 最新 2026.8.18)
command: "uvx"
args: ["mcp-server-git", "--repository", "/workspace/repo"]
# 只读审查场景:不要暴露 git_add / git_commit / git_checkout 等写工具
⚠️ 三件事必须知道(2026-10-08 补,一手探测):
- 旧写法已失效。本书此前写的
npx -y @modelcontextprotocol/server-git在 npm registry 已返回 404(2026-10-08 探测;同族的server-github与server-filesystem均返回 200,可确认是特定包下架而非 registry 不可达)。照抄会以npx报 404 的形式失败,而不是语义错误,读者很难定位到「书里的包名过期了」。官方 Git 参考实现现为 PyPI 的mcp-server-git,推荐启动方式uvx mcp-server-git --repository PATH。- 包名抄错一个字符会中招。npm 上无作用域的
mcp-server-git是一个被 npm 安全团队下架的恶意包,占位页仍在(原文:该包含恶意代码,已被移除并发布占位包以防后续用户受影响)。装包时必须走 PyPI 源或带@modelcontextprotocol/作用域,抄漏作用域前缀会命中同名恶意包。- 这个 server 有 12 个工具,其中含写操作。除 status / diff / log / show 等只读项外,还包含
git_add、git_commit、git_checkout、git_reset、git_branch。本章是只读审查场景,应显式裁剪工具面,不要整包注册——这与本节末「Copilot Code Review 版」刚立的「生产级审查应默认采用只读 MCP 作用域」原则一致。
MCP 工具的注册流程(框架自动处理):
YAML 解析阶段
→ 发现 mcp::git_analysis::git_analyze
→ 连接 MCP Server(stdio 或 HTTP)
→ 调用 MCP list_tools() 获取工具 schema
→ 自动注册到 tool_registry(名称:mcp::git_analysis::git_analyze)
→ 元数据里标记 tool_type: "mcp"
Agent 调用 MCP 工具时(框架自动处理):
Agent 决定调用 git_analyze
→ 框架把 tool_call 转发给 MCP Server(JSON-RPC)
→ MCP Server 执行,返回结果
→ 框架把结果包装成 ToolResponse
→ 传给 Agent(跟自定义工具无差别)
第二层:Agent 配置
# code_review.yaml(续)
agent_name: "main_agent"
agent_config:
system_prompt: |
You are a senior code reviewer.
First get the PR diff, then run linter on changed files,
then use git_analyze to understand commit patterns.
Output a structured review with severity levels.
max_iterations: 15
tools:
- "get_pr_diff"
- "run_linter"
- "mcp::git_analysis::git_analyze"
- "mcp::git_analysis::get_blame"
parallel_tool_calls: false
tool_output_truncation: 8000
关键点:
tools列表决定 LLM 能看到的 function schema- MCP 工具和自定义工具在
tools列表里平级——Agent 不知道工具是 Python 实现的还是 MCP Server 提供的 system_prompt里可以引导 Agent 使用工具的顺序(先拿 diff → 再跑 linter → 再做 git 分析)
第三层:Task 定义
# extensions/tasks/code_review/task.py
from nexus.core import task_registry
from nexus.core.task_protocol import TaskRunInput, TaskRunOutput, WithAgentRunner, WithScore
from pydantic import BaseModel
class CodeReviewInput(TaskRunInput, WithAgentRunner, WithScore):
pr_number: int
repo_url: str
expected_issues: list[str] # 预期发现的问题(用于评分)
class CodeReviewOutput(TaskRunOutput):
review_text: str
issues_found: list[str]
@task_registry.register("code_review_task")
async def run_code_review(task_input: CodeReviewInput) -> CodeReviewOutput:
# 1. 准备 Runtime 环境
runtime = await task_input.get_runtime("main")
await runtime.run_command(f"git clone {task_input.repo_url} /workspace/repo")
await runtime.run_command("cd /workspace/repo && git checkout pr-{pr_number}-branch")
# 2. 调用 Agent(通过注入的 run_agent)
agent_output = await task_input.run_agent(
user_prompt=f"Please review PR #{task_input.pr_number}. Use the tools to analyze the code.",
working_dir="/workspace/repo"
)
# 3. 提取 Agent 输出
review_text = agent_output.final_answer
# 4. 评分
# 4.1 用 LLM Judge 评估审查质量
judge_score = await judge_review_quality(
review=review_text,
expected_issues=task_input.expected_issues
)
# 4.2 检查是否用了预期的工具
tools_used = [call["function"]["name"] for call in agent_output.tool_calls]
tool_coverage_score = 1.0 if "run_linter" in tools_used else 0.5
final_score = 0.7 * judge_score + 0.3 * tool_coverage_score
return CodeReviewOutput(
review_text=review_text,
issues_found=extract_issues(review_text),
score=final_score
)
关键点:
WithAgentRunnerMixin → 注入run_agent()方法- Task 不关心 Agent 的具体实现——只管传 prompt,拿回结果
get_runtime("main")→ 从 YAML 的runtime_configs.main获取 Runtime 实例
第四层:YAML 完整配置
# code_review.yaml(完整版)
task_name: "code_review_task"
task_config:
pr_number: 42
repo_url: "https://github.com/example/project.git"
expected_issues: ["missing error handling", "unused import"]
runtime_provider_name: "docker"
runtime_configs:
main:
image: "nexus/swe-bench:latest"
worker_venv: false
agent_name: "main_agent"
agent_config:
system_prompt: |
You are a senior code reviewer.
max_iterations: 15
tools:
- "get_pr_diff"
- "run_linter"
- "mcp::git_analysis::git_analyze"
mcp_servers:
git_analysis:
# 与第二节同步更正(2026-10-08):npm 版已 404,改用 PyPI 的 mcp-server-git + uvx
command: "uvx"
args: ["mcp-server-git", "--repository", "/workspace/repo"]
openai_clients:
main:
name: "openai"
config:
client_args:
api_key: "${oc.env:OPENAI_API_KEY}"
request_args:
model: "gpt-4o"
第五层:执行全流程走读
这是最核心的部分。从 nexus run code_review.yaml 到结果返回,完整走一遍。
Step 1:框架启动,加载配置
nexus run code_review.yaml
↓
YAML 解析
→ task_name: "code_review_task" → task_registry.get("code_review_task")
→ agent_name: "main_agent" → agent_registry.get("main_agent")
→ runtime_provider_name: "docker" → runtime_provider_registry.get("docker")
→ openai_clients.main → openai_client_registry.get("openai")
关键点:所有 _registry.get() 都是懒加载——只有用到的对象才会被 import。
Step 2:准备 Runtime 环境
RuntimeProvider("docker").acquire("main")
↓
启动 Docker 容器(镜像:nexus/swe-bench:latest)
↓
容器里启动 Nexus Server(监听端口,等待 tool call)
↓
返回 RemoteRuntime 实例(Host 侧的客户端代理)
↓
存入 ExecutionContext(后续所有调用共享这个 Runtime)
Step 3:启动 Agent 推理循环
Task 函数被调用:run_code_review(task_input)
↓
task_input.get_runtime("main") → 拿到 RemoteRuntime 实例
↓
task_input.run_agent(user_prompt=...) → 注入的 AgentRunner 开始工作
↓
───────────────── Agent 推理循环开始 ─────────────────
Iteration 1:LLM 第一次调用
框架构造 LLM 消息:
{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "You are a senior code reviewer. First get the PR diff, then run linter..."
},
{
"role": "user",
"content": "Please review PR #42. Use the tools to analyze the code."
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_pr_diff",
"description": "获取指定 PR 的 diff 内容",
"parameters": {
"type": "object",
"properties": {
"pr_number": {"type": "integer"},
"repo_path": {"type": "string"}
},
"required": ["pr_number"]
}
}
},
{
"type": "function",
"function": {
"name": "run_linter",
"description": "在代码上运行 linter(pylint/flake8)",
"parameters": { ... }
}
},
{
"type": "function",
"function": {
"name": "mcp::git_analysis::git_analyze",
"description": "Analyze git commit patterns and code churn",
"parameters": { ... }
}
}
]
}
关键点:
tools列表来自 YAML 的agent_config.tools- MCP 工具的 schema 是框架启动时从 MCP Server 动态获取的
- LLM 看不到 Task 的其他细节(pr_number、expected_issues 等)——这些只有 Task 函数知道
LLM 返回:
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_pr_diff",
"arguments": "{\"pr_number\": 42, \"repo_path\": \"/workspace/repo\"}"
}
}
]
}
}]
}
Iteration 2:执行工具调用
框架收到 tool_call,开始执行:
1. 解析 tool_name: "get_pr_diff"
↓
2. tool_registry.get("get_pr_diff")
→ 拿到之前注册的函数(code_review_tools.py 里的 get_pr_diff)
↓
3. 检查 tool_type: "runtime"
→ 需要通过 Runtime 执行(因为操作文件系统)
↓
4. 序列化参数,通过 RemoteRuntime.invoke() 发送到沙箱
↓
5. 沙箱里的 ToolExecutor 收到调用
→ 反序列化参数
→ 调用 get_pr_diff(params, context)
→ 执行:runtime.run_command("git diff ...")
↓
6. 返回 ToolResponse
→ 通过 RPC 传回 Host
↓
7. 框架把工具结果包装成 LLM 消息
发送给 LLM 的下一轮消息:
{
"messages": [
{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{
"role": "assistant",
"content": null,
"tool_calls": [{"function": {"name": "get_pr_diff", ...}}]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "diff --git a/src/main.py b/src/main.py\n--- a/src/main.py\n+++ b/src/main.py\n@@ -10,6 +10,7 @@ def process_data():\n data = load_data()\n+ # TODO: add error handling\n+ process(data)\n return result"
}
],
"tools": [...]
}
Iteration 3:LLM 继续推理
LLM 返回:
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"function": {
"name": "run_linter",
"arguments": "{\"file_path\": \"/workspace/repo/src/main.py\", \"linter\": \"flake8\"}"
}
}
]
}
}]
}
执行流程同上,这次调用 run_linter → Runtime 里跑 flake8 /workspace/repo/src/main.py → 返回 linter 输出。
Iteration 4:LLM 调用 MCP 工具
LLM 返回:
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"function": {
"name": "mcp::git_analysis::git_analyze",
"arguments": "{\"file_path\": \"/workspace/repo/src/main.py\"}"
}
}
]
}
}]
}
MCP 工具的执行路径略有不同:
1. 解析 tool_name: "mcp::git_analysis::git_analyze"
↓
2. 识别为 MCP 工具(前缀 mcp::)
→ 查找 mcp_servers.git_analysis 的配置
↓
3. 通过 MCP 协议发送 tool_call(JSON-RPC)
格式:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "git_analyze",
"arguments": {"file_path": "/workspace/repo/src/main.py"}
}
}
↓
4. MCP Server(`mcp-server-git`,PyPI 包,2026-10-08 更正)执行
→ 跑 git blame / git log 分析
↓
5. 返回 MCP 格式的结果
{
"content": [{"type": "text", "text": "File has 15 commits in last week..."}]
}
↓
6. 框架转换成 ToolResponse
→ 传回 Agent(跟自定义工具无差别)
关键点:Agent 不知道 git_analyze 是 MCP 工具还是 Python 工具——对 Agent 来说,它们都是 tools 列表里的一个 function。
Iteration 5:LLM 生成最终答案
经过 3-4 轮工具调用后,LLM 收集了足够信息,生成最终审查意见:
{
"choices": [{
"message": {
"role": "assistant",
"content": "## Code Review for PR #42\n\n### Issues Found:\n1. **Missing error handling** in `process_data()` (line 13) - TODO comment suggests intended but not implemented\n2. **Unused import**: `import sys` not used in `src/main.py`\n3. **Linter warning**: flake8 reports E501 (line too long) at line 25\n\n### Recommendations:\n- Add try/except block around data processing\n- Remove unused import\n- Break line 25 to <79 characters"
}
}]
}
Step 4:Task 评分
Agent 推理循环结束,返回 AgentOutput(含 final_answer)
↓
Task 函数继续执行
→ 提取 review_text
→ 调用 judge_review_quality()(另一个 LLM 调用)
→ 计算 tool_coverage_score
→ 返回 TaskRunOutput(score=0.82)
↓
框架记录结果到 SQLite / 输出到终端
第六层:消息流可视化
┌───────────┐
Host 进程 │
│ │
│ YAML 配置 │
│ ↓ │
│ task_registry.get("code_review_task") │
│ ↓ │
│ agent_registry.get("main_agent") │
│ ↓ │
│ openai_client 构造 LLM 请求 │
│ ↓ │
│ ► LLM API Call ← 关键网络请求 │
│ ↓ │
│ 收到 tool_calls │
│ ↓ │
│ tool_registry.get("get_pr_diff") │
│ ↓ │
│ runtime.invoke("get_pr_diff", params) │
└───────────┘
↓ RPC(HTTP or Socket.IO)
┌───────────┐
Runtime 沙箱(Docker 容器) │
│ │
│ Nexus Server 收到 invoke 请求 │
│ ↓ │
│ ToolExecutor 查找工具实现 │
│ ↓ │
│ 执行 get_pr_diff() │
│ → runtime.run_command("git diff ...") │
│ ↓ │
│ 返回 ToolResponse │
└───────────┘
↓ RPC 返回
Host 进程
│
│ 工具结果包装成 LLM message
│ ↓
│ ► LLM API Call(带 tool result)
│ ↓
│ 下一轮 tool_call or final answer
│ ↓
│ 所有 iteration 完成
│ ↓
│ Task 函数评分
│ ↓
│ 返回最终结果
关键设计点总结
| 设计点 | 实现方式 | 好处 |
|---|---|---|
| Tool 注册解耦 | @tool_registry.register() + 懒加载 | Task/Agent 不 import 工具代码 |
| MCP 工具平级接入 | 框架自动把 MCP list_tools() 注册进 tool_registry | Agent 无感使用 MCP 工具 |
| Agent 不知道工具实现 | 只看 tools 列表里的 function schema | 换工具实现不影响 Agent |
| Task 不知道 Agent 实现 | run_agent() 是注入的工厂函数 | 换 Agent 只需要改 YAML |
| Runtime 隔离 | Docker 容器 + Nexus Server | 工具执行不影响 Host |
| ToolContext 不可变 | ImmutableDict | 并行工具调用无竞态 |
MCP 选型的行业收敛:2026-07-28 Microsoft .NET Agent Framework 宣布 MCP Skills Discovery,2026-07-29 GitHub Copilot Code Review GA——两个独立团队在同周选择同一协议(MCP)作为 skill 传输与发现的中间层,标志 MCP 从「周边协议」变为「通用中间件」,佐证本章将 MCP 作为代码审查等场景通用接入层的选型。(该结论目前来自单一二手来源,建议交叉验证。)
协议收敛 ≠ 参考实现可用于生产(2026-10-08 补):上面论证的是协议的采用度,与实现的成熟度是两件事。官方
modelcontextprotocol/serversREADME 明确写:参考 server 是「intended as reference implementations to demonstrate MCP features and SDK usage」,「not as production-ready solutions」;官方把想找目录的人指向 MCP Registry。当前仍在维护的只有 7 个(everything / fetch / filesystem / git / memory / sequentialthinking / time),另有 14 个已迁到servers-archived,归档 README 以大写警示「NO SECURITY GUARANTEES ARE PROVIDED FOR THESE ARCHIVED SERVERS」;归档包普遍 pin 在旧版 SDK、协议协商上限停留在 2024-11-05,无法使用 structured tool output / elicitation / tool annotations。 → 本章用参考 server 讲解机制是可以的;读者若要上生产,应走向 MCP Registry 里的实现,而不是照抄书里的示例。 来源纪律:官方 README 的「not as production-ready solutions」原文由第三方页面转引,本书未直读仓库 README;7 / 14 拆分名单与 SDK pin 细节亦来自二手技术博客,标注为二手。
另一条不同性质的证据线(2026-10-08 补):MCP-Bench(Accenture,ICLR 2026)把 28 个生产级 MCP server、250 个工具、11 个领域接进基准,且每个任务额外挂 10 个干扰服务,agent 实际面对 100+ 冗余工具;评估采用「规则检查 + rubric 驱动的 LLM 评委」双层。这与「两个团队同周选同一协议」性质不同:前者是采用度,后者是规模化之后是否仍可用。另需与 Ch21 的基准表联动——Ch21 的 MCP-Bench 行缺「11 领域」与「干扰服务」两项,且未说明它用 LLM 评委(与 Ch21 主张的「拒绝 LLM-as-a-judge」形成张力)。来源:评分机制与干扰服务细节来自 ICLR 2026 论文笔记站转述(二手);仓库 511 stars / 最后 push 2025-10-07 为 GitHub API 一手(2026-10-08)。
跨章一致性提示:MCP 2026-07-28 规范已 GA(非 RC)。全书凡涉及该规范状态的章节(Ch07 / Ch18 / 本章)表述须与此保持一致——Ch07 仍有若干处称其为 RC,若只改其中一章会制造新的书内矛盾。(2026-10-08 复测:
modelcontextprotocol/modelcontextprotocol最新 release 仍为 tag2026-07-28(published2026-07-28T16:47:49Z),前一条为2026-07-28-RC(2026-05-29),无更新版本,本提示继续成立。)
对比:如果不用这套架构
传统做法(把工具调用写死在 Agent 里):
# 紧耦合,难扩展
def my_agent(prompt):
diff = subprocess.run(["git", "diff", ...]) # 硬编码
linter_result = subprocess.run(["flake8", ...]) # 硬编码
# 想加新工具?改 Agent 代码
# 想换 LLM?改 Agent 代码
Nexus 做法(解耦,配置驱动):
# 想加新工具?改 YAML 的 tools 列表
tools:
- "get_pr_diff"
- "run_linter"
- "new_tool" # ← 加这一行,Agent 自动能用
一张图总结全流程
YAML 配置
│
├── task_name ─────→ task_registry ─────→ Task 函数
│ │
├── agent_name ────→ agent_registry ────→ Agent 实现
│ │
├── tools ────────→ tool_registry ─────→ 工具执行
│ │ │
│ ├── 自定义工具(Python) │
│ └── MCP 工具(JSON-RPC) │
│ │
└── runtime ──────→ RuntimeProvider ──→ Docker 沙箱
│
用户 prompt ──→ Agent 推理循环 ←──→ 工具执行 ←──┘
│
Task 评分
│
返回结果
从教学到生产:代码审查的三种规模
本章的代码审查案例是教学级简化版(3 个工具 + main_agent + LLM Judge 评分)。以下是三种规模的对比:
| 维度 | Nexus 教学版(本章) | GitLab MCP 版 | Cloudflare 企业版 | Cursor Bugbot | Code-Review-Graph 版 | CodeBuddy MCP 管线版 |
|---|---|---|---|---|---|---|
| 工具数 | 3 | 7-8 API endpoint | 182+(13 MCP Server) | 多维度审查工具集 | 30 个 MCP 工具 + 5 个 workflow prompt | 3 个自定义 MCP Server(安全+规范+性能) |
| Agent | main_agent | review skill + MCP | Claude Code + Workers AI | Cursor Agent | 任意 MCP 兼容 Agent | CodeBuddy IDE Agent + RAG 知识库 |
| 评分 | LLM Judge | 成本降 30x | 质量门 + 自动审查 | PR comment 反馈 | 82x token 缩减(中位数) | 审查覆盖率 60→95%,漏洞发现 3x |
| 规模 | 单 PR | MR 自动审查 | 所有 MR 自动审查(8700+/周) | 所有 PR 自动审查 | 2900 文件项目 <2s 索引 | 20 人团队,15-20 PR/天 |
| 触发方式 | 手动命令行 | 命令行手动触发 | API 集成触发 | CI/CD 自动触发 | MCP 请求(实时) | IDE 集成 + 自动 PR comment |
| 基础设施 | Docker 沙箱 | GitLab MCP Server | AI Gateway(2413.7 亿 token/月) | CI/CD Pipeline | Tree-sitter + 本地 Knowledge Graph | 腾讯云 CodeBuddy + MCP Protocol |
GitLab MCP 版:用 7-8 个 GitLab API endpoint + Jira 集成构建 MCP Server,配合 review skill 实现 MR 自动审查。成本比 Claude Code Review 低 30x。单文件 MCP server + skill 文件的结构简洁可复现。
Cloudflare 企业版:历时 11 个月构建内部 AI 工程基础设施,93% R&D 采用率。13 个 MCP Server(182+ 工具)、~3900 仓库生成 AGENTS.md、所有 MR 自动 AI 代码审查。MR 数量近翻倍(5600→8700+/周)。零信任认证 + 零数据保留策略。
Cursor Bugbot:CI/CD 级别的自动化代码审查——PR 创建时自动启动 Agent 代码审查(无需手动调用),覆盖 bug 检测、安全漏洞、架构问题、性能优化建议等多维度,理解 PR 的完整 diff 和代码历史,审查结果以 PR comment 形式反馈。代表代码审查从”on-demand”到”automated”的范式转变。
Code-Review-Graph 版(仓库 tirth8205/code-review-graph;31,960 stars / 2,927 forks,最后 push 2026-10-06,2026-10-08 一手;PyPI 最新 2.3.9,2026-09-18 发布):用 Tree-sitter 将代码库解析为持久化结构化知识图谱,图谱存于本地 SQLite(.code-review-graph/),无外部数据库、无云端。官方 Benchmarks 段(2026-08-02 重测,crg 2.3.7)给出:每问 token 缩减中位数 ~65x,区间 36x–376x,其中 376x 是 fastapi 这一最大语料的最佳个例而非典型值;样本为 6 个真实开源仓库、13 个 commit。
⚠️ 本章原写「中位数 82x、最佳场景 528x」,该组数字已退役。 官方 README 明确写道:现测值「低于它们所取代的 2026-05-25 那次采集」——原因是节点嵌入文本变丰富导致 graph 响应变大。82x/528x 正对应那次已退役的旧采集。引用时请以官方 README 的 Benchmarks 段为准并带采集日期。
版本绑定与倍数口径(2026-10-08 补):上引 65x(36x–376x)是 crg 2.3.7 于 2026-08-02 的采集;当前 PyPI 已到 2.3.9(2026-09-18),该组数字是否在 2.3.9 之后重测,本章未核——引用前请回官方 README 的 Benchmarks 段确认采集版本与日期。
另有一条可写的事实:本项目在第三方渠道上同时流传三套互不相同的数字——82x(本章已标注退役的 2026-05-25 旧采集)、65x(现行官方值)、6.8x–49x / 约 65x(另两家聚合站各自口径)。凡引用本项目的倍数,先问「哪个口径、哪次采集」。
与前五种规模的核心区别仍然成立:不是”直接分析原始代码”,而是”查询预构建的结构化索引”——token 节省来自架构级的上下文精确化。但这个判断需要再往前推一步:它已经不是一个接口,而是一层可替换的「上下文供给层」——30 个 MCP 工具覆盖 blast-radius 查询、语义/混合检索(FTS5 关键词 + 向量)、架构总览、社区发现、风险评分、执行流追踪、重构预览、wiki 生成与跨仓库检索;另有 5 个 workflow prompt(review / architecture / debug / onboard / pre-merge)把审查动作本身模板化。当工具集大到这个程度,「图谱层」不再是 Nexus 的一个工具,而是一个可整体替换的子系统。(30 工具 / 5 prompt 为官方 README 一手。)
CodeBuddy MCP 管线版(腾讯云 CodeBuddy,2026-07-20):审查逻辑完全在 MCP Server 层实现——Agent 不直接分析代码,只协调 MCP Server 之间的调用。与 Nexus 教学案例(Agent 自己调用 git diff/linter)的本质区别:审查能力不再是 Agent 的推理能力,而是 MCP Server 的工具能力。审查覆盖率从 60% 提升至 95%,安全漏洞发现率提升 3 倍。(该案例为单团队实践,普适性待验证。)
Copilot Code Review 版(GitHub,2026-07-29 GA):GitHub 将 Copilot code review 的 Agent skills 与 MCP server 连接从 public preview 转为 GA(覆盖 Pro/Pro+/Business/Enterprise 所有付费档位)。关键设计:审查期间所有 MCP 工具调用均为只读(read-only,无 write-back)——审查 Agent 可拉取 issue tracker/文档/service catalog 上下文但不能写回,印证本章「MCP 工具接入代码审查」的架构价值,并提示生产级审查应默认采用只读 MCP 作用域。
更进一步,GitHub 在 2026-07-17 的 changelog 中把 Copilot Code Review 的 Instructions、Setup Workflow、Firewall、Runner 与 Cloud Agent 网络策略一次性推进,组合成「可编程 Agent Runtime」——审查结果由仓库控制文件(copilot-instructions.md / AGENTS.md / Agent Skills)、运行时依赖、工具、网络策略与 Runner 共同决定,是比「固定规则 Reviewer」更进一步的形态。
关键洞察:Nexus 架构可以自然扩展到后两种规模——@tool_registry.register() 支持无限工具注册,MCP 接入支持外部 Server 扩展,run_agent() 的注入模式支持从教学到生产的无缝过渡。
评分策略选择
本章使用 LLM Judge 评估审查质量 + 工具覆盖度评分。但在生产场景中,存在更工程化的评分方式:
三种评分策略
- LLM Judge(当前方案):适合开放式评估——审查意见的深度和洞察力。灵活但成本高、不稳定。
- 质量门(SonarQube/GitLab CI):适合结构化评估——是否有安全漏洞、是否通过 lint、测试覆盖是否足够。确定性强但覆盖面窄。
- 混合方案:质量门做硬性通过/失败判断 + LLM Judge 做软性质量评分。
SonarQube + Claude Code 闭环实践
Claude Opus 4.6 通过 SonarQube MCP Server 实现「写代码 → 扫描 → 自修复 → 验证质量门」的闭环:
- CLAUDE.md 强制 Agent 先扫描再提交
--max-turns和 hooks 防止失控- Agent 发现 S3 bucket ownership 安全漏洞并自主修复
这种闭环模式可以在 Nexus 的 Task 评分中实现:质量门做硬性通过/失败,LLM Judge 评估审查意见的深度,两者加权得到最终分数。
三层审查架构
第 0 层:图谱预索引层(Pre-Indexing)——在 Agent 审查启动前,通过 Tree-sitter AST 解析将代码库预构建为结构化知识图谱。审查时 Agent 不再需要”搜索代码”再”理解上下文”,而是直接查询图谱获取依赖关系、调用链、影响半径。这层解决了三层架构中的上下文效率瓶颈:静态分析和 AI 审查都需要精确定位代码上下文,图谱预索引将上下文从”每次查询重新构建”变为”预计算+按需读取”。对 Nexus 的启示:在 Step 3(Agent 推理循环)中,Agent 获取代码信息的方式可以从”工具调用执行 git diff”演进为”查询 Knowledge Graph 索引”。
第 0 层不只是给 AI 供上下文,它自己也能做硬门禁。 Code-Review-Graph 已提供 composite GitHub Action:每次 push 在 PR 上发一条 sticky、就地更新的评论,含风险评分函数、受影响执行流与测试缺口;图谱构建与查询全部在 CI runner 内完成,源码不出内网;并可通过可选的 fail-on-risk 输入把审查结果直接升级为合并门禁(merge gate)。
这条路径跳过了 LLM——风险分来自图的结构性影响(blast radius / 耦合度 / 测试覆盖),是确定性的。于是本章的四层架构出现一条旁路:
图谱预索引 ──┬─→ 静态分析 → AI 审查 → 人工审批
└─→ 结构性风险评分 → fail-on-risk 合并门禁(无 LLM)
对 Nexus 的启示:Task 的评分不必一定是 LLM Judge。可以把「结构性风险分」作为一个独立、零 LLM 开销、完全确定的评分维度——与 Ch21 主张的「regex/程序化校验优先于 LLM-as-judge」是同一个取向。这也补上了本章四层架构中缺失的一环:图谱层此前只服务 AI 审查层,现在它自己就能当质量门。
⚠️ 第 0 层的适用边界(2026-10-08 补):上面这段是单向收益论述,真实边界有三条,读者照抄前必须先过这三关:
- 开销会反超。基线对照中 express 仓库的压缩比为 0.7×——低于 1,即小改动场景下走图谱比直接读文件更贵。图谱层的收益条件是「大仓 + 多文件改动」,不是无条件成立。
- 质量有上限。关键词检索 MRR 仅 0.35(部分 module-pattern 查询零命中);执行流检测整体召回 33%,且只在 Python 仓库可靠,JS / Go 需改进;blast-radius 平均精度 0.38——用「零漏报」换「多误报」。→ 把第 0 层当
fail-on-risk硬门禁时,误报率是设计取舍而不是 bug,上文「结构性风险分是完全确定的」应当理解为「确定地偏保守」,不是「精确」。- 对本章教学案例的反向启示:
get_pr_diff那个案例(3 个工具、单个 PR)恰好落在图谱不回本的区间。只有当工具集与仓库规模涨到 Code-Review-Graph 量级(27k+ 文件 monorepo),第 0 层才值得引入——这给本章「三层 → 四层」的升级加了一个触发条件。来源纪律:MRR 0.35 / 召回 33% / 精度 0.38 / express 0.7× 四项均来自单一第三方聚合站(ossaihub,2026-10-06 读取),其中 0.7× 与「graph metadata overhead can exceed naive file reads」在另一家站点有独立表述(两家一致),但四项都未在项目官方 README 上逐条复核。
SonarQube MCP、code-review-mcp 和 Bifrost+MCP 的出现,使”图谱预索引 → 静态分析 → AI 审查 → 人工审批”四层架构成为可工程化的标准模式:
- 静态分析层(SonarQube MCP):将静态分析结果通过 MCP 协议暴露给 AI Agent,查询代码质量指标、安全漏洞列表、技术债务等
- AI 审查层(code-review-mcp):开源 MCP Server,提供标准化的代码审查接口(diff 分析、评审意见提交、审批流程),支持 GitHub/GitLab 集成
- 人工审批层(Bifrost+MCP 或直接 PR review):企业级代码审查平台通过 MCP 协议接入 AI Agent,实现人机协同审查工作流
三层架构的关键优势:每层聚焦不同维度(确定性/语义性/业务性),误报逐层过滤——静态分析的误报由 AI 审查过滤,AI 审查的不确定判断由人工审批兜底。
| 评分策略 | 覆盖范围 | 确定性 | 一致性(重复运行是否稳定) | 集成复杂度 | 误报控制 |
|---|---|---|---|---|---|
| LLM Judge | 广(语义深度) | 低 | 低(同一轨迹多次评判可能翻转) | 低 | 弱 |
| 质量门 | 窄(结构化规则) | 高 | 高(规则确定) | 中 | 强(规则明确) |
| 混合方案 | 中 | 中 | 中(LLM 部分仍抖动) | 中 | 中 |
| 三层架构 | 最广 | 分层递增 | 中 | 高 | 最强(逐层过滤) |
| 四层+图谱 | 最广(上下文精确) | 中(图谱质量决定) | 中 | 高(需 Tree-sitter + 图谱维护) | 强(上下文精准减少误判) |
「确定性」与「一致性」是两个不同的维度:前者问「评分规则本身会不会变」,后者问「同一个 Agent 重复跑同一个任务,得分是否稳定」。本章此前的讨论只覆盖了前者,而行业基准已经开始把后者作为正式字段——MCPMark(ICLR 2026,127 个任务、5 类真实 MCP 环境)实测:最强模型 gpt-5-medium 的 pass@1 = 52.56%,pass@4 = 68.50%,而 pass^4 只有 33.86%;claude-sonnet-4 则是 pass@1 28.15% → pass^4 12.60%。重复尝试能拉高 pass@k,但「连续 k 次都成功」的 pass^k 会断崖式下跌。
对 Nexus 尤其重要:本章把 final_score 回传给 RL 训练,而 final_score = 0.7 * judge_score + 0.3 * tool_coverage_score 是单次采样。若只做单次采样,高方差策略会被误判为可用策略,污染 RL 训练样本。建议在关键决策点上重复采样 k 次,用 pass^k 而非 pass@1 定 reward。(与 Ch19 推荐的 τ 系列 pass^k、Ch21 的 RL 样本导出需同步处理。)
第四种必须并行的维度:安全(2026-10-08 补)。上表的表头是 覆盖范围 / 确定性 / 一致性 / 集成复杂度 / 误报控制——五个字段全部只统计「该做的做到了多少」,对「不该做的做了什么」不敏感。ICLR 2026 同期出现两个本章未收录的安全向基准:MCP Security Bench(MSB)(12 类攻击、2000 个真实攻击实例,通过真实工具调用评测 MCP 环境下 LLM Agent 的安全性)与 MCP-SafetyBench(基于真实 MCP server 的多轮多域安全评测)。它们不是「另一种评分法」,而是与评分并列的独立门禁:一个 Agent 可以完成度 100%、效率优秀、一致性达标,同时在工具调用链上泄漏凭据或执行了越权写操作。
→ 落到本章两个场景(水费查询与代码审查)的判据:凡评分只看目标状态达成与否的体系,必须另配一条「副作用 / 越权」检查线,因为「完成度」对「多做的坏事」是不敏感的。这与本节刚改的 MCP 示例联动:Git server 默认带 git_add / git_commit,一个「完成度满分」的审查 Agent 完全可能顺手提交一笔。来源纪律:MSB / MCP-SafetyBench 的规模与定位来自 ICLR 2026 论文列表聚合站(二手),未逐条回 arXiv abs 页核验编号、标题与数字。