工具设计反模式

K6
深入解析 · 工具与能力设计

多数失败的智能体不是败在模型上——它们败在四个反复出现的工具设计反模式上。

前五篇文章论证原则;这一篇是它们各种违例的现场指南。下面每个反模式,你都能在不到一分钟内从一个现有工具定义里认出来,每个在追踪里都有特征性的失败指纹,每个都有一个机械化的修法。如果一个智能体莫名其妙地不可靠而提示词看起来没问题,工具是第一个该看的地方——而这四个,就是你通常会找到的东西。

STEP 1

厨房水槽工具:一个带模式标志、其实是五个工具的调用。

指纹:一个带 actionoperation 参数的单一工具,模式里大多数字段只在特定条件下相关——一半只在 action="create" 时适用,一半只在 action="delete" 时适用。模型得先选工具、再选模式、再搞清这个模式要哪些字段,然后它选错模式或填错那组字段。修法是 K2 的拆分:每个动作一个工具,各带紧致的模式。追踪里的破绽,是同一个工具被以内部自相矛盾的参数调用。

# Anti-pattern: kitchen-sink, conditional fields
manage_user(action: str, id=None, name=None,
            email=None, role=None, hard=None)

# Fix: one tool per action, each schema tight
create_user(name, email, role)
delete_user(id, hard=False)
STEP 2

字符串化参数:该用结构的地方用了自由字符串。

指纹:一个类型为 string 的参数,实际期望的是一个日期、一个枚举值、一个 JSON 块,或一个模型得凭空构造的查询 DSL。自由字符串正是模型塞进似是而非垃圾的地方——把 "next Tuesday" 塞进想要 2026-05-26 的字段,把 "high priority" 塞进想要 P1 的字段。修法是 K3:把字符串换成 enum、一个带格式的类型化字段,或一个结构化对象,让那个坏值表达不出来。

在你的工具定义里 grep 那些类型为纯 string、没有枚举、格式或模式的参数。每一个都是模型当前可以自由幻觉的地方;它们大多本想要一个受约束的类型,却没人去收紧。

STEP 3

静默失败:什么都没发生时工具却返回成功。

指纹:一个捕获自己的异常、返回空列表、默认对象或一个干巴巴 200 而非错误的工具。这是最危险的反模式,因为它不产生任何局部症状——智能体在一个虚假前提上自信地继续,失败在许多步之后浮现为任何追踪都无法把它定位到原因的行为。修法是 K4:响亮地失败,把部分成功报为部分,绝不让「什么都没发生」看起来像「成功了」。一个不能可见地失败的工具,无法被调试。

「出错就返回空,免得智能体崩」是智能体工程里单一最昂贵的省事。智能体没崩——它自信地又做了十步错事,而事后复盘找不到它哪里出的错,因为工具把证据抹掉了。

STEP 4

泄漏的抽象:工具暴露的是它的实现,而非它的工作。

指纹:一个参数或输出由后端而非任务塑造的工具——模型得自己穿引的分页游标、内部状态码、原始连接键,一个逼模型自己写 SQL 的 db_query。模型被迫去推理你的存储层,而非用户的目标,并且推得很糟。修法是 K1:围绕任务设计工具、把机械隐藏起来;如果模型在构造你的内部查询语言,抽象就泄漏了,工具在做错的工作。

STEP 5

元破绽:这些很少单独出现。

一个厨房水槽工具通常也是字符串化的(那个 action 参数),而且往往是泄漏的抽象(它镜像了一个 CRUD 端点),并且它倾向于静默失败,因为宽泛的工具有着宽泛、含糊的错误处理。当你找到一个,就对同一个工具审查另外三个——修好粒度(K2)经常同时化解掉类型与抽象问题,因为一个任务形状的工具无处可泄漏、也没什么可切换模式。

STEP 6

何时一个「反模式」是务实之选。

这些是要有意识地去违反的默认,不是法律。一个被固定外壳用一次的一次性内部工具,可以字符串化、可以泄漏,无所谓。一个真正通用的逃生口——一个沙箱化的 run_code——是一个刻意的、受控的泄漏抽象,它的威力正是重点。反模式是把这些意外地交付在一个面向模型、自由选择、有副作用的工具上——而不是在理解了失败模式后、有范围地、明知故选其一。