这几天,我一直在做一件看起来很简单、但实际非常麻烦的事情。

让一个 Agent 看懂当前 3D 场景里的内容,然后替用户完成一些操作。

比如:

“帮我找出场景里的所有车,并把它们标注出来。”

听起来不复杂,对吧?

Agent 已经有截图工具。

它可以调用视觉模型看图。

它可以拿到屏幕上的 2D 坐标。

它也有工具可以把 2D 坐标转换成 3D 场景坐标。

最后,它还能调用 FrontEnd 的工具,在对应的位置创建 markup 或 entity。

从能力上看,这条链路是通的。

所以一开始我以为,这个问题主要是模型准不准、坐标准不准、2D 到 3D 的转换有没有偏差。

但后来我发现,真正麻烦的地方不是这些。

真正麻烦的是:Agent 有太多方法可以完成同一个任务。

同一个问题,两条完全不同的路径

我先讲一个真实例子。

我让 Agent 识别场景中的车,并把它们标注出来。

其中一次,它走的是我预期中的路径:

  1. 截取当前场景截图;
  2. 用视觉模型查看图片;
  3. 找出图片里的车;
  4. 返回车在图片中的 2D 坐标;
  5. 把 2D 坐标转换成 3D 场景坐标;
  6. 调用 FrontEnd 工具创建 markup。

这个流程不一定完美。

有时候坐标会有偏差,有时候车会漏检,有时候 2D 到 3D 的位置不够准确。但至少这条路径是合理的。它是可以 debug 的,也是可以优化的。

然后我换了一种问法。

任务本质上还是一样:看图,找车,标注。

但这一次,Agent 做了完全不同的事情。

它先截图,然后下载图片,接着打开 Python sandbox,试图用 Python 的图像处理库去分析图片。问题是,它想用的库在运行环境里并不存在。于是整个任务失败了。

这时候你会发现一个很有意思的问题:

同一个 Agent。

同一套工具。

同一个目标。

只是用户问法稍微变了一点。

最后得到的是完全不同的工具路径,甚至完全不同的结果。

这不是一个小问题。

这就是 tool-using agent 在真实产品里最容易出现的不稳定性。

问题不是工具不够,而是工具太多

很多人在做 Agent 的时候,会本能地想给它更多工具。

Agent 不能看图?加一个 screenshot tool。

需要理解图片?加一个 vision model。

需要处理数据?加 Python sandbox。

需要查数据库?加 query tool。

需要操作界面?加 UI action tool。

需要创建对象?加 FrontEnd tool。

每加一个工具,系统看起来就更强一点。

但工具一多,另一个问题就出现了:

Agent 不再只是需要回答“我能不能做这件事”,它还需要回答“我应该用哪条路径做这件事”。

而这个问题比想象中难得多。

因为同一个任务,可能有很多条看起来都合理的路径。

比如“识别截图里的车”这件事:

它可以直接让视觉模型看图。

它也可以下载图片后用 Python 分析。

它可以先查场景里已有的实体。

它也可以尝试用某个工具从页面里读 DOM 或 scene state。

它甚至可能组合几种方法,做出一条非常绕的路径。

站在人的角度,我们知道哪条路更合适。

但站在 Agent 的角度,这些都只是“可用工具”。

它不会天然知道哪条路径更稳定、哪条路径更慢、哪条路径在当前环境下大概率会失败。

所以很多时候,Agent 失败不是因为它不聪明,也不是因为它缺少工具。

它失败是因为它拥有太多选择。

路径本身,变成了问题的一部分

这也是我后来意识到的一个关键点:

对 tool-using agent 来说,执行路径本身就是产品质量的一部分。

以前我们评估一个回答,可能主要看最终答案对不对。

但在 Agent 系统里,只看最终答案是不够的。

你还要看它是怎么得到这个答案的。

它有没有用了不该用的工具?

它有没有绕远路?

