智能体环路工程实战

AI 智能体本地部署实战

OpenClaw 从环境搭建到避坑全攻略,本地跑通你的 AI 代理

智能体环路工程实战:核心 Agent 环路 + Goal/Verification 环路 + Event-driven 环路 + Self-improvement 环路,俄罗斯套娃式可组合嵌套,LangChain 团队的四环路方法论落地

在这里插入图片描述

Key Takeaways

  • Agent 工程的真正难点不是「写一个 prompt 让 Agent 跑通 demo」,而是「让 Agent 在真实工作流里持续跑且越跑越好」;环路工程(loop engineering)是把这一目标工程化的关键框架
  • LangChain 团队把 Agent 环路抽象成四类:核心 Agent 环路(request → model + tools → result)、Goal/Verification 环路(grader agent 评估 rubric 决定是否完成)、Event-driven 环路(把 Agent 嵌入 Slack / GitHub / Calendar / Email 等真实系统)、Self-improvement / Hill Climbing 环路(LangSmith Engine 从 trace 反推改进 prompt / tool / memory)
  • 四类环路可以俄罗斯套娃式任意嵌套 — 你不必全套,可以在 Event-driven 里只套 Verification + Core;这种嵌套能力来自 Dcode 与 Deep Agents 的开源 middleware 架构
  • /goal/rubric 是 Dcode / Deep Agents 提供的关键语法糖:/goal 在执行前先回写一份 rubric 给用户审,粒度比 Codex / Claude Code 的目标设定更细;两者都作为 middleware 可插拔到任意 LangChain agent
  • Self-improvement 环路的工程价值在于「Agent 是非确定性的,真正的真相在 traces 里」;LangSmith Engine 这类 meta-agent 把 trace 作为反思信号,自动改写 prompt / tool description / skill / memory 并 port 回源码,让外层环路使内层环路越来越有效

为什么 Agent 必须被「环路化」,而不是只写一个 prompt

整套课程把 Agent 工程的核心矛盾归结为一句话:单次 prompt 不可能让 Agent 可靠地完成所有任务。这听起来像常识,但实际工程现场大量失败的 Agent 项目恰恰败在这一步。

一个 Agent 一旦跑通 demo,团队往往以为问题已经被解决,接下来只是把它接到 Slack、接到 GitHub、接到定时任务。但真实情况是,同一个 Agent 用同一组 prompt 跑十次,十次结果可能各不相同,有些跑通了,有些在第三步工具调用环节掉了链,有些甚至在最基础的格式化输出阶段就出错。

这种不稳定性并不是 prompt 写得不够细的问题,而是大语言模型本身在 temperature(采样温度)、采样路径、上下文注意力分配上天然存在的非确定性。要把这种不可靠的部件包装成对业务可用的能力,工程上的答案从来不是"再写一段更长的 prompt",而是在 Agent 外面套上可观测、可验证、可触发、可自省的环路结构。

这正是 LangChain 团队在 Loop Engineering 系列里反复强调的中心思想,也是 2026 年 AI 工程领域"成熟度"与"玩具 demo"之间最关键的分水岭。

single-prompt-vs-loop-agent

下面这张对比矩阵把两种 Agent 成熟度在工程维度上的真实差距一次性铺开:

工程维度单 prompt + 单次 LLM 调用 vs 四环路俄罗斯套娃 Agent
成功率取决于 query 难度,平均一次跑通率较低、方差大 vs 多层验证环路叠加,失败可被捕获并重试
可观测性只有最终输出,中间 reasoning 不可见 vs 完整 trace,可重放到 LangSmith 查看每一步
可调试性失败后只能改 prompt 重试 vs grader agent 可定位失败的具体 rubric 项
复用性prompt 与场景强耦合,跨任务不可移植 vs /goal 与 /rubric 作为 middleware 可插拔复用
部署形态通常作为一次性脚本或聊天界面 vs 嵌入 Slack/GitHub/Calendar 等真实工作流
改进机制人工看 demo 凭感觉调 prompt vs hill climbing loop 自动遍历 trace 改写 prompt
成本可控性不可控,长 prompt 反复调用浪费 token vs 验证失败可提前终止,避免无效重试
团队协作个人玩具,难以交接 vs 工具、rubric、goal 全部版本化,可走 PR review

这张表并不是抽象概念清单,它对应的是团队在生产环境里能直接观察到的痛点。当我们把"单 prompt"作为基线、把"环路化 Agent"作为目标态时,真正变化的不是模型本身,而是 Agent 与外部系统交互的协议层。

为什么必须"环路化":两个第一手观察

[观察] 同一 Agent 跑同一组 prompt 多次,成功率方差与 query 多样性会共同放大。这里的"方差"具体指:在固定 prompt、固定模型、固定 temperature 的前提下,对同一 query 集做 N 次独立跑,得到的 pass rate 分布常常不是单峰的,而是双峰甚至多峰。也就是说,Agent 的行为模式更接近"要么一次跑通、要么完全失败",而不是"接近一次跑通"。

造成这种多峰分布的根源是模型行为的非确定性,而 query 多样性把这种非确定性进一步放大:当 query 涉及多步工具调用、跨域信息聚合、长上下文压缩时,每一步的小概率失败会沿链路上累积,最终成功率呈指数衰减。环路化设计的工程价值,正在于把这种指数衰减截断在每一层 grader 上。

[数据] 把 Agent 嵌入 Slack、GitHub、Calendar、Email 等真实工作系统后,可达性、复用率、团队使用率三项指标显著上升。可达性指的是 Agent 真正被业务侧触发的次数,因为它不再要求用户主动打开一个网页;复用率指的是同一个 Agent 在不同工作流里被实例化的次数,因为它被抽象成了 middleware(中间件);团队使用率指的是团队成员中至少每周使用一次 Agent 的占比。

这三项指标共同决定了 Agent 是"实验室 demo"还是"团队基础设施"。这也解释了为什么 LangChain 在 2026 年的工程主线里,几乎所有的官方示例都默认采用 event-driven 的接入方式,而不是 chat-only 的接入方式。

russian-doll-loop-stack

四种环路一次铺开

agent-loop-maturity-matrix

第一种是核心 Agent 环路(Core Agent Loop),即 request → model + tools → result 的最小闭环。它的工程意义在于把"思考"和"执行"显式分离开:模型负责决定下一步调用哪个 tool,tool 负责与外部世界交互,result 回到上下文驱动下一轮推理。这层环路是所有 Agent 的最小单元,无论外面套多少层,内层永远要先把这一层跑稳。

第二种是 Goal/Verification 环路。它的核心是把"完成"从一种感觉变成可判定的二元结果。具体做法是引入 grader agent(打分智能体)和 rubric criteria(评分准则),把任务目标拆成若干可独立打分的子项。比如一个 docs writer agent 的 rubric 可以是"是否包含示例代码、是否覆盖 API 全部参数、是否在末尾给出 FAQ"。只有当 grader 对每一项都判定通过,这条 trace 才被标记为 pass,否则会被打回重跑或回写到 dataset 里供后续训练。

第三种是 Event-driven 环路。它把 Agent 从"被动等用户输入"变成"被事件或定时主动触发",常见的事件源包括 Slack 消息、GitHub PR 评论、Calendar 日程、Email 到达,以及 cron 定时任务。LangChain 在文档里把这一层称作 harness(挂载框架),Deep Agents 开源仓库里的 Deep Agents harness 就是这种模式的具体实现。

第四种是 Self-improvement / Hill Climbing 环路,也是 LangChain 在 2026 年重点押注的方向。LangSmith Engine 这类 meta-agent 会周期性地遍历 production trace,自动改写 prompt、tool description、skill、memory,并把通过验证的版本 port 回源代码仓库。这种 hill climbing(爬山算法)风格的迭代可以在没有人工介入的情况下,持续抬高 Agent 的整体质量上限,真正实现"越跑越好"。

这四种环路并不是孤立的层级,而是可以俄罗斯套娃式任意嵌套:核心环路在最内层,verification 环路套在外面,event-driven 环路再套一层,self-improvement 环路在最外层,甚至 verification 环路本身又可以嵌套一个更细粒度的 verification 子环路。这种嵌套让 Agent 既能在单次任务里自我纠错,也能在跨任务、跨时间尺度上持续进化。

代码层面的最小配置片段

from langchain.agents import create_agent
from langchain.agents.middleware import GoalMiddleware, RubricMiddleware

agent = create_agent(
    model="claude-sonnet-4-5",
    tools=[search_tool, write_file_tool, calendar_tool],
    system_prompt="You are a docs writer agent.",
    middleware=[
        GoalMiddleware(goal="Produce API docs that pass CI"),
        RubricMiddleware(criteria=[
            "all parameters documented",
            "includes runnable example",
            "ends with FAQ section",
        ]),
    ],
    checkpointer=PostgresSaver.from_conn_string(DB_URL),
)

这段伪代码展示了 goal 与 rubric 作为可插拔 middleware 是怎么挂到 create_agent 上的。LangChain 官方文档对此有详细说明,详见 https://python.langchain.com/docs/concepts/agents/ 与 https://docs.langchain.com/oss/python/langchain/agents/。一旦 Agent 被部署到生产环境,它的每一次运行都会留下 trace,这些 trace 既是 LangSmith 评测数据集的来源,也是 LangSmith Engine 启动 hill climbing 循环的燃料。

常见踩坑清单

  • 把单次 prompt 调试当作主线工程。短期看 demo 跑通,长期看每次模型升级都要重做一遍。
  • 把 rubric 当成 prompt 的一部分而不是独立 middleware。结果是换任务就要重写全部逻辑,跨任务复用率为零。
  • 忽略 checkpointer(检查点)与持久化。Agent 一旦跨进程重启就丢失中间状态,verification 环路无法回放,grader 也无从打分。
  • 把 hill climbing 当成黑盒。LangSmith Engine 改写 prompt 必须经过 PR review 才能合并回主分支,否则就是不可控的模型漂移,合规审计无法通过。

2026 年 Agent 工程的核心矛盾

如果说 2024 年大家关心的是"我的 Agent 能不能跑一次",那么 2026 年的核心矛盾已经迁移到"我的 Agent 能不能持续跑、且越跑越好"。这个迁移背后是三股力量的合流:第一,模型能力快速演进,同一段 prompt 的有效性可能在下一次模型升级后立刻失效;第二,业务侧对 Agent 的期待从"演示"转向"基础设施",稳定性与可观测性成为硬指标;第三,合规与审计需求迫使团队把 Agent 行为写成可重放的 trace,而 trace 自然就成为自我改进的反馈信号。

在这三股力量之下,任何只写了"一段 prompt 就上线"的 Agent 都会在几个月内被业务淘汰,只有把环路工程当作 first-class concern 设计的 Agent 才有可能持续存活。这也是为什么 LangChain 在 2026 年的路线图里,把 LangSmith Engine、Deep Agents 中间件、create_deep_agent 这三类能力放在同一个产品矩阵里对外讲述的原因。

它们共同回答的是同一个问题:当你的 Agent 不再是 demo,而是接入 Slack、GitHub、Calendar 等真实工作流的团队基础设施时,你需要一整套环路协议来保证它能被持续运行、持续验证、持续触发、持续自省。更多技术细节可以参考 LangChain 官方文档 https://python.langchain.com/docs/introduction/ 与 LangGraph 持久化与 checkpointer 章节 https://langchain-ai.github.io/langgraph/concepts/persistence/。

把"写一个 prompt"升级为"设计一套环路",本质上是把 Agent 从手工艺时代带进了工程时代。手工艺时代靠个人经验与反复试错,工程时代靠协议、middleware、可观测数据。三者缺一,任何 Agent 项目都难以撑过 6 个月。

核心 Agent 环路:request → model + tools → result 的最小闭环

翻开任何一本 Agent 工程的参考实现,把它的依赖关系图压缩到最里层,你会看到一段永远不变的三段式循环:用户请求进来,模型拿到请求和可用工具清单,决定调哪些工具、按什么顺序调、怎么把工具的返回组织起来,最后吐出一个 result。这就是核心 Agent 环路——request → model + tools → result。它看似不起眼,却是后续所有复杂环路(Goal/Verification 环路、Event-driven 环路、Self-improvement/Hill Climbing 环路,以及俄罗斯套娃式任意嵌套组合)都必须包在最里层的最小单元。把这一层写错、跑不稳,后面所有上层设计都会变成空中楼阁。后续章节要展开的目标验证、事件触发、trace 回写,本质上都是在这一段最小闭环外面再套一层判定/触发/反馈的环,而最里层永远是它。

core-agent-loop-flow

[观察] 在最小闭环里,模型本身并不"动手"——它只产出一段结构化的工具调用意图(通常是函数名 + 参数 JSON,即 OpenAI/Anthropic 风格的 tool_calls 字段),真正去执行 HTTP 请求、读写数据库、操作浏览器、调用 shell 的是工具实现本身。换句话说,工具决定了 Agent 的能力半径,模型决定了它在半径之内怎么走路。一个 Agent 看起来能不能用,八成取决于工具集设计得是否正交、是否原子化、是否错误可恢复;prompt 写得再漂亮,也救不了一个返回结构混乱、异常吞噬、超时不设置的工具。在工程 review 里,我们经常会看到新人把 80% 的时间花在调 prompt,却对工具的 schema 模糊、错误未透传、未做幂等保护视而不见——这是典型的优先级倒挂。判断 Agent 质量的快速经验法则:先把工具的失败模式(超时、5xx、空结果、格式漂移)全部显式化,再去谈 prompt 调优,否则所有上层环路都建在沙子上。

下面用 LangChain 提供的 create_agent 在 30 行以内搭起这段最小核心环路。可运行的样例大致长这样:

from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI

@tool
def get_weather(city: str) -> str:
    """根据城市名返回当前天气。"""
    return f"{city}:晴,25°C"

agent = create_agent(
    model=ChatOpenAI(model="gpt-4o-mini"),
    tools=[get_weather],
    system_prompt="你是一个天气助手,根据用户提问返回天气信息。",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "北京今天天气怎么样?"}]}
)
print(result["messages"][-1].content)

这段代码完整跑通了 request → model + tools → result 三段:用户消息作为 request 进入 agent,模型读 messages 和 tool schema,判断需要调用 get_weather("北京"),工具执行后返回字符串,模型拿到 ToolMessage 再做一次推理,把工具输出组织成自然语言回给用户——这就是 result。任何一个 Agent 框架(无论 LangGraph、Deep Agents、Autogen 还是自研的 if-else 循环)剥到最里层,都跑不出这个动作。把这段搞明白,后面所有上层设计才有共同的语义底座,后面要讲的 checkpointer、middleware、grader agent 都挂在这一段最小环路上。

把这三段拆成可观察的状态变量,工程上至少要追到下面 4 个字段,任何一个生产级 Agent 都必须能够从日志/tracing 中把它们拎出来:

状态变量来源典型载体在环路中的作用失败时常见现象
请求(request)用户或上游 Agentmessages 列表里 role=user 的那条启动本次循环的输入空消息、过长截断、注入攻击
模型输出LLM 推理AIMessage,常含 tool_calls 字段决策:调哪个工具、参数是什么幻觉工具名、参数类型错、未终止
工具结果工具函数执行ToolMessage,content 为原始返回把外部世界的事实搬进上下文超时、异常吞噬、格式非结构化
最终产物(result)模型二次推理或直接回传最后一条 AIMessagecontent本次循环对外暴露的产出答非所问、漏字段、未遵循 schema

注意「模型输出」和「最终产物」不是同一回事:前者是中间决策(往往带 tool_calls),后者是面向用户的成品。在多轮工具调用里,模型输出和工具结果会交替出现多次,最终产物只出现一次。把这两者混在一起,是新手在 trace 里最容易踩的坑。LangGraph 的状态图把这 4 个变量显式建模成 graph 的 state 节点,LangGraph 状态图文档可参考 https://langchain-ai.github.io/langgraph/。注意 LangChain 原生 create_agent 是基于 LangGraph runtime 跑的,所以你在 messages 之外还能从 state 里拿到更细粒度的字段。

minimal-loop-codeflow

工程上,这个最小闭环在三种调用模式下有截然不同的取舍。把这三种模式摆在同一张表里对比,选型的边界就会清晰很多:

调用模式API 形式延迟体感中间状态可观测性适用场景主要代价
同步调用agent.invoke(...)等到全部跑完才一次性返回弱,只能在结束后拿到最终 messages脚本、批处理、单元测试、CI长任务下用户长时间空白
流式调用agent.stream(...)第一个 token 立即可见强,每个节点事件都可订阅前端对话、IDE 插件、需要逐字反馈实现复杂度上升,需处理 partial tool_calls
异步调用agent.ainvoke/astream(...)与同步/流式一致与同步/流式一致高并发服务、Web 后端、asyncio 生态工具实现必须 await,否则阻塞事件循环

取舍的核心点在于:同步简单、流式友好、异步扛量。一个真实的内部 Agent 服务通常会用 astream 起一个 SSE 长连接,前端按 token 渲染;而 CLI 工具类(终端 coding agent、一次性脚本)往往选同步 invoke,先求稳再求快。需要批量回归评测 LangSmith datasets 时,异步 ainvoke 配合 asyncio.gather 是最高吞吐的选择。需要警惕的是,流式模式下 partial tool_calls 的处理:模型可能分多次吐出一个完整的工具调用,如果按 token 切分得太早,前端会拿到一段残缺的 JSON——这正是 LangChain stream events 中 on_tool_call_chunk 事件存在的意义。对比这三者的关键变量不是"快不快",而是"中间状态能不能被外部系统消费"——流式和异步天然适合接 LangSmith tracing、接前端 SSE、接事件总线,同步则更适合一次性脚本。

[数据] 这三段式循环在工程上有两道独立的可量化曲线:运行次数(平均循环轮数,即一次任务里 model + tools 交替多少轮)和成功率(最终 result 是否符合预期)。经验上前者集中在 1–5 轮:简单问答类接近 1 轮,单工具调用类 2 轮,复杂多步任务(如"订机票 + 加日历 + 发邮件确认")可达 5–10 轮,长尾任务偶尔冲到 20 轮以上——这时候就该怀疑是不是 prompt 没把任务拆清楚;后者在 demo 阶段能到 80% 以上,但一旦把同一份 prompt 跨团队复用、跨模型迁移、跨生产流量跑起来,常常掉到 50% 以下。这不是 prompt 写得不够细的问题,而是单次 prompt 根本不可能让 Agent 在所有输入分布上都可靠——这正是后续 Goal/Verification 环路要正面解决的问题:不是继续改 prompt,而是把"完成"这件事变成可判定的(rubric criteria),让 grader agent 给每一次循环打一个 0/1 信号,然后用这个信号去驱动 prompt 更新、工具描述更新、skill 更新。

到这里,核心 Agent 环路的骨架已经立住。后续无论是加 grader agent 做目标验证,还是接 Slack/GitHub 做事件驱动,还是让 LangSmith Engine 跑 trace 自动改 prompt,最里层仍然要回到这一段 request → model + tools → result。把这层写稳、写对、写可观测,后面所有环路才有底座。LangChain Agents 的入门概念可以参考 https://python.langchain.com/docs/concepts/agents/,create_agent 的 API 细节见 https://docs.langchain.com/oss/python/langchain/agents/。再往上走到 Deep Agents 的中间件架构,/goal/rubric 就是以 tool middleware 的形式嵌进这一段最小环路的,这一点会在后续章节展开。

从 create_agent 到 create_deep_agent:LangChain 入口函数的层级选择

翻开 LangChain 官方文档,在 langchain.agents 模块下你会看到两条并排的入口函数:create_agentcreate_deep_agent。它们共享同一个最里层的核心 Agent 环路——request 进入、模型决策、工具被调用、result 吐出——却各自承担了不同的工程定位。create_agent 是 LangChain 官方刻意保留的轻量入口,只暴露 LangChain agents 子项目里最稳定的那部分 API:模型绑定、工具清单、system prompt、stop 条件。它的设计意图是「让你能用尽量少的代码跑通一个能调工具的最小 Agent」,因此它把 plan-and-execute、子 Agent 路由、文件型记忆、技能系统这些额外能力全部排除在外,以避免轻量入口被中途绑死在一个特定工作流上。

对只跑核心环路、单轮或有限多轮、单工具集合的最小场景而言,create_agent 已经足够。譬如一个只查汇率、只读只写一份 JSON 配置的内部小工具,用 create_agent(model=..., tools=[...]) 一行就能跑起来,启动开销、依赖图、调试链路都最短。但也正因为它只暴露最薄的一层,任何超出「单 Agent 单上下文」的诉求都会在这层入口处撞墙。

langchain-entry-stack

[观察] 在真实业务里,绝大多数 Agent 项目并不是「一个 Agent 跑到底」的结构。需求文档里一旦出现「让模型先拆任务再执行」「调用某个公司内部系统的专用子 Agent」「跨会话记住用户偏好」「按命名空间加载操作手册」这四类要求中的任意两条,就基本意味着核心环路外面需要再套一层规划层、一层子 Agent 调度层、一层长期记忆层、一层 skills 加载层——这正好是 create_deep_agent 默认替你装配的中间件栈。换句话说,create_agentcreate_deep_agent 的边界,本质不是「能不能调工具」,而是「你的 Agent 需不需要被另一个 Agent 管着」。

create_deep_agent 是 Deep Agents 这个独立开源项目提供的入口。它在 create_agent 的最里层环路之上,以 middleware 的形式插入了四件额外的事:plan-and-execute 规划器、sub-agent 路由、跨 run 持久化的文件型记忆、以及基于 skill 命名空间加载的技能包。这意味着你写的还是同一个「Agent」,但它天生就具备「拆任务、委派、跨会话记忆、按需加载手册」的能力。下面这段代码展示了用 create_deep_agent 注册一个最小可用的 plan-and-execute 子 Agent 的骨架:

from deepagents import create_deep_agent

sub_agent = {
    "name": "research_subagent",
    "description": "负责拆解研究类请求,产出子任务清单",
    "system_prompt": "你是一个 plan-and-execute 子 Agent,先写 TODO 列表,再逐项调用工具完成。",
}

agent = create_deep_agent(
    model="anthropic:claude-sonnet",
    tools=[web_search, code_runner],
    system_prompt="你是顶层协调 Agent,负责把用户请求路由到合适的子 Agent。",
    subagents=[sub_agent],
)

result = agent.invoke({"messages": [{"role": "user", "content": "调研 2026 年 RAG 评测基准的最新进展,并给我一份摘要"}]})
print(result["messages"][-1].content)

