数据截至 (上游 commit 2d68a10f8c15)
OpenAI Agents SDK (JS) — 架构与原理
30 秒导读: 这是 OpenAI 官方的 JS/TS 版「多 agent 编排框架」。你声明一个
Agent(指令 + 工具 + 交接 + 护栏),调一次run(agent, input),它内部跑一个循环:调模型 → 看模型想干嘛(说话?调工具?交接给别的 agent?)→ 执行 → 把结果喂回模型 → 直到出现最终答案。它 provider 无关,自带人审、会话记忆、流式、追踪。
1. 这是什么(零基础也能懂)
-
一句话定义: 一个把「LLM + 工具 + 多个子 agent」编排成可控工作流的运行时——你描述 agent,它负责一遍遍调模型、跑工具、按需在 agent 之间交接,直到产出最终答案。
-
解决什么问题 / 给谁用: 假设你要做一个客服 bot:它要能查订单(调工具)、遇到退款转给「退款专员 agent」(交接)、危险输入要拦截(护栏)、删数据前要人点「同意」(人审)。如果你手写,就是一堆「调一次模型、解析它要调哪个函数、调完再拼消息、再调模型」的胶水代码。这个 SDK 把那条胶水主线做成了引擎,你只填声明。
-
它能做什么(功能):
- 定义
Agent:指令(system prompt)、工具、可交接的子 agent、结构化输出类型。 - 跑
Agent为工具(agent-as-tool)或跑交接(handoff)两种「调别的 agent」方式。 - 工具:本地函数工具、计算 机操作(computer-use)、shell、apply_patch(改文件)、托管 MCP 工具。
- 护栏(guardrail):输入级、输出级、工具级的安全/合规检查,命中即中止。
- 人审(HITL):工具调用前暂停等人点同意/拒绝,状态可序列化成字符串存库、之后恢复。
- 会话记忆(Session)、流式事件(streaming)、全程追踪(tracing span)。
- 定义
-
用起来什么样: 一段最小代码就能感受主线(来自 README,可在克隆里跑):
// 示意,非源码(取自 README)
import { Agent, run } from '@openai/agents';
const agent = new Agent({
name: 'Assistant',
instructions: 'You are a helpful assistant',
});
const result = await run(agent, 'Write a haiku about recursion in programming.');
console.log(result.finalOutput);
你只给了 name + instructions + 一句话输入,run 内部就完成了「调模型 → 拿到文本 → 判定这是最终答案 → 返回」。
- 一句话直觉/类比: 把它想成一台 「带工具箱和分诊台的对话状态机」:模型是大脑,循环是心跳,每一拍问大脑「下一步干嘛」,然后引擎替它动手(调工具/换专家),把结果递回去,直到大脑说「我答完了」。
2. 顶层全景(它大概怎么转)
2.1 仓库布局:一个引擎 + 几层适配
这是个 pnpm monorepo,分层很清晰:核心引擎与具体模型供应商解耦。
| 包 | 干什么 | 在哪 |
|---|---|---|
@openai/agents-core | 引擎:Agent、Runner 主循环、工具、护栏、交接、人审、记忆、追踪的全部逻辑 | packages/agents-core/src |
@openai/agents-openai | OpenAI 模型适配:把引擎的 Model 接口接到 Responses / Chat Completions API | packages/agents-openai/src |
@openai/agents-realtime | 实时语音 agent(WebRTC / SIP / WebSocket 传输) | packages/agents-realtime/src |
@openai/agents-extensions | 第三方适配:Vercel AI SDK 模型、Twilio/Cloudflare 实时传输 | packages/agents-extensions/src |
@openai/agents | 元包:re-export 上面的东西,给用户一个 import 入口 | packages/agents |
关键点:引擎只认一个 Model 接口(packages/agents-core/src/model.ts:688 Model,两个方法 getResponse / getStreamedResponse)。换模型供应商 = 换一个实现这接口的对象,循环逻辑一行不动。
2.2 引擎内部:部件与职责
| 部件 | 干什么 | 文件 · 符号 |
|---|---|---|
Agent | 一个 agent 的声明:指令、工具、交接、输出类型;不含执行逻辑 | agent.ts:483 Agent |
run / Runner | 执行引擎:跑那条 while 循环 | run.ts:545 run、run.ts:573 Runner |
RunState | 一次 run 的全部可变状态(当前 agent、已生成 items、当前步骤、审批),可序列化 | runState.ts:1864 RunState |
RunContext | 跨工具/护栏共享的用户 context + usage + 审批表 | runContext.ts:189 RunContext |
processModelResponse | 把模型原始输出分类成「消息 / 函数调用 / 交接 / 计算机动作…」 | runner/modelOutputs.ts:688 |
resolveTurnAfterModelResponse | 执行那些动作、决定下一步(最终输出?再循环?交接?中断?) | runner/turnResolution.ts:1337 |
tool / handoff / guardrails | 三种挂件的工厂 | tool.ts:1860、handoff.ts:345、guardrail.ts |
2.3 主线走一遍(高层,不进代码)
这是整个 SDK 的「心跳」。从左到右是一拍(turn)内的流向,最右的分叉决定是否再来一拍:
输入(string / items / 恢复的 RunState)
│
▼
[准备这一拍] 会话记忆拼接 + 输入护栏(可并行) + 组装 system prompt/工具/交接
│
▼
[调模型] Model.getResponse(...) ← provider 无关
│
▼
[分类输出] processModelResponse: 消息? 函数调用? 交接? 计算机/shell 动作? MCP 审批?
│
▼
[执行 + 定下一步] resolveTurnAfterModelResponse
│
├─ 纯文本、无工具 → next_step_final_output ──► 跑输出护栏 ─► 返回 RunResult ✅
├─ 有工具调用 → 跑工具,把结果记进 items → next_step_run_again ──┐
├─ 模型要交接 → 换当前 agent → next_step_run_again ──────────┤ 回到[准备这一拍]
└─ 工具需人审/MCP 审批 → next_step_interruption ──► 返回 RunResult(带 interruptions) ⏸
「再来一拍」就是回到顶部,只是这次输入里多了上一拍的工具结果/交接消息。循环上限由 maxTurns 守住(runner/constants.ts DEFAULT_MAX_TURNS)。
3. 阅读地图(建议顺序)
这套文档按「先主线、再挂件、再高级特性」排:
01-agent-loop.md— 主线循环(必读)。讲Runner.run那个 while 怎么转:准备一拍 → 调模型 → 分类 → 决定下一步。理解了它,其它都是往这条线上挂东西。02-tools-and-agents-as-tools.md— 工具系统。tool()工厂怎么把一个函数变成模型可调用的工具(含 Zod 校验、超时、错误兜底),四类工具的区别,以及Agent.asTool如何「把一个 agent 当工具」(嵌套 run)。03-handoffs-and-guardrails.md— 交接与护栏。handoff(换掉当前 agent)vs agent-as-tool(不换,当子程序);三种护栏(输入/输出/工具)怎么并行跑、命中怎么中止。04-human-in-the-loop.md— 人审与状态序列化。工具审批怎么把循环暂停成next_step_interruption,RunState.toString()/fromString()怎么把整个 run 存盘、之后恢复继续。05-models-streaming-tracing.md— 模型/流式/追踪 + 兄弟包。Model接口、流式事件路径、tracing span,以及 sandbox-agent 和 realtime 语音两条扩展线。
4. 代码地图(导航索引)
| 主题 | 文件 | 符号 |
|---|---|---|
| 公共 API 出口 | packages/agents-core/src/index.ts | (re-export 全部) |
| Agent 声明 | agent.ts | Agent、AgentConfiguration、Agent.create |
| 顶层 run 函数 | run.ts | run、Runner、Runner.run |
| 非流式主循环 | run.ts | Runner.#runIndividualNonStream |
| 模型输出分类 | runner/modelOutputs.ts | processModelResponse、processModelResponseAsync |
| 一拍的下一步决议 | runner/turnResolution.ts | resolveTurnAfterModelResponse、resolveInterruptedTurn |
| 下一步类型 | runner/steps.ts | nextStepSchema、SingleStepResult |
| 工具工厂 | tool.ts | tool、computerTool、shellTool、applyPatchTool、hostedMcpTool |
| 工具执行/审批 | runner/toolExecution.ts | executeFunctionToolCalls、handleFunctionApproval |
| 交接 | handoff.ts | handoff、Handoff、getHandoff |
| 护栏 | guardrail.ts | defineInputGuardrail、defineOutputGuardrail |
| 运行状态 | runState.ts | RunState、RunState.toString、RunState.fromString |
| 运行 context/审批表 | runContext.ts | RunContext、approveTool、rejectTool、isToolApproved |
| 模型抽象 | model.ts | Model、ModelProvider、ModelRequest、ModelResponse |
| 追踪 | tracing/index.ts | addTraceProcessor、withTrace |