它有没有调用一个不稳定的 runtime?

它有没有把一个本来可以由 vision model 完成的任务,变成一个 Python 图像处理任务?

它有没有生成大量看起来合理但实际上没有必要的中间步骤?

这些都会影响系统的稳定性。

而且更麻烦的是,如果 Agent 每次都走不同路径,你根本很难调试。

今天失败是因为 Python 库不存在。

明天失败是因为坐标格式不对。

后天失败是因为它没有调用 2D-to-3D 工具。

再下一次它又突然成功了,因为它刚好选了一条正确路径。

这种系统很难维护。

不是因为某一个工具坏了,而是因为工具组合空间太大了。

Skill 真正解决的是什么

这时候 Skill 的价值就出现了。

我对 Skill 的理解,不是“给 Agent 多写一段 prompt”。

如果只是这样,Skill 很快也会变成另一种 prompt 垃圾场。

真正有价值的 Skill,应该是一个经过验证的执行路径。

它告诉 Agent:

这类任务应该怎么做;

应该先用哪个工具;

再用哪个工具;

中间结果应该是什么格式;

什么情况下继续;

什么情况下停下来;

什么情况下不要乱编答案。

换句话说,Skill 的作用不是让 Agent 变得更“自由”,而是让它在复杂工具集里更稳定。

在刚才那个例子里,如果我们已经知道“识别车并创建 markup”的稳定路径是:

screenshot → vision model → 2D coordinates → 2D-to-3D conversion → FrontEnd markup

那这条路径就应该被固化下来。

下次用户再问类似问题时,Agent 不应该重新探索一次“我要不要用 Python”或者“我要不要下载图片”。

它应该优先复用这条已经验证过的路径。

Skill 应该记住路径,而不是结果

这里有一个很容易踩坑的点。

当我们说“让 Agent 学会 Skill”时,很多人会想到缓存。

比如把上一次工具返回的 JSON 存下来,把上一次截图分析结果存下来,把上一次生成的坐标存下来。

但我觉得这不是 Skill 应该做的事情。

工具结果通常是一次性的。

截图会变。

场景会变。

用户会变。

坐标会变。

工具返回的数据也可能非常大。

这些东西不适合存在 Skill 里。

Skill 应该保存的是方法,而不是答案。

更具体一点,它应该保存:

这类任务应该使用哪些工具;

这些工具应该按什么顺序调用;

每一步需要什么输入;

输出应该被整理成什么格式;

什么时候认为结果可信;

什么时候需要 fallback;

什么时候应该告诉用户“不确定”。

一句话说:

Skill 不应该缓存工具结果,Skill 应该保存成功路径。

这个区别很重要。

缓存结果解决的是“上一次答案是什么”。

保存路径解决的是“下一次该怎么稳定地完成类似任务”。

人写 Skill 是有上限的

当然,最直接的办法是让工程师手写 Skill。

这在一开始很好用。

因为人知道系统怎么工作,也知道哪些路径可靠。

比如我知道,在这个场景里,用 vision model 直接看截图,比让 Agent 下载图片再跑 Python 更符合产品设计。

那我可以手写一个 Skill,告诉它遇到这种任务应该怎么做。

但问题是,人不可能预判所有情况。

用户的问法太多了。

工具也会不断增加。

产品场景也会变化。

同一个功能,在不同环境下可能还会有不同限制。

如果每增加一种情况,都要工程师写一段新的规则,最后系统就会变得很难维护。

System prompt 会越来越长。

Skill 会越来越多。

规则之间可能互相重叠。

有些规则过期了也没人知道。

这和代码里的技术债其实很像。

一开始每一条规则都有理由。

半年之后,它们就变成了一堆没人敢删的历史包袱。

所以我不希望把所有具体功能逻辑都塞进 system prompt。

Prompt 应该定义高层边界和行为原则。

具体任务的执行路径,应该沉淀在更接近工具层的 Skill 里。

