编码母题 / P0 / Agent 运行时 / 01
工具注册表与工具执行器
先读清题意与边界,独立尝试,再展开思路并对照可运行的参考代码。
重要程度必练难度中等预计完成35–45 分钟
题目是什么
把工具注册表与执行器分开。工具声明名称、校验器和执行函数;调用结果统一结构化,明确区分未知工具、参数错误、执行失败和取消。
输入输出示例
未知工具应返回 TOOL_NOT_FOUND,而不是直接抛出内部异常或暴露 stack。
练习起手式与验证
复制代码后补全空方法,再运行下方三组验证;复杂题所需的辅助类型与输入准备一并放在代码块中。
/*
* 题目:工具注册表与工具执行器
* 要求:把工具注册表与执行器分开。工具声明名称、校验器和执行函数;调用结果统一结构化,
* 明确区分未知工具、参数错误、执行失败和取消。
* 示例:未知工具应返回 TOOL_NOT_FOUND,而不是直接抛出内部异常或暴露 stack。
*/
class ToolRegistry {
constructor() {}
register(tool) {}
get(name) {}
list() {}
}
class ToolExecutor {
constructor(registry) {}
async execute(call, context = {}) {}
}
(async () => {
const registry = new ToolRegistry().register({
name: "add",
validate: (x) => ({
ok: Number.isFinite(x?.a) && Number.isFinite(x?.b),
message: "a/b required",
}),
execute: ({ a, b }) => a + b,
});
const executor = new ToolExecutor(registry);
console.log(await executor.execute({ id: "1", name: "add", arguments: { a: 2, b: 3 } })); // 预期:{ toolCallId: "1", toolName: "add", ok: true, output: 5 }
console.log((await executor.execute({ id: "2", name: "missing" })).error.code); // 预期:"TOOL_NOT_FOUND"
console.log((await executor.execute({ id: "3", name: "add", arguments: {} })).error
.code); // 预期:"INVALID_ARGUMENTS"
})().catch(console.error);
/*
* 题目:工具注册表与工具执行器
* 要求:把工具注册表与执行器分开。工具声明名称、校验器和执行函数;调用结果统一结构化,
* 明确区分未知工具、参数错误、执行失败和取消。
* 示例:未知工具应返回 TOOL_NOT_FOUND,而不是直接抛出内部异常或暴露 stack。
*/
class ToolRegistry {
constructor() {}
register(tool) {}
get(name) {}
list() {}
}
class ToolExecutor {
constructor(registry) {}
async execute(call, context = {}) {}
}
(async () => {
const registry = new ToolRegistry().register({
name: "add",
validate: (x) => ({
ok: Number.isFinite(x?.a) && Number.isFinite(x?.b),
message: "a/b required",
}),
execute: ({ a, b }) => a + b,
});
const executor = new ToolExecutor(registry);
console.log(await executor.execute({ id: "1", name: "add", arguments: { a: 2, b: 3 } })); // 预期:{ toolCallId: "1", toolName: "add", ok: true, output: 5 }
console.log((await executor.execute({ id: "2", name: "missing" })).error.code); // 预期:"TOOL_NOT_FOUND"
console.log((await executor.execute({ id: "3", name: "add", arguments: {} })).error
.code); // 预期:"INVALID_ARGUMENTS"
})().catch(console.error);解题思路
展开思路、不变量与常见边界
关键切入点:Registry 管静态能力,Executor 管校验、取消、异常隔离和结果边界。
核心模型:Registry 负责静态能力发现,Executor 负责运行时边界。一次调用必须 关联 toolCallId;执行前验证,执行后统一封装。工具异常被隔离为结果而非击穿 整个批次。生产环境可在此叠加超时、权限、Trace 和幂等。
参考代码与验证
“关键机制”注释用于解释实现,下载链接提供未经页面注释修改的原始 JS 文件。
// 关键机制:Registry 管静态能力,Executor 管校验、取消、异常隔离和结果边界。
class ToolRegistry {
constructor() {
this.tools = new Map();
}
register(tool) {
if (
!tool ||
typeof tool.name !== "string" ||
typeof tool.execute !== "function"
)
throw new TypeError("invalid tool");
if (this.tools.has(tool.name))
throw new Error(`duplicate tool: ${tool.name}`);
this.tools.set(tool.name, tool);
return this;
}
get(name) {
return this.tools.get(name);
}
list() {
return [...this.tools.values()].map(({ name, description, schema }) => ({
name,
description,
schema,
}));
}
}
class ToolExecutor {
constructor(registry) {
this.registry = registry;
}
async execute(call, context = {}) {
const base = { toolCallId: call?.id, toolName: call?.name };
const tool = this.registry.get(call?.name);
if (!tool)
return {
...base,
ok: false,
error: { code: "TOOL_NOT_FOUND", message: "tool not found" },
};
try {
if (context.signal?.aborted)
throw context.signal.reason || new Error("aborted");
const validation = tool.validate
? tool.validate(call.arguments)
: { ok: true };
if (validation === false || validation?.ok === false)
return {
...base,
ok: false,
error: {
code: "INVALID_ARGUMENTS",
message: validation?.message || "invalid arguments",
},
};
const output = await tool.execute(call.arguments, context);
return { ...base, ok: true, output };
} catch (error) {
const aborted = context.signal?.aborted;
return {
...base,
ok: false,
error: {
code: aborted ? "ABORTED" : "TOOL_EXECUTION_FAILED",
message: aborted ? "tool execution aborted" : "tool execution failed",
},
};
}
}
}
// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
const registry = new ToolRegistry().register({
name: "add",
validate: (x) => ({
ok: Number.isFinite(x?.a) && Number.isFinite(x?.b),
message: "a/b required",
}),
execute: ({ a, b }) => a + b,
});
const executor = new ToolExecutor(registry);
console.log(await executor.execute({ id: "1", name: "add", arguments: { a: 2, b: 3 } })); // 预期:{ toolCallId: "1", toolName: "add", ok: true, output: 5 }
console.log((await executor.execute({ id: "2", name: "missing" })).error.code); // 预期:"TOOL_NOT_FOUND"
console.log((await executor.execute({ id: "3", name: "add", arguments: {} })).error
.code); // 预期:"INVALID_ARGUMENTS"
})().catch(console.error);
// 关键机制:Registry 管静态能力,Executor 管校验、取消、异常隔离和结果边界。
class ToolRegistry {
constructor() {
this.tools = new Map();
}
register(tool) {
if (
!tool ||
typeof tool.name !== "string" ||
typeof tool.execute !== "function"
)
throw new TypeError("invalid tool");
if (this.tools.has(tool.name))
throw new Error(`duplicate tool: ${tool.name}`);
this.tools.set(tool.name, tool);
return this;
}
get(name) {
return this.tools.get(name);
}
list() {
return [...this.tools.values()].map(({ name, description, schema }) => ({
name,
description,
schema,
}));
}
}
class ToolExecutor {
constructor(registry) {
this.registry = registry;
}
async execute(call, context = {}) {
const base = { toolCallId: call?.id, toolName: call?.name };
const tool = this.registry.get(call?.name);
if (!tool)
return {
...base,
ok: false,
error: { code: "TOOL_NOT_FOUND", message: "tool not found" },
};
try {
if (context.signal?.aborted)
throw context.signal.reason || new Error("aborted");
const validation = tool.validate
? tool.validate(call.arguments)
: { ok: true };
if (validation === false || validation?.ok === false)
return {
...base,
ok: false,
error: {
code: "INVALID_ARGUMENTS",
message: validation?.message || "invalid arguments",
},
};
const output = await tool.execute(call.arguments, context);
return { ...base, ok: true, output };
} catch (error) {
const aborted = context.signal?.aborted;
return {
...base,
ok: false,
error: {
code: aborted ? "ABORTED" : "TOOL_EXECUTION_FAILED",
message: aborted ? "tool execution aborted" : "tool execution failed",
},
};
}
}
}
// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
const registry = new ToolRegistry().register({
name: "add",
validate: (x) => ({
ok: Number.isFinite(x?.a) && Number.isFinite(x?.b),
message: "a/b required",
}),
execute: ({ a, b }) => a + b,
});
const executor = new ToolExecutor(registry);
console.log(await executor.execute({ id: "1", name: "add", arguments: { a: 2, b: 3 } })); // 预期:{ toolCallId: "1", toolName: "add", ok: true, output: 5 }
console.log((await executor.execute({ id: "2", name: "missing" })).error.code); // 预期:"TOOL_NOT_FOUND"
console.log((await executor.execute({ id: "3", name: "add", arguments: {} })).error
.code); // 预期:"INVALID_ARGUMENTS"
})().catch(console.error);
复杂度分析
时间复杂度:O(1) 执行开销。单次调用只查找、校验并派发一个工具;工具自身耗时另计。
空间复杂度:O(T)。注册表保存 T 个工具描述,单次执行临时对象为常数级。