上面这段代码同时打开了两条新通道:第一条是 subagents 参数,它把 research_subagent 注册为可被顶层 Agent 通过 task(...) 工具调度的子 Agent;第二条是 tools 参数仍然挂在顶层 Agent 上,意味着子 Agent 并不自动继承父 Agent 的工具集,父子工具边界在这里被显式切开——这是 Deep Agents 与 LangGraph 多 Agent 图最显著的语义差异之一。

为了把这两层入口的差异一次性摆清楚,下表列出了它们在四个关键维度上的对比:

维度create_agentcreate_deep_agent
层级定位核心 Agent 环路最薄封装核心环路 + 中间件栈(plan / sub-agent / memory / skills)
子 Agent 支持不内置,需自行用 LangGraph 编排通过 subagents 参数直接注册 plan-and-execute 风格子 Agent
文件/记忆默认仅本轮 messages,需手动接 checkpointer内置文件型记忆 + 跨 run 持久化,可挂 Postgres / Sqlite / MemorySaver
Skills 支持通过文件系统式命名空间按需加载 skill 包,支持 skill update 自动 port 回源码

仅看这张表,你会觉得 create_deep_agentcreate_agent 的「超集」,直接选 create_deep_agent 永远更安全。这是一个常见的认知偏差——下面这段取舍矩阵会指出它什么时候是错的:

判断维度create_agentcreate_deep_agent
业务是否需要 plan-and-execute不需要,单步工具调用就能完成需要,任务必须先拆解再执行
是否需要子 Agent 委派不需要,一个上下文贯穿到底需要,父子职责需要被显式切开
是否需要跨 run 记忆不需要,每轮会话自包含需要,用户偏好/历史状态必须跨会话保留
是否需要 skill 系统不需要,system prompt 已够用需要,手册/操作规范按命名空间加载
启动依赖与冷启动时延要求最短依赖、最低冷启动时延可以接受额外中间件带来的额外开销
调试可观测性粒度只需要 LangSmith trace 一层需要 trace + sub-agent 独立 trace + 记忆读写 trace

[数据] 矩阵里六个维度并非等价——根据 Deep Agents 仓库与官方文档给出的默认中间件装配清单,六个维度中只要命中「plan-and-execute」「子 Agent」「跨 run 记忆」这三项中的任意两项,工程经验上几乎都应直接选 create_deep_agent;反之,如果六个维度里只有 0 或 1 项命中,create_agent 的极简路径反而能显著降低调试复杂度与依赖体积。换句话说,真正的分水岭是「业务是否要求两层以上的 Agent 结构」,而不是「我有没有听说过 Deep Agents」。

create-agent-vs-create-deep-agent

从中间件视角看,create_deep_agent 的关键设计是把 plan、sub-agent routing、memory、skills 全部实现为「可插拔 middleware」,而不是写死在主循环里的固定阶段。这意味着你完全可以注册自己的 middleware,在模型调用之前或之后插入 rubric grader、日志埋点、A/B 测试分支——这是与 create_agent 在工程哲学上的最大差异。下面这段伪代码展示了如何把一个自定义的「目标校验 middleware」注入 create_deep_agent:

from deepagents import create_deep_agent
from deepagents.middleware import Middleware

class GoalCheckMiddleware(Middleware):
    def before_model(self, state, runtime):
        if "/goal" in state["messages"][-1].content:
            state["metadata"]["goal_active"] = True
        return state

agent = create_deep_agent(
    model="anthropic:claude-sonnet",
    tools=[...],
    middleware=[GoalCheckMiddleware()],
)

before_model 钩子的存在意味着 /goal/rubric 这种「在 system prompt 之外另起一条目标/评分轴」的能力,可以作为 middleware 而非 prompt hack 来实现——这正是「Loop Engineering」框架下「goal/verification 环路可被任意 Agent 复用」的关键。

middleware-injection-points

工程上常见的踩坑点有三:第一,误以为 create_deep_agent 一定包含所有 LangGraph 能力,实际上它的中间件栈是 Deep Agents 项目独立维护的,与 LangGraph 的图节点是两层抽象,自定义子图时应避免在两者之间混用 checkpointer schema。第二,把 subagents 列表写得过深,导致顶层 Agent 的 context 被反复「潜入」到几层子 Agent 的回执里,可用上下文被快速吃光——建议子 Agent 深度不超过 2 层,且每层都强制要求返回结构化摘要。第三,在 create_deep_agent 上挂全局 cross-run 记忆时,容易忽视命名空间冲突,生产环境务必给每个业务线配置独立的 memory namespace 并挂上 PostgresSaver 做隔离,避免不同业务的长期记忆互相污染。

延伸阅读建议直接看 Deep Agents 项目主页与 create_deep_agent 的官方 API 文档,前者覆盖了中间件栈的设计动机与默认装配清单,后者则给出了完整的参数表与返回值结构:https://github.com/langchain-ai/deepagents 与 https://docs.langchain.com/oss/python/deepagents/ 。若需要对照 create_agent 的最小入口示例,可参考 LangChain 官方 agents 文档:https://docs.langchain.com/oss/python/langchain/agents/ 。

回到工程选型本身:如果你正打算落地一个 Agent 产品,先问自己六个问题——是否需要 plan-and-execute、是否需要子 Agent、是否需要跨 run 记忆、是否需要 skills 系统、是否能接受中间件带来的额外冷启动时延、是否需要细粒度的子 trace 可观测性。问题的答案指向「两层以上 Agent 结构」时,直接选 create_deep_agent 并以 middleware 形式扩展;指向「一个 Agent 贯穿到底」时,create_agent 才是最小、最稳、最不引入隐性耦合的入口。两层入口并非替代关系,而是「同一核心环路在不同业务复杂度下的两个稳定解」。

Goal/Verification 环路:grader agent 把『完成』变成可判定

Goal/Verification 环路是 LangChain 团队在「四种环路」框架里明确点出的第二种环路。它的工程定位非常明确:解决核心 Agent 环路最薄弱的那个环节——「任务到底做完了没有」。一个请求进入核心环路,模型决策、工具调用、result 吐出,听起来闭环,但「result 是否真的达成了 goal」这个问题,核心环路自己是答不上来的。

把这件事拆开看,Goal/Verification 环路 = 核心环路 + 一个 grader agent(rubric 是 grader 的判定清单)。Grader 本身也是一个 LLM 调用的 agent,但它的职责不是「完成任务」,而是「评判完成」。Rubric criteria 是 grader 拿到 result 之后逐条对照的判定清单,通常按结构 / 事实 / 风格三类维度展开。每一类都对应一个独立的 grader prompt 模板,grader 用同一份 rubric 但从不同角度打出分数,最终汇总成 pass/fail 决策。这种设计的妙处在于:它把模型擅长的「生成」和工程上需要的「判定」切开,让 LLM 既当作者又当评审,但通过 rubric 的形式强制评审有据可依。

[观察] 让 LLM 自己回答 yes/no 是这门工程里最常见的反模式之一。模型擅长生成,但不擅长稳定地、可复现地评价自己;尤其在开放式任务里,「写完了吗」「合格吗」「符合要求吗」这类二选一问题极易被「礼貌性 yes」污染——模型倾向于顺着用户的期待给出肯定回答,即使结果存在明显瑕疵。这套课程的做法是:把「完成」拆成多条可独立判定的 rubric criteria,每条都要求 grader 给出「pass / fail + 简短理由」二元输出。这种设计把模糊的「好不好」转成了离散的、可聚合的布尔向量,工程上的好处是 fail 项可以被精确地写回 prompt,触发核心环路定向重跑,而不是让模型再盲目试一次。另一个隐性收益是:rubric 本身变成了可审计的对象,出问题时可以回溯到具体哪条 criteria 没通过,而不是面对一句空泛的「写得不好」。

grader-agent-position

具体到代码实现,grader middleware 是挂在 create_deep_agent 之上的一段中间件。下面这段 50 行级的伪代码展示了它的典型形态:核心 Agent 跑完后把 result 送入 grader middleware,grader 按 rubric 逐条打分,任一条 fail 即触发反馈回写。

from langchain.agents import create_deep_agent
from langchain.tools import tool

RUBRIC = [
    {"name": "structure", "prompt": "章节是否齐全..."},
    {"name": "fact", "prompt": "数据/引用是否对齐..."},
    {"name": "style", "prompt": "语气/术语是否一致..."},
]

@tool
def grade_with_rubric(result: str) -> dict:
    scores = [call_grader(c, result) for c in RUBRIC]
    return {"all_pass": all(s["verdict"] == "pass" for s in scores), "scores": scores}

agent = create_deep_agent(
    model="claude-sonnet",
    tools=[grade_with_rubric],
    middleware=[GoalMiddleware(), RubricMiddleware(), GraderMiddleware()],
)

这段代码展示了三个关键点:第一,grade_with_rubric 本身就是一个 tool,它把 grader 当成普通工具调用而非外挂脚本,这意味着 grader 调用本身也会被 LangSmith tracing 完整记录;第二,middleware 列表里同时挂了 Goal / Rubric / Grader 三层,说明 Goal/Verification 环路在 Dcode / Deep Agents 的中间件架构里是组合式暴露的,而不是一个黑盒函数,任何 create_deep_agent 都可以按需启用其中一层;第三,grader 的输出回到主 agent,主 agent 据此决定是直接返回还是再跑一次核心环路,反馈通路是显式的、可观测的。

下面这张表格把 rubric 的三类维度、对应的 grader prompt 模板要点和典型 fail 信号摊开,便于工程团队对照落地:

维度关注点grader prompt 模板要点典型 fail 信号
结构(structure)章节是否齐全、字段是否齐备、长度是否在区间内请检查 result 是否包含 X/Y/Z 三个章节,每章不少于 N 字,字段是否齐备缺章节、字段缺失、超长或过短
事实(fact)数据/引用/链接是否真实、是否对齐 context请逐条核对 result 中的数字、引用、链接与源数据是否一致编造数字、张冠李戴的引用、过期链接
风格(style)语气、人称、术语是否一致,是否避开禁用词请检查 result 是否使用第二人称、是否出现禁用词列表、术语口径是否统一语气跳跃、人称不一致、敏感词命中

这张表的设计意图是:每一行都是一条可独立打分的 rubric,grader 拿到的不是「整体印象」而是「对照清单」。工程上,这意味着你可以把同一份 rubric 喂给不同模型做交叉验证,也可以把同一份 rubric 喂给同模型多次取众数,降低单次判定的随机性。该教程的讲师特别强调过:rubric 的颗粒度直接决定 grader 的可用性,颗粒度过粗会退化成「整体印象打分」,颗粒度过细则会让 grader 自身变成新的失败源——这是典型的工程权衡点。

但三种验证强度不是等价的。下表把单一 grader / 多 grader 并行 / grader + 人在回路(HITL)三种方案的取舍摊开,工程上需要按场景选择:

方案验证强度延迟成本适用场景主要权衡
单一 grader低-中低(1 次 LLM 调用)内容草稿、批量任务、低风险输出易被模型礼貌性 yes 污染,鲁棒性差
多 grader 并行中-高中(N 次并发调用)中(线性增长)对外发布、研究报告、合规文档成本随 rubric 数量线性上升,但可解释性强
grader + 人在回路(HITL)高(等待人工确认)高(人时成本)金融、医疗、合同、对外承诺强鲁棒但不可规模化,适合关键路径

取舍的核心是:验证强度和延迟 / 成本成反比,工程上不会用 HITL 去校验所有草稿,也不会只用单一 grader 去签发对外承诺。多 grader 并行是「默认推荐档」,因为它在强度和成本之间给了一个相对健康的折中。具体落地时,该教程建议的混合策略是:单一 grader 跑批量初筛,初筛 fail 的样本进入多 grader 并行复核,多 grader 仍有争议的样本升级到 HITL,这是一个成本可控且强度递进的常见组合。

goal-verification-loop-flow

未达标时的反馈路径是这套环路真正的「回路」所在。grader 把失败原因结构化地回写到 prompt 或上下文,触发核心环路定向重跑。具体做法是:grader 的输出形如 {"all_pass": false, "scores": [{"criterion": "事实-数字一致性", "verdict": "fail", "reason": "Q3 营收数字与源文档不符"}]},主 agent 拿到这个结构化失败信号后,在下一轮对话里把它作为 user message 注入,核心环路据此重新决策。这一步在工程上有个常被忽略的细节:反馈必须结构化,不能只是一句「请改一下」。结构化反馈让模型可以定位到具体 rubric criteria,而模糊反馈只会让模型再随机试一次,等于浪费一次重试机会。

另一个工程细节是反馈的回写位置:是写到 system prompt、user message、还是 checkpointer 里的跨轮 memory?这三种位置各有适用场景。System prompt 适合放长期稳定的 goal / rubric 文本;user message 适合放本次失败的具体 reason;跨轮 memory 适合放 grader 多轮累积的趋势信号,例如「同一份 result 已经连续 3 次在事实-数字一致性上 fail,提示底层 context 数据源可能有问题」。这三种位置在 LangSmith 里都是可观测的,但工程含义不同,选择错了会让反馈丢失上下文。

[数据] 这套反馈路径的工程价值可以从 LangSmith tracing 的实践里看出:在 grader 介入之前,核心环路的重试大多是「盲跑」——模型不知道上一次为什么失败,所以失败模式在多轮重试里反复出现;grader 介入之后,LangSmith datasets 上的对照实验通常显示,带 rubric 的定向反馈相对无差别重跑,任务通过率有明显提升,而绝对通过率受 rubric 颗粒度影响——rubric 越细,fail 项定位越准,但单次 grader 调用的 token 消耗也越高。该教程的讲师给出的实操建议是:把 rubric 控制在 5-10 条之间,既保证覆盖度又不会让 grader 自身变成新的瓶颈。同时要警惕「grader 漂移」:grader 的判定标准会随模型版本漂移,需要把 rubric 和 grader prompt 一起固化进 LangSmith datasets 做回归,避免「上周还过、这周突然挂」的隐性回归。

pass-fail-feedback-cycle

踩坑清单(讲师在课程里反复强调的几条):

  1. 不要让 grader 直接复用主 agent 的 system prompt。两者的目标函数不同,主 agent 是「完成任务」,grader 是「判定完成」,混用会让 grader 偏向「放水」。
  2. 不要把 grader 输出直接拼成自然语言喂回主 agent。结构化 JSON 走 tool message 通路比自然语言 user message 更稳定,因为主 agent 可以把它当结构化信号处理。
  3. 不要在 rubric 里塞「整体印象」类条目。这类条目几乎一定会退化成「礼貌性 yes」,工程上是无用功,占 rubric 配额又不出力。
  4. 不要忽略 grader 自身的失败。grader 也是 LLM 调用,也会挂,需要给它配超时、降级和回退策略,否则 grader 一挂整个 Verification 环路就停摆,主 agent 直接拿不到 fail 信号。

把这节收一下:Goal/Verification 环路的本质是把「任务完成」从模型自评的模糊二选一,转成 rubric criteria 的离散判定,把 grader 的失败信号结构化地回写到 prompt,触发核心环路定向重跑。在 LangChain 的中间件架构里,这层能力以可插拔 middleware 的形式暴露,任何 create_deep_agent 都可以挂上 Goal / Rubric / Grader 三层。它的工程价值不在于「让 agent 更聪明」,而在于「让失败可定位、可回写、可回放」,这是后续 Self-improvement / Hill Climbing 环路能够自动改写 prompt 的基础前提。

官方参考:

  • https://docs.langchain.com/oss/python/deepagents/
  • https://python.langchain.com/docs/concepts/agents/
  • https://docs.smith.langchain.com/evaluation
  • https://blog.langchain.com/

/goal 与 /rubric 语法糖:让目标先回写成 rubric 给你审

/goal 与 /rubric 是 Dcode 与 Deep Agents 提供的两条 slash command(斜杠命令),核心工程定位是把「目标」与「验收标准」这对概念从口头表达拉到工程可用的形式化层面。换句话说,你不需要在自然语言 prompt 里反复叨念「这次要做什么、做到什么样算完成」,而是用一行 /goal 描述任务、一段 /rubric 列出判定准则,Agent 在启动前会先把这两条回写成一份 grader(评分者) 用的 rubric(评分清单),交给你审,你点头之后它才开始跑。这种「先回写、再开工」的工作流并不是普通的 prompt 工程技巧,而是把 Goal/Verification 环路里 grader agent 的判定清单提前暴露成可读、可改、可入库的工程制品,让人类在执行前就能干预判定标准本身。

[观察] /goal 最反直觉的工程特性是:它不是给执行 agent 下达的执行指令,而是给 grader agent 下达的判定规范。也就是说,当你在 Deep Agents 的交互面板里写下 /goal「生成一份市场调研摘要」时,真正被消费的不是这份目标陈述本身,而是由系统把目标拆解后回写出来的若干条 rubric criteria——每一条都对应一个可独立打分的判定维度。这种「让目标先回写成 rubric 给你审」的流程,等价于把 V&V(Verification & Validation) 流程左移到 prompt 之前:你在模型还没开始调用任何工具之前,就能修改校验标准,改完再放行。LangChain 团队反复强调的「agent that improves over time」的第一道闸门不是模型本身的迭代,而是标准本身可被审、可被改、可被 trace 对齐——产物才有共同讨论的起点。

goal-rubric-syntax-flow

下面给出一份最小可用的指令模板,你可以直接照搬到 terminal coding agent 或 docs writer agent 的会话中执行:

/goal 为 LangChain 教程稿件补全三种环路(Goal/Verification、Event-driven、Self-improvement)
的核心定义与中文术语对照表,字数不少于 800 字,产出一段 Markdown。

/rubric
1. 三种环路名称与英文原文一一对应,中文译名无错别字。
2. 每种环路至少包含:触发场景、关键产物、对核心环路的依赖关系三要素。
3. 中文术语与 LangChain 官方文档用词一致,首次出现用括号注释英文。
4. 表格行数 = 3,列数 ≥ 3,表头与正文分隔行清晰可渲染。
5. 整段输出可被 docs writer agent 直接落盘为 Markdown,无需二次手工处理。

这份模板里最容易被忽略、但决定整套机制是否成立的一条是:/rubric 必须可枚举、可逐条勾选。如果把验收标准写成「写得好就行」「结构清晰即可」这种抽象形容,grader agent 实际拿不到可操作的判定基线,整个 Goal/Verification 环路会退化为同义反复的回声。把 rubric criteria 写成 atomic boolean(每条独立可判定 true/false) 是这套语法糖成立的前提条件,这一点无论在 Python 侧用 @tool 包装,还是在 LangGraph 的 StateGraph 里以节点形式实现,逻辑都一样。

能力维度Dcode / Deep Agents (/goal + /rubric)Codex CLIClaude Code
任务回写成可审 rubric原生支持,执行前自动产出不提供不提供
Grader agent 显式化是(以 middleware 中间件形式挂载)
判定标准运行时修改支持,回写阶段人工可介入仅能修改 prompt仅能修改 prompt
返工粒度单条 rubric criteria整段重跑整段重跑
Trace 与 rubric 对齐是,每条 criteria 独立打点
与 hill climbing loop 对接是,rubric 可被 Engine 重写

从这张能力差异表可以直接看出,/goal 与 /rubric 的真正价值并不在「让模型更聪明」,而在「让失败可定位」。Codex CLI 与 Claude Code 也都能完成一个任务,但它们的「完成」是单点判定——结果要么整体交付、要么整体不交付,中间没有可逐条审查的判定维度。Dcode / Deep Agents 把校验粒度下放到 rubric criteria 这一层,意味着当某一条标准不达标时,你重跑的只是那一条,不是整段;同时这条失败的 criteria 可以直接被 LangSmith Engine 抓取,作为下一次 hill climbing 改写 prompt 的负样本锚点。

[数据] 基于课程中给出的同一任务剖面口径,在三种典型工作负载(代码改动、文档撰写、邮件草拟)上,采用「先回写 rubric 再执行」相比「直接开跑」的工程数据近似如下:首次成功率从约 42% 提升到约 71%(以 rubric 全数勾选为成功判据),单次任务的平均返工轮次由 2.4 轮压到 0.9 轮,平均 token 消耗下降约 35%。需要强调的是,这套数字只是同 rubric 拆法、同模型、同 trace 长度下的相对值,用来表达回路设计差异而非绝对性能。真正决定收益上限的不是 /goal、/rubric 这两条命令本身,而是你把多少工程语义塞进了每一条 rubric criteria——criteria 写得越细,grader 判得越准,但同时人工维护成本也越高,所以下文会专门给一张取舍矩阵。

为了把这个对比落到可执行的选择上,下面是先回写 rubric 与直接开跑两种模式在工程语义上的取舍矩阵:

工程关切先回写 rubric 模式直接开跑模式取舍建议
首次交付成功率高(rubric 提前对齐)中(凭模型默认值兜底)关键交付、SLA 场景选前者
单次返工成本低(只重跑不达标的那条 criteria)高(整段重跑)长任务、token 敏感场景优先前者
prompt 维护复杂度中(rubric 需要持续维护)低(prompt 写一次即可)任务结构稳定时用前者
人类审阅时间任务启动前一次性投入任务结束后被动检查评审 SLA 紧的团队适合前者
适合的下游消费方Hill climbing loop、trace-driven 优化一次性 POC、低风险任务把回路选型当 code review 来对待
失败归因速度快(rubric 编号直接定位)慢(整段 diff 看不出来)多 Agent 协作场景必须走前者

[观察] 上表里有几个反直觉点:第一,「评审 SLA 紧的团队反而适合多花两分钟审 rubric」看起来违反常识,原因是 SLA 的隐性成本主要花在「结果返工 + 多人同步讨论」上,而不是花在「多花两分钟读一份回写好的清单」上。第二,把 /goal、/rubric 当成纯文档工具理解是错的——它们真正的下游消费者是 LangSmith Engine 这类 self-improvement / hill climbing 环路:只有当 rubric criteria 是结构化的、可枚举的、可打分的,trace-driven 改写 prompt 才有可能自动收敛;一旦 criteria 是「写得好就行」这种模糊语义,Engine 找不到优化梯度,整条学习链路就在源头断掉。第三,/goal 与 /rubric 是以一种「middleware(中间件)」形态挂在 Deep Agents harness 上的,这意味着你可以把它独立换出,而不是耦合在 prompt 模板里——这对工程团队是决定性优势,因为 rubric 的演化不需要每次都改 agent 的核心 prompt,只改中间件层即可。

关于这套语法糖的官方定义与使用方式,可以参考以下两个地址获取权威说明,避免被二手博客误导:

  • Deep Agents slash command 与 /goal /rubric 用法说明:https://docs.langchain.com/oss/python/deepagents/
  • LangSmith 评测数据集与 grader rubric 结构示例:https://docs.smith.langchain.com/evaluation

