Skip to content

第 14 章 · 综合实战三:规划、委派与审批的研究流水线 ​

把第 5、8、9、10 章合成一条研究流水线:write_todos 规划 → researcher 子智能体收集 → 按 skill 起草报告 → publish_report 前暂停等人工批准。检索走本地 fixture,审批是唯一的真实副作用点。

机制与设计选择 ​

子智能体是 stateless 的一次性收集器,只把结论带回主 agent,搜索过程不进主上下文;TodoListMiddleware 提供可检查的规划状态;interrupt_on 让发布可被拒绝。结构自检只验证 task 工具在位,不需要调用模型。

示例 ​

python
import os, pathlib, tempfile, shutil
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents.middleware import TodoListMiddleware
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command
from deepagents import create_deep_agent

FIXTURE_RESULTS = {
    "langgraph": "LangGraph: durable execution, streaming, human-in-the-loop.",
    "deepagents": "Deep Agents: harness with file tools, subagents, and context management.",
}

@tool
def search_topics(query: str) -> str:
    """Search the local course knowledge base (fixture)."""
    key = query.strip().lower().split()[0] if query else ""
    return FIXTURE_RESULTS.get(key, "No results.")

@tool
def publish_report(report: str) -> str:
    """Publish the final research report (mock side effect)."""
    return "published"

root = tempfile.mkdtemp(prefix="deepagents-pipeline-")
try:
    skill = pathlib.Path(root) / "skills" / "research-report"
    skill.mkdir(parents=True)
    (skill / "SKILL.md").write_text(
        "---\nname: research-report\ndescription: Produce a short structured research report\n---\n"
        "Structure: 1) Scope, 2) Findings (with source labels), 3) Open questions. Under 15 lines.\n"
    )
    model = ChatOpenAI(
        model=os.environ["MODEL_ID"],
        api_key=os.environ["DEEPAGENT_API_KEY"],
        base_url=os.environ.get("DEEPAGENT_BASE_URL", "https://api.openai.com/v1"),
    )
    researcher = {
        "name": "researcher",
        "description": "Collects findings from search_topics and returns concise bullets",
        "system_prompt": "Collect only what the task asks for. Return findings as short bullets with source labels.",
        "tools": [search_topics],
    }
    agent = create_deep_agent(
        model=model,
        tools=[search_topics, publish_report],
        subagents=[researcher],
        skills=[str(skill.parent)],
        middleware=[TodoListMiddleware()],
        interrupt_on={"publish_report": True},
        checkpointer=MemorySaver(),
        system_prompt=(
            "Plan with write_todos, delegate collection to the researcher subagent, "
            "draft the report per the research-report skill, then call publish_report."
        ),
    )
    # Structural check (no model call needed):
    available = sorted(agent.nodes["tools"].bound.tools_by_name)
    assert "task" in available, available
    print("task tool available; planning + subagent + approval wired.")

    if os.environ.get("DEEPAGENT_API_KEY") not in ("", "sk-fake"):
        cfg = {"configurable": {"thread_id": "ch14"}}
        paused = agent.invoke(
            {"messages": [{"role": "user", "content": "Compare langgraph and deepagents, then publish the report."}]},
            cfg,
        )
        assert paused["__interrupt__"]
        final = agent.invoke(Command(resume=True), config=cfg, version="v2")
        print(final.value["messages"][-1].content)
    else:
        print("model step skipped: DEEPAGENT_API_KEY not configured; structure check passed.")
finally:
    shutil.rmtree(root, ignore_errors=True)

运行与验收 ​

无 key 运行:打印 task tool available 与 model step skipped。配置 key 运行:第一次 invoke 停在 publish_report 之前(interrupt 非空),resume=True 后输出报告并打印 published。

容易踩的坑 ​

把 fixture 检索换成真实搜索后,子智能体的返回质量直接决定报告质量,委派描述要写清验收标准。审批不能替代幂等:resume 重放仍可能重复执行发布,生产要在 publish 侧加唯一请求 ID。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 子智能体回传什么?

2. task 工具缺了会怎样?

3. 发布副作用要防什么?

4. 规划工具的状态?

动手实践 ​

  1. 把 researcher 换成并行两个子智能体并合并结论。
  2. publish_report 加幂等键并测试重复 resume。
  3. 把规划状态推到前端进度条。

官方参考 ​

核验日期:2026-09-17,示例已在本机离线运行验证。