OpenAI Agents SDK消息类型与对话历史管理实战指南

将Agent交互视为事件序列而非聊天历史,是构建健壮Agent系统的关键思维转变。
本文基于OpenAI Agents SDK的开发经验,提出应将Agent交互视为事件序列而非简单聊天历史。文章系统梳理了静态与动态指令的使用、五种消息类型(Developer、User、Assistant、Function Call及其Output)的含义与关系,并重点指出两个关键陷阱:Agent不会自动维护对话历史(需手动传入),以及Function Call必须与Output通过call_id严格配对。最后介绍了动态插入Developer Message的高级技巧。
引言:重新思考Agent的对话历史
构建AI Agent应用时,很多开发者会把与Agent的交互当作普通的"聊天历史"来处理——用户说一句,助手回一句,如此往复。但这种理解方式往往会限制你构建更复杂Agent系统的能力。
本文基于OpenAI Agents SDK的实际开发经验,系统梳理Agent消息系统的核心机制。文章的核心观点是:不要将Agent交互视为聊天历史,而应将其视为事件序列(sequence of events)。理解这一点,能帮助你设计出更灵活、更健壮的Agent工作流。
这一思维转变有其深厚的工程背景。事件驱动架构(Event-Driven Architecture, EDA)是现代分布式系统的核心设计范式之一,系统组件通过发布和消费事件来协作,而非直接调用彼此的接口,这使得系统具备更强的解耦性和可扩展性。将Agent交互视为事件序列,本质上是将这一成熟的工程思想引入AI应用开发。对于Agent系统而言,这意味着触发Agent行动的不仅仅是用户的主动输入,还可以是定时器、Webhook回调、数据库变更通知等任何可以被序列化为消息的信号。
基础提示词:静态与动态指令
静态指令(Static Instructions)
在Agents SDK中,instructions参数本质上就是系统提示词(system prompt),用来定义Agent的行为准则:
agent = Agent(
name="pirate_agent",
model="gpt-4.1-mini",
instructions="Speak like a pirate"
)
运行Agent时通过Runner对象执行,推荐使用异步方式:
result = await Runner.run(starting_agent=agent, input="Write me a haiku")
这里starting_agent参数的命名值得留意——它暗示了Agent可以将任务移交(handoff)给其他Agent,即使当前只有一个Agent在工作。
动态指令(Dynamic Instructions)
当指令需要包含实时信息(比如当前时间、用户状态)时,静态字符串就不够用了。Agents SDK允许你传入一个函数作为指令生成器:
def time_based_instructions(context: RunContextWrapper, agent: Agent):
current_time = get_current_time()
if is_afternoon(current_time):
return "Speak like a pirate"
return "Do not speak like a pirate"

这个函数会在Agent每次被调用时执行,确保指令始终反映最新状态。虽然本例中没有用到context和agent参数,但在多Agent协作、状态共享等复杂场景中,它们非常关键。
五种消息类型:理解Agent的事件流
从聊天记录到事件流的思维转变
传统理解中,对话历史就是"用户说→助手回→用户说→助手回"的线性交替。但更准确的理解是:Agent本质上是一个工作流执行引擎,它基于各种触发器来执行不同任务。
触发器不一定是用户主动发送的消息——可能是用户打开了某个页面、系统定时任务到期、或者其他后台事件。用户甚至可能完全不知道自己触发了某个流程。

