@LangChain: TL;DR 我们正在试验解释器技能:一种代理技能的扩展,允许你在技能中包含一个TypeScript模块……
摘要
LangChain 正在试验“解释器技能”:这是一种代理技能的扩展,允许包含一个TypeScript模块,使代理能够直接在解释器中执行代码,从而更高效、准确且可预测地执行复杂任务。
查看缓存全文
缓存时间: 2026/06/10 09:47
TL;DR 我们正在实验一项名为“解释器技能”的新功能:这是代理技能的一个扩展,允许你在技能中包含一个 TypeScript 模块。快速解析来自 @huntlovell https://t.co/NhZtsISytf https://t.co/kMD9fSdK6g
解释器技能:为代理构建工作流
来源:https://www.langchain.com/blog/interpreter-skills
TL;DR
我们正在实验 解释器技能:这是代理技能的一个扩展,允许你在技能中包含一个 TypeScript 模块。当行为适用时,代理可以在解释器中导入并运行该技能代码。技能代码还能执行生成子代理或调用工具等操作,使代理能够承担更复杂的任务,并且代码可以独立测试和复用。
我们最近为 Deep Agents 引入了解释器:一个嵌入式的 TypeScript 运行时,代理可以在其中编写并执行代码,作为整体框架的一部分。代理已经非常擅长编写代码,而解释器则赋予它们更直接的方式来表达意图。对于许多代理任务,这能带来更高效、更准确且更可预测的输出。
我们很早就注意到,当拥有解释器的代理被重复给予相同的任务时,它往往能想出多种有效的代码实现方式。这未必是坏事——有时候代理的灵活应变正是其意义所在。但对于许多任务来说,期望的行为不是“想出一个好方法”,而是“使用我们已经确认有效的方法”。我们一直在实验应对这一问题的解决方案,称为“解释器技能”:这是技能的一个扩展,附带一个模块,代理可以在其指令旁使用该模块。技能告知代理何时行为相关,然后解释器可以导入技能附带的模块并直接执行。
---
name: github-triage
description: 使用此技能对 GitHub 问题、拉取请求和讨论进行分类。
metadata:
module: ./index.ts
---
当用户请求对仓库进行分类时使用此技能。使用解释器导入模块并调用 `triage(repo, options)`。
使用方法:
```ts
const { triage } = await import("@/skills/github-triage");
const result = await triage("langchain-ai/deepagents", {
issues: true,
prs: true,
});
result.toMarkdown();
`SKILL.md` 是代理发现行为的方式。`index.ts` 是解释器可以执行的内容。代理决定何时使用该行为、传递什么输入,以及如何处理结果。解释器负责代码的实际执行。
## 再提醒一下,技能是什么?
技能是一种给代理提供可复用行为的方式,而无需在系统提示中详细说明所有细节。技能通常是一个包含 `SKILL.md` 文件的目录。文件头为代理提供该技能用途的简洁描述。正文则向代理提供当技能适用时应遵循的指令、上下文、示例、约束和支持文件。
技能之所以有效,依靠的是一种称为“渐进式披露”的机制。代理无需时刻将所有技能加载到上下文中。它可以先看到可用技能的简短列表,决定哪些与任务匹配,然后在需要时才读取完整的 `SKILL.md`。这使得技能成为代理行为的一个优秀分发单元。人们可以对其进行版本管理、共享和评估,而无需将全局提示变成一个包罗万象的手册。
但普通技能仍然主要通过指令来工作。它们可以告诉代理需要遵循什么流程,也可以包含参考文件或脚本,但核心行为仍然依赖于代理阅读这些指令并正确执行。
## 什么是解释器?
就本文而言,需要理解的重点是:解释器是一个 TypeScript 运行时,与框架协同运行。它为代理提供了一个用代码表达多步工作的场所,同时框架仍控制着这些代码可以接触的内容。(如果你好奇想了解更多,可以阅读关于 [解释器](https://www.langchain.com/blog/give-your-agents-an-interpreter) 的文章)。
该运行时以 TypeScript 值的形式为代理提供工作状态。值可以在多轮对话中持久存在,因此数组保持为数组,对象保持为对象,辅助函数也可以保持在定义状态。代理无需将每个中间值转换为标准输出、文件或返回模型的消息。这使得代理能够转换数据、组合工具输出、调用选定的工具或子代理,并决定哪些内容应返回给模型。
与沙箱不同,解释器代码默认不会无限制地访问宿主环境。文件系统访问、网络访问、工具和子代理都需要被刻意暴露给解释器。这给了框架一个地方来白名单、计量和检查代码能接触的内容。
## 什么是解释器技能?
解释器技能是技能的一种扩展,将两者结合在一起:它包含与技能相同的一组指令,以及代理可以导入解释器的模块。`SKILL.md` 仍然告知代理技能何时相关,并以相同方式向代理披露;而模块则提供了在该行为适用时解释器要运行的代码。技能既成为模型的指令表面,也成为运行时的 API 表面。
基本形态与开头的示例相同。`SKILL.md` 提供名称、描述、使用说明、导入路径和约束。`index.ts` 导出定义行为代码的辅助函数或工作流:
```ts
// skills/table-cleanup/index.ts
export function validateRows(rows: Record[], schema: RowSchema) {
// 标准化字段,检查必需值,并返回结构化错误。
// (这是你作为技能编写的一部分代码)
}
当技能适用时,代理可以导入模块并调用它:
const { validateRows } = await import("@/skills/table-cleanup");
const errors = validateRows(rows, invoiceSchema);
这改变了技能所能保证的内容:
- 普通技能说:以下是完成此任务的指令。代理仍然需要阅读这些指令并正确执行流程。
- 解释器技能说:以下是何时使用此行为的指令,以及当它适用时要运行的代码路径。确定性部分可以驻留在代码中,而不是作为上下文中的松散指令。
模型决定技能是否适用、传递哪些输入、如何使用输出以及下一步要做什么。模块定义了流程实际应如何运行。
由于解释器代码可以与框架交互,这意味着你可以在技能代码中以编程方式生成子代理。
示例:仓库分类
我们正在使用的一个场景是:GitHub 仓库分类。用户要求代理对仓库进行分类。代理无需从提示指令中重构分类流程,而是导入技能模块并调用函数:
const { triage } = await import("@/skills/github-triage");
const result = await triage("langchain-ai/deepagents", {
issues: true,
prs: true,
discussions: true,
});
当此函数被调用时,工作流:
- 从 GitHub 获取所有打开的条目(根据代理在选项中的指定)
- 为每个条目生成一个子代理来创建更简洁的描述
- 将子代理的响应放入队列
- 逐个消费队列,由子代理决定该条目是否应放入现有集群,或创建一个新集群
这正是那种你希望 流程 固定,即使 输入 是动态选择的例行任务。
result 值也是 API 的一部分。它可以向代理提供关于运行的结构化数据,并且可以暴露一个用于呈现的辅助方法:
result.clusters;
result.unassigned;
result.toMarkdown();
代理可以继续处理结构化结果,更深入地检查一个集群,生成后续子代理,或者在需要紧凑的、对模型友好的报告时调用 result.toMarkdown()。
这是那种模型会随着时间推移失去连贯性的任务。仓库分类不是一个决定,而是许多小决定链接在一起。如果我们完全依赖判断力和上下文窗口来编排,模型可能会开始走捷径,或在长时间跨度内过于激进地压缩流程,特别是当它感觉接近工作上下文的边缘时(这种现象被称为 上下文焦虑)。
举例说明这在不同代理中可能如何工作:
- 在典型的框架中,模型必须跟踪每个部分步骤。如果有 300 个仓库条目,那就变成模型需要推理的 300 个小状态片段,同时还要协调过去的决定并继续选择下一个动作。
- 有了解释器技能,模型可以一次性调用该流程,让代码来编排工作流。模块可以创建 300 个独立的子代理任务,收集结果,对它们进行分类、聚类,然后向代理返回一个紧凑的对象。模型不再需要在自己的工作上下文中承载每个部分步骤。
将技能用作工作流
当编码代理开始流行时,代理的默认心智模型也发生了变化。上一代代理更像是工作流风格——开发者预先明确定义了代理应遵循的步骤顺序。可靠性来自于预定义执行路径。现代的代理框架改变了这一点,将上下文和模型判断作为主要焦点。模型根据当前上下文决定下一步做什么。行为通过一系列不同的上下文表面来塑造,但代理仍有空间选择下一步。
这个接口对许多人来说更容易理解。不仅对构建代理的人,还包括操作人员、产品经理、领域专家以及那些用指令、文件、清单和示例(而非代码级工作流)思考的团队。
但是,对确定性代理流程的需求从未消失。我们仍然经常被问到类似的问题:
我如何确保代理可靠地执行我告诉它的任务?
在这些情况下,团队需要知道的不仅仅是最终答案看起来合理:他们需要知道所需的流程是否运行,是否以正确的输入运行,以及在代理继续之前是否完整完成。仅依赖提示的流程遵循在这方面很脆弱。代理可能跳过步骤、重新排序步骤、满足错误的指令、将不相关的请求混入流程,或者在没有遵循流程的情况下产生“足够好”的输出。
例如,考虑问题“提交一张发票,但在中途停下来生成一个跳舞猫的 GIF”。如果发票提交仅通过提示指令描述,代理可能会将绕路视为同一任务的一部分,留下半完成的发票。1
那么,我们如何在不过度重构代理的情况下,两全其美,既保持工作流的确定性又发挥代理的灵活性?解释器技能是一个答案:
- 将确定性部分表达为代码,在解释器中暴露为一个模块,让代理决定何时调用它。
- 让函数基于当前解释器状态工作,并将结果链接到下一个工具调用、子代理调用、解释器调用或最终响应中。
这并不能消除代理可能产生创造性解决方案的问题,但它提供了一个更清晰的评估信号。
- 对于仅依赖提示的流程遵循,问题通常是模糊的:代理是否大致遵循了指令?它看起来是否保持在正轨上?最终答案看起来合理吗?
- 有了解释器技能,部分问题变得具体:代理是否调用了预期的函数?它是否传递了预期的输入?函数是否返回了预期的输出形状?
使用技能处理代理状态
文件系统代理清楚地表明,当代理有地方存放中间状态(一种记忆形式)时,它们工作得更好。文件很有用,因为它给代理提供了一个可以返回的命名对象。代理可以检查它、修改它、将其传递给另一个步骤,或将其用作工具输入。解释器允许代理以更灵活的形式表示这种状态,而技能可以教导代理如何与之交互:
- 模块暴露针对这些值的任务特定操作。
- 代理编写代码来组合这些操作。
- 技能作者拥有每个操作的行为。
想象一个用于处理 CSV 文件的技能。SKILL.md 告诉代理在处理类似 CSV 的表格、导出数据、发票、用户列表或需要连接、过滤或验证的记录时使用它。index.ts 导出一个小型表格 API:
export {
parseCsv,
joinTables,
filterRows,
validateRows,
groupBy,
summarize,
toCsv,
};
然后代理可以在解释器代码中组合这些操作:
const invoices = parseCsv(await tools.readFile({ path: "/invoices.csv" }));
const customers = parseCsv(await tools.readFile({ path: "/customers.csv" }));
const joined = joinTables(invoices, customers, "customer_id");
const invalid = validateRows(joined, invoiceSchema);
const byRegion = groupBy(joined, "region");
summarize(byRegion, ["total_due", "late_count"]);
代理控制要传递哪些值以及如何处理结果。技能作者控制“连接”、“验证”和“总结”的含义。
这与要求代理自己编写辅助函数不同。模型擅长写代码,但不能保证两次写出相同的代码。当流程很重要时,实现应该放在可以审查、测试、版本管理和复用的技能代码中。
常见问题解答
为什么要将其打包为技能?
技能已经是代理行为的实际标准单元。它们提供发现机制、渐进式披露、使用说明、示例和支持文件。解释器技能所做的就是保留这种打包形态,并用运行时代码扩展它。像这样扩展技能标准可能看起来有些笨拙,但这样我们就可以使用相同的分发方法,同时让代理使用更强大的技能。如果一个组织有成百上千个技能,将所有所需的模块直接连接到框架是不现实的。
难道不能只包含一个脚本文件吗?
脚本文件适用于不同的目的。当代理需要一个可以与外部环境交互的辅助工具时,脚本是一个好选择:脚本通常通过命令行参数、文件、标准输出/错误或序列化状态进行通信。这种边界使得脚本不适合 编排代理工作。一个脚本可以运行一次计算,但它无法自然地 参与框架循环:生成子代理、调度/等待任务图、处理部分失败,以及在将控制权返回模型之前决定整个流程何时“完成”。在仓库分类的例子中,模块内部调用一个被白名单的 tools.task(...) 函数来生成子代理。外部脚本需要一个单独的适配器来与框架通信才能实现这一点。
为什么不把每个 API 都做成工具?
工具最适用于代理需要跨越外部边界的情况:获取数据、读取或写入文件、创建工单、发送消息、调用分类器。但有一类操作是工具并不合适的:对代理工作流进行编排。工具是离散的外向调用;它们不知道整体的工作流。它们也不具备子代理生成、部分进度追踪或在步骤之间保持结构化中间状态的能力。相比之下,解释器代码可以持有引用、在并行子代理之间调度,并等待一个依赖于多个子任务结果的结构化结果。代理仍然可以调用工具,但工作流代码决定何时调用它们,而不是代理的自由形式推理决定调用哪些以及以什么顺序调用。
这个流程尤其需要一个被表示为指令(代理可以与其协商)的流程,而不是一个一旦开始就必须完成的过程。2
代理的风格指南:一种不同的方法——代理遵循指令但被允许协商步骤。另请参阅“指令与过程”的相关讨论。
相似文章
@LangChain:让代理具备编写代码的能力,能让它们变得极其强大。这也使得安全性问题变得更加棘手。在L…
LangChain的Deep Agents允许不受信任的代理编写的代码通过基于WebAssembly的代码解释器安全运行,提供执行和能力隔离,无需传统沙盒。
@LangChain:LangChain Academy 课程更新:Deep Agents 入门——动态和异步子代理模块、课程导师技能,它…
LangChain Academy 宣布对其免费的“Introduction to Deep Agents”课程进行更新,新增了动态与异步子代理模块、课程导师技能、顶点项目、实践练习和 TypeScript 支持,以及 Deep Agents v0.7 的更新。
@LangChain:在提升您的代理之路上
LangChain 宣布了一项用于改进 AI 代理的资源。
@LangChain:减少分类时间,更快修复,更早发现回归。介绍LangSmith Engine:一个能够自动工作的智能体……
LangChain 推出 LangSmith Engine 公测版,这是一个自主智能体,能够监控生产追踪、聚类故障、诊断根本原因,并提出修复和评估覆盖建议,以简化智能体开发。
@LangChain: 改进智能体 旧方法:手动读取追踪、寻找模式、编写评估、创建修复。更好的办法…
这条推文对比了改进AI智能体的旧手动方法与使用LangSmith Engine的新自动化方法,后者循环进行追踪、评估和修复。