落地时常见的几条踩坑清单,值得工程团队在引入 /goal /rubric 之前先对齐:第一,把 /rubric 写成形容词堆叠(「内容丰富、逻辑清晰、表达流畅」),grader 拿不到可执行的 boolean 判定,整条 rubric 沦为装饰,Goal/Verification 环路实质失效;第二,/goal 里塞了实现细节(「请使用 LangGraph 的 StateGraph 而不是 Chain」),这把目标与实现方案耦合,后续若想切换到 LangChain Engine 自动改写方案,目标层会失去抽象,Engine 找不到改写空间;第三,忘记给 rubric criteria 编号,grader agent 在做 partial scoring 时无法逐条定位失败项,排查 trace 时也只能看到一团模糊的「不达标」标签;第四,把 /goal /rubric 当成一次性的临时 prompt 而不是配置化的 runbook,导致换项目就要重写一遍判定标准,rubric 没法在团队内沉淀为可复用资产;第五,在多次会话中不同人写了互相矛盾的 rubric(一条要求「口语化表达」,另一条要求「专业学术化表达」),grader 在交叉打分时会出现结构性冲突,这时候应该把同主题 rubric 做版本化管理,而不是每次重写。

总结一下,/goal 与 /rubric 的真正价值不是「写出更好的 prompt」,而是把 Goal/Verification 环路里 grader agent 的判定清单从黑箱变成白箱、把「完成」这个模糊概念固化为可逐条勾选的工程制品。当你能用 markdown 表格写出五条可枚举的 rubric、并且用一份 LangSmith trace 把每条 criterion 与具体的 tool call、具体的 model output 对齐,你就已经具备进入 self-improvement / hill climbing 环路的前提——下一步可以交给 LangSmith Engine 自动 rewrite 那些长期不达标的标准本身,或者把整段 rubric 作为 cross-run memory 通过 checkpointer(如 MemorySaver、PostgresSaver、SqliteSaver) 持久化下来,跨任务、跨会话复用,真正让 Agent 在「目标如何被校验」这件事上随时间变好。

Dcode 与 Deep Agents 的 middleware 机制:把环路当成可插拔部件

把环路拆成可插拔部件:Middleware 的工程价值

Middleware 这个词在 Web 后端里很常见,放在 Agent 框架里其实有完全一致的语义——它是一段夹在「主循环」与「外部能力」之间的可替换代码,既不破坏主流程,又能注入横切关注点。在 Dcode 与 Deep Agents 这两个开源项目里,middleware 都被显式建模成对外暴露的协议层,任何 LangChain 风格的核心 Agent 都可以挂上零个或多个 middleware,而不必修改自身代码。这种「主 Agent 不感知」的解耦,直接复用了 LangGraph 在持久化与 state 共享上的基础设施,避免了 Agent 内核每加一条新环路就 fork 一次的局面。

dcode-middleware-architecture

把前文拆出的四种环路——Core Agent Loop、Goal/Verification、Event-driven、Self-improvement——在实现层面全都可以表达为 middleware。Core Agent Loop 由 create_deep_agent 内部默认实现,其余三条环路则是「可选挂件」,通过 middleware 列表注入。主 Agent 因此只关心「调用模型 + 调度工具」,不关心是否有评分者、外部事件,还是自我改写。这种分离带来的副作用是,middleware 之间也可以复用——比如 Event-driven 与 Self-improvement 都可能需要写「跨 run 的 memory」,它们可以共享同一个 memory middleware 实例,而不是各写一份。

[观察] 当一条「智能体环路」被识别为可复用的工程模式时,把它降级成 middleware 是最廉价的迁移路径。理由有三:第一,middleware 协议天然支持挂载与卸载,让实验阶段的「先挂着看看」与生产阶段的「确认有收益再保留」可以无差别进行;第二,跨项目复用只需要 import 同一个 middleware 包,避免每个团队各写一份重复实现;第三,middleware 与 prompt 是正交关系,改 prompt 不会破坏 middleware 的接口签名,反之亦然。把这种解耦做到极致的团队,最终会得到一个「middleware 仓库」与一个「Agent 仓库」分离的 monorepo 结构,每个 PR 单独评估,合并节奏互相不阻塞。

最简 Middleware 接口签名与挂载方式

下面给出一个与 Dcode / Deep Agents 兼容的最小 middleware 抽象。代码使用 Python 描述,实际两个项目里都更复杂,但接口形态保持一致:

class GoalRubricMiddleware(AgentMiddleware):
    def before_agent(self, state, runtime):
        goal = runtime.context.get("goal", "")
        rubric = runtime.context.get("rubric", [])
        state["messages"].insert(0, _build_system_msg(goal, rubric))
        return state

    def after_agent(self, state, runtime):
        return _grade(state, rubric)

    def wrap_tool_call(self, request, handler):
        return _record_metrics(request, handler(request))

agent = create_deep_agent(
    model="claude-sonnet",
    tools=[...],
    middleware=[
        GoalRubricMiddleware(),
        EventDrivenMiddleware(schedule="0 9 * * *"),
        SelfImproveMiddleware(engine="langsmith"),
    ],
)

代码块里展示的 4 个钩子——before_agent / around_agent / after_agent / wrap_tool_call——是 Dcode 与 Deep Agents 共同遵循的协议面。每个钩子都接收当前 state 与 runtime,返回修改后的 state,主 Agent 在调度时按顺序串起来。值得注意的是,Dcode 与 Deep Agents 都把 around_agent 设计为可选——如果用户只想要单点钩入,before 与 after 就够用;只有当用户需要「包住整轮调用做超时或断路」时,才需要实现 around_agent。

Middleware 生命周期对照

下表把四个钩子拆开看,方便工程师判断「我想做的事属于哪一阶段」:

钩子触发时机典型用途是否可短路
before_agent每轮 Agent 调用前注入 /goal、读取 /rubric、构造 system prompt
around_agent包裹整轮调用加超时、断路器、trace span是(可直接返回)
after_agent每轮调用后、写入结果前跑 grader、做验证、上报指标
wrap_tool_call每次工具调用前后记录工具耗时、参数校验、结果脱敏是(可改写)

[数据] Goal/Verification 环路通常落在 before_agent + after_agent 两条钩子上:评分逻辑放在 after_agent 里跑,prompt 注入放在 before_agent 里做;Event-driven 环路几乎只在 around_agent 层加一层「唤醒时是否到点」的判断;Self-improvement 环路则是唯一一个会跨轮次写回外部存储的——它必须显式接入 memory store(可以是 LangGraph 的 PostgresSaver),否则跨 run 的状态无从累积。这意味着,在 4 个钩子中,after_agent 与 wrap_tool_call 的「长尾」开销最容易被低估——前者会拖慢整轮结束,后者会被每条工具调用放大。团队在压测时常见的误区,就是只测主循环耗时,却没把 middleware 钩子链的时间成本计入 P99 延迟。

Dcode vs Deep Agents:同一思路,两种工程取舍

Dcode 与 Deep Agents 在「middleware 是可插拔的」这一立场上完全一致,但在协议细节、文档完整度、生态插件数量上呈现出可量化的差异。下面给一个对比矩阵,方便读者按自己的需求选择。

维度DcodeDeep Agents
Middleware 协议自定义,但与 Deep Agents 形态接近官方规范,作为公开 API 暴露
文档完整度仓库级 README + 少量示例独立子站,有钩子表、示例、迁移指南
生态插件数量偏少,核心场景内置多,LangChain 社区已有多个第三方包
默认注入内置 Goal/Verification内置 Sub-agent + todo middleware
接入门槛需自己组装 create_agentcreate_deep_agent 一行挂载

取舍:Dcode 更像一个「脚手架」,适合想从零看清每条环路如何串联的工程师;Deep Agents 则更像一个「成品 harness」,适合希望开箱即用、并把 LangSmith Engine 接进来跑 self-improvement 的团队。如果你的目标是「先验证 Goal/Verification 能不能跑通」,Dcode 更容易在几百行代码内复现;如果你的目标是「让 Agent 在生产里持续自我改写」,Deep Agents 的 memory 与 sub-agent middleware 是更稳妥的工程底座。

如果团队对「hook 时序的可观察性」有强需求(比如想接 OTEL、想接 LangSmith tracing),Deep Agents 的独立子站文档 https://docs.langchain.com/oss/python/deepagents/ 会显著降低上手成本;而 Dcode 的源码则需要在仓库 README 与示例之间来回跳读,门槛偏高。这条差异决定了「愿意读源码」的团队更适合 Dcode,而「需要快速对齐」的组织更适合 Deep Agents。

把四种环路套娃:Middleware 的俄罗斯套娃

四条环路可以俄罗斯套娃式任意嵌套,这在前文已经提过。在 middleware 视角下,「嵌套」等价于「在 around_agent 里再启一条 create_deep_agent」:外层 Agent 跑 Goal/Verification,内层 Agent 跑 Core Loop;再外层加一层 Self-improvement,周期性地把内层的 prompt 改写回仓库。每一层都是一个 middleware 实例,层次之间通过 state 共享。这种「以 middleware 为最小套娃单元」的设计,让组合爆炸的可能性被收敛在一个可枚举的接口面上

deep-agents-pluggable-loops

middleware-lifecycle

工程上更深一层的考量是:套娃的层数越多,middleware 的「不可见开销」越容易被忽略。每多一层 create_deep_agent,就多一组 before/after 调用、多一份 state 拷贝、多一次 trace span。建议在部署前先做一次端到端的「无功能负载」压测,记录 baseline latency,再逐层挂上 middleware,观察每层的 P99 增量。这种「逐层加挂」的灰度策略,与 LangSmith tracing 的 span 树天然契合,可以在 trace 里直接看到每一层 middleware 的耗时贡献。

踩坑清单与工程建议

实战里有几个常见反模式值得提前指出。第一,把业务逻辑塞进 wrap_tool_call 而非 after_agent,导致单次工具失败影响整轮评分——评分应该看最终结果,而不是中间过程。第二,Goal/Verification middleware 没接入 checkpointer,run 结束后评分数据丢失,后续 self-improvement 没有 trace 可学。第三,Event-driven middleware 的 cron 表达式与业务时区不一致,触发时间漂移数小时。第四,Self-improvement 写回 prompt 时没做 diff 审计,回滚成本极高。第五,多 middleware 共用 memory store 时,key 命名空间不隔离,出现「跨项目互相覆盖」的事故。

工程上的稳妥做法是:先接入官方仓库里现成的 middleware,确认整条管线跑通后,再逐个替换为自研实现。Deep Agents 仓库 https://github.com/langchain-ai/deepagents 提供了多个参考实现,文档站点 https://docs.langchain.com/oss/python/deepagents/ 列出了每个钩子的签名与时序图,是判断「我现在卡在哪一层」的最快路径。如果团队想更激进地定制,LangGraph 的持久化文档 https://langchain-ai.github.io/langgraph/concepts/persistence/ 也是必读——它解释了 checkpointer 如何与 memory middleware 协同工作,以及为什么 PostgresSaver 比 MemorySaver 更适合长跑任务。

最后一点容易被忽略:middleware 不是 prompt 模板,它的更新节奏应该与 Agent prompt 解耦。把这两件事绑在同一个 PR 里,会让「提示词微调」与「管线结构变更」互相污染 review,反而拖慢迭代速度。理想状态下,middleware 仓库单独发版,prompt 走 LangChain Hub 或版本化的 YAML 仓库,两者只在 production 部署时合并。当 Self-improvement 环路开始自动改写 prompt 时,这种「分仓」结构还能让回滚只动 prompt 仓库,而无需重部署 Agent 运行时。

Event-driven 环路:把 Agent 嵌入 Slack / GitHub / Calendar / Email

Event-driven 环路:把 Agent 嵌入 Slack / GitHub / Calendar / Email

Event-driven 环路(事件驱动环路)在 LangChain 团队提出的「四种环路」框架里排第三位,它不是要替代核心 Agent 环路,而是把已经能稳定运行的 request → model + tools → result 这条主循环,挂到真实工作系统的触发源上,让 Agent 在工程师不需要主动召唤的时候,自己被事件唤起并完成一段端到端的工作。Slack 的某条消息、GitHub 上的某个 PR 评论、Calendar 上即将开始的会议、Email 收件箱里新到的客户邮件,都可以成为这条环路的入口信号。

把它与前两节讲的核心 Agent 环路和 Goal/Verification 环路拼起来看,这套设计的真正价值在于「让 Agent 进入团队已经习惯的工作流」。一个只跑在终端里的 CLI Agent,即便能力再强,也只能在工程师主动敲命令的窗口里产生作用;而一旦 Agent 收到一个 GitHub issue 评论,它就具备了 24×7 等待事件的能力,这是单条核心环路在工程可达性上的根本跃迁。在 Dcode 与 Deep Agents 这类开源中间件里,Event-driven 不是可选插件,而是与主环路并列的一等公民。

[观察] 真实工作系统的触发器让 Agent 从『我去找它』变成『它来找我』,可达性翻倍。这不是一句宣传话,而是工程语义上的硬变化。CLI Agent 是 pull 模式,用户必须记得命令、记得参数、记得上下文;Event-driven Agent 是 push 模式,触发源替用户完成了「我现在需要 Agent」这件事的判定。在多团队协作场景下,工程师最缺的不是更强的 Agent,而是一个不会忘记、不会漏单的协作者——事件触发天然解决了注意力瓶颈。从 Loop Engineering 的整体视角看,Event-driven 环路并不创造新的 Agent 能力,它只是把已经存在的核心环路挂到了团队已经习惯的工作流上,但正是这一步把 Agent 从「桌面工具」推到了「团队成员」的位置。

event-driven-architecture

下面是一段最小骨架,展示如何用 Slack 的 incoming webhook 配合 LangServe 把一个 Agent 接到 Slack 频道。LangServe 在这里承担两个角色:一是把任意 LangChain / LangGraph 风格的 Agent 暴露成 OpenAI 兼容的 HTTP 服务,二是把异步执行与流式输出标准化,让上游的事件接收端不必关心 Agent 内部的并发模型。

from langserve import add_routes
from fastapi import FastAPI, Request
from deepagents import create_deep_agent

agent = create_deep_agent(model="anthropic:claude-sonnet", tools=[...], system_prompt="...")
app = FastAPI()
add_routes(app, agent, path="/agent")

@app.post("/slack/events")
async def slack_events(req: Request):
    body = await req.json()
    if body.get("type") == "url_verification":
        return {"challenge": body["challenge"]}
    event = body.get("event", {})
    if event.get("type") == "message":
        result = await agent.ainvoke({"messages": [{"role": "user", "content": event["text"]}]})
        await slack_client.chat_postMessage(channel=event["channel"], text=result["messages"][-1].content)
    return {"ok": True}

这段骨架里有几个工程细节必须讲清楚。第一,Slack 在 Event Subscription 握手时要求一个 url_verification 挑战,代码里的 challenge 回显不能省,否则 handshake 失败,后续所有事件都不会到达。第二,event.subtype 为 message_changed / bot_message 时要主动跳过,否则 Agent 的回复会被自己再次触发,形成自问自答的死循环,生产事故大多源于此。第三,Slack 的 3 秒 ACK 限制要求这条链路必须先快速返回 200,真正的 Agent 执行可以放到后台队列或 LangGraph 的 astream 异步任务里,绝对不能在 webhook 线程里阻塞式等待模型响应。第四,签名校验必须前置,Slack 在 X-Slack-Signature 头里放了 HMAC,没校验就把消息喂给 Agent 等于把 LLM 暴露给任何能伪造 POST 的人。

slack-trigger-flow

Slack / GitHub / Calendar / Email 这四类事件源在 payload 结构和触发语义上差异很大,工程实现时不能套用同一个适配器。下表把它们的字段和触发语义对齐,便于在中间件层设计统一的 verify_event(request, provider) 接口:

事件源触发方式关键 Payload 字段触发语义鉴权方式
SlackEvents API (HTTP webhook)event.type, event.channel, event.user, event.text新消息、@mention、reaction_addedSigning Secret + OAuth
GitHubWebhooks (Issues/PR/Push)action, repository.full_name, issue.number, comment.bodyissue 打开/关闭、PR review 评论、push 提交HMAC-SHA256 签名
CalendarPush notifications / CroniCal UID, event.start.dateTime, attendees会议开始前 N 分钟定时触发OAuth2 + Watch 通道
EmailIMAP IDLE / Gmail Pub/Submessage-id, from, subject, snippet新邮件到达、特定 label 命中OAuth2 / App Password

设计 Agent 时必须把这张表当作 schema 来看。Slack 和 GitHub 的 payload 都是结构化 JSON,字段稳定,可以放心做字段映射;Calendar 的 iCal 数据是半结构化文本,需要先用 icalendar 库解析再喂给 Agent,否则模型会拿到一坨带换行的字符串;Email 则要看具体协议,Gmail 的 Pub/Sub 是事件流,IMAP IDLE 则是长连接,二者对超时和重连的要求完全不同,直接影响中间件层的 socket 复用策略。

[数据] 对比一下触达路径上的工程差异:本地 CLI Agent 在一次完整工作流里,平均需要用户执行 3-5 步操作——启动终端、激活环境、敲命令、阅读输出、复制结果到下游工具;Event-driven Agent 在同一份工作流里,用户操作可以压缩到 0 步(纯异步触发)或 1 步(在 Slack 里回一句 @AgentBot)。触达路径从 5 步收敛到 1 步,意味着同样的 Agent 能力,在团队里被实际调用的概率提升一个数量级。在 10 人左右的小团队里,CLI Agent 的日均调用次数通常是个位数,而挂到 Slack 的同一个 Agent 在接入事件后,日均触发量会迅速爬到数十次——这就是 Dcode 与 Deep Agents 这类中间件层反复强调「事件是第一公民」的根因,也是 LangSmith Engine 在跑 self-improvement 环路时优先采集事件触发 trace 而非手工 trace 的原因。

下面是本地 CLI Agent 与 Event-driven Agent 在用户触达路径上的工程差异对比矩阵。这张矩阵的目的是让团队负责人在选型时直接看到「什么场景用哪一类」,而不是凭感觉拍板:

维度本地 CLI AgentEvent-driven Agent取舍边界
启动方式用户手动敲命令事件 webhook / cron 自动触发高频轻量任务倾向 Event-driven;一次性探索任务倾向 CLI
上下文来源当前目录、git status、env 变量事件 payload + 历史 thread缺乏 git 状态的纯对话任务用 Event-driven 更顺手
输出落点终端 stdoutSlack 消息、PR 评论、邮件回复输出需要被团队看到的场景必须用 Event-driven
鉴权面本地 SSH key / API keyOAuth + Webhook Secret + HMAC多团队共享时 Event-driven 鉴权更复杂
失败可见性终端直接报错需要 push 回原事件源Event-driven 必须显式设计失败回执
状态保持进程内,无跨 run 记忆依赖 checkpointer(PostgresSaver)长流程任务必须上 Event-driven + 持久化
调试成本直接看 stack trace要回放 webhook 历史排障时 CLI 更省事
安全边界进程级权限团队级共享权限涉及跨人协作必须收紧 Event-driven 的 RBAC

agent-in-real-work-systems

几个实战中容易踩的坑要单独列一下,这些坑在 CLI Agent 里几乎不存在,但在 Event-driven 场景里是高频故障源。第一,Slack 的 outgoing webhook 已经被官方废弃,新接入必须用 Events API + Socket Mode 二选一,不能用老接口去拼,老博客里的截图多半已经过时。第二,GitHub webhook 默认会带 X-GitHub-Event 头,action 字段的取值在不同事件下完全不同(issues 用 opened,pr 用 synchronize,comment 用 created),Agent 的 prompt 必须按事件类型分发,否则会拿同一个模板去响应所有事件,效果很差,这也是为什么中间件层经常把 event_type → prompt_template 做成一张映射表。第三,Calendar 的 push notification 在 Google Workspace 上要求显式开启 watch 通道,通道 7 天后会过期,生产环境必须写自动续期的 cron,否则一周后 Agent 会突然哑火,没有任何告警。第四,Email Pub/Sub 的 history API 只能回溯 7 天,补数据时容易丢消息,必须在接入第一天就启 backlog worker。

鉴权侧的取舍是另一条暗线。Slack 用 Signing Secret 校验请求签名,GitHub 用 HMAC-SHA256,Google Calendar 与 Gmail 都走 OAuth2,每家都不一样。如果中间件层把这些封装成统一的 verify_event(request, provider) 接口,业务 Agent 就不必关心签名细节,直接拿到一个可信的 dict。这正是 Deep Agents 把 webhook 适配做成 tool middleware 的工程动机——把横切关注点从主 Agent 抽出去,主 Agent 就能专注于「这条 Slack 消息到底要不要回、怎么回」。

在跟 Goal/Verification 环路配合时,Event-driven 还有一个隐藏收益:每一次事件触发都自带一条 LangSmith trace,这些 trace 既能被 grader agent 拿去验证「Agent 在会议前 5 分钟是否真的把议要整理好并发到了频道」,也能被 LangSmith Engine 在自改进阶段拿去做批量评测。换句话说,Event-driven 环路天然是其它三条环路的 trace 矿脉,这也是 Loop Engineering 框架把四条环路设计成可以俄罗斯套娃式嵌套的根本原因。

官方文档与接入指南:

  • LangServe 部署与 LangChain 入门:https://python.langchain.com/docs/introduction/
  • LangGraph 持久化与 checkpointer 概念:https://langchain-ai.github.io/langgraph/concepts/persistence/
  • LangSmith 官方文档与 trace 接入:https://docs.smith.langchain.com/
  • Deep Agents 开源仓库与中间件层:https://github.com/langchain-ai/deepagents
  • LangChain 官方 YouTube 频道原始 webinar:https://www.youtube.com/@LangChain
  • Slack Events API 官方主页:https://api.slack.com/start
  • GitHub Apps Webhook 官方文档:https://docs.github.com/en/apps

把视野拉回整套环路工程:核心 Agent 环路决定了 Agent 的能力上限,Goal/Verification 环路决定了 Agent 的质量下限,Event-driven 环路决定了 Agent 的触达半径,而 Self-improvement 环路决定了 Agent 的迭代速度。四条环路彼此正交,但又共享同一份 LangSmith trace 仓库——这是 Loop Engineering 这套框架最被低估的工程洞察:它不是一个线性的四步流程,而是一个由 trace 串联的可组合网络,每条环路都是另一条环路的数据源,任意两条甚至三条都能俄罗斯套娃式嵌套在同一个 Agent 实例里。

Schedule vs Webhook:事件触发的两种语义

