学习资料:Datawhale《Deep Agents 实战》第十三章:Grading Rubrics
本章重点:
RubricMiddleware、LLM-as-a-Judge、Evidence Tool、RubricEvaluation、on_evaluation、Fail-Closed。
一、为什么 Agent 需要运行时验收?
前面学习了 Deep Agents 的任务规划、SubAgent、Skills 和长期记忆等机制,它们分别解决任务执行、能力复用和状态管理的问题。
但还有一个问题:Agent 生成了答案,是否就意味着任务完成?
例如,让 Agent 实现一个 find_duplicates(values) 函数,要求返回所有重复元素,每个元素只出现一次,并支持不可哈希类型。
常见实现:
def find_duplicates(values):
seen = set()
duplicates = []
for value in values:
if value in seen:
duplicates.append(value)
seen.add(value)
return duplicates
这个函数可以处理整数,却无法处理嵌套列表:
find_duplicates([[1], [1]])
# TypeError: unhashable type: 'list'
模型虽然生成了完整代码,但并没有满足全部要求。
传统方案通常依赖 Prompt 约束、自检或者失败后重新生成。然而,这些方法往往缺少明确的验收标准和独立证据。
因此,本章引入 Grading Rubrics(评分量规)。
它的核心思想是:
在 Agent 生成结果后,使用预先定义的验收标准进行独立评审;如果未通过,就将具体差距反馈给 Agent,驱动下一轮修订。
二、RubricMiddleware 的工作原理
RubricMiddleware 将评审过程接入 Agent 的运行循环。
其中涉及四个核心角色:
| 角色 | 职责 |
|---|---|
| Working Model | 生成和修订任务结果 |
| Rubric | 定义明确的验收标准 |
| Grader Model | 评审候选结果并给出结论 |
| Evidence Tool | 通过测试等方式提供客观证据 |
整个过程形成一个闭环:
用户任务 + Rubric
|
v
Working Model
生成结果
|
v
Grader Model
独立评审
|
v
Evidence Tool
获取测试证据
|
v
评审结论
|
+-- satisfied
| |
| v
| 验收通过
|
+-- needs_revision
| |
| v
| 反馈具体差距
| |
| v
| Working Model
| 重新修订
|
+-- failed / grader_error
|
v
终止并拒绝
需要注意,工作模型和评分模型可以是同一个底层模型,但承担不同角色。
不过,使用独立评分模型并不能保证结果正确。能够通过测试确定的事实,仍然应该交给工具验证。
三、如何设计高质量 Rubric?
Rubric 不是简单重复用户需求,而是把任务要求转换为可验证的标准。
以 find_duplicates 为例:
rubric = """
- 函数名称必须为 find_duplicates。
- 每个重复元素只返回一次。
- 结果按照元素第二次出现的位置排序。
- 必须支持列表等不可哈希对象。
- 不允许修改输入列表。
- 必须运行 run_test_suite。
- 只有测试结果 ok=true 才能通过。
"""
一个有效的 Rubric 应满足三个特点:
| 特点 | 含义 |
|---|---|
| 可判定 | 每项标准能够明确判断是否通过 |
| 可取证 | 可以通过测试、Schema 或文件获取证据 |
| 可修订 | 失败时能够指出具体修改方向 |
例如,"代码质量优秀"属于模糊要求,而"函数不能修改输入参数"可以通过测试直接验证。
设计 Rubric 时,最好将复杂标准拆成多个原子条件,避免评分模型无法定位问题。
四、Evidence Tool:使用工具验证结果
LLM-as-a-Judge 可以进行语义判断,但代码正确性、文件格式和数值计算等问题,最好使用确定性工具验证。
下面定义一个简化版测试工具:
from langchain.tools import tool
@tool
def run_test_suite(code: str) -> dict:
"""测试 find_duplicates 函数。"""
namespace = {}
try:
exec(code, namespace)
func = namespace["find_duplicates"]
tests = [
([1, 2, 2, 3, 1], [2, 1]),
([], []),
([[1], [1]], [[1]])
]
failures = []
for values, expected in tests:
result = func(values.copy())
if result != expected:
failures.append({
"input": values,
"expected": expected,
"actual": result
})
return {
"ok": len(failures) == 0,
"failures": failures
}
except Exception as e:
return {
"ok": False,
"failures": [str(e)]
}
工具返回两个关键字段:
-
ok:表示测试是否通过。 -
failures:记录失败用例和错误信息。
评分模型可以根据这些结构化证据判断哪些标准未满足。
需要注意,这里使用 exec() 仅用于教学演示。它不能安全隔离不可信代码,生产环境必须使用独立沙箱限制文件、网络、计算资源和执行时间。
另外,这个简化示例只覆盖部分行为,实际测试还应验证输入是否被修改、结果是否包含重复项,以及更多边界条件。
五、创建 RubricMiddleware
本章使用两个模型角色:
from langchain.chat_models import init_chat_model
working_model = init_chat_model(
"openai:<working-model-id>"
)
grader_model = init_chat_model(
"openai:<grader-model-id>"
)
实际运行时,需要替换为可用的模型 ID 并配置 API Key。评分模型应支持结构化输出和工具调用。
创建评分中间件:
from deepagents import RubricMiddleware
rubric_middleware = RubricMiddleware(
model=grader_model,
tools=[run_test_suite],
max_iterations=3,
)
其中主要参数:
| 参数 | 作用 |
|---|---|
model |
指定评分模型 |
tools |
提供评分模型的取证工具 |
system_prompt |
定义评分角色与规则 |
max_iterations |
最大评分轮数 |
on_evaluation |
每轮评分后的回调 |
特别注意,RubricMiddleware(tools=[...]) 配置的工具只供评分模型使用,不会自动成为工作模型的工具。
然后将 Middleware 挂载到 Agent:
from deepagents import create_deep_agent
from langgraph.checkpoint.memory import InMemorySaver
agent = create_deep_agent(
model=working_model,
middleware=[rubric_middleware],
checkpointer=InMemorySaver(),
)
调用时需要同时传入任务和 Rubric:
result = agent.invoke(
{
"messages": [{
"role": "user",
"content": "实现 find_duplicates(values)"
}],
"rubric": rubric,
},
config={
"configurable": {
"thread_id": "rubric-demo"
}
},
)
这里的 rubric 属于运行状态,而不是创建 Agent 时固定的构造参数,因此同一个 Agent 可以处理不同任务和验收标准。
六、RubricEvaluation:如何判断是否真正通过?
评分过程中可能出现五种状态:
| 状态 | 含义 | 是否通过 |
|---|---|---|
satisfied |