工具
工具是 Agent 访问外部能力的方式。你可以把数据库查询、HTTP 请求、业务函数、代码执行、搜索等能力包装成工具。
tool 由 SDK 导出,不是全局函数。定义自定义工具时,需要和 createAgent 一起显式 import。
import { createAgent, tool } from "agent-lattice";import { z } from "zod/v4";
const calculator = tool( "calculator", "Evaluate a simple arithmetic expression", z.object({ expr: z.string(), }), async input => { return { content: String(Function(`return ${input.expr}`)()) }; },);
const agent = createAgent({ apiKey: process.env.DEEPSEEK_API_KEY, baseURL: "https://api.deepseek.com/anthropic", model: "deepseek-v4-flash", tools: [calculator],});SDK 会做三件事:
- 把 Zod schema 转成模型需要的 JSON Schema。
- 在工具执行前解析和校验输入。
- 把工具返回值封装成
tool_result回填给模型。
工具 handler 返回:
return { content: "done" };content 可以是字符串,也可以是内容块数组。工具抛错时,SDK 会把错误转成 is_error: true 的 tool_result,让模型有机会解释或选择其它路径。
从工具里结束整个运行
Section titled “从工具里结束整个运行”需要 0.16.0 及以上版本。
拿到最终答案的工具可以返回 endTurn: true 直接结束运行:SDK 以 subtype: "success" 收尾,把该工具的文本内容作为结果,不再发起下一轮模型调用:
const finish = tool( "finish", "Submit the final answer and end the run", z.object({ answer: z.string() }), async ({ answer }) => ({ content: answer, endTurn: true }),);endTurn 不会取消同批次的其他工具——它们已经并发启动,其 tool_result 照常进入历史,onToolResult hook 和 trace 事件也照常执行,只是跳过了下一轮模型调用。同一批次多个工具都设置 endTurn 时,取第一个工具的内容作为结果文本。
结束运行的工具还可以在 endTurn: true 之外返回 structuredResult,payload 会落在 SDKResultMessage.structuredResult 上;不带 endTurn 时 structuredResult 会被忽略。需要 0.20.0 及以上版本。
并发执行互不依赖的工具
Section titled “并发执行互不依赖的工具”需要 0.9.25 及以上版本。
模型在同一次回复中返回多个 tool_use,表示希望一起执行这些工具;哪些 handler 可以真正同时运行,仍由 SDK 判断。当工具输入不会引发冲突的副作用时,通过 isConcurrencySafe(input) 标记:
const search = tool( "search", "Search documents", z.object({ query: z.string() }), async ({ query }) => { // 业务代码:替换成你的数据库或搜索客户端。 return { content: await documentIndex.search(query) }; }, { isConcurrencySafe: () => true },);
const agent = createAgent({ model: "claude-sonnet-4-6", tools: [search], toolConcurrency: { mode: "safe", maxConcurrency: 8, },});默认使用 mode: "safe"。连续的工具调用只有在 isConcurrencySafe(input) 返回 true 时才会并发。工具没有声明安全规则、规则返回 false、输入无效或规则抛错时,该调用都会顺序执行。内置的 Read、LS、Glob 和 Grep 已标记为安全;Write、Edit 和 Bash 没有标记。
存在可并发工具时,SDK 会告诉模型:互不依赖的调用可以放在同一次回复中;后一个调用需要前一个结果时,必须分成两次回复。模型不能绕过 isConcurrencySafe、maxConcurrency 或 toolBatchPolicy。
只有确认 Agent 中所有工具都能安全地同时运行时,才使用 mode: "all"。需要强制逐个执行时,使用 mode: "sequential"。maxConcurrency 默认是 10,并且必须是正整数。
整批工具结束后,SDK 才会再次请求模型。Handler 可以按不同顺序完成,但发回模型的 tool_result 始终保持原始 tool_use 顺序。一个 handler 失败不会丢失同批其它成功结果。取消请求时,已启动的 handler 会收到本次 query 的 AbortSignal,仍在等待并发名额的调用不会启动;SDK 会等待已经启动的 handler 结束。
执行前检查整批工具
Section titled “执行前检查整批工具”需要 0.9.25 及以上版本。
如果某些工具不能出现在同一次模型响应中,通过 AgentOptions.toolBatchPolicy 提供检查规则。任何工具 handler 运行前,策略会先收到当前响应中的全部工具调用。
const agent = createAgent({ model: "claude-sonnet-4-6", tools: [incrementRevision], toolBatchPolicy: { validate({ toolCalls }) { const update = toolCalls.find(call => call.name === "incrementRevision"); const handoff = toolCalls.find( call => call.kind === "agent_tool" && (call.input as { mode?: string }).mode === "handoff", ); if (update && handoff) { return { allowed: false, code: "invalid_tool_batch", message: "请先更新 revision,再安排依赖新 revision 的成员任务。", conflictingToolCallIds: [update.id, handoff.id], suggestedNextStep: "先执行更新工具,拿到新 revision 后再 handoff。", }; } return { allowed: true }; }, },});策略拒绝时,整批工具都不会执行,handoff 也不会进入 mailbox。SDK 会给本批每个工具调用返回 is_error: true。如果策略自身抛错,SDK 返回 tool_batch_policy_error,同样不会执行任何工具。配置策略后,SDK 总会先检查完整批次,再进行并发调度。
该检查只能阻止同一次模型响应中的已知冲突。其它请求或进程也可能修改数据,因此数据库事务和 revision 校验仍然必须保留。
工具 metadata
Section titled “工具 metadata”需要 0.22.0 及以上版本。
ToolOptions.metadata 接收一个 Record<string, unknown>,原样透传到 ToolDefinition.metadata。SDK 不读也不解释它,也不会展示给模型——它是宿主持有的机器可读注解(例如契约版本)。不传时 ToolDefinition 上没有这个键。AgentToolOptions.metadata 对 agentTool() 生成的工具同理。
严格 options 校验
Section titled “严格 options 校验”需要 0.22.0 及以上版本。
createAgent()/createBareAgent()/defineAgent()(AgentOptions)、agentTool()(AgentToolOptions)、delegateTool()(DelegateToolOptions)和 tool()(ToolOptions)的 options 对象现在严格校验:遇到未知键会在装配期直接抛错——AgentOptions: unknown option "bogusOption". Check for a typo, or upgrade the SDK if this option was added in a newer version.(agentTool()/delegateTool() 的错误前缀是 agentTool("<name>"): / delegateTool("<name>"):)。目的是让「旧版 SDK + 新版 API」的组合快速失败——以前这种组合会成功但功能静默缺失。*行为变化:*以前会被静默忽略的多余键(例如 spread 进来的宿主字段)现在会抛错,升级时记得剔除。