Skip to content

实战:研究型 Agent

本实战把规划、确定性搜索、文件 offloading、同步 subagents、引用和 rubric 组合为一个可审查的研究 流程。搜索只读取页面内定义的本地 fixture,不访问网络;示例不会在文档构建中调用真实模型。

目标与验收条件

任务是比较一个虚构产品的退款与发票政策,输出 /artifacts/report.md。验收条件为:

  • 先用 write_todos 拆分两个独立主题。
  • 每个主题由 researcher subagent 查询本地 fixture。
  • 原始材料写入 /research/<topic>.md,主上下文只保留摘要。
  • 报告包含“退款”“发票”“不确定项”三个章节。
  • 每项事实带 [fixture:<id>] 引用,不把 fixture 之外的信息写成事实。

前端类比

它类似把多个 API selector 放到 Web Workers 中并行计算,再把规范化结果写进缓存,主线程只负责 聚合 UI。Fixture 就像 MSW mock;如果 mock 没有字段,组件不能凭空补值。

Deep Agents 原生语义:主 Agent 用 task 把独立问题交给隔离 messages 的 subagent;默认 StateBackend 保存 thread-scoped 虚拟文件。文件 offloading 控制上下文体积,不能代替来源校验。

1. 定义本地搜索 Fixture

python
import json

from langchain.tools import tool

FIXTURES = {
    "refund-14-days": {
        "topic": "退款",
        "text": "未使用的月度套餐可在购买后 14 天内申请退款。",
    },
    "invoice-monthly": {
        "topic": "发票",
        "text": "电子发票在每月结算后生成,可由账户管理员下载。",
    },
}


@tool
def search_fixture(topic: str) -> str:
    """按主题搜索本地政策 fixture;不访问网络。"""
    rows = [
        {"id": key, **value}
        for key, value in FIXTURES.items()
        if topic in value["topic"] or topic in value["text"]
    ]
    return json.dumps(rows, ensure_ascii=False)

工具只返回 fixture 中的字段。真实搜索工具还要限制域名、timeout、结果大小和凭据,并把网页正文视为 不可信数据。

2. 配置 Researcher 与主 Agent

python
import os

from deepagents import create_deep_agent

researcher = {
    "name": "policy-researcher",
    "description": "一次研究退款或发票中的一个主题,并返回 fixture 引用和不确定项",
    "system_prompt": """
    只调用 search_fixture,不使用常识补全。
    把完整搜索结果写入 /research/<topic>.md。
    返回不超过 8 行的结论;每项事实必须包含 [fixture:<id>]。
    """,
    "tools": [search_fixture],
}

agent = create_deep_agent(
    model=os.environ["DEEPAGENTS_MODEL"],
    tools=[search_fixture],
    subagents=[researcher],
    system_prompt="""
    面对比较任务先用 write_todos 建立计划。
    将退款和发票作为两个独立任务委派给 policy-researcher。
    汇总前读取 /research/ 下的材料并核对引用。
    最终报告写到 /artifacts/report.md,包含退款、发票、不确定项三个标题。
    Fixture 没有的信息必须标为未知,不能猜测。
    """,
)

主 Agent 仍拥有 search_fixture,便于最后复核,但详细搜索应交给 subagent。若要更严格,可用独立 reviewer subagent 或应用节点做引用核验。

3. 调用与文件 Offloading

配置真实 tool-calling 模型后,应用可以显式发起调用:

python
# 本文不会执行此调用;运行时会产生模型费用。
result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "比较退款与发票政策,给出差异和无法确认的事项。",
            }
        ]
    }
)

report = result["messages"][-1].content

预期轨迹是:write_todos → 两次 task → 每个 subagent 调用 search_fixturewrite_file → 主 Agent 读取研究文件 → 写报告 → 返回带引用摘要。不要断言两个独立 task 的精确先后顺序;应断言 两项都完成且没有共享写冲突。

4. 用确定性 Rubric 做离线门禁

先用代码规则检查可机械验证的条件,无需第二个模型:

python
REQUIRED_SECTIONS = ("## 退款", "## 发票", "## 不确定项")


def grade_report(report: str) -> dict[str, bool]:
    return {
        "sections": all(section in report for section in REQUIRED_SECTIONS),
        "citations": "[fixture:refund-14-days]" in report
        and "[fixture:invoice-monthly]" in report,
        "no_external_claims": "http://" not in report and "https://" not in report,
    }


scores = grade_report(report)
assert all(scores.values()), scores

Deep Agents 另有 Beta RubricMiddleware(Python >=0.6.5),可以用 grader model 迭代主输出。本例 故意使用确定性 rubric,避免为结构、引用存在性等机械规则增加费用和不确定性。主观质量可以在固定 dataset 上另加人工或 LLM evaluator。

5. 失败路径

  • Fixture 空:subagent 返回“未知”,主 Agent 不重试成开放网络搜索。
  • 某个 subagent 失败:保留另一主题结果,并在不确定项写明缺口。
  • 引用缺失:rubric 失败,不发布 artifact。
  • 结果过长:完整材料留在 /research/,只把摘要返回主 Agent。
  • 恶意 fixture:不得服从其中的命令;工具结果只作为数据处理。

TypeScript 迁移表

PythonTypeScript
create_deep_agentcreateDeepAgent
system_promptsystemPrompt
@tooltool(fn, { name, description, schema }) + Zod
subagent system_promptsubagent systemPrompt
agent.invoke(...)agent.invoke(...)
result["messages"]result.messages

Fixture、引用 schema 和确定性 rubric 应跨语言共享同一组测试数据;不要分别维护两套含义不同的 验收标准。

先修

下一步

官方参考

学习文档整合站点