跳转到内容

人机协作

本指南演示如何使用 SDK 内置的人机协作(Human in the loop)支持,在有人为干预时暂停并恢复智能体运行。

目前的主要用例是对敏感工具执行进行审批。

你可以通过将 needsApproval 选项设置为 true,或设置为返回布尔值的异步函数,来定义需要审批的工具。

工具审批定义
import { tool } from '@openai/agents';
import z from 'zod';
const sensitiveTool = tool({
name: 'cancelOrder',
description: 'Cancel order',
parameters: z.object({
orderId: z.number(),
}),
// always requires approval
needsApproval: true,
execute: async ({ orderId }, args) => {
// prepare order return
},
});
const sendEmail = tool({
name: 'sendEmail',
description: 'Send an email',
parameters: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
needsApproval: async (_context, { subject }) => {
// check if the email is spam
return subject.includes('spam');
},
execute: async ({ to, subject, body }, args) => {
// send email
},
});
  1. 如果智能体决定调用一个或多个工具,它会通过评估 needsApproval 来检查该工具是否需要审批。
  2. 如果需要审批,智能体会检查审批是否已被授予或拒绝。
    • 如果尚未批准或拒绝,工具会向智能体返回一条静态消息,说明该工具调用无法执行。
    • 如果缺少批准/拒绝,将触发工具审批请求。
  3. 智能体会收集所有工具审批请求并中断执行。
  4. 如果发生中断,执行结果 中将包含一个描述待处理步骤的 interruptions 数组。当工具调用需要确认时,会出现一个 type: "tool_approval_item"ToolApprovalItem
  5. 你可以调用 result.state.approve(interruption)result.state.reject(interruption) 来批准或拒绝该工具调用。
  6. 处理完所有中断后,你可以将 result.state 传回 runner.run(agent, state) 来恢复执行,其中 agent 是触发整个运行的原始智能体。
  7. 流程从第 1 步重新开始。

下面是一个更完整的人机协作流程示例:在终端提示审批,并将状态临时存储在文件中。

人工干预
import { z } from 'zod';
import readline from 'node:readline/promises';
import fs from 'node:fs/promises';
import { Agent, run, tool, RunState, RunResult } from '@openai/agents';
const getWeatherTool = tool({
name: 'get_weather',
description: 'Get the weather for a given city',
parameters: z.object({
location: z.string(),
}),
needsApproval: async (_context, { location }) => {
// forces approval to look up the weather in San Francisco
return location === 'San Francisco';
},
execute: async ({ location }) => {
return `The weather in ${location} is sunny`;
},
});
const dataAgentTwo = new Agent({
name: 'Data agent',
instructions: 'You are a data agent',
handoffDescription: 'You know everything about the weather',
tools: [getWeatherTool],
});
const agent = new Agent({
name: 'Basic test agent',
instructions: 'You are a basic agent',
handoffs: [dataAgentTwo],
});
async function confirm(question: string) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const answer = await rl.question(`${question} (y/n): `);
const normalizedAnswer = answer.toLowerCase();
rl.close();
return normalizedAnswer === 'y' || normalizedAnswer === 'yes';
}
async function main() {
let result: RunResult<unknown, Agent<unknown, any>> = await run(
agent,
'What is the weather in Oakland and San Francisco?',
);
let hasInterruptions = result.interruptions?.length > 0;
while (hasInterruptions) {
// storing
await fs.writeFile(
'result.json',
JSON.stringify(result.state, null, 2),
'utf-8',
);
// from here on you could run things on a different thread/process
// reading later on
const storedState = await fs.readFile('result.json', 'utf-8');
const state = await RunState.fromString(agent, storedState);
for (const interruption of result.interruptions) {
const confirmed = await confirm(
`Agent ${interruption.agent.name} would like to use the tool ${interruption.rawItem.name} with "${interruption.rawItem.arguments}". Do you approve?`,
);
if (confirmed) {
state.approve(interruption);
} else {
state.reject(interruption);
}
}
// resume execution of the current state
result = await run(agent, state);
hasInterruptions = result.interruptions?.length > 0;
}
console.log(result.finalOutput);
}
main().catch((error) => {
console.dir(error, { depth: null });
});

参见完整示例脚本以获取可运行的端到端版本。

人机协作流程被设计为可以长时间中断,而无需让你的服务器持续运行。如果你需要终止请求并稍后继续,可以序列化状态并在之后恢复。

你可以使用 JSON.stringify(result.state) 序列化状态,并在稍后将序列化的状态传入 RunState.fromString(agent, serializedState) 以恢复,其中 agent 是触发整个运行的智能体实例。

这样你就可以将序列化的状态存储在数据库中,或与请求一起保存。

如果你的审批请求需要较长时间,并且你打算以有意义的方式为智能体定义做版本化或升级 Agents SDK 版本,我们目前建议你通过包别名并行安装两个版本的 Agents SDK,自行实现分支逻辑。

在实践中,这意味着为你的代码分配一个版本号,并将其与序列化状态一起存储,同时在反序列化时引导到你代码的正确版本。