而且更进一步,我认为 Skill 不应该只能由人来写。

让 Agent 自己生成 Skill

我的想法是:

当 Agent 成功完成一个任务之后,它应该有机会回顾这次执行过程,并生成一个新的 Skill。

这个 Skill 不保存结果,只保存路径。

大概流程是这样的:

用户提出一个任务。

Agent 自己选择工具并完成任务。

在最终回答之前,Agent 先做一次自我检查。

如果它确认任务完成得不错,就总结这次成功路径。

系统把这个路径保存成一个可复用的 Skill。

下一次遇到类似任务时,Agent 可以先读取这个 Skill,再决定怎么调用工具。

这里最重要的是“自我检查”。

因为不是每一次成功都值得学习。

有些成功只是运气好。

有些路径虽然成功了,但非常慢。

有些路径用了不该用的工具。

有些结果看起来对,但其实没有验证。

这些都不应该变成 Skill。

所以在生成 Skill 之前,Agent 至少应该问自己几个问题:

我有没有真正完成用户的请求?

我有没有使用合适的工具?

有没有更简单、更稳定的路径?

我有没有在不确定的时候编造结果?

这个路径未来是否还有复用价值?

这个过程是否依赖某个一次性的上下文?

只有当这些问题通过之后,才应该生成 Skill。

这样可以避免把坏路径沉淀进系统里。

自生成 Skill 长什么样

还是用前面的例子。

如果 Agent 成功完成了“识别场景里的车并添加 markup”,它生成的 Skill 不应该长这样:

“上次识别到了三辆车,坐标分别是……”

这没用。因为下一次场景就变了。

它应该更像这样:

当用户要求标注当前场景中可见的车辆时:

1. 先获取当前 FrontEnd 视图的截图;
2. 使用视觉模型识别截图中的车辆;
3. 要求返回结构化的 bounding box 和中心点坐标;
4. 校验坐标是否在图片范围内;
5. 如果 confidence 太低,不要继续创建 markup;
6. 将中心点坐标传给 FrontEnd 的 2D-to-3D 工具;
7. 使用返回的 3D 坐标创建 markup;
8. 最终回答中说明标注是否成功,以及是否存在不确定性。

这个 Skill 的价值在于,它把一条成功路径固定了下来。

它没有保存具体车辆坐标。

它没有保存截图。

它没有缓存工具输出。

它保存的是 Agent 应该如何完成这一类任务。

这才是可复用的部分。

运行时,不要把所有工具都丢给 Agent

有了 Skill 之后,下一步是运行时怎么用。

我越来越觉得,一个复杂 Agent 系统不应该默认把所有工具都暴露给 Agent。

这听起来好像是在限制能力,但实际是在提高稳定性。

如果用户问的是“标注场景里的车”,系统可以先检索相关 Skill。

这个 Skill 会告诉系统,这类任务通常只需要几类工具:

截图工具;

视觉模型;

2D-to-3D 转换工具;

FrontEnd markup 工具。

那在这次执行里,就可以优先暴露这些工具。

这样 Agent 就不会轻易跑去用 Python sandbox,也不会突然选择一条很奇怪的路径。

这不是让 Agent 变笨。

这是让它少走弯路。

工具越多,越需要这种路径约束。否则,Agent 的“智能”会被工具组合空间消耗掉。

Skill 也需要进化和淘汰

还有一个问题不能忽略。

如果 Skill 的作用是固定一条工具路径,那它会不会也带来新的风险?

答案是会的。

因为一条路径一旦被固化下来,它就有可能被反复使用。

如果这个 Skill 本身并不够好,或者它曾经有效但后来过时了,那么 Agent 就会一次又一次地重复这条不好的路径。

这其实和代码里的技术债很像。

一开始,某个实现可能是合理的。

后来工具变了,模型变了,用户需求变了,环境也变了。

