LangSmith Engine 如何构建用于改进智能体的智能体
LangSmith Engine: How We Built an Agent for Improving Agents
LangChain 发文详解上周发布的 LangSmith Engine 的技术实现,这是一个运行在智能体 trace 之上的智能体,负责发现反复出现的失败、生成 issue,并给出评估器、数据集样本和修复建议。
LangChain 公开了 LangSmith Engine 的完整架构取舍,包括轨迹压缩、筛选与调查分离等可迁移的工程方法。
上周我们推出了 LangSmith Engine。Engine 是一个智能体,它基于你的智能体追踪数据运行,发现反复出现的问题,并建议下一步该怎么做。
本文将深入介绍我们构建它的技术细节:我们为什么构建 Engine、它使用哪些输入和输出,以及让它能够分析大量追踪数据的架构决策。
我们为什么构建 Engine
LangSmith 是智能体改进循环的大本营。构建、测试、部署和监控是这个循环的四大支柱,支撑着智能体的开发。
随着你部署的智能体数量增长,它们产生的追踪数据量也随之增长。结果就是,你花越来越多的时间去梳理追踪数据,弄清楚你的智能体在哪里出了问题。
基本的工具错误相对容易发现。整体轨迹也能从追踪视图中看到。但许多智能体问题要难发现得多,除非你逐条细粒度地检查追踪数据:
- 智能体反复循环调用相同的工具
- 它使用了错误的工具参数
- 它执行效率低下
- 它漏掉了本该使用的工具
- 它在不同运行中反复失败于同一类请求
在 LangChain 内部遇到这个问题后,我们着手构建了 LangSmith Engine。
Engine 有三项职责:
- 在追踪数据中发现反复出现的失败。
- 将这些失败转化为可操作的问题。
- 将这些转化为持久的改进:评估器、数据集示例和修复方案。
Engine 本身就是一个智能体:一个编排器,使用专门的组件端到端地运行改进循环。它拉取追踪数据,在连接了代码仓库时读取代码,将失败归类为问题,提出评估器和数据集示例,并随时间推移不断更新对你智能体的理解。

Engine 产出什么:问题
Engine 的核心是识别问题。
一个问题是一种反复出现的失败模式,有证据追踪数据作为支撑,并附带建议的后续行动。问题会展示在 Issue Board 中呈现给用户:这是 Engine 在追踪项目中找到的问题列表。
一个问题包含:
- 名称:问题的标题
- 描述:对问题的段落式描述
- 类别:预定义的一组智能体失败类别之一
- 严重程度:低、中或高
- 追踪数据:相关的追踪数据,为问题发生的位置提供证据
- 建议行动:防止问题再次出现的建议后续步骤
- 标签:用于驱动后续工作流的元数据,例如
needs_fix
建议行动可以包括:
- 建议的在线评估器:一个能在问题再次发生时将其标记出来的评估器
- 建议的数据集示例:添加到离线数据集中的、能代表该问题的示例
- 建议的修复方案:修复底层问题的代码或提示词更改。
关键点在于,Engine 不只是指向一条糟糕的追踪数据。它试图将一次生产失败转化为你的团队可以采取行动、并在未来进行测试的东西。
Engine 消费什么
Engine 接收或能够获取四类主要输入。
指令
Engine 由 Agent Overview 引导。它类似于 AGENTS.md 文件:一份动态描述,说明你的智能体做什么、预期会出现哪些追踪结构、需要留意哪些失败模式,以及你的团队表达了哪些偏好。
首次运行通过引导问卷的回答和项目上下文进行引导。在那次初始运行中,Engine 会分析 trace,并利用它学到的内容创建第一版 Agent Overview。在后续运行中,Agent Overview 会成为 Engine 读取并更新的持久化输入。
你也可以随时手动编辑 Agent Overview。
Traces
Engine 通过 LangSmith CLI 从相关的 LangSmith 追踪项目中拉取 trace。
一条完整的 trace 包含一次 agent 运行的消息和轨迹。出于规模考虑,Engine 并不总是从加载每条 trace 的完整内容开始。它通常从紧凑的轨迹摘要开始,然后在某条 trace 需要深入调查时,有选择地加载完整的 trace 内容。
现有 issue
Engine 会获取当前的 Issue Board,包括未关闭的 issue 和之前已关闭的 issue。
这让 Engine 掌握项目的当前状态。它可以避免重复创建已知 issue,为现有 issue 补充证据,并了解哪些问题已经解决或关闭。
代码库(可选)
你可以选择将 Engine 连接到你的代码库。这让 Engine 能更精确地诊断问题,并让一个独立的修复 agent 提出更改建议。
如果连接了仓库,该仓库会被安装到沙箱中。在设置过程中,你可以指定 Engine 应使用哪个分支或子目录。
Engine 会更新什么
Engine 在运行过程中可以更新多项输出。
Issue Board
Engine 的主要职责是更新 Issue Board。它可以创建新 issue、更新现有 issue、附加证据 trace、更改 issue 元数据。
对于每个 issue,Engine 可以提出一个评估器,用于在未来的 trace 中捕获相同的模式。它还可以从证据 trace 中提出回归示例,让生产环境中观察到的失败转化为离线测试覆盖。它还可以建议修改 prompt 或代码来修复底层问题。
Agent Overview
Engine 可以记录它发现的内容,并更新 Agent Overview 以供未来运行使用。
这就是 Engine 随时间记住项目特定信息的方式:常见失败模式、trace 模式、工具行为和用户偏好。
高层架构
Engine 构建在 Deep Agents 之上,并连接到一个沙箱,在其中可以写文件、检查 trace、执行代码,并使用已检出的仓库。