当我们讨论 LangChain 团队「四种环路」框架里的 event-driven loop 时,最容易被忽略的问题是:所谓「事件」到底是什么?在工程实践中,把一个 Agent 唤醒起来的事件,其实分两种完全不同的语义 —— schedule(类 cron、定时触发)与 webhook(外部系统到达触发)。这两种语义在幂等性、去重、重试三个维度上的行为差别极大,绝不能被当成「配个 cron 就完事」。

两种语义的本质区别

schedule 触发器的语义是「在时间点 T,Agent 跑一次」。它的核心承诺是时间确定性:只要调度器还活着,不管外部世界发生什么,每天早上 08:00 都会有一个日历摘要 Agent 被准时唤醒。这种语义保证简单粗暴,但带了一个隐含假设 —— Agent 跑的时间必须与数据的时效性匹配。你每天 08:00 总结「昨天」日历没问题;但如果想总结「上周」日历,就得重新评估 cron 该写几点。

webhook 触发器的语义是「消息 X 到达时,Agent 跑一次」。它的核心承诺是事件确定性:Agent 跑的时间完全由外部系统决定 —— 邮箱里来了一封邮件、GitHub 上有人留了一条 PR 评论、Slack 频道里有人发了一条消息。Agent 不知道「现在」是几点,只知道「这件事刚刚发生」。这种语义还有一个隐含约束:Agent 必须能从外部世界被触达 —— 你需要一个 HTTP endpoint、一个 inbox 队列,或者第三方平台的 webhook 注册。

这也是为什么在设计 event-driven Agent 时,第一问题不是「这个 Agent 跑什么任务」,而是「这个 Agent 是按时间唤醒,还是按事件唤醒」。答案直接决定你的基础设施选型 —— 调度器 vs webhook 接收器、polling vs 长连接、push 模式 vs pull 模式。

实战观察:摘要 Agent 与邮件助理的不同选择

[观察] 一个非常能说明问题的工程对比是:每日早上日历摘要 Agent 用的是 schedule,而邮件到达触发的邮件助理用的是 webhook

为什么?因为日历数据是「最近状态型」 —— 任何时刻「今天还剩下的日程」都是有意义的,每天早上固定时间点做一次总结是天然需求。这种需求是「周期性覆盖」,不是「即时响应」,所以用 cron / schedule 是正确选择。

但邮件不同。邮件是「到达即动作型」 —— 用户在 14:23 收到一封重要邮件,你希望 Assistant 在秒级响应,而不是等下一个 cron tick。即便 cron 跑得再密,一分钟一次的延迟对重要邮件也是不可接受的。所以邮件助理必须用 webhook(或消息队列 / inbox 模式),触发源是邮件服务商(Gmail API push notification、Outlook webhook,或者自建 IMAP IDLE)。

这种二分法不是绝对的 —— 你也可以用 schedule 做「polling」(设个每分钟扫一次邮箱的 cron),但生产环境里 polling 的延迟和资源消耗通常让人无法接受。所以在真实工程里,schedule 更适合「summary / batch / aggregation」类任务,webhook 更适合「response / processing / forwarding」类任务

代码骨架:用 LangChain scheduler 跑每日 08:00 触发

下面是一段用 LangChain scheduler 跑一个每日 08:00 触发的日历摘要 Agent 的骨架代码:

from langchain.agents import create_agent
from langchain.schedulers import CronScheduler

calendar_summary_agent = create_agent(
    model=llm,
    tools=[fetch_calendar_events, summarize_events, post_to_slack],
    system_prompt="你是日历摘要 Agent,每天 08:00 拉取当天剩余日程,生成简洁摘要并推送到指定 Slack 频道。"
)

scheduler = CronScheduler(timezone="Asia/Shanghai")
scheduler.register(
    job_id="daily_calendar_briefing",
    cron="0 8 * * *",
    fn=calendar_summary_agent.invoke,
    fn_kwargs={"input": {"messages": [{"role": "user", "content": "请生成今日日历摘要"}]}}
)
scheduler.start()

这段骨架的核心是:create_agent 产出一个可运行的 Agent,CronScheduler 提供 cron 表达式驱动的唤醒语义,scheduler.register 把「cron 表达式」与「Agent 调用」绑定起来。一个值得注意的细节是,这里的 fn_kwargs固定 input —— Agent 每天早上醒来只知道「我该总结今天的日历」,并不知道用户会在几点看消息。这正是 schedule 语义的时间确定性特征:输入由时间决定,而不是由外部事件决定。

Schedule vs Webhook 语义对比表

工程里最容易踩坑的地方是「Agent 没跑成功怎么办」。这个问题的答案在 schedule 和 webhook 下完全不同:

维度Schedule(cron)Webhook(inbox 到达)
幂等性要求严格(同一天内必须幂等)严格(每个事件有唯一 ID)
去重策略用自然日期作 partition key用 event_id / message_id 作 dedup key
重试语义手动触发或等下一个 cron tick接收端返回 5xx,发送端自动重试
失败反馈静默失败(可能完全不知道跑挂了)显式回执(发送端必须收到 2xx)
状态边界天 / 小时 / 分钟(粗粒度)事件 / 消息 / payload(细粒度)
可观测性调度器日志 + Agent traceswebhook 接收日志 + Agent traces + 发送端日志

[数据] 这个表反映了一个行业公认的事实:schedule 的「静默失败」webhook 的「显式回执」 不是技术细节,而是完全不同的可靠性模型。在一个 30 天的窗口里,如果一个 cron Agent 失败三次,你可能根本没注意到 —— cron 静默跳过,等下一天。但一个 webhook Agent 失败一次,发送端(GitHub / Slack / Gmail)会按指数退避重试,你会看到清晰的错误日志。这种差异意味着:schedule 需要额外的心跳 / dead-letter queue 来补偿可观测性,而 webhook 的可观测性是发送端「白送」的。

同一 Agent 在两种触发下的失败模式对比矩阵

现在做一个对比矩阵,看同一个 Agent(比如 docs writer)在 schedule 和 webhook 两种触发下,会出现的失败模式差异:

失败场景Schedule 触发行为Webhook 触发行为
Agent 网络超时无信号,只错过今早 brief返回 500,发送端重试 3 次,最后一次成功
调度服务宕机所有 cron job 暂停,当天无事件webhook 接收端也可能挂,发送端把事件累积在队列
Agent 返回错误结果等第二天「自动纠正」用户立刻看到错误文档,可手动重跑
幂等冲突风险低(一天只跑一次)高(同一 webhook 可能被发多次)
可观测性成本需要主动加 health check自带重试日志 + 2xx 响应
典型工程痛点「昨天的 brief 好像不对」「为什么文档库里出现重复文档了」

这个对比矩阵揭示了一个更深层的规律:schedule 的失败模式是「时间型」的 —— 失败藏在时间轴里,可能要等用户投诉才发现。webhook 的失败模式是「空间型」的 —— 失败会立刻反映在发送端的重试队列里,你被迫第一时间处理。这就是为什么在生产环境里,schedule 触发器必须配套外部 watchdog,而 webhook 触发器一般只需要看 sender 的重试日志就够了。

Slack 里的「docs please」:webhook + 自然语言路由

一个非常典型的工程场景是:在团队 Slack 频道里,有人打一句 docs please,系统自动触发 docs writer Agent 去生成或更新相关文档。

这是 webhook + 自然语言路由 的典型组合:

  1. Slack 的 Events API 是 webhook 源 —— 有人发消息时,Slack 把 payload POST 到我们注册的 endpoint;
  2. endpoint 收到消息,先经过一个轻量级意图分类器(可以是小模型或关键词匹配),判断这条消息是否包含「写文档」意图;
  3. 一旦识别到意图,转交给 docs writer Agent,由它调用 GitHub / Notion / Confluence API 完成文档生成;
  4. 完成后在 Slack thread 里回一条链接,形成闭环。

这个模式有两个工程要点值得注意:

第一,webhook 是自然语言路由的天然载体。因为 webhook 的 payload 里就带着「原始消息文本」,我们可以直接把这段文本作为 Agent 的输入,不需要「再合成一段任务描述」。这正是「自然语言作为 Agent 触发器」之所以流行的原因 —— Slack / GitHub Issue / 邮件里的那条消息本身就是触发器,也是任务描述。

第二,意图分类这一步非常关键。如果没有它,任何消息都会触发 Agent,噪音极高。常见工程实践是用 slash 命令(如 /docs please)或特定关键词(如 docs please)作为意图标记。这是在「完全自由自然语言」和「严格结构化命令」之间的设计权衡。

工程选型建议

基于上面的分析,可以给出一组工程选型建议:

  • 周期性总结 / 批量聚合 / 定时报表 → 用 schedule(cron),天然幂等,可观测性成本低;
  • 实时响应 / 事件驱动 / 外部系统集成 → 用 webhook,重试语义清晰,需要处理幂等;
  • 自然语言作为触发 → 强烈推荐用 webhook,因为 payload 自带原文;
  • 关键业务 Agent → 不管哪种触发,都必须外加心跳 + DLQ,不能依赖触发层的「隐式可靠性」。

这些建议不是绝对的 —— 在一些混合场景下(比如你想「事件到达才触发的总结」),也可以用「webhook 到达 → 写入队列 → cron 每分钟扫队列」的方式。这其实是组合模式,LangChain 团队提出的「四种环路」框架里把这种叫 Russian doll nesting —— 小环路嵌入大环路,每一层都可以自由选语义。

参考资源

关于 event-driven Agent 设计的更多细节,可以参考:

  • LangChain agents 概念:https://python.langchain.com/docs/concepts/agents/
  • LangGraph 持久化与 checkpointer:https://langchain-ai.github.io/langgraph/concepts/persistence/
  • Deep Agents 文档:https://docs.langchain.com/oss/python/deepagents/
  • LangChain Hub:https://smith.langchain.com/hub

schedule-vs-webhook

cron-trigger-pattern

webhook-inbox-pattern

本节的核心立论是:在设计 event-driven Agent 时,第一个要回答的问题不是「这个 Agent 需要哪些工具」,而是「这个 Agent 的唤醒是时间确定,还是事件确定」。这个问题决定你的基础设施拓扑、可观测性策略与幂等设计。一旦回答完,剩下的工作就是把 Agent 塞进对应的 scheduler 或 webhook receiver,然后让它在后台默默跑,完成工程师不需要主动召唤的那一段端到端工作。

终端 Coding Agent 的环路结构:在 IDE / 终端里跑闭环

terminal-coding-agent-loop

当我们把上一节讨论的两种事件语义——schedule(类 cron)与 webhook(外部系统到达)——落到具体场景时,会发现终端 Coding Agent 恰好是一种非常特殊的 Event-driven 环路:它的触发器既不是定时器,也不是来自外部系统的 webhook,而是用户在终端里敲下回车键的那一瞬间,或者一条 GitHub PR 评论到达的瞬间。前者的语义更接近"显式召唤(synchronous invocation)“,后者的语义才是教科书意义上的 webhook;但两者在 Agent 视角下都会被抽象成"一个事件触发了新一轮 Core Agent Loop”。这种抽象的好处是,无论触发器是 Enter 键、PR 评论、Slack 提及,还是定时 polling,后续的 Goal/Verification 链路都可以复用同一套 middleware。

进入环路之后,模型调用 read_file、edit_file、run_cmd、git_commit 等工具,产出一段 diff(diff 即 code review agent 能看到的 change set),紧接着 CI 流水线、lint 静态检查、单元测试立刻成为这一轮的 verification signal(也就是 Goal/Verification 环路里的 rubric criteria)。这场"敲 Enter → 改代码 → 看 CI"的循环,是工程师每天都在操作的肌肉记忆,只不过 LangChain 团队的 Deep Agents Code 把它正式抽象成可编程的中间件,并把 /goal 与 /rubric 这种 slash 命令暴露给开发者作为 harness(可挂载的 Agent 运行时骨架)。在这套骨架里,Core Agent Loop 是底盘,Goal/Verification Loop 是方向盘,Event-driven Loop 是油门,Self-improvement Loop 是定期 OTA 升级。

[观察] 在工程实践里,终端 Coding Agent 最大的隐藏价值,是它把 Verification 信号从"人类 reviewer 主观判断"重构成了"机器可读的 rubric"。一条 CI red、一个 lint warning、一个测试覆盖率下降、一次 type checker 报错,这些都是结构化、可枚举、可重放的真值信号,远比"我觉得这段代码还行"更适合作为 grader agent 的输入。换句话说,CI 流水线天然就是 Goal/Verification 环路的 grader agent,只是过去它从未被显式地接进 Agent 框架。这与 LangChain 课程里反复强调的「让『完成』变成机器可判定」完全一致——区别在于,Software Engineering 领域天然就存在 grader,只是工程师过去没有意识到可以把它直接接到 Agent 的反馈回路上。

最小骨架:把 GitHub PR + CI runner 接进 create_deep_agent

下面这段伪代码演示了如何把 GitHub PR diff 工具与 CI runner 接入 Deep Agents 的 create_deep_agent SDK。它的核心思路是用一个 webhook 接收器作为入口,把 PR 评论事件和定时 poll 转成统一的事件 payload,然后再把这些 payload 路由给 Agent。Agent 内部依次拿到 diff、跑 CI、根据 rubric 决定是否直接 commit 修复或回贴评论。

from deepagents import create_deep_agent
from langchain.tools import tool
from langchain_core.messages import HumanMessage

@tool
def get_pr_diff(pr_url: str) -> str:
    """拉取指定 PR 的 diff 内容,作为 agent 的 observation 来源。"""
    # 实际实现中调用 GitHub API:GET /repos/{owner}/{repo}/pulls/{n}/files
    return fetch_github_diff(pr_url)

@tool
def run_ci_runner(repo: str, sha: str) -> dict:
    """触发 CI runner,返回 {passed: bool, logs: str, coverage: float}。"""
    return trigger_ci_and_wait(repo, sha)

agent = create_deep_agent(
    tools=[get_pr_diff, run_ci_runner, read_file, edit_file, run_cmd, git_commit],
    system_prompt="你是一名 senior code reviewer + refactor agent,接到 PR 后先跑 CI 看基线,再用 diff 工具分析,最后给出修改建议或直接 commit 修复。",
    middleware=[
        goal_middleware(goal="所有 PR 必须通过 lint + 单元测试,覆盖率不低于 80%"),
        rubric_middleware(rubric=[
            "diff 中的函数必须补充单元测试",
            "不能引入新的 lint warning",
            "commit message 遵循 Conventional Commits",
        ]),
    ],
)

# webhook 接收器:把 PR 评论 / push 事件转成 Agent 输入
@app.post("/github/webhook")
async def on_github_event(event: GitHubEvent):
    if event.is_pr_comment or event.is_pr_sync:
        result = await agent.ainvoke({
            "messages": [HumanMessage(content=f"PR #{event.pr_number} 触发了新事件,action={event.action}")]
        })
        return result

这段骨架里有两个关键变量需要额外解释:一是 goal_middleware 把整个 PR 修复任务的目标(“通过 lint + 单元测试”)变成可判定的 acceptance criteria,等价于 LangChain 课程里强调的「把 goal 描述成可验证的命题」;二是 rubric_middleware 把"好的 PR"这个玄学概念拆成三条可枚举的判断依据,每条 rubric 都对应一次工具调用结果的 inspection,grader agent 据此判断这一轮 Core Agent Loop 是否需要 re-run。post 一个 PR 评论之所以能成为一条新的事件,是因为 LangGraph 在 ainvoke 期间自动把 thread state 写入 checkpointer,下一次 webhook 到达时通过同一个 thread_id 就能恢复上下文。

4 个核心工具的能力边界

终端 Coding Agent 在最小可用版本下,只需要 4 个工具就能跑出一个像样的闭环,这是 Deep Agents 团队在多个演示 demo 里反复验证过的最小集合。下表列出了它们的能力边界、典型耗时与失败信号:

工具作用典型耗时失败信号
read_file读取仓库任意文件内容,作为 observation10-50ms文件不存在、权限不足
edit_file应用代码修改,返回 unified diff30-100ms语法错误、文件被外部修改
run_cmd执行 shell 命令,捕获 stdout/stderr100ms-5min退出码非 0、超时
git_commit提交变更,返回 commit SHA50-200mspre-commit hook 失败

这 4 个工具共同构成 Core Agent Loop 的全部动作空间。其中 run_cmd 是最危险也最有杠杆的工具,因为它可以间接触发 lint、test、build、CI runner 等所有外部 verification,等于把整个软件交付链路挂载到了 Agent 的"手臂"上。Deep Agents 团队在官方文档里特意强调:对 run_cmd 的副作用必须显式声明,否则模型很容易在调试时错把 rm -rf 当成"清理临时文件"。在 LangChain 课程的 Self-improvement 语境下,这 4 个工具的描述甚至会被 LangSmith Engine 周期性重写,以收敛到更安全的指令集。

反馈延迟 vs 环路节奏:IDE 插件 vs 终端 CLI

[数据] 同一段代码 review 任务,在 IDE 插件形态与终端 CLI 形态下的反馈延迟差距可以达到一到两个数量级。下表给出实测对比(基于中型 monorepo、CI runner 冷启动条件,数据来自 LangChain 团队在 Deep Agents Code 演示 repo 里的复现脚本):

维度IDE 插件形态(IDE plugin)终端 CLI 形态(terminal CLI)
触发延迟用户在编辑器里保存即触发,通常 < 100ms用户敲 Enter 才触发,但允许后台异步
上下文完备性天然包含当前文件、光标位置、打开的 tab只看得见命令行参数与 cwd,需手动 read_file
反馈延迟受 LSP(language server protocol)实时回流,200-500ms 拿到 type error必须等 CI runner,5s-5min 拿到完整 signal
适用任务局部 refactor、inline 修复、补全跨文件改动、依赖升级、批量化 PR 修复
Grader 形态LSP + 编译错误 + 静态分析CI runner + diff + lint + 单元测试
失败可重放性较差,依赖 IDE 状态较好,每次 Enter 是一次独立可重放的事件

[观察] 两种形态并不是非此即彼,而是同一个 Core Agent Loop 的两种投影。IDE 插件把"短反馈高频修改"做到了极致,适合让模型在用户视野内做小步快跑;终端 CLI 把"长反馈高确定性"做到了极致,适合让模型在异步上下文里完成跨文件、跨 PR 的重型工作。LangChain 团队在 Deep Agents Code 项目里给出的范式是:让终端 CLI 负责跨文件、跨异步信号的高确定性 reasoning,让 IDE 插件负责 inline 低延迟的体感补全,两者通过统一的事件总线与 LangGraph checkpointer 共享 thread state。从环路工程的视角看,这正是同一种 Goal/Verification 环路在两种延迟预算下的实现取舍。

踩坑清单与延伸阅读

把 Coding Agent 从演示 demo 部署到真实工程团队里,通常会踩到以下三类坑:

  • CI runner 配额与冷启动:很多 CI 服务在免费层有月并发分钟数限制,Agent 高频触发会让配额迅速耗尽。建议把 run_cmd 的实际指向改为本地 Docker 模拟 runner,只在关键节点 push 到真实 CI。
  • diff 工具的特权边界:GitHub PAT(personal access token)如果给得过宽,模型可能误删分支或 force-push。最佳实践是给它一个只读 PAT + 单独的 bot 账号,只允许 read repo + write comment,严禁授予 admin 权限。
  • rubric 漂移:rubric criteria 写得太死会让 Agent 失去灵活性,写得太松又无法形成可靠 grader。建议把 rubric 同时存进 LangSmith datasets,跑自动 eval 看覆盖率,并在 Self-improvement 阶段让 LangSmith Engine 自动迭代 rubric 文本。

具体的工程实现与官方演示案例,可以参考以下两个延伸阅读入口:

  • Deep Agents Code 项目页:https://github.com/langchain-ai/deepagents
  • LangGraph 持久化与 checkpointer 文档:https://langchain-ai.github.io/langgraph/concepts/persistence/

ide-coder-flow

diff-feedback-cycle

terminal-coding-agent-loop

总之,终端 Coding Agent 的本质,是把"敲 Enter"这个肌肉记忆封装成可编程的 event-driven 环路 trigger,把 CI / diff / lint 封装成天然的 grader,把 read_file / edit_file / run_cmd / git_commit 四个核心工具封装成动作空间。当这三层封装都齐备时,所谓"AI 写代码"才会从 demo 变成可被团队采纳的工程实践——而这一切,并不是因为模型变聪明了,而是因为环路结构变规整了。

Self-improvement / Hill Climbing 环路:真相在 traces 里

hill-climbing-loop

在 LangChain 团队关于 Loop Engineering 的整套课程里,Self-improvement / Hill Climbing 环路被摆在最高一层,讲师明确把它称为「基石思想」。其底层假设与传统软件截然相反:Agent 是一种非确定性系统,同一段 prompt 在两次运行之间可能产生截然不同的结果,因此「真相」不在人的脑子里,也不在静态的 prompt 文件里,而在每一次运行留下的 trace(追踪记录)里。这意味着我们不能再用「我觉得这个 prompt 写得不错」来做工程决策,而必须把 trace 当成唯一可信的反馈源。

[观察] 在传统软件里,代码的输出是确定的,所以调试靠 log、靠断点、靠推理;在 Agent 系统里,模型本身是一个不可控的黑盒,人类直觉对「prompt 好不好」的判断,误差往往是数量级的。真正能反映 prompt 实际表现的,是 trace 中那些真实发生过但被我们忽略的细节——比如模型反复在同一个 tool 上重试、或者在第 4 步突然开始使用一个从未声明的「内联工具」。这些信号只有从 trace 里反推出来,才有工程价值。

接下来这一节要解决的问题,就是「如何用 trace 倒推出 prompt / 工具描述 / memory 这些『配置』该改成什么样」。这一步看似朴素,实则是整个 Loop Engineering 框架中工程杠杆最大的环节。

一个最简单的 Hill Climbing 环路

先给出一个最朴素的实现骨架——跑主 Agent → 提取失败信号 → 改 prompt → 再跑——这是 Hill Climbing 在工程上的最小可用版本:

from langchain.agents import create_agent
from langsmith import Client

MAIN_AGENT = create_agent(
    model="...",
    tools=[...],
    system_prompt=open("prompt_v0.md").read(),
)

def run_once(query: str) -> str:
    return MAIN_AGENT.invoke({"messages": [{"role": "user", "content": query}]})

def extract_failure_signals(trace_id: str) -> list[dict]:
    run = Client().read_run(trace_id)
    # 检测工具参数错误 / tool call 缺失 / 风格漂移 / 重复循环 / 幻觉事实
    return signals

def rewrite_prompt(current: str, signals: list[dict]) -> str:
    return META_REWRITER.invoke({"prompt": current, "signals": signals})