但那条旧路径还留在那里,而且因为它被写成了 Skill,Agent 还会继续信任它。

这时候,Skill 就不再是稳定性的来源,而会变成系统里的惯性。

所以 Skill 不应该是一次生成、永久有效的东西。

它需要有自己的更新机制。

一种可能的方式是:在大多数情况下,Agent 使用已有 Skill 来完成任务;但在少数情况下,系统允许 Agent 暂时抛开已有 Skill,重新探索一条新的工具路径。

这有点像工程系统里的 exploration。

我们不希望 Agent 每次都自由探索,因为那会让结果不稳定。

但我们也不希望 Agent 永远只使用旧 Skill,因为那会让系统无法进化。

所以比较合理的方式是,在稳定性和探索之间保持一个比例。

比如:

大部分情况下,Agent 复用已有 Skill。

少数情况下,Agent 允许重新规划工具路径。

完成任务后,系统比较新路径和旧 Skill 的表现。

比较的维度可以包括:

  • 是否更准确地完成了用户请求;
  • 是否使用了更少的工具;
  • 是否执行得更快;
  • 是否失败率更低;
  • 是否更容易验证;
  • 是否更符合当前系统能力;
  • 是否减少了不必要的中间步骤。

如果新路径明显更好,那就应该更新原来的 Skill。

如果旧 Skill 多次执行效果不好,就应该降低它的优先级,甚至废弃它。

也就是说,Skill 应该有生命周期。

它可以被创建。

可以被验证。

可以被更新。

也可以被淘汰。

这点很重要。

因为我们真正想要的不是一堆永远不变的规则,而是一个可以持续学习和自我修正的 Agent 系统。

稳定不应该意味着僵化。

好的 Skill 系统应该让 Agent 在大多数时候走可靠路径,同时保留少量探索能力,让它发现更好的路径,并逐渐淘汰过时或低质量的路径。

如果说自生成 Skill 解决的是“人无法穷尽所有场景”的问题,那么 Skill 的更新和淘汰机制解决的就是另一个问题:

Agent 不能永远被自己过去的经验困住。

Skill 的目标不是让 Agent 永远重复过去,而是让它从过去的成功中开始,并在新的执行中继续改进。

我真正想解决的问题

所以这整套设计,真正想解决的不是“让 Agent 更会写 prompt”。

也不是“让 Agent 记住更多历史”。

我真正想解决的是:

当工具很多时,如何让 Agent 的行为更稳定?

我的答案是:

不要只优化最终回答。

要优化工具路径。

不要只增加工具。

要沉淀成功路径。

不要只让人写规则。

要让 Agent 在成功执行后,生成自己的 Skill。

不要缓存一次性的结果。

要保存可复用的方法。

同时,也不要让 Skill 变成永久不变的死规则。

要让它可以被验证、被更新,也可以在不再有效时被淘汰。

这几个点组合起来,才可能让 Agent 在真实系统里变得更可靠。

最后

现在很多人谈 Agent,喜欢讨论模型有多强,context window 有多长,工具调用能力有多复杂。

这些当然都重要。

但在真实工程里,还有一个更朴素的问题:

当 Agent 面前有二十个工具时,它到底该用哪几个?

如果这个问题解决不好,工具越多,系统越不稳定。

我越来越觉得,Agent 的未来不只是“更多工具”,而是“更稳定的工具路径”。

Skill 是一个很好的起点。

而自生成 Skill,则可能是让这个机制真正扩展起来的一种方式。

但 Skill 也不能变成新的死规则。

它需要被验证,需要被更新,也需要在不再有效时被淘汰。

一个成熟的 Agent,不应该只是每次从零开始解决问题。

它也不应该永远被过去的路径限制住。

它应该能从成功经验中提炼方法。

记住方法。

复用方法。

也能在新的任务里发现更好的方法。

对我来说,这才是 Skill 真正有意思的地方。