跳转到内容

工具

工具是 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: truetool_result,让模型有机会解释或选择其它路径。

需要 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 上;不带 endTurnstructuredResult 会被忽略。需要 0.20.0 及以上版本。

需要 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、输入无效或规则抛错时,该调用都会顺序执行。内置的 ReadLSGlobGrep 已标记为安全;WriteEditBash 没有标记。

存在可并发工具时,SDK 会告诉模型:互不依赖的调用可以放在同一次回复中;后一个调用需要前一个结果时,必须分成两次回复。模型不能绕过 isConcurrencySafemaxConcurrencytoolBatchPolicy

只有确认 Agent 中所有工具都能安全地同时运行时,才使用 mode: "all"。需要强制逐个执行时,使用 mode: "sequential"maxConcurrency 默认是 10,并且必须是正整数。

整批工具结束后,SDK 才会再次请求模型。Handler 可以按不同顺序完成,但发回模型的 tool_result 始终保持原始 tool_use 顺序。一个 handler 失败不会丢失同批其它成功结果。取消请求时,已启动的 handler 会收到本次 query 的 AbortSignal,仍在等待并发名额的调用不会启动;SDK 会等待已经启动的 handler 结束。

需要 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 校验仍然必须保留。

需要 0.22.0 及以上版本。

ToolOptions.metadata 接收一个 Record<string, unknown>,原样透传到 ToolDefinition.metadata。SDK 不读也不解释它,也不会展示给模型——它是宿主持有的机器可读注解(例如契约版本)。不传时 ToolDefinition 上没有这个键。AgentToolOptions.metadataagentTool() 生成的工具同理。

需要 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 进来的宿主字段)现在会抛错,升级时记得剔除。