人机协作
本指南演示如何使用 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 },});- 如果智能体决定调用一个(或多个)工具,它会通过评估
needsApproval来检查该工具是否需要审批。 - 如果需要审批,智能体会检查审批是否已被批准或拒绝。
- 如果尚未批准或拒绝,该工具将向智能体返回一条静态消息,说明该工具调用无法执行。
- 如果缺少批准/拒绝,则会触发一个工具审批请求。
- 智能体会收集所有工具审批请求并中断执行。
- 如果发生任何中断,执行结果 将包含一个
interruptions数组,用于描述挂起的步骤。当某个工具调用需要确认时,会出现一个ToolApprovalItem,其type: "tool_approval_item"。 - 你可以调用
result.state.approve(interruption)或result.state.reject(interruption)来批准或拒绝该工具调用。 - 处理完所有中断后,你可以通过将
result.state传回runner.run(agent, state)来恢复执行,其中agent是触发整个运行的原始智能体。 - 然后流程从第 1 步重新开始。
下面是一个更完整的 Human in the loop 流程示例,它会在终端中提示审批,并将 state 临时存储到文件中。
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.name} with "${interruption.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 });});参见完整示例脚本以获得可运行的端到端版本。
处理较长审批时间
Section titled “处理较长审批时间”Human in the loop 流程被设计为可在较长时间内中断,而无需持续运行你的服务器。如果你需要先结束请求并在之后继续,可以序列化 state 并稍后恢复。
你可以使用 JSON.stringify(result.state) 序列化 state,并在之后通过将序列化的 state 传入 RunState.fromString(agent, serializedState) 来恢复,其中 agent 是触发整个运行的智能体实例。
这样你就可以将序列化后的 state 存储到数据库,或与请求一同存储。
挂起任务的版本管理
Section titled “挂起任务的版本管理”如果你的审批请求需要较长时间且你打算以有意义的方式对智能体定义进行版本化,或升级你的 Agents SDK 版本,我们目前建议你通过使用包别名并行安装两个版本的 Agents SDK,自行实现分支逻辑。
实际做法是为你的代码分配一个版本号,并将其与序列化的 state 一起存储,同时在反序列化时引导到你代码的正确版本。