@effect/ai-anthropic 客户端执行工具修复解析:Memory / Text Editor / Computer Use / Bash 的线上映射与 Schema 兼容
2026/9/15 20:07:52 网站建设 项目流程

@effect/ai-anthropic 客户端执行工具修复解析:Memory / Text Editor / Computer Use / Bash 的线上映射与 Schema 兼容

【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect

本篇文章基于@effect/ai-anthropic包的一条 changeset 修复记录(.changeset/pre/fix-anthropic-memory-tool.md)展开。该修复解决了 Anthropic 提供商定义的客户端执行工具(Memory、Text Editor、Computer Use、Bash)在真实网络请求(on the wire)中不可用的问题,核心涉及三个技术点:provider wire 名称到自定义名称的映射、file_text必需字段的补齐、以及Schema.optionalKeySchema.optional对 Anthropic codec 的兼容性。阅读本文后,你将理解 Effect AI SDK 中"提供商定义工具"的双名称机制,掌握客户端执行工具的 Schema 编写规范,并能定位与避免ToolNotFoundError与 "Unsupported AST Undefined" 两类典型故障。

背景:什么是客户端执行的提供商定义工具

在 Effect AI SDK 中,工具分为普通用户定义工具与"提供商定义工具"两类。后者由模型提供商(如 Anthropic、OpenAI)在协议层原生定义,开发者通过Tool.providerDefined将其接入请求。以 Anthropic 为例,这类工具包括:

  • Bashbash):在沙箱中执行 shell 命令,需要computer-use-*beta header;
  • Computer Usecomputer/computer_use):控制屏幕的鼠标键盘操作,同样依赖 computer-use beta;
  • Memorymemory):跨会话的持久化文件操作(create/view/insert/str_replace/rename/delete);
  • Text Editorstr_replace_editor/str_replace_based_edit_tool):文件读写编辑;
  • 以及无需客户端处理的Web SearchWeb FetchTool SearchCode Execution等服务端执行工具。

所有定义集中在 packages/ai/anthropic/src/AnthropicTool.ts,并由 index.ts 以export * as AnthropicTool的方式对外暴露。

其中Memory、Text Editor、Computer Use、Bash都通过requiresHandler: true标记为客户端执行:模型的tool_use块会先被本地 Schema 解码,再交给开发者注册的 handler 执行,执行结果再编码回传给提供商。正因为多了一次"本地编解码 + 名称映射"的往返,这几类工具在 wire 上最容易出错——本次 changeset 修复的正是这条链路上的三个缺陷。

缺陷一:wire 名称无法映射回自定义名称,导致 ToolNotFoundError

双名称机制:customName 与 providerName

Tool.providerDefined要求同时提供两个名称,providerDefined 定义:

  • id:全局唯一标识,如"anthropic.memory_20250818"
  • customName:Effect AI SDK 内部(Toolkit 键名)使用的名称,例如"AnthropicMemory""AnthropicBash""AnthropicTextEditor""AnthropicComputerUse"
  • providerName:真正发送给 Anthropic 协议的名称,例如"memory""bash""str_replace_editor""computer"

采用双名称的原因在 NameMapper 的文档注释中写得很清楚:一个 Toolkit 可能同时包含多个提供商的工具,它们会撞名(例如 OpenAI 和 Anthropic 都有web_search),因此 SDK 用"OpenAiWebSearch"/"AnthropicWebSearch"这类自定义名作为 Toolkit 内的唯一键,而把各自提供商的 wire 名保存在providerName中。

makeResponse 中的映射修复

当模型返回的 content 块带有type: "tool_use"时,makeResponse 处理逻辑 通过toolNameMapper.getCustomName(part.name)把 provider 返回的 wire 名(如"memory")映射回 Toolkit 使用的自定义名(如"AnthropicMemory"):