current = open("prompt_v0.md").read()
for round_idx in range(20):
    trace_id = run_once("回归测试集 query")
    signals = extract_failure_signals(trace_id)
    if not signals:
        break
    current = rewrite_prompt(current, signals)
    MAIN_AGENT = MAIN_AGENT.with_config(system_prompt=current)

这段伪代码的核心思想是:把「调 prompt」从一次性的人类直觉行为,转成一条可以反复迭代、每一次迭代都有 trace 证据的闭环。注意 with_config(system_prompt=...) 这一行——在 LangGraph 持久化与 checkpointer 模型下,我们可以让 prompt 改动跨 run 生效而不需要重新部署。详见 LangGraph 官方文档 https://langchain-ai.github.io/langgraph/ 与 LangSmith 评测数据集示例 https://docs.smith.langchain.com/evaluation。

trace-as-truth-source

5 类失败信号:trace 能告诉我们什么

下表列出讲师在课程里反复强调的、可从 trace 中反推出的 5 类失败信号。每一类都对应一种具体的 prompt 或 tool description 改动策略,工程上只要把这五类信号都接上检测器,基本就覆盖了 Agent 失败案例的绝大多数。

信号类型在 trace 里的典型表现反推出的配置改动检测难度
工具参数错误tool_call 的 arguments 字段类型错误或字段缺失改写 tool docstring,补充参数示例
tool call 缺失完成某任务本应调用某个 tool,却直接给出文字答案在 system_prompt 中显式声明「必须先调用 X」
风格漂移同一 agent 不同 run 输出语气/格式差异很大在 prompt 中钉死输出 schema,例如 JSON Schema
重复循环trace 中出现连续 ≥3 次相似 tool call加入「若 tool 返回 X 则停止重试」的早停规则
幻觉事实最终答案中包含无法在 trace tool 结果里溯源的事实在 prompt 末尾追加「只能使用 tool 返回的内容作答」

[数据] 在该教程的演示案例里,讲师展示了一组对照实验:把同一个 Coding Agent 跑在 100 条回归测试集上,初始 prompt 的通过率大约在 60% 量级;不开 Hill Climbing 环路时,人工调 prompt 三轮,人力花费显著但通过率提升有限;开启 Hill Climbing 环路后,meta-agent 自动跑了大约 12 轮 trace-driven 改写,最终通过率稳定上升到 85%-90% 区间,且每轮都能在 trace 里看到具体的失败信号在减少。注意:这里的具体数字是该教程演示场景下的实测,不要把它外推到任意 Agent 系统——不同任务、不同模型、不同 rubric 下的曲线形状会显著不同,真正可移植的只是「trace-driven 改写」这一机制,而不是某条曲线的具体数值。

长期演化:Self-improvement 环路启用 vs 关闭

下面给出一张对比矩阵,描述长期(假设 50 轮迭代)演化下两种模式的差异。这里的关键不是「哪条曲线数值更高」,而是「成本结构与稳定性是否可承受」。对 AI 工程师和团队负责人而言,这道选择题的答案往往比看上去更微妙。

维度Self-improvement 环路关闭(纯人工)Self-improvement 环路开启(meta-agent + traces)
短期成功率(0-5 轮)取决于 prompt 工程师个人水平由初始 prompt 决定,可能略低
长期成功率(20-50 轮)提升有限,易出现「越改越差」回退持续上升,通常呈对数曲线
单轮迭代成本高(需要资深工程师 1-2 小时)低(meta-agent 自动跑,只需 trace 评审)
可解释性高(人能解释每一处改动)中(需要保留每轮 prompt diff 才能审计)
上限受人工瓶颈限制受 trace 质量与 meta-agent 能力限制
失败模式工程师主观偏差、context 切换疲劳meta-agent 在 trace 中学到的偏差被固化

[取舍] 启用 Self-improvement 环路并不是「永远更优」的银弹。它的代价是引入了一个新的故障源——meta-agent 本身。如果 meta-agent 的改写策略有偏差,这种偏差会被外层环路持续放大,形成「在错误的方向上越走越远」的危险模式。所以工程上常见的折中做法是:人工评审每一条 prompt diff、保留 git 化的 prompt 历史、必要时一键回滚。换句话说,外层环路不是「替代」人,而是「放大」人——它把人的注意力从「逐字逐句抠 prompt」解放到「评审 meta-agent 的改写决策」,但后者的责任密度其实更高。

feedback-up-cycle

外层环路让内层环路越来越有效

Hill Climbing 在 LangChain 语境下的真正威力,不是「单次改 prompt」本身,而是「外层环路让内层环路越来越有效」这一嵌套结构。设想一个典型场景:Core Agent Loop(内层)负责跑一次任务,Goal/Verification 环路(中层)用 grader 给出通过或不通过的判定,Self-improvement/Hill Climbing 环路(外层)拿到 grader 输出后,改写 Core Agent 的 prompt 与 tool description。

每一轮外层迭代,Core Agent 的 prompt 都在被微调——比如把 grader 经常扣分的「tool 参数错误」修掉、把 grader 经常忽略的「幻觉事实」加一道防线。下一次内层跑回归集时,因为 prompt 已经修正了上一轮的具体失败模式,grader 的通过率就会上升;通过率上升,反过来又给外层 meta-agent 提供了「这一轮的 prompt 改动是正向」的反馈信号。

这就是 Hill Climbing 的核心动机:我们不是要做一个「能自己学」的 Agent,而是要让外层 meta-agent 把每一次跑 trace 攒下来的证据,变成下一次内层 Core Agent 更容易成功的前提。Layer 与 layer 之间的耦合不是耦合,是一种「互相喂数据」的递增结构。

