近日,OpenAI 低调开源了一套完整的客服智能体(Agent)Demo,基于官方 openai-agents SDK 构建,集成了前端与后端的完整项目。表面上看,它只是一个航空客服模拟场景,但深入分析其源码,你会发现其中蕴含了多智能体协作、安全切换以及护栏设计的最佳实践,堪称一本教科书级的实战指南。
总体而言,这个项目非常值得深入研究和借鉴,尤其适合那些希望在真实业务场景中落地多智能体(Multi-Agent)系统的团队。下面是从源码中提炼出的几个核心设计模式。

核心架构
该 Demo 模拟了航空公司的客服场景,其架构采用了经典的编排器-工作者(Orchestrator-Worker)模式——许多大厂的产品也沿用这一思路,只是命名各异。下面直接看它的具体分工:
- 1 个调度智能体(Triage Agent): 唯一职责是理解用户意图,然后将任务“派发”给最合适的专家智能体。
- N 个专家智能体(Specialist Agents): 每个专家只负责一个垂直领域,例如:
Seat Booking Agent—— 预订座位Flight Status Agent—— 查询航班状态Cancellation Agent—— 取消航班FAQ Agent—— 回答常见问题
这种架构的优势非常明显:高内聚、低耦合。每个专家智能体的提示词、工具和知识都可以独立维护,扩展起来极为方便,互不干扰。
模式一:带状态的智能体切换 (Stateful Handoff)
多智能体协作的核心难题在于如何优雅地切换并传递上下文。OpenAI 在该 Demo 中采用了 handoff 方案来解决此问题。
Triage Agent 的核心配置如下,它通过 handoffs 列表定义了可以移交的任务目标:
triage_agent = Agent[AirlineAgentContext](
name="Triage Agent",
model="gpt-4.1",
handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.",
instructions=(
f"{RECOMMENDED_PROMPT_PREFIX} "
"You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents."
),
handoffs=[
flight_status_agent,
handoff(agent=cancellation_agent, on_handoff=on_cancellation_handoff),
faq_agent,
handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff),
],
input_guardrails=[relevance_guardrail, jailbreak_guardrail],
)
关键点在于 handoff() 函数中的 on_handoff 回调。例如,当用户想要更改座位时,Triage Agent 会将任务转交给 Seat Booking Agent,交接瞬间触发 on_seat_booking_handoff 函数:
async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext]) -> None:
"""Set a random flight number when handed off to the seat booking agent."""
context.context.flight_number = f"FLT-{random.randint(100, 999)}"
context.context.confirmation_number = "".join(random.choices(string.ascii_uppercase + string.digits, k=6))
该回调在“交接前”更新共享上下文(Context),自动生成航班号和确认码。这样一来,当 Seat Booking Agent 接手时,初始信息已经准备就绪,可以直接开始工作,无需再向用户询问。这种带状态的切换模式显著提升了用户体验和系统效率。
模式二:低成本的安全护栏 (Guardrail)
Agent 安全性一直是个难题。用户可能提出无关问题(例如“写一首关于草莓的诗”),或者尝试进行提示词注入(如“忽略你之前的指令……”)。OpenAI 的解决方案是设置安全护栏,并且采用了一种非常经济高效的方式。
他们创建了两个专门的护栏函数:
relevance_guardrail—— 用于判断输入是否与航空业务相关jailbreak_guardrail—— 用于检测是否试图进行越狱攻击
这两个护栏的背后,各自使用独立且更小、更便宜的模型(gpt-4.1-mini)。流程如下:
- 在主 Agent(例如 Seat Booking Agent)处理用户输入之前。
- 先用这个小模型快速检查输入的“安全性”和“相关性”。
- 如果检测到问题(例如
is_relevant: False),直接触发“Tripwire”(绊线),拒绝回答,并阻止请求进入更昂贵、更强大的主模型。
@input_guardrail(name="Relevance Guardrail")
async def relevance_guardrail(
context: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
) -> GuardrailFunctionOutput:
"""Guardrail to check if input is relevant to airline topics."""
result = await Runner.run(guardrail_agent, input, context=context.context)
final = result.final_output_as(RelevanceOutput)
return GuardrailFunctionOutput(output_info=final, tripwire_triggered=not final.is_relevant)
这种“前置检查”设计将安全逻辑与业务逻辑解耦,既保证了系统的鲁棒性,又节约了成本。
模式三:后端驱动的前端交互
还有一个有趣的设计:当 Seat Booking Agent 需要让用户选座时,它会调用一个名为 display_seat_map 的工具。该工具的实现非常简单,仅返回一个特殊字符串:DISPLAY_SEAT_MAP。
@function_tool(
name_override="display_seat_map",
description_override="Display an interactive seat map to the customer so they can choose a new seat."
)
async def display_seat_map(
context: RunContextWrapper[AirlineAgentContext]
) -> str:
"""Trigger the UI to show an interactive seat map to the customer."""
# The returned string will be interpreted by the UI to open the seat selector.
return "DISPLAY_SEAT_MAP"
前端 UI 监听到这个特殊字符串后,便会触发渲染“座位图”组件。这是一种非常巧妙的“后端驱动 UI”模式,使得智能体能够控制前端的复杂交互,而不仅仅是返回文本。
如何上手?
这个项目上手非常简单,步骤如下:
- 克隆仓库: 执行
git clone https://github.com/openai/openai-cs-agents-demo.git - 设置 API Key: 在
python-backend目录下创建.env文件,并填入你的OPENAI_API_KEY。 - 安装依赖: 分别进入
python-backend和ui目录,安装所需依赖。 - 运行: 在
ui目录下执行npm run dev,它将自动同时启动前端和后端。