从高层来看,Engine 由以下部分组成:
- 系统 prompt 和指令:包括 Agent Overview
- 沙箱:Engine 工作的环境
- LangSmith CLI:Engine 用来获取数据并将更新推送回 LangSmith 的主要接口
- 自定义工具:尤其是用于测试评估器和提出回归示例的工具
- 子 agent:用于筛选 trace 并调查可能的问题,同时不会使主 agent 的上下文溢出
- 记忆:通过 Agent Overview 维护,并根据用户操作进行更新
本文的其余部分将逐步介绍核心循环:
- 准备 agent 的上下文。
- 大规模筛选 trace。
- 调查可能的问题。
- 创建 issue、评估器和数据集示例。
- 在需要时将修复工作交给一个独立的 agent。
- 为下一次运行更新记忆。
1. 准备 agent 的上下文
在 Engine 能够分析 trace 之前,它需要一个可工作的环境,以及足够的上下文来理解它正在检查的 agent。
沙箱设置
Engine 在连接到一个沙箱的情况下运行。我们为此使用 LangSmith Sandboxes。
在运行 Engine 之前,我们先设置智能体的环境。首先,我们拉取基础 Engine Docker 镜像。该镜像包含所需的库以及 LangSmith CLI,Engine 使用它来与 LangSmith 数据交互。
如果 Engine 连接到了 GitHub 仓库,我们还会拉取相关的代码产物。用户可以在设置期间指定使用哪个分支或子目录。
沙箱环境很重要,因为 Engine 经常需要检查 trace 数据、写入中间文件、测试评估器代码,并对提出的输出进行迭代。为智能体提供一个受控的工作环境,能让这一工作流程可靠得多。
智能体概览
智能体概览既是一个指令文件,也是一个记忆层。
当你设置 Engine 时,你会回答一组基础的入门问题。Engine 会利用这些答案,以及它在首次运行中发现的内容,来创建初始的智能体概览。
该概览帮助 Engine 持续记录以下内容:
- 你的智能体做什么
- 预期会出现哪些 trace 结构
- 需要注意的常见陷阱
- 项目特定的上下文
- 用户偏好
Engine 会在后续运行中读取并更新此文件。
LangSmith CLI
Engine 与 LangSmith 交互的主要方式是通过 LangSmith CLI。
在大多数情况下,我们更倾向于这种方式,而不是为每个 LangSmith 操作创建自定义工具。CLI 为 Engine 提供了一个通用接口,用于拉取 trace、查询 issue、创建 issue、附加 trace、更新 issue 元数据以及提出产物。
它还使 Engine 更易于调试和复现。CLI 与可供下载的接口相同,也可以在本地提供给编码智能体使用。如果 Engine 通过 CLI 执行了某项操作,通常也可以在 Engine 之外理解和复现该操作。
2. 大规模筛查 trace
构建 Engine 时最大的架构挑战是 trace 的数量。
让智能体一次调查和梳理 50 条 trace 相对容易。但一旦我们将系统连接到生产环境的智能体,在这个数量级上有效的技术就开始失效了。生产项目在回溯窗口内可能有数千甚至数万条 trace。
将所有完整 trace 内容加载到主智能体的上下文中是不可行的。即使是来自长时间运行智能体的 10 条 trace,也可能包含数百次工具调用和消息。
因此我们将问题拆分为两个阶段:
- 一个广泛的筛查阶段,快速识别可疑的 trace。
- 一个深入调查阶段,仅对可能重要的 trace 加载完整上下文。

