编码母题 / P1 / Agent 运行时 / 05
支持权限与人工审批的工具执行器
先读清题意与边界,独立尝试,再展开思路并对照可运行的参考代码。
重要程度高价值难度困难预计完成50–70 分钟
题目是什么
工具按权限和风险等级执行。高风险调用先产生审批单;批准后仅允许原参数、未过期、具备权限的一次执行。
输入输出示例
批准了参数 A 后改成参数 B,恢复执行必须拒绝。
练习起手式与验证
复制代码后补全空方法,再运行下方三组验证;复杂题所需的辅助类型与输入准备一并放在代码块中。
/*
* 题目:支持权限与人工审批的工具执行器
* 要求:工具按权限和风险等级执行。高风险调用先产生审批单;批准后仅允许原参数、未过期、
* 具备权限的一次执行。
* 示例:批准了参数 A 后改成参数 B,恢复执行必须拒绝。
*/
const stable = (value) =>
value && typeof value === "object"
? Array.isArray(value)
? `[${value.map(stable)}]`
: `{${Object.keys(value)
.sort()
.map((k) => `${JSON.stringify(k)}:${stable(value[k])}`)
.join(",")}}`
: JSON.stringify(value);
class ApprovalStore {
constructor() {}
create(call, expiresAt) {}
decide(id, approved) {}
consume(id, call, now) {}
}
class ApprovedToolExecutor {
constructor(registry, approvals, now = Date.now) {}
async execute(call, context) {}
}
(async () => {
let effects = 0;
const registry = new Map([
[
"pay",
{ risk: "high", requiredPermissions: ["pay"], execute: () => ++effects },
],
]);
const store = new ApprovalStore();
const executor = new ApprovedToolExecutor(registry, store, () => 100);
const call = { id: "t1", name: "pay", arguments: { amount: 10 } };
const pending = await executor.execute(call, { permissions: ["pay"] });
console.log(pending.status); // 预期:"waiting_approval"
store.decide(pending.approvalId, true);
console.log((
await executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})
).output); // 预期:1
console.log(await Promise.resolve(executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})).then(() => false, () => true)); // 预期:true(拒绝)
})().catch(console.error);
/*
* 题目:支持权限与人工审批的工具执行器
* 要求:工具按权限和风险等级执行。高风险调用先产生审批单;批准后仅允许原参数、未过期、
* 具备权限的一次执行。
* 示例:批准了参数 A 后改成参数 B,恢复执行必须拒绝。
*/
const stable = (value) =>
value && typeof value === "object"
? Array.isArray(value)
? `[${value.map(stable)}]`
: `{${Object.keys(value)
.sort()
.map((k) => `${JSON.stringify(k)}:${stable(value[k])}`)
.join(",")}}`
: JSON.stringify(value);
class ApprovalStore {
constructor() {}
create(call, expiresAt) {}
decide(id, approved) {}
consume(id, call, now) {}
}
class ApprovedToolExecutor {
constructor(registry, approvals, now = Date.now) {}
async execute(call, context) {}
}
(async () => {
let effects = 0;
const registry = new Map([
[
"pay",
{ risk: "high", requiredPermissions: ["pay"], execute: () => ++effects },
],
]);
const store = new ApprovalStore();
const executor = new ApprovedToolExecutor(registry, store, () => 100);
const call = { id: "t1", name: "pay", arguments: { amount: 10 } };
const pending = await executor.execute(call, { permissions: ["pay"] });
console.log(pending.status); // 预期:"waiting_approval"
store.decide(pending.approvalId, true);
console.log((
await executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})
).output); // 预期:1
console.log(await Promise.resolve(executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})).then(() => false, () => true)); // 预期:true(拒绝)
})().catch(console.error);解题思路
展开思路、不变量与常见边界
关键切入点:审批绑定完整调用摘要;consume 原子标记防重复,并在恢复时重新校验权限。
核心模型:审批绑定 toolCall 的稳定摘要,而不是只绑定工具名;恢复时重新校验权限 和摘要。状态 approved 与 executed 分离,并以 consume 原子操作防止重复副作用。 本例使用稳定 JSON 序列化与内存仓库,生产环境需数据库事务、强哈希和审计身份。
参考代码与验证
“关键机制”注释用于解释实现,下载链接提供未经页面注释修改的原始 JS 文件。
const stable = (value) =>
value && typeof value === "object"
? Array.isArray(value)
? `[${value.map(stable)}]`
: `{${Object.keys(value)
.sort()
.map((k) => `${JSON.stringify(k)}:${stable(value[k])}`)
.join(",")}}`
: JSON.stringify(value);
// 关键机制:审批绑定完整调用摘要;consume 原子标记防重复,并在恢复时重新校验权限。
class ApprovalStore {
constructor() {
this.items = new Map();
this.sequence = 0;
}
create(call, expiresAt) {
const item = {
id: `a${++this.sequence}`,
digest: stable(call),
status: "pending",
expiresAt,
};
this.items.set(item.id, item);
return { ...item };
}
decide(id, approved) {
const item = this.items.get(id);
if (!item || item.status !== "pending")
throw new Error("approval unavailable");
item.status = approved ? "approved" : "rejected";
}
consume(id, call, now) {
const item = this.items.get(id);
if (!item || item.status !== "approved")
throw new Error("approval not granted");
if (item.expiresAt <= now) {
item.status = "expired";
throw new Error("approval expired");
}
if (item.digest !== stable(call))
throw new Error("approved call was changed");
item.status = "executed";
}
}
class ApprovedToolExecutor {
constructor(registry, approvals, now = Date.now) {
this.registry = registry;
this.approvals = approvals;
this.now = now;
}
async execute(call, context) {
const tool = this.registry.get(call.name);
if (!tool) return { status: "error", code: "TOOL_NOT_FOUND" };
const permissions = new Set(context.permissions || []);
if ((tool.requiredPermissions || []).some((p) => !permissions.has(p)))
return { status: "error", code: "FORBIDDEN" };
if (tool.risk === "high" && !context.approvalId) {
const approval = this.approvals.create(
call,
this.now() + (context.approvalTtl || 300000),
);
return { status: "waiting_approval", approvalId: approval.id };
}
if (tool.risk === "high")
this.approvals.consume(context.approvalId, call, this.now());
return {
status: "completed",
output: await tool.execute(call.arguments, context),
};
}
}
// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
let effects = 0;
const registry = new Map([
[
"pay",
{ risk: "high", requiredPermissions: ["pay"], execute: () => ++effects },
],
]);
const store = new ApprovalStore();
const executor = new ApprovedToolExecutor(registry, store, () => 100);
const call = { id: "t1", name: "pay", arguments: { amount: 10 } };
const pending = await executor.execute(call, { permissions: ["pay"] });
console.log(pending.status); // 预期:"waiting_approval"
store.decide(pending.approvalId, true);
console.log((
await executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})
).output); // 预期:1
console.log(await Promise.resolve(executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})).then(() => false, () => true)); // 预期:true(拒绝)
console.log(effects); // 预期:1
})().catch(console.error);
const stable = (value) =>
value && typeof value === "object"
? Array.isArray(value)
? `[${value.map(stable)}]`
: `{${Object.keys(value)
.sort()
.map((k) => `${JSON.stringify(k)}:${stable(value[k])}`)
.join(",")}}`
: JSON.stringify(value);
// 关键机制:审批绑定完整调用摘要;consume 原子标记防重复,并在恢复时重新校验权限。
class ApprovalStore {
constructor() {
this.items = new Map();
this.sequence = 0;
}
create(call, expiresAt) {
const item = {
id: `a${++this.sequence}`,
digest: stable(call),
status: "pending",
expiresAt,
};
this.items.set(item.id, item);
return { ...item };
}
decide(id, approved) {
const item = this.items.get(id);
if (!item || item.status !== "pending")
throw new Error("approval unavailable");
item.status = approved ? "approved" : "rejected";
}
consume(id, call, now) {
const item = this.items.get(id);
if (!item || item.status !== "approved")
throw new Error("approval not granted");
if (item.expiresAt <= now) {
item.status = "expired";
throw new Error("approval expired");
}
if (item.digest !== stable(call))
throw new Error("approved call was changed");
item.status = "executed";
}
}
class ApprovedToolExecutor {
constructor(registry, approvals, now = Date.now) {
this.registry = registry;
this.approvals = approvals;
this.now = now;
}
async execute(call, context) {
const tool = this.registry.get(call.name);
if (!tool) return { status: "error", code: "TOOL_NOT_FOUND" };
const permissions = new Set(context.permissions || []);
if ((tool.requiredPermissions || []).some((p) => !permissions.has(p)))
return { status: "error", code: "FORBIDDEN" };
if (tool.risk === "high" && !context.approvalId) {
const approval = this.approvals.create(
call,
this.now() + (context.approvalTtl || 300000),
);
return { status: "waiting_approval", approvalId: approval.id };
}
if (tool.risk === "high")
this.approvals.consume(context.approvalId, call, this.now());
return {
status: "completed",
output: await tool.execute(call.arguments, context),
};
}
}
// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
let effects = 0;
const registry = new Map([
[
"pay",
{ risk: "high", requiredPermissions: ["pay"], execute: () => ++effects },
],
]);
const store = new ApprovalStore();
const executor = new ApprovedToolExecutor(registry, store, () => 100);
const call = { id: "t1", name: "pay", arguments: { amount: 10 } };
const pending = await executor.execute(call, { permissions: ["pay"] });
console.log(pending.status); // 预期:"waiting_approval"
store.decide(pending.approvalId, true);
console.log((
await executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})
).output); // 预期:1
console.log(await Promise.resolve(executor.execute(call, {
permissions: ["pay"],
approvalId: pending.approvalId,
})).then(() => false, () => true)); // 预期:true(拒绝)
console.log(effects); // 预期:1
})().catch(console.error);
复杂度分析
时间复杂度:O(P log P)/审批校验。稳定摘要需遍历参数并对对象键排序;执行工具耗时另计。
空间复杂度:O(A+P)。保存 A 张审批单,并为当前参数构造至多 P 大小的摘要。