case "tool_use": { // ... const toolName = toolNameMapper.getCustomName(part.name) // 后续用 toolName 在 Toolkit 中查找 handler }

NameMapper在构造时遍历 Toolkit 中的全部工具,为每个providerDefined工具建立customName ⇄ providerName双向映射(NameMapper 实现):

this.#customToProvider.set(tool.name, tool.providerName) this.#providerToCustom.set(tool.providerName, tool.name)

getCustomName(providerName)返回映射后的自定义名;若某个 wire 名从未注册,transformToolCallParams会在 AnthropicLanguageModel.ts 第 3107-3118 行 抛出ToolNotFoundError,并把当前可用工具名列表一并放入错误信息,便于排查。

本次修复之前,正是这条反向映射在客户端执行工具上失效:模型返回name: "memory",但 SDK 未能把它还原为"AnthropicMemory",导致每次调用都触发ToolNotFoundError,工具在 wire 上完全不可用。修复后(含流式等价路径,见 第 2260 行 等处的同名调用)映射正确回归。

缺陷二:MemoryCreateCommand 丢失 file_text,create 命令写不出文件内容

Memory 工具的命令是一个可辨识联合(discriminated union),command字段决定载荷形态:Memory_20250818_Commands 由create / delete / insert / rename / str_replace / view六种命令 Schema 组合而成。

其中创建文件的命令此前缺少file_text字段。在 Anthropic 的 Memory 协议中,create命令必须携带文件正文内容;缺失该字段意味着模型发出的"创建文件"指令在解码后丢掉文件体,落盘时只能得到空文件(或直接失败)。修复后的 MemoryCreateCommand 完整定义如下:

export const MemoryCreateCommand = Schema.Struct({ command: Schema.Literal("create"), path: Schema.String, file_text: Schema.String // 修复:此前缺失,create 会丢失文件正文 })

测试 AnthropicLanguageModel.test.ts 第 1079-1118 行 专门验证了这条链路:模拟提供商返回{ command: "create", path: "/memories/notes.txt", file_text: "hello world" },断言 handler 收到的参数完整包含file_text: "hello world",且工具结果以自定义名AnthropicMemory返回。

缺陷三:optionalKey 与 optional 的编解码差异,触发 "Unsupported AST Undefined"

两种可选字段的语义区别

Effect Schema 提供两种"可选"字段声明,二者编码侧语义不同(见 Schema.ts 中 optionalKey/optional 的说明):

  • Schema.optionalKey(S):字段可缺席(absent),编码为 JSON 时直接不输出该键;
  • Schema.optional(S):等价于optionalKey(UndefinedOr(S)),字段可缺席,也可显式出现为undefined,编码侧类型是S | undefined

关键在于:Schema.optional会在 AST 中引入Undefined字面量分支,而 Anthropic 结构化输出 codec 不支持Undefined这个 AST 节点,因此在把工具参数 Schema 转换为 Anthropic 可用的 codec 时会直接报错:

Unsupported AST Undefined

本次涉及的具体字段

changeset 明确指出四个此前误用Schema.optional的字段,修复后全部改为Schema.optionalKey

工具字段修复后定义位置
Memory / Text Editorview_range[start, end]行号区间)MemoryViewCommand、TextEditorViewCommand
Computer Usecoordinate[x, y]屏幕坐标)如 ComputerUseLeftClickAction、DoubleClickRightClickScrollLeftMouseDown/Up
Bashrestart(是否重启 shell)Bash_20241022 / Bash_20250124

以 Bash 为例,修复后的参数 Schema 为:

export const Bash_20241022 = Tool.providerDefined({ id: "anthropic.bash_20241022", customName: "AnthropicBash", providerName: "bash", requiresHandler: true, success: Schema.String, parameters: Schema.Struct({ command: Schema.String, restart: Schema.optionalKey(Schema.Boolean) }) })

view_range而言,其值本身是 1 起始、以-1表示"读到文件末尾"的行区间[start, end](ViewRange 定义),字段本身允许缺席,但显式undefined没有意义,因此optionalKey是语义上更准确的声明。

回归测试的验证方式

测试 client provider tool parameters compile with the Anthropic codec 遍历全部客户端执行工具(Bash 两个版本、Computer Use 三个版本、Memory、Text Editor 四个版本),对每个工具的parametersSchema调用AnthropicStructuredOutput.toCodecAnthropic(...),断言能成功编译出 codec——任何残留的Schema.optional字段都会让该测试失败,从而防止问题回归。

完整修复链路一览

把三个缺陷串起来,客户端执行工具在 wire 上的完整数据流是:

  1. 开发者用Toolkit.make(AnthropicTool.Memory_20250818({}))注册工具,并通过toolkit.toLayer({ AnthropicMemory: handler })提供 handler;
  2. 请求发出后,模型返回content: [{ type: "tool_use", name: "memory", input: {...} }]
  3. makeResponseNameMapper.getCustomName("memory")还原为"AnthropicMemory"(修复一),否则抛ToolNotFoundError
  4. 按自定义名找到工具后,用其parametersSchema(已通过optionalKey通过 codec 编译,修复三)解码input,例如{ command: "create", path: "...", file_text: "hello world" }(修复二保证file_text不丢失);
  5. 解码后的参数交给AnthropicMemoryhandler 执行,结果编码回传。

三处修复在 AnthropicLanguageModel.test.ts 第 1014-1146 行 有完整的回归覆盖,对应 changeset 提及的 issue #2615。

实践要点

  • 为客户端执行工具编写参数 Schema 时,一律使用Schema.optionalKey而非Schema.optional,避免Undefined进入 AST 触发 "Unsupported AST Undefined"。Schema.optional只适用于确实需要在编码侧表达undefined的场景。
  • 不要假设模型返回的 tool name 等于 Toolkit 键名。wire 上永远是providerName(如memorybash),SDK 内部映射到customName;排查ToolNotFoundError时,先确认工具是否通过providerDefined注册、providerName是否与协议一致。
  • 协议要求必填的字段(如 Memorycreatefile_text)必须出现在 Schema 中,否则解码结果会静默丢字段,表现为"文件内容凭空消失"这类难以定位的问题。
  • 涉及 Anthropic 相关功能改动时,可运行packages/ai/anthropic/test/AnthropicLanguageModel.test.ts中的 Memory tool 与 codec 编译两组用例做快速验证。

上述行为在@effect/ai-anthropic的当前源码中均已生效,改动以patch级别随.changeset/pre/fix-anthropic-memory-tool.md发布。如需查看完整工具定义(含各版本 Computer Use 动作集、Text Editor 命令集、Web Search/Fetch 参数等),可直接阅读 packages/ai/anthropic/src/AnthropicTool.ts;工具双名称映射的通用机制则在 packages/effect/src/unstable/ai/Tool.ts 的providerDefinedNameMapper中。

【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询