OpenAI定义的五种消息类型
1. Developer Message(开发者消息)
就是原来的System Message,OpenAI近期做了重命名。这一改动并非只是换个叫法——它反映了OpenAI对提示词层级控制权归属的重新定义。在OpenAI的模型规范(Model Spec)中,存在一个明确的信任层级:OpenAI训练时植入的价值观 > Developer Message > User Message。这一层级设计的目的是防止用户通过对话内容覆盖开发者设定的安全边界和行为约束,即所谓的"提示词注入攻击"(Prompt Injection)。在Agents SDK中对应instructions参数,用于指导Agent的行为边界、能力范围和限制条件。
2. User Message(用户消息)
对应input参数。可以是用户手动输入的文本,也可以是任何事件触发后传入的信息。
3. Assistant Message(助手消息)
由LLM生成的响应内容,通常是最终返回给用户的输出。
4. Function Call(函数调用)
由LLM生成的结构化JSON数据,包含工具名称、唯一的call_id以及调用参数。这是Agent调用外部工具的核心机制。
Function Calling(现称Tool Use)是OpenAI在2023年引入的核心能力,它让LLM从单纯的文本生成器进化为可以与外部世界交互的执行引擎。其底层机制是:开发者在API请求中以JSON Schema格式描述可用工具,模型在推理时决定是否调用工具以及如何填充参数,然后以结构化JSON的形式输出调用意图(而非直接执行)。实际执行由开发者代码完成,结果再以Function Call Output的形式回传给模型。call_id的设计是为了支持并行工具调用(Parallel Tool Calls)——模型可以在一次响应中同时发起多个工具调用,call_id确保每个调用的结果能被正确归因。
5. Function Call Output(函数调用输出)
工具执行后返回的结果,必须与对应的Function Call通过call_id配对。
一个容易混淆的细节:User、Developer和Assistant消息实际上都属于同一个message类型,只是通过role字段来区分身份。
对话历史管理的关键陷阱
陷阱一:Agent不会自动维护对话历史
这一点非常容易被忽略:Agents SDK的Runner不会自动保存和维护对话历史。每次调用Runner.run()都是独立的,如果你需要上下文连续性,必须手动传入之前的历史消息。
这一设计体现了无状态(Stateless)服务的工程哲学。无状态设计使得每次调用都是独立的、可重复的,极大简化了水平扩展和故障恢复的复杂度——任何一台服务器都可以处理任何一次请求,无需会话亲和性(Session Affinity)。代价是状态管理的责任转移给了调用方,开发者可以自由选择将历史消息存储在Redis、数据库或内存中,框架本身不做任何假设。这与有状态框架(如LangChain的Memory模块)形成鲜明对比,各有适用场景。

具体做法是使用to_input_list()方法将上一次的运行结果转换为可重新输入的格式:
# 将之前的结果转换为输入格式
previous_messages = result.to_input_list()
# 追加新消息并再次运行
new_input = previous_messages + [new_user_message]
result = await Runner.run(starting_agent=agent, input=new_input)

如果不做这一步,Agent每次运行都像是"失忆"状态,完全不记得之前的交互内容。
陷阱二:Function Call必须成对出现
这是另一个常见的报错来源:每个Function Call必须有对应的Function Call Output,而且两者的call_id必须完全一致。
# 正确示例
function_call = {
"type": "function_call",
"call_id": "call_123",
"name": "get_current_weather",
"arguments": {"location": "London"}
}
function_output = {
"type": "function_call_output",
"call_id": "call_123", # 必须与上面一致
"output": "London: It is raining"
}
如果缺少配对项或者call_id不匹配,OpenAI API会直接拒绝请求并返回错误。在手动拼接对话历史时尤其需要注意这一点。
实战技巧:动态插入开发者消息
一个实用但容易被忽视的高级模式:在对话流程的特定节点动态插入Developer Message。
举个典型场景——当Agent通过RAG工具检索到相关文档后,你可以在检索结果之后插入一条开发者消息来强化行为约束:
rag_reminder = {
"role\
相关推荐
教程攻略Cursor+Codex双IDE协同:开源项目二开实战方法论
基于实战经验总结的开源项目二次开发完整方法论,详解Cursor+Codex双IDE协同工作流,涵盖二开七环节、MVP验证、AI读源码技巧,帮助开发者三天跑通项目、两周完成业务集成。
教程攻略Cursor多Agent实战:50分钟搭建Next.js全栈博客
使用Cursor IDE多Agent协作模式,50分钟内从零搭建全栈博客。涵盖Next.js、Clerk认证、Supabase数据库集成,详解4个AI Agent分阶段开发流程与关键避坑经验。
教程攻略从零搭建AI软件工厂:Cursor工程师的多Agent协作实战经验
Cursor工程师Eric分享AI软件工厂构建实战:从自动化六层级、护栏设计、并行Agent管理到规模化扩展,详解如何用多Agent协作实现7×24小时高效软件开发。