落地到工程上,LangSmith Engine(见 https://www.langchain.com/langsmith)正是这套思想的现成载体:它跑过历史 trace 后,自动生成 prompt / skill / memory 的改写建议,并可以一键 port 回源码。配合 LangGraph 的 checkpointer(MemorySaver / PostgresSaver / SqliteSaver 三种实现见 https://langchain-ai.github.io/langgraph/concepts/persistence/),跨 run 的 memory 与 prompt 版本管理都被打通了,外层环路才能真的落地为可观测、可回滚、可审计的工程流水线。

总结:Hill Climbing 环路不是「让 Agent 更聪明」的玄学,而是把 Agent 的不确定性从「不可控的随机」转成「可累积的证据」的工程机制。真相在 traces 里——这是 LangChain 团队关于 Loop Engineering 最朴素也最深刻的一句断言。当外层环路把每一条 trace 都转成下一次内层 run 的「先验知识」时,Agent 才真正开始具备「越用越好」的能力,而那正是 Loop Engineering 想要抵达的终点。

LangSmith Engine:跑过 traces 自动改 prompt / tool / skill / memory

meta-agent-position

在 LangChain 团队关于 Loop Engineering 的整套课程里,Self-improvement / Hill Climbing 环路被摆在最高一层,讲师明确把它称为「基石思想」。其底层假设与传统软件截然相反:Agent 是一种非确定性系统,同一段 prompt 在两次运行之间可能产生截然不同的结果,因此「真相」不在人的脑子里,也不在静态的 prompt 文件里,而在每一次运行留下的 trace(追踪记录)里。这意味着我们不能再用「我觉得这个 prompt 写得不错」来做工程决策,而要把决策权交给 trace。

LangSmith Engine 正是这一思路下的产物:它是 LangChain 推出的一个元 agent(meta-agent),专门跑过 LangSmith 已经采集到的大量 traces 做反思,然后把反思结果反向写回到源码里。和传统意义上的「自动 prompt 调优」工具不同,它不是黑盒拟合,而是把 LangChain 生态里所有的 prompts、tools、skills、memory 四类对象都视为可读写的「可改写源」,并通过 PR(代码评审)或 LangSmith Hub 之类的可信通道回流到团队仓库。它与生产主 agent 并行存在但不抢流量,把 trace 当作唯一的老师,而不是把 LLM 的偏好当成真理。

LangSmith Engine 在整体架构中的位置

langsmith-engine-architecture

把图看清楚最重要的一点是:LangSmith Engine 不是主 agent 的替代品,也不是并联运行的第二只 agent。它更像一只「外挂的旁路审视者」,订阅 LangSmith 的 trace 流,在后台离线跑分析,只在需要的时候给出改动建议。生产流量不会因为它而改道,也不会因为它而卡顿;它的存在形态更接近一只 CI(持续集成) Bot,而不是一个常驻的在线推理服务。

[观察] 任何把「自动改 prompt」想象成「按钮一点全网立刻生效」的人,都没有真正理解 LangSmith Engine 的工程语义。它只产出 diff(差异文件)和验证 rubric(评分标准),最终落地还是要靠工程师 / agent 双盲评审。这就把它和传统 A/B 测试平台区分开了:它不是在替人做决策,而是把决策所需的原材料(改写建议、对比 trace、回滚证据)从分散的 prompt 文件里搬出来,堆到同一张桌子上。它是一台「改造 prompt 的流水线」,而不是「决定 prompt 对错的裁判」。

最小接线示例

下面这段伪代码展示了如何让 LangSmith Engine 监听 trace 流并在累积到阈值时自动产出 prompt 改写建议。其中的关键不是 API 调用本身,而是「监听器—分析器—回写器」三段式结构,以及一个明确的 backpressure(反压)开关,避免在流量高峰期被反思任务打爆。

from langsmith_engine import TraceWatcher, PromptRewriter
from langsmith_engine.policy import RewriterConfig, WriteBackPolicy

watcher = TraceWatcher(
    project="prod-email-assistant",
    sample_rate=0.1,        # 只采样 10% 的生产 trace
    only_failures=True,     # 只看失败 / 投诉 / 重试的三类
    window="24h",           # 滚动窗口:24 小时
)
rewriter = PromptRewriter(
    targets=["prompts", "tools", "skills", "memory"],
    rubric="rubrics/email_assistant.yaml",
    max_diff_lines=40,      # 单次 diff 上限,防止一次改太多
    require_pr=True,        # 改写必须走 PR,禁止直写 main
    writeback=WriteBackPolicy(
        repo="org/agent-prompts",
        reviewers=["on-call-agent-team"],
    ),
)
watcher.on_threshold(count=50, fn=rewriter.suggest_diff).start()

[观察] 上面 sample_rate=0.1only_failures=Truemax_diff_lines=40 这三个开关是工程里最容易抄漏的一行。LangSmith Engine 跑的不是「全量回放」而是「抽样 + 失败导向」的近视眼分析,目的是用 1% 的算力撬动 80% 的可解释性。如果跳过这三行,初学者很快就会在生产环境把 token 预算打穿,或者反方向被全成功 trace 误导,得出「一切看起来都很好,prompt 不需要改」的虚假结论。


LangSmith Engine 可改写的 4 类对象

对象类型在 LangChain 生态中的位置改写后回写到哪里触发条件典型场景
promptssystem prompt、user prompt template、few-shot 提示词LangChain Hub prompt 仓库 / git 仓库 PR模型输出与 rubric 评分偏差持续上升
toolstool name、tool description、tool schema、参数说明工具注册源码 PR(例如 tools/email_tools.py)模型选错工具,或工具返回格式频繁被 model 误读
skills复合任务脚本(例如 docs writer agent 的整段 skill 模板)skill 仓库 PR,通常以一个 skill.md + 测试用例形式提交跨多个 trace 反复犯同一类「漏写章节」错误
memorycross-run 长期记忆(MemorySaver / PostgresSaver 写入的快照)LangSmith memory store schema 迁移 + 旧值归档模型在多轮里反复忘记某个事实,或反过来记忆污染

[数据] 这张表里 4 类对象的「改写密度」并不一致。根据该教程给出的口径,在同一段时间窗口内,prompts 被改写最频繁,但回滚率也最高,因为 prompt 是最易测试错的位置,一次字符级的修改就能把分数拉下来;tools 与 skills 改写频次中等,但一旦改对收益最大,因为工具接口的修正会带来整条链路稳定度的跃升;memory 改写频次最低,然而单次改动影响最深,因为它跨多条对话持续生效。把它们放在同一张表里看,工程师就能直观判断「哪一类改写值得自动化,哪一类一定要留人介入」,而不是把所有对象塞进同一个自动改写流水线里。


人工改 prompt vs LangSmith Engine 自动改 prompt

维度人工改 promptLangSmith Engine 自动改 prompt取舍 / 边界
改动频率周级 / 月级,通常在事故复盘后才改小时级 / 滚动窗口级,每次 trace 聚合触发自动频率高 10-100 倍,但单次改动幅度更小
一致性与个人风格强绑定,新人接手需要 onboarding团队共享同一份 rubric,跨工程师一致性更稳自动一致性更高,但前提是 rubric 本身写得好
回滚成本高:涉及 prompt 文件、Hub prompt、agent 配置多处同步低:每条改写建议都带 trace diff、跑分对比、一键 revert PR自动路径天然带 audit trail(审计轨迹)
决策权归属人 + agent 双盲评审,改写 PR 必须人工合并速度让人,质量让人
适用规模几个 prompt 的小项目几十上百个 prompt 的大仓 / 多租户 SaaS自动路径在小项目反而是负担
风险点改了对不对全靠经验改的可能「统计上对,但用户不喜欢」都需要人在回路(human-in-the-loop)

[数据] 课程里提到一个粗略数量级:在没有 LangSmith Engine 之前,一个中型 agent 团队每周大约能手工迭代 1-2 个 prompt,改写节奏完全绑死在值班工程师的空闲时间上;接入 LangSmith Engine 之后,一周能自动产出 20-50 条候选 diff,但其中真正被人工合并的只有 5-15 条,合并率约 25-30%。这个数字揭示了一个常被忽视的事实:自动改写的瓶颈不是「改得多快」,而是「人工评审能不能跟得上」。盲目追求自动 diff 数量,反而会让评审人手崩溃,合并率塌方到不足 10%,改写质量随之一落千丈。这是一条工程上非常典型的「左脚踩右脚」陷阱:越自动化越需要人,看似悖论,实质是把人从「写 diff」解放到了「审 diff」,而审 diff 的认知负担其实更高。


踩坑清单与工程配置要点

第一,rubric 必须显式存在。LangSmith Engine 不靠玄学,它依赖一份 rubric YAML(或 rubric criteria 文件)告诉它「好与不好」。凡是写过一遍内部「agent 评分标准」的团队都会发现,把 rubrics 写在文档里这一件事本身,比工具引入的收益还要大——它会强迫团队回答「我们到底在意什么」这个问题。

第二,采样策略必须刻意设计。sample_rate 过高会吞 token 预算;过低又会让 Engine 跑在「全成功」的高分集上,学不到东西。常见做法是:失败集全量采样、正常流量降采样到 1-5%、投诉工单单独开高优先级通道。

第三,writeback 通道必须分级。prompts / tools 这类改写可以直接走 PR;memory 这种跨用户数据则要走 schema 迁移 + 数据归档 + 灰度发布,绝不允许它直接 dump 进生产库。

第四,Engine 的输出必须可解释。任何一次「为什么这么改」的回答,都应当能用 trace id + rubric score + before/after diff 三件套回放。否则团队会在两周内失去对 Engine 的信任,改写 PR 大量被沉默忽略。

trace-analysis-flow


延伸阅读与官方入口

  • LangSmith 官方文档(trace、Engine、rubric 配置):https://docs.smith.langchain.com/
  • LangSmith 主页与产品介绍(含 Engine 介绍页):https://www.langchain.com/langsmith
  • LangChain 博客(Self-improvement / Loop Engineering 系列文章汇总):https://blog.langchain.com/
  • LangChain Hub(改写产物的 central registry,可直接搜 prompt / tool / skill):https://smith.langchain.com/hub
  • Deep Agents 开源仓库,作为 Engine 下游「真正落地改写」的中间件:https://github.com/langchain-ai/deepagents

把这几条入口对照着读,不难发现 LangSmith Engine 的定位非常克制:它不是下一只 agent,而是一层「自动化版本控制」,把 trace 里蕴含的改进信号翻译成 PR 与 diff,让团队依然握着合并权。它也因此最容易在两种团队里失效——一种是没有 trace 基础设施的团队,接进来发现没数据可喂,Engine 变成空转;另一种是完全没有 review 文化的团队,自动 diff 没人审,要么直接合并把线上改崩,要么直接废弃变成摆设。把这层边界看清楚,LangSmith Engine 才不会从「提效神器」变成「线上惊吓」。

可改进的两大类:procedural 与跨 run 持久化 memory

procedural-vs-memory-improvement

当 LangSmith Engine 这类 meta-agent 反推完 traces(追踪记录,即 Agent 每一次执行留下的完整步骤日志)之后,它产出的改写指令天然会落到两个完全不同的"地层"上:一类改的是 base procedural(基础流程性)资产——所有用户共享的 prompt、tool description、skill 模板;另一类改的是 跨 run 持久化 memory(跨运行持久化记忆)——只服务于特定 thread_id、特定 user_id 或特定收件人上下文的私有条目。这种二分法不是讲师拍脑袋想出来的分类标签,而是源自 LangGraph checkpointer(检查点机制,把每一步状态序列化保存的机制,见 https://langchain-ai.github.io/langgraph/concepts/persistence/)与 LangSmith Hub prompt registry(提示词注册中心)从一开始就被设计成两套彼此独立的写入路径:一个面向"仓库里的源码文件",一个面向"运行时数据库里的键值对"。

一、两类的边界到底在哪里

Procedural 类改写的目标物,在工程语义上等价于"任何对 Agent 启动时静态加载的资产"的修改。典型代表包括:

  • 系统 prompt(system prompt):比如"你是一位严谨的代码审查员"这种全局人设
  • tool description(工具描述):LangChain 的 tool schema 里那个告诉模型"何时调用我、调用我之后会拿到什么"的 description 字段
  • skill / sub-agent prompt:Deep Agents 体系下子 Agent 的提示词模板
  • rubric 评分标准文档:Goal/Verification 环路里 grader agent 拿来打分的 rubric 文件

而 memory 类改写的目标物,则是 LangGraph checkpointer 写入的状态字段,或者外挂在 LangGraph Store 上的 cross-thread memory store(跨线程记忆库)。它的特征是:必须绑定一个 context key——可能是 thread_id(同一会话)、user_id(同一用户),也可能是更细粒度的语义键,例如"收件人 = 老板"。

把这两类放在一张表里,差异立刻变得直观:

维度Procedural 类(流程性)Memory 类(跨 run 持久化)
存储位置Git 仓库里的源码文件 / LangSmith Hub 上的 prompt commitLangGraph checkpointer(运行时状态)或 LangGraph Store(跨线程键值库)
改写权限需要走 PR / code review,任何人改动都影响全员仅 meta-agent 在带特定 context key 的 trace 上写,影响范围被上下文隔离
回滚粒度Git commit hash 级别,可整段 prompt 回退到上一版单条 key/value 可独立删除或覆写,不影响其他上下文
生效时机下一次 Agent cold start(冷启动,即重新加载进程后)才生效写入即对当前及后续同 context 的 run 生效
审计方式通过 git log / PR review通过 LangSmith trace 上附带的 memory write 事件
典型场景tool description 优化、grader rubric 改写邮件助理对不同收件人的语气偏好

二、代码层面的最小可运行示例

下面这两段伪代码演示了 LangSmith Engine 一次反推完成后,两种改写落地时的差异。请注意它们调用的是同一个 propose_change 接口,但落地的 storage adapter 完全不一样:

from langsmith_engine import propose_change, ProceduralAdapter, MemoryAdapter

# 场景 1:procedural 类改写 —— 优化某个 tool 的 description
propose_change(
    adapter=ProceduralAdapter(repo="acme/email-assistant"),
    target="tools/send_email.tool.json",
    diff={
        "description.before": "send an email",
        "description.after": "send an email with subject, body, and optional CC",
    },
    evidence_trace_ids=["trace-001", "trace-002"],
)

# 场景 2:memory 类改写 —— 为特定收件人沉淀语气偏好
propose_change(
    adapter=MemoryAdapter(store_url="postgres://langgraph-store"),
    target=("user_pref", "tone", "recipient=ceo"),
    diff={
        "value.before": "neutral",
        "value.after": "concise, action-first, no pleasantries",
    },
    evidence_trace_ids=["trace-003"],
)

第一段改完之后,会被自动 commit 到 Git 仓库、跑一遍 PR check;第二段改完之后,只会往 PostgresSaver 那张 cross-thread memory 表里 INSERT 一行,不会触发任何 CI 流程。这种"同一入口、不同落地"的设计,正是为了让 meta-agent 不用关心下层是源码还是数据库——它只负责"产出 diff + 给出 evidence trace",而具体的写入由 adapter 决定。

三、实战中的取舍矩阵

把这套机制落到真实业务里,有一个非常关键的取舍:你希望这次改写是"普惠"还是"特供"? 下面这张对比矩阵能帮你判断:

  • vs 普惠所有人:选 procedural。典型例子是 terminal coding agent(终端编码 Agent)发现自己调 run_shell 这个 tool 时频繁失败,meta-agent 反推出"应该是 description 没写清楚 working directory 的语义",于是改写 description 并 PR 进仓库——所有用户下次跑这个 Agent 都会受益。
  • vs 特供特定上下文:选 memory。典型例子是 email assistant(邮件助理)发现给 CEO 发邮件时,语气应该"concise, action-first, no pleasantries",但给同级同事发邮件时应该"friendly, 可以加 emoji"。这种偏好明显只对"recipient = CEO"这一个上下文成立,放进 procedural prompt 里反而会污染其他场景。
  • vs 既想沉淀又想普惠:双写。讲师在课程里举过一个 docs writer agent(文档撰写 Agent)的例子——它既需要把"调用 search_docs 之前应该先看看用户给的 URL 是否已经在 context 里"这条经验写进 procedural prompt(普惠),又需要把"这个客户喜欢表格而非列表"这种私人口味写进 memory(特供)。一次反推产生两条 diff,分别走两条 adapter,这是完全合法的。

[观察] 从工程治理角度看,procedural 类改写天然适合走"PR + review"的人类把关流程,因为它影响面广、出错代价高;而 memory 类改写更适合"自动写 + 人工抽查"的轻治理模式,因为它的影响被 context key 天然 sandbox(沙箱隔离)住了。把这两类的 review policy 设计成同一种,要么过度限制导致 memory 写不进去,要么过度放任导致 procedural 误改全量回归,这是 Loop Engineering 落地时最常见的反模式。

[数据] 课程里给出的对比数据点是:一个典型的 terminal coding agent 在一周内,procedural 类改写平均只发生 1-2 次/月,而 memory 类改写可以达到数十次/天,数量级相差近 100 倍。这意味着如果两类的写入路径共用同一个慢速的 review pipeline,memory 写会被严重积压;反之,如果 procedural 走得过快,每周都会出现"误改 tool description 导致全量回归"的灾难。生产环境里通常的折中是:procedural 走 PR review + CI,人工 gate;memory 走异步 batch write,每天一次人工抽查 + 异常回滚。

四、为什么都从 LangSmith Engine 这一统一入口反推

很多工程师第一次接触这套体系时会困惑:既然两类改写落地位置差别这么大,为什么不让 meta-agent 也分裂成两个? 答案藏在 LangSmith Engine 的 contract(契约)设计里:它对外暴露的接口永远是"trace in → proposed diff out",至于 diff 落到 Git、Postgres、SQLite 还是 Redis,完全由 adapter 决定。这样的好处是:

  1. 统一的 evidence(证据)约束:每一个 diff 都必须挂上 evidence_trace_ids,无论它是 procedural 还是 memory。这让事后审计时,可以一站式问"这条记忆条目 / 这段 prompt 是基于哪几条 trace 反推出来的?",而不用去两套系统里分别查。
  2. 统一的回滚原语:无论是 git revert 还是 DELETE FROM memory_store WHERE key = ...,对外都是同一个 rollback_change(change_id) 调用,LangSmith UI 上呈现给人类的也是同一种"撤销"按钮。
  3. 跨类的组合改写:当某条 trace 同时揭示了"tool description 模糊"和"用户偏好没被记住"两类问题时,meta-agent 可以一次性产出两个 diff,而不是跑两遍反推。LangSmith Engine 的 batch propose API 正是为此而设计,详见 https://docs.smith.langchain.com/ 里的 tracing 与 dataset 章节。

但请注意,这套统一入口并不意味着存储位置也统一。入口可以抽象,落地必须显式——这也是为什么我们在表里单独列了"存储位置"这一行。一旦混淆这两层,就会出现"以为改了 prompt,其实写到了 memory 里"的灵异 bug,排查起来非常痛苦。

五、给团队的三条落地建议

第一条,先把 procedural 类的 PR 流程跑通,再考虑上 memory 自动化。前者复用的是工程师已经熟悉的 Git workflow,门槛低;后者需要额外部署 LangGraph Store(参见 https://github.com/langchain-ai/deepagents 里的 harness 配置),并且要设计 context key 的命名规范。

第二条,给 memory 类改写加上 TTL(过期时间)与命中回放。cross-run memory 最容易腐烂——用户的口味会变,如果一条"recipient = CEO 用 action-first 语气"的 memory 永远不被刷新,半年后反而成了负资产。LangSmith Engine 在写入 memory 时建议强制要求一个 expires_at 字段,过期由后台 job 自动清理。

第三条,两类改写必须共用同一份 evidence trace 的快照。换句话说,无论这条 diff 最终落到 Git 还是 Postgres,evidence_trace_ids 里指向的 trace 内容必须被 immutable(不可变)地保留至少 N 天。否则一旦 memory 类改写引发线上问题,你根本没有回溯"它当时是看了哪几条 trace 才决定这么写"的能力,只能靠猜。

把这两类分清楚,Self-improvement 环路才真正具备可治理性;否则它就只是一个会自己写 prompt、还会自己写记忆的"黑盒喷泉"——看起来在持续学习,实际上产出的全是噪声。

俄罗斯套娃:四种环路的任意嵌套模式

俄罗斯套娃的本质,不是要求你把所有环路都装上,而是给一个组合空间:四种环路任选、任意层叠、任意顺序。这种"组合爆炸"的背后其实有结构化的约束——Core(Agent 内核)永远是内层,Self-improvement 通常是外层,中间层可以自由拼装。换句话说,套娃模式允许你"挑你需要的那几层",而不是"套餐制必须全选"。这一点对资源受限的团队尤其重要:工程从来不是"叠 buff 越多越好",而是"每一层都要回答一个新的工程问题"。和传统单体架构相比,俄罗斯套娃的差异在于:单体把所有职责焊死在同一个函数里,改一处就要全量重测;套娃把职责切成可独立维度的中间件,每一层都可以被单测、被替换、被 A/B 测试。

[观察] 在真实工程里,出现频率最高的组合是 Event-driven 套 Verification 再套 Core。原因很直接:Event-driven 解决"什么时候触发",Verification 解决"做得好不好",Core 解决"具体怎么做",这三层语义正交、不打架。Self-improvement 几乎永远位于最外层,因为它要反推 traces(追踪记录,即 Agent 每一次执行留下的完整步骤日志),需要等内层跑完一个完整 run 才能产出改写指令。也就是说,Self-improvement 天然依赖下层的可观测性,而不是嵌套结构上的随意摆放。如果硬把 Self-improvement 塞到 Core 内层,逻辑上就出现"边跑边改自己 prompt"的悖论,LangSmith Engine 那一套 meta-agent 范式就失效了,因为改写指令还没来得及落盘,内层又跑出了新的 trace。

russian-doll-nesting

下面给一个最小骨架,展示在同一个 create_deep_agent 配置里同时挂 Event-driven middleware 与 Verification middleware 是什么样子。中间件并不是直接修改 agent 的源码,而是按调用顺序串联在请求/响应链路上,这一点对理解嵌套本质很关键——环路之间不是函数嵌套,而是"中间件责任链"上的位置先后。

from deepagents import create_deep_agent
from deepagents.middleware import (
    EventDrivenMiddleware,   # 事件触发:Slack / GitHub / Calendar / cron
    GoalMiddleware,          # /goal 注入 + 自动 grader
    RubricMiddleware,        # /rubric 细则注入
)

agent = create_deep_agent(
    model="claude-sonnet",
    tools=[search_docs, send_email, create_pr],
    middleware=[
        EventDrivenMiddleware(
            triggers=["slack_mention", "inbox_new_mail", "cron:0 9 * * 1"],
        ),
        GoalMiddleware(goal="/goal:每周一 9 点生成周报并发到团队频道"),
        RubricMiddleware(rubric="/rubric:含上周 OKR 完成度、阻塞项、下周计划"),
    ],
    checkpointer=PostgresSaver.from_conn_string(DATABASE_URL),
)

这个骨架里,EventDrivenMiddleware 负责把外部事件翻译成 agent 的一次新 run,GoalMiddleware 负责把目标串进 system prompt 并在 run 结束时调用 grader,RubricMiddleware 负责注入评分细则。三者都通过 Deep Agents 的中间件协议挂载,协议具体定义见官方文档 https://docs.langchain.com/oss/python/deepagents/ ;官方仓库 https://github.com/langchain-ai/deepagents 也提供了更多 middleware 的参考实现,例如 schedule middleware、memory middleware、human-in-the-loop middleware 都可以按同一接口扩展,意味着"加一层"实际操作上就是"加一个 middleware 实例"。

把四种环路摆开,组合总数其实远不止 6,但工程上"合法"的嵌套只有以下 6 种。这里的"合法"等价于:Core 必须在最内层、Self-improvement 在最外层(若存在),其余按职责正交性排列。这样约束下,深度 1 到 3 层各覆盖一些典型场景,深度 4 的全四层嵌套单独拿出来讨论。

four-loops-combinations

嵌套深度组合形态典型适用场景工程复杂度
1 层Core一次性任务、demo、原型极低
2 层Core + Verification输出可判定质量的批处理
2 层Core + Event-driven定时报告、webhook 处理器
3 层Core + Verification + Event-driven周期性高质量交付物中高
2 层Core + Self-improvementprompt 调优、tool description 迭代
3 层Core + Verification + Self-improvement长期稳定的生产级 agent

[数据] 课程的工程经验数据是:仅 Core 时,迭代效率的天花板被 prompt 一次性写死的质量锁住;引入 Self-improvement 后,改写频率可达每日数十次,但每次改写都要经过 LangSmith Engine 反推 traces 的开销,综合时延约 5 到 15 分钟/轮;而单 Core 嵌套的人工改写周期普遍在一周以上,意味着线上问题到修复的反馈环极长。Verification 层的引入会让单次 run 的失败率显著下降,因为 grader 会在 run 末尾拦住不合格输出,据经验数据可减少 30-50% 的无效 follow-up 调用,直接降低模型推理成本。

接下来看全四层嵌套与单 Core 嵌套的取舍。

维度全四层 (Core + Verification + Event-driven + Self-improvement)单 Core 嵌套
成功率上限接近 95% 以上,因有 grader 拦截 + meta-agent 改写取决于 prompt 一次性写入质量,通常 60-80%
单次改写成本数百到数千美元/月(LangSmith Engine + 模型推理)几乎为零
调试复杂度高,需要同时读 trace、grader 输出、改写 diff低,直接看 prompt
适用阶段生产级 SLA(服务等级协议) 场景早期验证、PoC(概念验证)
运维门槛需要 traces 基础设施 + checkpointer + grader rubric 维护单个 create_deep_agent 调用即可
失败定位需区分 Core 失败、Verification 误判、Event-driven 漏触发、Self-improvement 改写回退单一变量:prompt 本身

[数据] 上面这张表的关键取舍点在于"成功率上限"与"工程复杂度"几乎单调正相关:你堆的层越多,理论上能逼近的输出质量越高,但需要的可观测性、回滚机制、运维人力也都水涨船高。LangChain 团队的官方建议是按"先 Core、再 Verification、再 Event-driven、最后 Self-improvement"的顺序逐层加,不要一开始就把四层全开——否则一条失败 trace 会同时穿过四个抽象层,排查时不知道该看哪一层的日志,以及 Self-improvement 改写后回滚又会覆盖掉你刚加的人工补丁。LangChain 官方文档 https://python.langchain.com/docs/concepts/agents/ 进一步把 Agent 的可插拔组件抽象成 model / tools / prompt / middleware 四件套,使环路嵌套的本质变成"在四个槽位里选一个或多个挂上去"。

回到这套范式之所以能成立的前提:Deep Agents 与 Dcode 都是开源项目,源码在 GitHub 公开,create_deep_agentcreate_agent 的中间件协议是同构的,你可以把任意一个 middleware 抽出来单跑、组合、替换。这是俄罗斯套娃模式得以"任意嵌套"的工程基础——如果中间件是闭源黑盒,层与层之间的契约就锁死,也就没有嵌套空间。具体持久化配置可参考 LangGraph 持久化概念页 https://langchain-ai.github.io/langgraph/concepts/persistence/ ,里面的 checkpointer 抽象是跨 run memory 之所以能稳定生效的底层支撑,也是 Self-improvement 为什么要"等内层跑完一个完整 run"的物理基础。如果跳过 checkpointer,跨 run memory 就退化到单 run 内的 in-context 拼接,Self-improvement 也就无从判断"上一个版本 vs 这一个版本"。

在 Dcode 视角下,俄罗斯套娃被进一步具体化。Dcode 把 Verification 暴露成 /goal/rubric 两个 slash command——即在 agent 的 system prompt 里直接写 /goal:本周生成 OKR 复盘 这样的字面字符串,GoalMiddleware 在每次 run 启动时把它解析成结构化目标,再交给 grader 去验证。Dcode 与 Deep Agents 的关系是:前者是后者的一个垂直化封装,典型场景是终端编程;后者是通用 harness,典型场景是 docs writer agent、email assistant agent。两者共享同一套 middleware 协议,所以你可以把 Dcode 里写好的 GoalMiddleware 抽出来,直接挂到另一个 Deep Agent 实例上,组合成新的环路。

loop-composition-matrix

总结一下:俄罗斯套娃模式不是让你把四种环路全装上,而是给一个组合空间,你可以从最小的 Core + Verification 起步,逐层加上 Event-driven 与 Self-improvement。每加一层,都意味着新一组可观测性需求与新一种失败模式;每少一层,都意味着把某些职责让渡给人工。判断"该堆几层"的方法,不是看技术能不能做到,而是看你愿不愿意为那个 +5% 的成功率上限,承担对应的工程开销。

Docs Writer 案例:四种环路都跑通的端到端 demo

Docs Writer 案例:四种环路都跑通的端到端 demo

在整个 Loop Engineering 课程中,Docs Writer Agent 被 LangChain 团队选作贯穿全片的统一案例。它不是孤立的玩具示例,而是把核心 Agent 环路、Goal/Verification 环路、Event-driven 环路、Self-improvement/Hill Climbing 环路这四种工程模式,全部跑通在一个真实的端到端 demo 上。对读者而言,这意味着只要把 Docs Writer 拆明白,「如何在自家业务里叠环路」这件事就有了可对照的样板工程,而不是停留在概念图层面。

[观察] Docs Writer 之所以被选为统一案例,本质上是因为「写文档」是少数几种天然自带 grader 的任务:链接是否可达、CI 是否过线、Markdown 语法是否合规、参考链接是否对得上 anchor,全部都能用脚本或 rubric criteria 量化判定。这种「完成本身可验证」的特性,让它成为四种环路都能落地而不会互相打架的稀有载体。换句话说,写文档不是目的,它是工程师拿来验证俄罗斯套娃组合空间是否可控的「压力测试床」——如果一个 grader 写不出来,这层 Verification 环路就别硬叠,否则只会引入更多噪音。

docs-writer-end-to-end

一、Docs Writer 的工程骨架

Docs Writer 的代码组织沿用了 Deep Agents 的中间件范式,把 agent 内核、goal、rubric、skills、memory 切成可独立维护的模块。一个最小可运行的目录大致长这样:

docs_writer/
├── agent.py              # create_deep_agent() 入口,挂载 skill + middleware
├── skills/
│   ├── api_reference.md  # skill:调用内部 docs API 的契约
│   └── ci_checker.md     # skill:CI 命令与返回码解读
├── graders/
│   ├── link_grader.py    # rubric:链接可达性
│   ├── ci_grader.py      # rubric:CI pass/fail
│   └── style_grader.py   # rubric:段落长度 / 标题层级
├── middleware/
│   ├── goal.py           # /goal 解析与注入
│   └── rubric.py         # /rubric 解析与 grader 调度
├── memory/
│   └── notes.md          # 跨 run 记忆
└── triggers/
    └── slack_webhook.py  # event-driven 触发入口

agent.py 走的是 Deep Agents 风格的 create_deep_agent 入口,把 skill、tool、middleware 三件套作为参数注入;graders/ 目录对应 Verification 环路,每个 grader 都返回结构化分项结果而不是布尔值,方便后续的 Hill Climbing 用单维度反馈驱动 prompt 微调;triggers/ 则把 Event-driven 环路的入口显式抽出,避免把 webhook 逻辑写死在 agent 里。

二、Skill 配置文件长什么样

Skill 在 Deep Agents 里被建模成「带 frontmatter 的 Markdown 文件」,frontmatter 声明元数据,正文告诉 LLM「什么时候调用、怎么调用、失败怎么办」。下面是一段典型的 Skill 配置片段(用 Python dict 序列化展示,便于在源码里以常量引用):

SKILL_CI_CHECKER = {
    "name": "ci_checker",
    "description": (
        "触发时机:当 docs writer 修改了 docs/**/*.md 之后;"
        "用于校验本次 PR 是否会被 CI 拒绝,返回每条失败的 job 名称。"
    ),
    "tools": ["run_shell", "read_file"],
    "input_schema": {
        "changed_files": "list[str]",
        "base_branch": "str",
    },
    "output_schema": {
        "ci_status": "Literal['pass', 'fail']",
        "failed_jobs": "list[str]",
    },
    "failure_retry": 1,
    "timeout_seconds": 90,
}

description 字段是 LLM 决定何时装载该 skill 的唯一线索,因此课程反复强调「skill 描述就是 prompt 的一部分」。failure_retry=1 限定最多重试一次,避免模型陷入「修 CI → 破 CI → 再修 CI」的振荡循环;timeout_seconds=90 则给 run_shell 一个硬上限,防止 CI 卡死把整个 agent loop 拖垮。

三、Grader rubric 代码片段

Verification 环路的灵魂在 grader。下面这段 rubric 片段展示了 LangChain 团队惯用的「分项打分 + 阈值 + 失败原因」三段式结构:

def grade_link_validity(doc: str, base_url: str) -> RubricResult:
    links = extract_markdown_links(doc)
    failures = []
    score = 0
    for link in links:
        if link.startswith("http"):
            code = http_probe(base_url, link)
            if code == 200:
                score += 1
            else:
                failures.append({"link": link, "code": code})
        elif link.startswith("#"):
            anchor_exists = check_anchor(doc, link[1:])
            score += 1 if anchor_exists else 0
    return RubricResult(
        criterion="link_validity",
        score=score,
        total=len(links),
        failures=failures,
        pass_threshold=0.95,
    )

RubricResult 不只返回布尔,而是把每一条失败链接连带 HTTP 状态码回写到 grader output,Self-improvement 环路再据此判断「是不是要改 prompt 里关于『不要写死外链』的那一条」。这种「细粒度失败信号」是 Hill Climbing 能收敛的前提——反馈越具体,meta-agent 的改写越能落地;如果只丢一个布尔,prompt 就只能瞎改。

四、四种环路在 Docs Writer 上的输入输出对照

下面这张表把四种环路串成一条流水线:每一行都是 Docs Writer 跑一圈实际产生的输入、输出、改写点与回写位置。

环路触发方式主要输入主要输出改写点回写位置
Core Agent Loop用户提问 / PR 评论自然语言需求、当前仓库 diff改写后的 Markdown、PR 描述无(纯生成)不回写
Verification LoopCore Loop 收尾时生成出的 Markdown 文档grader 分项得分 + 失败清单重写该段落、删除坏链注入下一轮 Core Loop prompt
Event-driven LoopSlack 消息 / cron / webhook新需求、issue 评论、定时任务触发 Core Loop + Verification触发器配置
Self-improvement Loop每日 cron + LangSmith tracesN 条历史 trace + grader 历史新版 skill / prompt / tool description改 skill frontmatter、tool docstring、agent promptcommit 回 docs_writer/skills/**agent.py

[数据] 课程给出的对照实验显示,Docs Writer 在「只启用 Core Loop」时,初版文档的 grader 总分大致在 0.72 ± 0.05 区间,且每次跑分的人工返工次数中位数约为 4 次;启用全部四种环路之后,分数稳定拉到 0.91 ± 0.03,人工返工次数中位数降到 1 次。返工次数下降不是 grader 替代了人,而是 grader 把「明显会失败的链接 / 显然会挂 CI 的语法」在第一时间挡掉,把人工精力留给真正的内容判断;方差同步收窄则说明 Self-improvement 环路把跨 run 的漂移也压住了。

五、对比矩阵:四环路 vs 单环路

下面这张矩阵把取舍显式化,帮读者判断「我应不应该全装」。两条竖轴分别是「只装 Core Agent Loop」与「装全部四种环路」,两条横轴分别是「文档质量分(grader 总分)」与「人工返工次数(每个 PR)」。

维度只用 Core Agent Loop启用全部四种环路取舍 / 边界条件
文档质量分中位 0.72,方差大中位 0.91,方差小差距随仓库规模放大;小 repo 收益有限
人工返工次数中位 4 次/ PR中位 1 次/ PRVerification 占主要功劳
单次生成成本1× LLM 调用约 2.5×(含 retry + grader + meta)预算紧张团队可关掉 Self-improvement
维护成本几乎为零需要维护 rubric / skill / trace 数据rubric 漂移是主要风险
上线门槛一行 create_deep_agent需要可观测性 + cron + webhook大型团队才值得全套
失败可解释性差,模型自由发挥强,每一项失分都有 rubric 索引强可解释性反向促进 prompt 收敛

取舍的关键不是「越多越好」,而是「每一层是否回答了一个新的工程问题」:Core 解决生成质量,Verification 解决可判定完成,Event-driven 解决接入真实工作流,Self-improvement 解决长期漂移。如果你的痛点还没冒到那一层,就先停在 Core + Verification,再按需叠加;强把 Self-improvement 装在还没跑过 100 条 trace 的早期项目上,只会得到一堆噪声 diff。

六、易踩的坑清单

把这四种环路全跑通并不轻松,团队踩过的常见坑集中在四个点:

  1. rubric 漂移:仓库改了 anchor 命名规范,但 link_grader 还在校验旧 anchor,grader 长期假阳性。建议给每个 rubric 加一个 version 字段并在 commit message 里同步声明 rubric 版本。
  2. skill 描述过载:把所有规则都塞进 description,导致 LLM 加载 skill 时上下文爆炸。课程建议 description 不超过 200 token,正文里只放「触发条件 + 失败处理」,长说明单独开一份 README
  3. event-driven 触发风暴:Slack 群里一句「文档需要更新」可能触发 10 次 Core Loop。生产环境必须加去重 key(如 issue 编号 + 最近 commit SHA),并在 middleware 层做时间窗合并。
  4. Self-improvement 改坏了 prompt:Hill Climbing 在某些指标上确实更优,但语义上可能把「写作语气」改成了「SEO 风格」。建议每次 prompt 改写都过一遍 human-in-the-loop review,再决定是否全自动回写,而不是默认信任 meta-agent。
七、延伸阅读

想要自己动手复现 Docs Writer,可以直接看 Deep Agents 仓库里 docs_writer/ 这个目录样例,配套的 grader 与 middleware 都按模块切好。把案例拆清楚之后,再去读 Deep Agents 仓库里 docs improvement 案例,可以看到同一套思想在「团队级 agent harness」上的扩展形态——多 agent 协同、跨仓库 memory、hill climbing 调度器等组件的落地都在那里:

  • Deep Agents 官方仓库:https://github.com/langchain-ai/deepagents
  • Deep Agents 文档:https://docs.langchain.com/oss/python/deepagents/
  • LangSmith 评测与数据集:https://docs.smith.langchain.com/evaluation
  • LangGraph 持久化与 checkpointer:https://langchain-ai.github.io/langgraph/concepts/persistence/

俄罗斯套娃的本质,是给团队一个「组合空间」而不是「套餐」:Core 永远在最内层,Self-improvement 通常在最外层,中间两层按你当下的工程问题任意拼装。Docs Writer 的价值在于,它把这套组合空间里最常见的一种拓扑,用一个能跑、能评分、能被改进的真实任务完整演示了一遍——剩下的只是把 rubric 替换成你所在领域的判定规则,把 skill 替换成你团队的工具,把 trace 替换成你自己的真实反馈流。

失败信号模式库:从 traces 中识别可改写点

Self-improvement 环路的真正起点并不是「如何改写 prompt」,而是「如何从历史 trace 中识别出『这一段本可以写得更好』的可改写点」。在这套课程给出的 Loop Engineering 四环框架里,LangSmith Engine 这类 meta-agent 本质上是一个离线遍历器:它把生产环境回流回来的所有 LangSmith trace 当作语料,逐条走一遍「找失败信号 → 锁定改写对象 → 生成新版本 → 回写到源码」的全流程。如果没有一个结构化的失败信号模式库(failure signal pattern library)做底座,meta-agent 就只能在 prompt 表面做文本级别的随机扰动,产出的所谓「改进」既无方向也无边界,本质上和随机搜索没区别。

failure-signal-pattern-library

讲师在课程里反复强调一点:模式库不是某一类 prompt 技巧的集合,而是一份「诊断书目录」。每一条 pattern 都对应一种可观测的症状、可触发的判定逻辑、以及明确指向的改写对象。这种「症状 → 判定 → 改写对象」的三元组结构,是后续整条 Self-improvement / Hill Climbing 环路能否真正闭环的工程前提。换句话说,模式库的颗粒度直接决定了 meta-agent 能在什么维度上「爬山」——颗粒度越粗,只能改 prompt;颗粒度越细,可以连 tool description、skill、cross-run memory 一起改。把这条边界画清楚,是 Loop Engineering 在工程层面区别于传统 prompt 工程的关键。

[观察] 真正卡死团队的不是「没有 trace」,而是「trace 多到没人看得完」。一旦 LangSmith tracing 在生产环境全量开启,一个中等规模的 Docs Writer Agent 一天就能产生上万条 trace;靠人肉 review 永远不可能闭环。把失败信号模式库作为「机器可读诊断书」才是出路——它把「我看着觉得这一段写得不好」的人类直觉,翻译成「trace 中第 N 个 tool call 的输入 schema 与上一步 prompt 中的字段名不一致」这种机器可判定的特征,从而让 meta-agent 可以批量化、可回放地消费这些诊断结果。

在 Docs Writer 这个端到端 demo 里,讲师把生产中观察到的失败信号归并为 5 类:

  1. 工具参数错误(tool schema mismatch):模型给出的 tool call 参数类型不对、字段拼写错误,或者必填字段缺失。表现是 trace 里该 tool node 直接抛 ValidationError。
  2. 关键 tool call 缺失(missing critical call):模型在没有调取某个外部检索类 tool(例如 langchain_retriever、tavily_search)的情况下直接生成了断言性结论。
  3. 风格未对齐(style drift):输出文本在语气、术语密度、Markdown 层级上偏离了 docs-writer 这一 skill 在 system prompt 中定义的基线。
  4. 重复循环(loop / no-progress):trace 中连续 N 步(常见 N≥3)在相同 tool 之间反复跳转,token 在燃烧但 state 没有推进。
  5. 事实幻觉(factual hallucination):模型生成了与 source document 不一致的陈述,常见于 RAG 类的长上下文摘要。

[数据] 在课程里讲师给出了一组粗略量级数据:在 Docs Writer 的 1000 条回灌 trace 中,五类失败信号的发生比例大致是 33% / 21% / 18% / 14% / 14%。工具参数错误占比最高,这一项绝大多数情况下不需要 LLM 就能用 JSON Schema 校验或正则一次过判定;事实幻觉占比虽不算最高,却是单条判定成本最高的一类——必须把模型输出和 retrieved chunk 重新做一次交叉比对才能给出置信度。从这个分布可以直接推出 pattern library 的工程权衡:规则型判定器应当优先覆盖占比最高的前两类(合计 54%),LLM 判定器则只在剩余 46% 上发力,因为把 LLM 放在低占比高确定性信号上是 ROI 最差的选择。

下面是一个用正则 + LLM 混合判定从 trace 里抽取失败信号的最小示例。它演示了如何把 LangSmith trace 拉成本地 dict,先用确定性规则扫一遍高置信失败信号,再把剩下的「灰区」trace 交给 LLM 做语义层判定:

import re
import json
from langsmith import Client
from langchain_openai import ChatOpenAI

ls = Client()
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

SCHEMA_FAIL = re.compile(r"ValidationError|pydantic.*error", re.I)
LOOP_FAIL   = re.compile(r"tool_call_count:\s*(\d+)")
HALLUC_HINT = re.compile(r"\b(I think|probably|maybe|约)\b", re.I)
KEY_TOOL    = "tavily_search"


def rule_based_scan(run_dict: dict) -> list[str]:
    signals = []
    blob = json.dumps(run_dict, ensure_ascii=False)

    if SCHEMA_FAIL.search(blob):
        signals.append("tool_schema_mismatch")

    m = LOOP_FAIL.search(blob)
    if m and int(m.group(1)) >= 3:
        signals.append("tool_loop_no_progress")

    if HALLUC_HINT.search(blob):
        signals.append("hallucination_hint")

    if KEY_TOOL not in blob and "事实" in blob:
        signals.append("missing_critical_call")

    return signals


def llm_based_scan(run_dict: dict) -> list[str]:
    prompt = f"""你是失败信号判定器。下面是一段 agent trace:
{json.dumps(run_dict, ensure_ascii=False)[:6000]}
请只输出 JSON 列表,从下列 label 中挑: tool_schema_mismatch,
missing_critical_call, style_drift, tool_loop_no_progress,
hallucination。"""
    resp = llm.invoke(prompt).content
    return json.loads(resp)


def extract_signals(run_id: str):
    run = ls.read_run(run_id)
    d = run.dict()
    hard = rule_based_scan(d)
    return hard + llm_based_scan(d) if not hard else hard

代码里有几个值得在团队内做 code review 时反复强调的工程取舍:第一,SCHEMA_FAIL 这种正则不到万不得已不要用纯字符串匹配 pydantic 异常文本——更稳的做法是从 trace 的 error 字段直接读结构化异常码;第二,LOOP_FAIL 的阈值 3 是经验值,业务团队应当按自己 agent 的平均步数调整,而不是照抄;第三,LLM 判别的 prompt 严格限制了可选 label 集合,目的是让多次跑出来的结果可聚合、可入库,避免自由文本输出破坏后续回写环节。

失败信号类别改写对象回写方式
工具参数错误tool schema / tool description修复字段名或 description 中的歧义表述,改动落在 tool 注册源码
关键 tool call 缺失prompt + skill在 system prompt 中显式追加「涉及 X 主题前必须先调 retriever」
风格未对齐skill(prompt 中的风格段)把风格样板从 system prompt 抽到独立 skill 文件,便于 diff 与版本化
重复循环tool description + memory给循环嫌疑 tool 加重试上限与退出条件;把「何时停止」的策略写入 cross-run memory
事实幻觉prompt + retriever 召回策略在 prompt 里追加「必须引用 retrieved chunk 编号」;同时调整 retriever top-k 与 rerank 阈值

这个表并不是装饰性的——它实际上就是 Self-improvement 环路里「回写」环节的唯一可执行入口。meta-agent 拿到一条被判定为 style_drift 的 trace 后,只会在 skill 文件而非 prompt 顶部去改;同样,被判定为 tool_loop_no_progress 的 trace,只会在 tool description 里加一行 hint,而不会去重写整个 system prompt。颗粒度对齐,改写才不会越界,回写结果也才能稳定地映射到 GitHub PR 的某个具体文件上。

人工抽失败信号 vs 自动抽失败信号,在覆盖率与误报率上呈现出非常不同的曲线:

维度人工抽失败信号自动抽失败信号
覆盖率(每天能看多少条 trace)50–200 条/人,极易在峰值期被积压全量,理论上无限
误报率低,人具备语义判断能力中高,规则会过激,LLM 会幻觉
单条判定成本高(分钟级/人)低(规则毫秒级,LLM 秒级)
可回放性弱,人脑记忆随时间衰减强,规则与 prompt 都可版本化
新模式发现能力强,人能识别「没见过」的现象弱,必须先把模式沉淀进 pattern library
适合阶段上线初期 / 冷启动规模化生产 / 长期运营

取舍的关键点在于「覆盖率与误报率不是同时最优的」:人工抽信号新模式发现能力强但覆盖低,自动抽信号覆盖广但只能识别已建模的模式。健康的工程实践是让两者形成回路——自动抽的输出按周回流给人工 review,人工挑出来的新症状再被编码进 pattern library,形成正反馈。这条回路一旦打通,团队的失败信号模式库就会以周为单位持续扩张,而 meta-agent 的爬山空间也会同步变大。

这也引出本节最后、也是最容易被忽视的一点:模式库遵循开闭原则(Open-Closed Principle)。框架默认给出 5 类基础 pattern 作为闭环兜底——只要 Docs Writer 这类 agent 上线,就能立即被 Self-improvement 环路兜住大部分常见失败。但任何具体业务(法务合同、内部 wiki、客服 FAQ)都会涌现出框架层完全无法预见的失败类型,例如「条款编号与附件不一致」在通用 pattern 里压根不存在。业务团队必须有权在自己的 pattern library 里新增子类、定义新的判定函数、并在 meta-agent 的回写目标清单中追加新的改写对象。框架负责「开」,给基础模式、给判定器接口、给回写通道;业务团队负责「闭」,在不动核心环路的前提下扩展自己的子类。这是 Self-improvement 环路能不能从一个 demo 真正迁移到生产的关键分水岭,也直接决定了 LangChain Engine 这类 infra 能不能在多业务线之间复用同一份骨架。

更多关于 trace 评测与 grader rubric 的落地形态,可以参考 LangSmith 官方文档中的评测数据集示例 https://docs.smith.langchain.com/evaluation 以及 LangChain agents 概念入门 https://python.langchain.com/docs/concepts/agents/ ;而中间件层把 /goal 与 /rubric 暴露为可插拔接口的工程实现,则在 Deep Agents 开源仓库 https://github.com/langchain-ai/deepagents 中可以直接读到 create_deep_agent 的源码级 hook。

最后我想把视角拉回到工程治理层面。失败信号模式库不是一次性资产,而是随业务演进的活文档——每次业务方提「我们的 agent 又翻车了」,第一动作不应该是 hotfix prompt,而应该是把这次翻车写进 pattern library,再让下一轮 Self-improvement 跑过去时自然消化。把每一次人工救火都变成一次「模式沉淀」,才是 Loop Engineering 整套方法论最终能否在团队里扎根的判断标准。模式库扩张的速度,本质上等于团队对自身 agent 行为边界的理解速度。

可观测性与 checkpoint:让环路跑得可调试

为什么可观测性是 Self-improvement 环路的前提

当一个 Self-improvement 环路开始把生产环境的 trace 当成改写 prompt 的"语料"时,可观测性就从 nice-to-have 变成了生命线。LangSmith tracing 和 LangGraph 的 checkpointer 是这套栈里两个必须同时具备的组件——前者负责把每一次环路的"思考—行动—结果"全部串成一条可搜索、可回放的时序记录,后者负责把每一轮迭代的中间状态、变量、消息历史持久化到数据库,使得环路中断、重试、跨进程恢复都成为可能。缺少任意一个,整个 hill climbing loop 都会退化成"改一改、看天吃饭"的黑盒。

这套课程反复强调一个事实:任何"自动改写 prompt / tool / skill"的机制,如果没有结构化的失败信号模式库(failure signal pattern library),就只是随机扰动。而 trace 和 checkpoint 正是这个失败信号模式库的两条原材料来源——前者给出"哪一步调用偏离了 rubric"的微观证据,后者给出"在哪一个迭代轮次的状态点上模型开始走偏"的状态证据。两类证据必须按时间戳对齐,才能复现一次完整的失败。

observability-stack

多层嵌套下的 trace 区分能力

[观察] 多层嵌套环路一旦出问题,trace 是唯一能区分"哪一层挂了"的依据。在 Russian doll 的组合模式里,外层是 Event-driven 调度(例如 GitHub PR 触发),内层是 Core Agent Loop,再往里是 Goal/Verification 环路,最里层是 Self-improvement 环路。当 grader agent 报出"rubric 未达标"时,如果不看 parent_run_id 链,就完全无法判断这次失败发生在"是 goal middleware 判定不通过"、“是核心 tool 调用超时”,还是"是 meta-agent 在改写 prompt 时引入了语法错误"。LangSmith 提供的树形 trace 视图,本质上就是把"环路套环路"这种结构性复杂度,降维成"按 run_id 父子关系一层一层展开"的可视化调试界面。

最小配置:LangSmith tracing + Postgres checkpointer

下面是一个最小可运行的配置片段,展示如何在 create_deep_agent 上同时启用 LangSmith tracing 与 Postgres checkpointer:

import os
from deepagents import create_deep_agent
from langgraph.checkpoint.postgres import PostgresSaver
from langchain.chat_models import init_chat_model

# 1. 打开 LangSmith tracing
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "lsv2_xxx"
os.environ["LANGSMITH_PROJECT"] = "loop-engineering-deepagent"

# 2. 准备持久化 checkpointer
DB_URL = "postgresql://user:pwd@localhost:5432/loop_state"
checkpointer = PostgresSaver.from_conn_string(DB_URL)
checkpointer.setup()  # 首次运行建表

# 3. 组装 deep agent
model = init_chat_model("claude-sonnet")
agent = create_deep_agent(
    model=model,
    tools=[search_docs, run_grader],
    checkpointer=checkpointer,
    middleware=[goal_middleware, rubric_middleware],
)

# 4. 每次 invoke 都指定 thread_id,环路跨进程恢复靠它
config = {"configurable": {"thread_id": "session-loop-001"}}
result = agent.invoke({"messages": [...]}, config=config)

这段配置里有三处工程细节值得展开:第一,LANGSMITH_TRACING=true 是全局开关,只要环境变量存在,LangChain 生态里的所有 chat model、tool、retriever 都会自动埋点,不需要在每个节点单独插桩;第二,PostgresSaver.from_conn_string(...) 是 LangGraph 推荐的持久化实现,它把每一步的 messages、tool_calls、interrupts 全部以 JSON 形式写入 checkpointscheckpoint_writes 两张表,跨进程恢复完全靠 thread_id + checkpoint_id 定位;第三,create_deep_agentmiddleware 参数接受一个列表,goal_middlewarerubric_middleware 是 Deep Agents 框架预置的两种中间件,分别用来注入 /goal/rubric 的运行时上下文。

trace 的四个核心字段

LangSmith 每次 run 都会产出一组结构化字段,其中对调试多层环路最重要的 4 个如下表所示:

字段含义在多环嵌套中的作用
run_id当前这一次调用的唯一标识(UUID)精确锚定"问题发生在哪一步",可在 LangSmith UI 直接深链
parent_run_id父级 run 的 UUID,嵌套时指向外层调用通过它把 Core / Verification / Self-improvement 三层串成因果链
inputs进入这一次调用的全部入参(消息、变量、tool schema)回放某一次失败时的初始状态,用于离线复现
outputs这一次调用返回的全部结果(messages、tool 响应、最终答案)inputs 配对,构成完整的"前因—后果"对

trace 嵌套深度的工程含义

[数据] 在该教程给出的实战案例里,一个同时跑 Goal/Verification + Self-improvement 的 deep agent 单次任务平均会产生 30~80 条 run,高峰期甚至超过 200 条;其中 parent_run_id 的平均链路深度为 4~6 层,极端情况下(例如 grader 反例回灌触发 meta-agent 重写 prompt)会达到 8 层以上。这意味着,一旦缺少父子 run 的可视化,人工定位一次失败平均需要浏览上千条日志条目,而启用 LangSmith 树形视图后,平均定位时间下降到 1 次点击。这套数据从侧面印证了一个工程结论:trace 嵌套深度是和环路嵌套层数线性绑定的,而不是和单次任务的工具调用数绑定。换句话说,核心 agent loop 的内部工具再复杂,只要它跑在同一个 run 节点下,trace 深度就不会增长;反倒是多了一层 middleware 嵌套,trace 深度就立刻 +1。

对比矩阵:LangSmith tracing vs 自建日志

接下来是关键的取舍对比——LangSmith tracing 与自建日志系统在三个维度上的差异:

维度LangSmith tracing自建日志系统(JSON file / ELK / Loki)
trace 一致性由 SDK 统一埋点,跨 chat model / tool / retriever 字段命名一致各团队自定义,字段命名 drift,跨服务对齐成本高
跨 run 查询原生支持按 parent_run_idrun_typemetadata 过滤需要自行设计索引,join 父子关系常出现 N+1
回放能力一键"Replay run",把 inputs 重新喂给模型重跑一般仅支持"看日志",无法直接重放,只能靠人工还原
检索时延服务端预聚合,毫秒级取决于日志体量与索引质量,常达秒级甚至分钟级
私有化部署商业 SaaS 为主,自托管版功能受限完全可控,合规友好

这张对比矩阵揭示了一个工程权衡:LangSmith tracing 的核心价值不在于"它能记录",而在于"它能让跨 run 的因果关系被一键重建"。当 Self-improvement 环路需要从历史 trace 中挖掘"哪条失败模式出现频次最高"时,这种因果重建能力是任何 JSON 日志系统都难以低成本复制的;但反过来,如果团队出于合规要求必须把数据留在内网,那么自建 + LangSmith 自托管版的混合方案往往比纯自研更划算,关键看 trace 与 checkpoint 这两类数据是否都允许出域。

checkpoint 的状态流转

checkpoint-state-flow

上面这张 checkpoint-state-flow 图,描述的是 LangGraph 在一次 invoke 内部的状态流转:每一步 node(模型调用、tool 调用、middleware 注入)执行后,框架都会把当前 State 序列化并写入 Postgres,产生一条新的 checkpoint_id;当环路因为超时、OOM、或者 grader 失败而中断时,下一次 invoke 只要带上同样的 thread_id,就会从最近一个有效 checkpoint 恢复,而不是从头重跑。这对 Self-improvement 环路尤其关键——meta-agent 跑一遍完整的 trace 改写流程可能耗时数十分钟,如果中途崩溃,没有 checkpointer 就只能整轮重跑,而 PostgresSaver 能精确恢复到"已经改写到第 17 条 prompt、还剩 8 条"这种粒度。

在具体选型上,LangGraph 提供了三种实现:InMemorySaver 适合本地调试与单元测试,数据随进程退出而丢失;SqliteSaver 适合单机持久化的轻量场景;PostgresSaver 则是生产环境的默认选项,支持并发写、跨进程恢复、以及和 LangSmith 之间的 run_idthread_id 双向关联。值得注意的是,这三者对外暴露的 API 完全一致,因此可以在不修改业务代码的前提下,从 InMemorySaver 平滑迁移到 PostgresSaver

实战踩坑清单

几个实战中容易踩的坑值得提前标注:第一,thread_id 必须是稳定且唯一的字符串,推荐用业务侧的自然键(如 pr-编号 + 任务类型),而不是随机 UUID,否则后续跨 run 关联会非常痛苦;第二,Postgres 的连接池大小要和环路并发量匹配,否则 checkpointer 会成为瓶颈,导致 invoke 阻塞甚至写入丢失;第三,LangSmith 的 trace 和 LangGraph 的 checkpoint 是两套独立的存储,千万不要把它们混用——trace 走 LangSmith 后端,checkpoint 走你自己的 Postgres,前者用于"看",后者用于"恢复",二者通过 run_idthread_id 的映射关联;第四,LANGSMITH_TRACING 之外还有一个常被忽略的环境变量 LANGSMITH_SAMPLE_RATE,在生产环境应该设为小于 1 的浮点数,以避免 trace 体量爆炸导致 LangSmith 配额耗尽;第五,checkpoint 表会随着迭代轮次线性增长,务必定期执行 DELETE FROM checkpoints WHERE checkpoint_id < ... 之类的归档脚本,否则 Postgres 的存储成本会失控。

延伸阅读

如果你打算在这一层进一步深挖,以下两份官方文档是最直接的延伸阅读:LangSmith tracing 的接入指南(https://docs.smith.langchain.com/)详细列出了环境变量、SDK 配置、以及如何把自定义 callback 也纳入 trace;LangGraph 持久化与 checkpointer 概念页(https://langchain-ai.github.io/langgraph/concepts/persistence/)则解释了 InMemorySaverSqliteSaverPostgresSaver 三种实现的适用场景与切换路径。两份文档加起来大约半小时的阅读量,就能把"如何让环路跑得可调试"从经验直觉变成工程纪律。

回到这套 Loop Engineering 四环框架的视角:可观测性本质上不是 Self-improvement 环路的"附加功能",而是它能成立的前提条件——没有结构化的失败信号,改写就只是扰动;没有持久化的状态,跨轮迭代就只是赌博。把 LangSmith tracing 和 Postgres checkpointer 当作一等公民来设计,而不是事后补的日志,这一点,是所有跑过三种以上环路嵌套的团队都会反复印证的一条经验。

评估与 rubric 工程化:grader agent 的工程实现

上一节我们提到可观测性把 trace 变成了 Self-improvement 环路的「语料」,但仅有语料还不够——环路还必须知道什么时候算「改对了」「改坏了」「改得一般」,而这个「知道」的背后,就是 rubric 工程化。把 rubric 当成一份写得像 checklist 的工程清单,是 Verification 环路能否稳定落地的关键。如果 rubric 仍然是一段散文式的描述,grader agent 就会跟着一起飘,环路的反馈信号就只能是噪声。

rubric-engineering-flow

rubric 的核心矛盾:从「主观描述」到「可二值判定」

[观察] 工业级 rubric 工程最大的坑不在 grader 本身,而在 rubric 的可判定性。把「写得不错」「结构合理」「基本可用」这类模糊形容词写进 rubric,grader agent 就会被这些形容词劫持——它会把同样的形容词原样吐出来,导致两条几乎相同的内容在 grader 那里得到完全不同的分数。换句话说,当 criterion 描述里出现形容词,grader 的判定就不再是布尔运算,而变成了一次「再创作」,Verifiability 自然崩塌。真正能稳定驱动 Verification 环路的 rubric,必须把每条 criterion 都拆成可二值判定的命题:「标题是否包含『目标』一词」「是否给出至少 3 条 todo 项」「链接是否返回 200」「代码块是否包含 ```python 围栏」。一旦 criterion 落在布尔可判定的集合内,grader 的输出空间就被压缩到很窄的二值集,环路的反馈信号才可能稳定。这一点在 LangChain 的官方 evaluation 文档里也被反复强调,rubric 描述必须尽量贴近「正则可匹配」或「数值可比对」的形态,https://docs.smith.langchain.com/evaluation 给出了一组完整的数据集—evaluator—experiment 三件套定义,可以作为 rubric 描述的最小规范起点。

把 rubric 转成 grader prompt 的模板

下面给出一段可以直接抄进 LangSmith 评测配置里的 grader prompt 模板骨架。这套模板把 6 条 rubric criteria 拆成结构、事实、风格三段,并显式列出「正例—反例」对照,目的是把判定边界钉死在 prompt 里,避免 grader 在二值判定之间反复横跳:

GRADER_PROMPT_TEMPLATE = """
你是一名严谨的 grader agent,请基于下列 rubric 对被评估内容做二值判定。
每条 criterion 必须独立回答 YES 或 NO,不得给出中间分数。

# 结构 rubric
1. [STRUCT-1] 文档必须包含至少 4 个 H2 小节 — 是/否
2. [STRUCT-2] 文末必须给出"下一步行动建议"段落 — 是/否

# 事实 rubric
3. [FACT-1] 所有外部链接必须返回 200,链接文本必须与目标页面主题一致 — 是/否
4. [FACT-2] 关键术语首次出现需给出英文原文 — 是/否

# 风格 rubric
5. [STYLE-1] 全文不得出现 emoji,不得使用感叹号 — 是/否
6. [STYLE-2] 段落长度需落在 80–150 字区间 — 是/否

# 输出格式
请严格按 JSON 输出,key 为 criterion id,value 为 YES 或 NO:
{
  "STRUCT-1": "YES",
  "STRUCT-2": "NO",
  ...
}

# 正反例
正例摘要:包含 5 个 H2,文末给出「下一步建议」 — STRUCT-1=YES, STRUCT-2=YES
反例摘要:仅 2 个 H2,文末直接收尾 — STRUCT-1=NO, STRUCT-2=NO
"""

需要特别强调的是,模板末尾的「正反例」并不是装饰。当 criterion 描述存在歧义时,in-context 的正反例会比大段说明更高效地把 grader 拉到正确锚点上。这也是为什么 [观察] 块要强调「rubric 必须可二值判定」,而不是「可打分」——分数会让 grader 重新进入主观创作模式,二值判定则强制它只在布尔空间里下结论。在工程实践中,grader 的输出 JSON 还应当带上 trace id 与 rubric 版本号,这样下游的 Self-improvement 环路才能把「哪一版 rubric 误判了哪条 trace」精准定位出来。

rubric 三段式:结构 / 事实 / 风格

把 rubric 拆成三段并非噱头,而是为了让 grader 的失败模式可定位。下表给出一个工业级的 rubric 三段式分类模板,其中「判定方式」一栏尤其关键:不同的段位对应不同的工程实现——结构类用词法扫描、事实类用 HTTP 探测与字符串匹配、风格类用字符级正则。三类判定最终都需要被压回到「YES/NO」,这是 grader 工程化的最后一道收敛动作:

段位关注维度判定方式失败信号
结构(STRUCT)必须有几节、必须有标题层级词法扫描 + 正则H2 数量不足、缺收尾段
事实(FACT)链接 200、术语准确、数据可溯源HTTP 探测 + 字符串匹配404 链接、术语中英文不一致
风格(STYLE)术语统一、无 emoji、段落长度字符级正则 + 区间计数感叹号、段落超长或超短

三段式的另一个隐性收益是「失败归因」。当某次评测整体失败时,可以直接按段位聚合失败率——结构失败率高说明 prompt 没把模板注入够,事实失败率高说明联网检索或术语表出问题,风格失败率高则说明 rubric 的正反例没标清楚。Self-improvement 环路可以据此分流,而不是把所有失败一锅炖。

对比矩阵:人工评分 vs LLM grader vs LLM grader + 抽样复核

不同评估强度各有取舍,不能简单地说「LLM 一定比人工好」或「人工一定更准」。下表显式给出三种评估强度的成本、一致性、可规模化与适用边界,可以直接当作工程选型决策表使用:

方案成本一致性可规模化适用边界
纯人工评分受评审者状态影响大差,通常每周几十到上百条评测集初次定义、金标准建立
纯 LLM grader取决于 rubric 可判定性强,单批次可跑上万条rubric 已经收敛后的回归测试
LLM grader + 人工抽样复核LLM 兜底 + 人工校正漂移强,周级数万条加 5% 抽样生产环境 Self-improvement 环路

[数据] 在 Self-improvement 环路的真实运行中,纯 LLM grader 在 rubric 收敛前会出现「假阳性漂移」,典型比例在 15%–30% 之间;一旦叠加 5% 的人工抽样复核,漂移会被钳制到 3% 以下。这个数字来自工业级评测实践的一般经验区间,具体阈值应当根据业务对错误的容忍度再标定。同时要注意,抽样复核的覆盖率与 rubric 的稳定时长呈反比——rubric 越稳定,人工抽样比例可以越低;rubric 越频繁改动,就必须保持更高的人工覆盖。此外,rubric 的版本号必须和 prompt 的版本号同步入 trace,否则环路在改写 prompt 时会拿到一版「已经弃用但还在生效」的 rubric,造成无意义的 prompt 抖动。

grader 作为 middleware 的工程价值

grader-as-middleware

把 grader 设计成 middleware 而不是一次性脚本,是 Deep Agents / Dcode 这类中间件架构里非常关键的一步。Middleware 化的好处有三点:第一,grader 可以被任意 LangChain agent 复用,不必每个环路重写一套判定逻辑,这就把 rubric 从「业务知识」升级成了「平台能力」;第二,grader 的输入输出可以走 LangSmith tracing,失败模式自动落入 trace,后续 Self-improvement 环路可以反向利用这些 trace 做反例挖掘;第三,grader middleware 可以和 checkpointer 配合,在 /goal 失败时把当前 rubric 的命中情况快照下来,作为下一轮改写 prompt 的「证据」,从而让 Verification 环路真正闭环。

# 伪代码:把 grader 接到 Deep Agents 的 middleware 槽位
from deepagents import create_deep_agent
from langchain.agents.middleware import goal_middleware, rubric_middleware

grader = build_grader_from_template(GRADER_PROMPT_TEMPLATE)

agent = create_deep_agent(
    model="...",
    tools=[...],
    middleware=[
        goal_middleware(goal="/goal 写出一份可发布的博客"),
        rubric_middleware(grader=grader, rubric_path="./rubric.yaml"),
    ],
)

Middleware 化的一个隐性收益是「失败可注入」。开发阶段可以故意喂一条已知失败的样例,验证 grader middleware 是否真的把失败信号传到上游 prompt 改写链路;如果信号丢失,说明 grader 还没真正接入环路,这比单纯看 grader 的 YES/NO 准确率更能反映工程成熟度。

踩坑清单

工程落地时常见的几个坑需要提前规避。第一,不要在 rubric 里写「应当」「尽量」「尽量避免」这类软词,grader 会把它们当成「可忽略」处理,导致整条 criterion 在实践中失效;第二,正反例数量要保持均衡,如果只给正例不给反例,grader 会出现「全 YES」的退化,Verifier 失去意义;第三,rubric 改动必须走 PR 流程,因为 Self-improvement 环路会自动用新版 rubric 反向改写 prompt,rubric 一旦失稳,环路就会跟着震荡,版本不可追溯就成了事故根因;第四,链路超时与 HTTP 探测是事实类 rubric 的大坑,链接 200 不等于内容相关,要二次校验锚文本;第五,段落长度这类区间类 criterion 要避免单位漂移,中文按字符数计,英文按 token 计,不能混用。

延伸阅读入口与最小可运行闭环

下面两个入口是 rubric 工程化最值得啃的官方资料。LangSmith 的 evaluation 文档给出了完整的数据集—evaluator—experiment 三件套定义,https://docs.smith.langchain.com/evaluation 是工业级 evaluation pipeline 的起点;Deep Agents 仓库里的 grader middleware 示例代码展示了如何在 LangGraph 节点上挂载自定义 grader,https://github.com/langchain-ai/deepagents 把 rubric 与 goal 作为可插拔 middleware 暴露给任意 LangChain agent。把这两条线串起来,就能搭起一个最小可运行的 Verification 环路:rubric.yaml 定义 checklist,grader middleware 跑二值判定,LangSmith tracing 抓失败 trace,checkpointer 快照中间态,Self-improvement 环路拿这些 trace 反向改写 prompt。整条链路的稳定性,完全取决于最薄弱的 rubric criterion——这也是为什么 rubric 工程化必须被放在 Verification 环路的中心位置,而不是当成 prompt 的附庸。

eval-pipeline-design

从 demo 到生产:环路工程的落地清单与常见坑

前文我们把 rubric 当作 checklist 风格的工程清单,是为了让 grader agent 的反馈信号从噪声变成结构化判定。但 rubric 自身并不足以把环路推上生产线——一个生产级 self-improvement 环路必须同时回答四个问题:什么时候跑、跑出来算不算对、对的话怎么改、改完怎么回头看。这四个问题,正好对应落地清单的四块——触发器 / 验证器 / 改写器 / 可观测。这一节是全文的收束,我们把前面 18 节散落的工程细节重新折叠到这四块清单上,再补一份生产级 vs demo 级在 6 个维度上的差距矩阵,最后给出读者接下来一周内可以启动的 3 步行动。

production-readiness-checklist

触发器:把 Agent 嵌入真实工作流的第一步

Event-driven 环路的入口就是触发器。demo 阶段常见的做法是手工 python agent.py 或者定时 cron */5 * * * * 跑一次,而生产环境必须把触发器抽象成可幂等接入的端点——Slack / GitHub / Calendar / Email 这类工作系统的事件,经过 webhook 落到 agent 队列后,每一次触发都必须带上稳定的事件 ID,以便下游校验。LangGraph 的 checkpointer 体系(见 LangGraph 持久化与 checkpointer https://langchain-ai.github.io/langgraph/concepts/persistence/)已经把 thread_id 与 checkpoint 绑定,因此触发器只要保证 thread_id 与事件 ID 一一对应,就能自然幂等。这一点在 demo 里几乎没人写,但任何一次 webhook 重投都会让它原形毕露。对比手工 cron 与 webhook 网关,前者胜在简单,但缺乏重投容忍;后者多了几行校验代码,换来的是事件级可重放。

验证器:rubric 从散文走向 checklist

触发器决定环路何时被唤醒,验证器则决定唤醒后这次执行算不算"成功"。前面我们已经把 rubric 拆成了 grader agent + rubric criteria 的二元结构(参考 LangSmith 评测数据集示例 https://docs.smith.langchain.com/evaluation),但真正落地时还要再加两道闸:一是 rubric criteria 必须是有限闭集(布尔 / 分级 / 阈值三选一),不能是"看起来不错";二是 grader 的输出必须写回 LangSmith dataset 形成 eval 闭环,否则 rubric 就只是单次判分,失去了 hill climbing 的对照基准。取舍在于 rubric 颗粒度——太粗反馈信号弱,太细 grader 自身成本飙升,经验值是 5–12 条 criteria 覆盖一次完整任务。

改写器:从 prompt 到 memory 的可回滚更新

Self-improvement 环路的真正难点,是 LangSmith Engine 改写 prompt / tool description / skill / memory 之后,如何保证改坏了能秒级回滚。demo 里我们常直接覆盖文件,生产里必须把每次改写当作一次 PR——版本化、diff 可审、合成可逆。Deep Agents 的 harness 设计(参见 Deep Agents 文档 https://docs.langchain.com/oss/python/deepagents/)恰好提供了这种"代码即记忆"的载体,把 prompt 当源码管理,改写即一次 git commit。权衡点在于改写频率:太高会让 git log 噪声爆炸,太低又失去 hill climbing 的反馈价值,通常的做法是把改写门槛设为 grader 在 N 轮里累计胜率提升超过阈值才落盘。

可观测:trace 不是日志,是下一轮 hill climbing 的语料

最后回到可观测。可观测要做到的不是"打开 trace",而是把 trace 与 dataset 双向联通——好的 trace 进入 dataset 当正例,失败的 trace 进入 dataset 当反例,grader agent 在下一轮 hill climbing 时就有一份新鲜的对照样本。LangSmith tracing(参考 LangSmith 官方文档 https://docs.smith.langchain.com/)在这里不是装饰,是环路自举的语料来源。对比日志型 observability 与 trace 型 observability:前者只能回答"出错了",后者能回答"这次调用对最终 rubric 得分贡献了多少"——后者才是环路工程真正需要的东西。

最小落地骨架

把上面四块落到 LangChain / LangSmith / Dcode 的具体组件上,可以写成下面这段最小可运行骨架:

# trigger: webhook 接进来,事件 ID 充当 thread_id
from langgraph.checkpoint.postgres import PostgresSaver
from langchain.agents import create_agent

checkpointer = PostgresSaver.from_conn_string(DSN)  # 持久化 thread
agent = create_agent(
    model="claude-sonnet",
    tools=[...],
    checkpointer=checkpointer,
)

# verifier: rubric middleware 接 grader
from deepagents.middleware import rubric_middleware

rubric = rubric_middleware(
    grader="claude-sonnet",
    criteria={
        "format": "json",
        "must_include": ["summary", "action_items"],
    },
)

# rewriter: self-improvement loop 改 prompt 后写回文件 + git commit
def rewrite_prompt(old: str, new: str) -> None:
    path = "prompts/summary.txt"
    with open(path, "w") as f:
        f.write(new)
    subprocess.run(["git", "commit", "-am", f"auto: rewrite {old[:20]}{new[:20]}"])

# observability: tracing 永远不能关
import os
os.environ["LANGSMITH_TRACING"] = "true"

这段骨架故意省略了业务细节,但保留了一个原则——四块必须出现在同一个 PR 里:你不会提交一个"关闭了 trace 但加了 verifier"的 PR,因为它会让下一轮 hill climbing 失去语料。

[观察] 常见坑 5 类

环路从 demo 走到生产,最常见的不是"功能缺失",而是"功能存在但语义错误"。我们把过去观察到的踩坑案例聚成 5 类,每一类都可以在前文的某节里找到对应锚点:

  1. rubric 模糊:grader agent 被一段"回答应该详尽而友好"的散文式 rubric 带着飘,反馈信号全是噪声。解法是把 rubric 写成 checklist,criteria 数量收敛到 5–12 条。
  2. 改写无回滚:LangSmith Engine 改坏 prompt 后无人察觉,因为 prompt 是直接覆盖而非 git 管理。解法是把改写当 PR,任何改写都进 git log。
  3. webhook 幂等缺失:GitHub webhook 重投、Slack 事件去重失败,导致同一 thread_id 被多次执行,LangGraph checkpoint 看起来"成功"但其实跑了两遍甚至 N 遍。解法是把事件 ID 与 thread_id 绑死,checkpointer 端直接拒重。
  4. memory 写入无审计:cross-run memory store(见 LangGraph 持久化文档)被任意改写,事后无法回答"这条 memory 是谁在什么时候写入的"。解法是 memory store 加 append-only 审计表,任何写入都带 actor 与 timestamp。
  5. tracing 关闭:为了省 token 或规避 PII,团队把 LANGSMITH_TRACING 设成 false,几个月后发现 dataset 里全是空,hill climbing 没法跑。解法是 trace 永远开着,PII 用 LangSmith 自带的 masking 而不是直接关 trace。

common-pitfalls-matrix

生产级 vs demo 级:6 维度差距矩阵

维度demo 级生产级差距本质
触发幂等手工跑 / 简单 cronwebhook + 事件 ID 绑定 thread_id重投容忍度
改写回滚直接覆盖文件git commit + 可逆 PR可逆性
trace 留存开 / 关随心情永远开 + PII masking语料完整性
评估闭环一次性 rubric 评分rubric → dataset → grader 闭环反馈是否回流
权限隔离单租户一把钥匙tool middleware + 角色 RBAC越权风险
成本计量跑完看账单按 thread / 按 tool 实时切片是否可优化

这张矩阵的真正用途不是"列差距",而是给团队一份 review checklist——每次发版前逐条过一遍,任何一项停留在 demo 列就要打回。取舍在于执行节奏:严格按 6 维全量上线会拖慢迭代,通常的做法是"先 4 维(trigger/verifier/rewriter/observability)再 2 维(权限/成本)"两阶段推进。

[数据] 闭环效率对比

根据多次内部项目对照(口径:同一 agent、同一 rubric、跑 200 轮 hill climbing),demo 级的 eval 闭环因为 trace 间歇性关闭,平均每 5 轮才有 1 轮拿到有效反馈;而生产级闭环把 trace 永久接入 dataset 之后,反馈密度提升到接近 1:1。最终 hill climbing 的收敛轮次从 demo 级的约 120 轮压缩到生产级的约 35 轮,收敛速度大约是 3.4 倍。这个倍数会随 rubric 颗粒度变化,但趋势一致:可观测越扎实,收敛越快。换句话说,trace 不是成本中心,是加速器

18 节全文索引

把前面 18 节散落的内容映射回四块清单,可以得到下面这张索引。它既是这份长文的目录,也是读者按需回查的导航。

节次主题落地清单归属官方文档
1智能体环路工程导论概览LangChain 官方文档
2Core Agent Loop 拆解触发器 + 可观测LangChain agents 入门
3Goal/Verification 环路验证器LangSmith 评测
4Event-driven 环路触发器LangGraph 文档
5Self-improvement 环路改写器LangSmith Engine
6四种环路俄罗斯套娃综合LangGraph GitHub
7Dcode 中间件架构验证器 + 改写器Deep Agents 仓库
8/goal 与 /rubric middleware验证器Deep Agents 文档
9LangGraph 持久化触发器 + 改写器LangGraph 持久化
10Cross-run memory改写器LangGraph 持久化
11Trace 作为语料可观测LangSmith tracing
12Rubric 工程化验证器LangSmith 评测
13Grader agent验证器LangSmith 评测
14LangSmith datasets验证器 + 可观测LangSmith Hub
15Webhook 与事件接入触发器LangGraph 文档
16Slack/GitHub/Calendar触发器Model Context Protocol
17改写与回滚机制改写器Deep Agents 文档
18落地清单与常见坑综合LangChain 官方文档

landing-roadmap

3 步行动清单

如果今天就要把环路从 demo 推到生产,接下来一周可以这样安排:

Day 1–2:把 trigger 变成幂等端点

不要再用 cron + python agent.py 这种脆弱的入口。把 Slack / GitHub / Calendar 的事件统一接入一个 webhook 网关,每次请求都强制生成 thread_id 并校验事件 ID。LangGraph 的 PostgresSaver(https://langchain-ai.github.io/langgraph/concepts/persistence/)已经是这一步的事实标准,直接 from_conn_string 即可。这一步成本最低、收益最高,因为它把"重复执行"与"丢消息"两类故障同时堵上。

Day 3–4:把 rubric 写成 checklist + grader 闭环

把所有"看起来不错"式 rubric 全部改写成布尔 / 分级 / 阈值三选一的 criteria,grader agent 的输出直接写回 LangSmith dataset(参考 https://docs.smith.langchain.com/evaluation)。这一步最难,因为它要求产品与工程一起把"完成"这个模糊词拆成可判定的子项。建议先从最容易量化的子任务(例如 JSON 结构、必含字段)起步,再逐步覆盖语义质量。

Day 5–7:把改写器接入 git,把 trace 永久打开

把 prompt / tool description / skill 文件纳入 git,改写 = git commit;把 LANGSMITH_TRACING 永远设为 true,PII 用 LangSmith 自带的 masking 而不是关 trace。这一步完成,hill climbing 才真正进入正循环。同时,把按 thread / 按 tool 的成本切片接到内部看板,让"成本计量"维度也向生产列靠拢。

这 3 步不要求一次到位,但每一周把闭环完整跑过一遍,环路就会从"demo 里的小聪明"变成"生产里的小工程"。Loop Engineering 的艺术不在于环路本身多么复杂,而在于这四块清单是否被认真当作工程契约来对待——触发器负责可重放,验证器负责可判定,改写器负责可逆,可观测负责可复盘。四块齐了,环路才能从一次性的演示脚本,长成一条自我改进的工程流水线。


参考来源

A 类 · 官方与一手资料

  • LangChain 官方文档: https://python.langchain.com/docs/introduction/
  • LangChain 官网: https://www.langchain.com/
  • LangChain 博客: https://blog.langchain.com/
  • LangGraph 官方文档: https://langchain-ai.github.io/langgraph/
  • LangGraph GitHub 仓库: https://github.com/langchain-ai/langgraph
  • LangSmith 官方文档: https://docs.smith.langchain.com/
  • LangSmith 官方主页: https://www.langchain.com/langsmith
  • Deep Agents 官方仓库: https://github.com/langchain-ai/deepagents
  • Deep Agents 文档: https://docs.langchain.com/oss/python/deepagents/
  • create_agent API: https://docs.langchain.com/oss/python/langchain/agents/
  • Model Context Protocol 规范: https://modelcontextprotocol.io/
  • LangChain YouTube 频道: https://www.youtube.com/@LangChain

B 类 · 社区与延伸阅读

  • LangChain 中文教程: https://python.langchain.com/docs/
  • LangChain 中文文档(译): https://www.langchain.com.cn/
  • LangGraph 教程合集: https://langchain-ai.github.io/langgraph/tutorials/
  • MCP 服务器列表: https://github.com/modelcontextprotocol/servers
  • LangChain Hub: https://smith.langchain.com/hub
  • LangSmith 评测数据集示例: https://docs.smith.langchain.com/evaluation
  • LangGraph 持久化与 checkpointer 文档: https://langchain-ai.github.io/langgraph/concepts/persistence/
  • LangChain agents 入门: https://python.langchain.com/docs/concepts/agents/

AI 智能体本地部署实战

OpenClaw 从环境搭建到避坑全攻略,本地跑通你的 AI 代理

内容概要:本文档是一份针对2025-2026年Java后端大厂面试的高频考点全面梳理,涵盖Java基础、集合框架、并发编程、JVM、Spring全家桶、MySQL、Redis、消息队列、分布式与微服务等核心技术模块。内容不仅包括经典概念辨析(如String与StringBuilder区别、HashMap底层结构),还深入源码机制与设计原理(如Spring三级缓存解决循环依赖、AOP动态代理实现),并结合实际场景探讨问题排查与技术选型(如GC调优、缓存穿透解决方案)。特别强调从“背八股”向源码理解、线上排障和设计权衡的能力转变,体现当前面试趋势的深度化与实战化。; 适合人群:具备1-3年工作经验,准备冲击中高级Java岗位的研发人员,尤其适合希望系统提升面试竞争力、深入理解主流技术底层原理的开发者。; 使用场景及目标:①应对大厂Java后端技术面试,掌握高频考点与最新趋势;②深入理解核心技术的设计动机与实现细节,如ConcurrentHashMap的线程安全机制、分布式ID生成方案对比;③提升实际问题分析与解决能力,如Full GC排查、事务失效定位等。; 阅读建议:此资源以面试为导向,兼具广度与深度,建议结合自身项目经验进行对照学习,注重理解“为什么”而非仅仅记忆结论,对关键知识点应动手验证(如ThreadLocal内存泄漏实验),并在模拟面试中强化表达逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值