轨迹格式
为了实现筛查,我们需要对每条 trace 进行压缩表示。
问题是:
如何在压缩 trace 中信息的同时,保留导航回原始 trace 所需的信息?
答案是智能体轨迹:trace 的紧凑骨架。

轨迹每一轮对应一个条目,包含角色、可选的工具名称、延迟和内容大小。它不包含完整内容。
{ role: "human", chars: 142 }
{ role: "ai", latency_ms: 1820, chars: 89 }
{ role: "tool", tool_name: "search_db", latency_ms: 340, chars: 2100 }
{ role: "tool", tool_name: "search_db", latency_ms: 312, chars: 1980 }
{ role: "tool", tool_name: "search_db", latency_ms: 298, chars: 2040 }
{ role: "ai", latency_ms: 2100, chars: 210 }
轨迹充当导航工具。它让筛查器快速发现可疑形态,然后在完整 trace 中搜索,并仅将所需信息加载到上下文中。
优先处理带有反馈的 trace
Trace 可能已经关联了反馈。这可能来自人工标注、LLM-as-a-judge 评分或最终用户反馈(点赞/点踩)。
Engine 将 trace 上的反馈视为可能存在 issue 的高优先级信号。
Engine 拉取的初始 trace 集合包含反馈统计信息。Engine 被指示查找带有反馈的 trace,并优先对它们进行分诊。
反馈不会自动成为一个 issue。它只是提高了筛查和调查的优先级。agent 仍然需要判断该 trace 是否属于一个真实 issue。
筛查器子 agent
核心筛查问题是:
给定这条 trace,其中是否存在一个值得进一步调查的 issue?
Engine 为此使用一个专用的筛查器子 agent。筛查器是一个基于 Haiku 的子 agent,由主 agent 分派处理每组约 20 条 trace。
筛查器的职责被有意限定得很窄。它不创建 issue。它不诊断根因。它只在表层判断一条 trace 是干净的还是可能包含 issue。
筛查器向主 agent 返回结构化响应。响应中每条被标记的 trace 占一行,包含 trace ID、类别和简短原因,最后是被判定为干净 trace 的数量。
<trace_id> | <category> | <one-line reason>
CLEAN: 47
这一步缩小了搜索空间。我们不再要求主 agent 对每条 trace 进行完整推理,而是使用并行筛查器来识别值得深入关注的 trace。
3. 调查可能的 issue
筛查之后,主 agent 读取筛查器的输出并分派更深入的调查。
调查器子 agent
调查器接收被标记的 trace,拉取完整的 trace 内容,在可用时阅读代码库,并对潜在 issue 进行更深入的分析。
我们鼓励主 agent 为此使用子 agent,因为完整的 trace 内容可能很大。将多条完整 trace 和相关代码加载到主 agent 的上下文窗口中会使其迅速溢出。
与筛查器不同,调查器不是一个具有固定系统提示的专用子 agent。它是一个通用子 agent,由主 agent 针对具体调查进行提示。这赋予了主 agent 灵活性:不同的 issue 类型可能需要不同的调查策略。
调查器的职责是判断被标记的 trace 是否代表一个真实 issue、这些 trace 是否应被归为一组,以及应在该 issue 上记录什么内容。
Issue 类别
Engine 为每个 issue 引入了类别的概念。
我们确定了一组预定义的常见 agent 故障模式,并提示 Engine 主要查找这些类型的 issue。该列表包括:
pii_leakagent_loopingincorrect_tool_argsmissing_tool
将 Engine 限制在已知类别中有助于我们控制它发现的 issue,并在将这些 issue 类型引入给客户之前对其进行评估。这也使用户更容易理解输出。
用户仍然可以通过 Agent Overview 自定义 Engine 应关注的内容。如果某个团队关心特定的 issue 类型,他们可以在那里描述这些优先级。
随着我们识别并验证新的、反复出现的 agent 故障模式,我们正在积极扩展这个类别列表。
4. 创建 issue、评估器和数据集示例
一旦 Engine 识别出一个真实 issue,主 agent 就会创建或更新该 issue,并附上证据 trace。
主 agent 负责 issue 的创建以及围绕该 issue 的评估产物。它不负责直接修复底层代码或提示。
对于每个 issue,Engine 可以生成:
- issue 本身,附带证据 trace。
- 一个建议的评估器。
- 建议的回归示例。
- 一个
needs_fix标签,如果应启动单独的修复 agent。
评估器
Engine 被提示为每个 issue 提出一个评估器。
思路很简单:一旦发现某种失败模式,你就想要一个能在未来轨迹中捕获相同模式的检查。
Engine 支持两种评估器类型。
代码评估器是 JavaScript 函数,用于检查轨迹的结构——字段值、工具输出、步骤数、错误模式。当失败无需阅读内容即可检测时,它们是合适的选择。
LLM 作为评判者的评估器处理需要理解的情况:幻觉、接地失败、无益的拒绝、错误的建议。
智能体根据问题选择评估器类型。结构性失败使用代码评估器。语义性失败使用评判者评估器。
在 Engine 提出某个评估器之前,它会调用 test_evaluator 工具。
test_evaluator 工具
test_evaluator 工具让 Engine 在向用户建议之前,先在证据轨迹上测试所提出的评估器。
这一点很重要,因为评估器可能看起来合理,却无法捕获实际问题。Engine 调用该工具时会传入评估器定义以及它想要运行评估器的轨迹。该工具执行评估器并返回从轨迹 ID 到结果的映射。
def test_evaluator(evaluator, traces) -> {run_id: PASS | FAIL | SKIPPED}
其中:
PASS caught issue
FAIL missed issue, or evaluator errored
SKIPPED evaluator did not apply to this trace
如果评估器未能捕获正确的轨迹,Engine 可以迭代代码或提示词。目标是发布最能捕获证据轨迹所代表的失败模式的版本。
回归示例与断言
每当创建问题或向问题添加新轨迹时,Engine 会被指示对每条证据轨迹调用一次 propose_regression_example。
这会为该轨迹创建一个提议的回归示例。该示例由智能体的原始输入以及对预期输出的断言组成。
我们提出断言而非完整的真实输出,因为断言更简单也更灵活。正确的响应可能有多种不同的表述方式。重要的是它是否满足轨迹所隐含的关键主张。
每个断言都有:
- key:反馈标识符,写为简短的 slug
- comment:一句人类可读的声明,说明正确响应应满足什么
{
"key": "must_cite_max_connections_4096",
"comment": "Response cites the max_connections value of 4096 returned by the get_config tool call."
}
{
"key": "must_not_reference_strict_mode_flag",
"comment": "Response must not suggest enabling strict_mode, which was deprecated in this version."
}
提议的示例会显示在前端的问题上。审查者随后可以将它们提升为数据集。
这闭合了从生产失败到离线测试覆盖的循环。
5. 将修复交给单独的智能体
一个关键的设计决策是将问题创建与修复生成分离。
在早期版本中,我们试图让主智能体既识别问题又提出提示词或代码修复。这让智能体的工作过于宽泛。它必须扫描轨迹、判断什么重要、对失败进行分组、创建问题、生成评估器、提出数据集示例,还要推理出正确的修复方案。
我们发现,当所有这一切都在一次执行中完成时,主智能体很难可靠地做出修复。
因此我们拆分了工作流:
- 主 Engine 智能体识别并记录问题。
- 它创建数据集和评估器产物。
- 如果该问题需要修复,它会留下一个
needs_fix标签。 - 一个单独的修复智能体被启动,以提出实际的代码或提示词更改。
这让主智能体更简单,并给修复智能体一个更聚焦的任务。修复智能体可以从问题、证据轨迹以及关联的仓库上下文出发,而无需同时执行完整的轨迹筛查工作流。
6. 为下一次运行更新记忆
Engine 并非每次都从零开始。
Agent Overview 不仅会由 Engine 的调查更新,也会由用户操作更新。当你解决一个问题、关闭一个问题或创建一个评估器时,该操作就会成为信号。
Engine 可以通过 LangSmith CLI 拉取近期事件,从这些事件中归纳观察结果,并更新 Agent Overview。
它被专门提示维护一个 User Preferences 部分。该部分记录 Engine 通过观察用户如何与问题交互而学到的内容。
每个团队关注的问题集合都不同。Agent Overview 正是 Engine 随时间将其分析适配到这些偏好的方式。
架构决策与经验教训
有几个架构决策最终变得尤为重要。
将 CLI 作为主要的 LangSmith 接口
Engine 所做的大部分事情,都是通过 LangSmith CLI 完成的。这使得系统比针对每个操作创建狭窄的自定义工具更加灵活。它也让 Engine 的行为更容易复现和调试。
在读取轨迹前先压缩它们
完整轨迹太大,无法在生产规模下进行筛查。轨迹让 Engine 能够对许多轨迹的形态进行推理,然后有选择地加载真正重要的细节。
将筛查与调查分离
筛查器针对规模进行优化。调查器针对更深入的分析进行优化。
这种分离让 Engine 能够处理大量轨迹,而不必让每条轨迹都经过昂贵的完整调查。
使用专用筛查器,但保持调查器灵活
筛查器有狭窄且可重复的工作,因此受益于专用的提示和结构。
调查则更加多样。有些需要阅读代码,有些需要理解评估器失败,还有些需要比较轨迹。因此,我们使用通用目的的调查子代理,由主代理动态提示它们。
约束问题类别
让代理随意发明问题类别会使输出更难评估、更难信任。预定义的分类法让我们能够控制质量、衡量性能,并有意识地扩展覆盖范围。
优先使用断言而非完整预期输出
对于回归示例,断言通常比完整参考答案更合适。它们能捕捉必须为真的内容,而不会过度约束正确响应的确切措辞。
让主代理专注于问题
主代理只创建问题以及相关的数据集/评估器产物。它不会直接尝试修复提示或代码。
当某个问题需要修复时,主代理会用 needs_fix 标记它。然后由单独的修复代理处理修复提案。
这种分离源于观察到:当一个代理必须在同一轮中既识别问题又提出修复时,它会很吃力。
结论
Engine 是我们尝试将更多代理改进循环自动化的成果。
代理可观测性的难点不仅在于看到单条轨迹中发生了什么。它在于跨许多轨迹发现反复出现的模式,判断哪些模式重要,并将这些模式转化为问题、评估器、数据集示例和修复。
该架构反映了这一循环。Engine 准备上下文,大规模筛查轨迹,调查可能的问题,创建问题产物,在需要时将修复交给单独的代理,并为下一次运行更新其记忆。
它已经改变了我们在内部改进自己代理的方式。我们不再需要手动翻查轨迹并单独编写评估,而是可以将生产行为直接转化为问题、修复和测试。
你可以在 LangSmith 追踪项目的 Issues 标签页中找到 Engine。如果你想获得代码感知的修复建议,可以连接一个代码仓库;或者仅从追踪数据开始,看看 Engine 能发现哪些反复出现的模式。
来源:LangChain Blog · langchain.com