OpenAI Assistants环境中的“assistants中的instructions”与“threads中的messages”
OpenAI Assistants API 中,instructions 和 messages 是两个关键参数,分别定义助手的全局行为准则和单次对话的上下文内容。instructions 作为助手的"出厂设置",作用于所有线程,需通过 assistants.update 修改;而 messages 作为线程的"聊天记录",仅影响当前对话,通过 threads
·
在 OpenAI Assistants API 中,client.beta.assistants.create 的 instructions 和 client.beta.threads.create 的 messages 是两个核心参数,二者定位、作用和生命周期完全不同,前者是助手的“系统级行为准则”,后者是对话的“上下文内容载体”。
一、核心维度对比表
| 对比维度 | instructions(助手创建参数) |
messages(线程创建参数) |
|---|---|---|
| 核心定位 | 定义助手的角色、能力边界、行为规则,是助手的“底层逻辑” | 承载用户与助手的对话内容,是助手生成回复的“直接依据” |
| 作用对象 | 作用于整个 Assistant 实例,所有使用该助手的线程都会遵循 | 作用于单个 Thread 实例,仅影响当前线程的对话流程 |
| 生命周期 | 与 Assistant 绑定,修改前永久生效 | 与 Thread 绑定,随线程的对话轮次动态追加/更新 |
| 内容性质 | 偏向指令性、规则性、全局性,无时效性 | 偏向交互性、场景性、时效性,是对话上下文 |
| 修改方式 | 需调用 assistants.update 接口修改 |
需调用 threads.messages.create 接口追加新消息 |
| 是否必填 | 非必填(但不填的话助手行为无约束) | 非必填(线程可先创建,后续再添加消息) |
二、具体解释与场景举例
假设你要开发一个 Scratch 少儿编程教学助手,分别看两个参数的用法:
1. instructions:助手的“教学手册”
instructions 是给助手的全局指令,告诉它“你是谁、该做什么、不能做什么”,贯穿这个助手的所有对话。
import openai
client = openai.OpenAI()
# 创建 Scratch 教学助手时的 instructions
assistant = client.beta.assistants.create(
name="Scratch少儿编程导师",
instructions="""你是一个面向小学高年级学生的Scratch编程老师,需要做到:
1. 用通俗的语言解释编程概念,避免专业术语;
2. 回答问题时必须附带简单的Scratch积木示例;
3. 遇到学生问非编程问题时,引导回到Scratch学习;
4. 鼓励学生动手尝试,拒绝直接给出完整项目代码。""",
model="gpt-4o"
)
关键点:
- 这个指令对所有使用该助手的线程都生效,比如学生A问“怎么移动角色”、学生B问“怎么加音效”,助手都会遵循同一个教学规则。
- 修改指令需要调用
assistants.update,比如你想新增“允许给低年级学生提供分步代码”,修改后所有后续对话都会按新规则执行。
2. messages:线程的“对话记录”
messages 是单个对话线程的内容,包含用户的提问、助手的回复,是助手生成下一轮回复的上下文。
一个线程对应一个学生的单次对话会话,你可以先创建空线程,再添加消息:
# 1. 创建一个空线程(对应学生小明的对话)
thread = client.beta.threads.create()
# 2. 给线程添加第一条用户消息(小明的问题)
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="怎么让小猫在Scratch里走直线?"
)
# 3. 运行助手,生成回复(助手会结合 instructions + 这条 message 输出内容)
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id
)
关键点:
- 这条
message只属于小明的这个线程,不会影响其他学生的对话。 - 后续小明继续问“怎么让小猫碰到边缘就反弹”,你需要调用
threads.messages.create追加新消息,助手会基于历史所有 messages + instructions 生成回复。
三、要点
- 关联逻辑:
instructions是助手的“行为框架”,messages是助手的“对话素材”;助手生成回复时,会先遵循 instructions 的规则,再基于 messages 的上下文输出内容。 - 使用建议
- 写
instructions时要明确、可落地,避免模糊表述(比如不要写“做一个好老师”,要写“用小学生能懂的话解释”)。 - 管理
messages时要控制上下文长度,如果对话轮次太多,可清理无关历史消息,避免超出模型上下文窗口。
- 写
| 一句话总结 | 类比 |
|---|---|
instructions 是助手的“出厂设置” |
老师的教学大纲 |
messages 是单次对话的“聊天记录” |
老师和一个学生的课堂对话 |
四、线程(Thread)中的 messages
- 线程 ID(thread_id):一旦创建永久不变,多次 run 同一个线程只会生成新的
run_id,不会改变thread_id; - Messages 特性:同一线程内的
messages是追加式存储(包含用户发送的消息 + 助手返回的回复),再次 run 时会自动衔接所有历史messages; - Messages 可修改:在再次 run 线程前,你可以追加新消息、甚至修改/删除已有消息,修改后 run 会基于最新的
messages上下文生成回复。
完整代码示例
以下代码会完整演示:创建线程 → 第一次 run → 查看追加的 messages → 第二次追加消息并 run → 验证线程 ID 不变、messages 衔接 → 演示修改已有 messages 后再次 run。
import openai
import time
# 1. 初始化客户端(替换为你的 API Key)
client = openai.OpenAI(api_key="你的 OpenAI API Key")
# 2. 创建一个简单的助手(用于测试)
assistant = client.beta.assistants.create(
name="测试助手",
instructions="你是一个数学老师,回答问题时要简洁明了,基于上下文衔接回答。",
model="gpt-4o"
)
print(f"创建的助手 ID:{assistant.id}\n")
# 3. 创建一个线程(核心:这个 thread_id 全程不变)
thread = client.beta.threads.create()
THREAD_ID = thread.id # 保存线程 ID,后续所有操作都用这个
print(f"创建的线程 ID:{THREAD_ID}\n")
# ---------------------- 第一步:第一次 run 线程 ----------------------
# 3.1 给线程添加第一条用户消息
message_1 = client.beta.threads.messages.create(
thread_id=THREAD_ID,
role="user",
content="请问 1+1 等于多少?"
)
print(f"第一条用户消息 ID:{message_1.id}")
# 3.2 第一次运行助手(生成 run_id,每次 run 都会生成新的)
run_1 = client.beta.threads.runs.create(
thread_id=THREAD_ID,
assistant_id=assistant.id
)
print(f"第一次 run ID:{run_1.id}\n")
# 3.3 等待 run 完成(必须等待,否则获取不到助手回复)
def wait_for_run_completion(client, thread_id, run_id):
while True:
run = client.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run_id)
if run.status == "completed":
return run
time.sleep(1)
wait_for_run_completion(client, THREAD_ID, run_1.id)
# 3.4 获取第一次 run 后的所有 messages(验证:包含用户消息 + 助手回复)
messages_after_run1 = client.beta.threads.messages.list(thread_id=THREAD_ID)
print("===== 第一次 run 后的 messages 列表 =====")
for msg in messages_after_run1.data:
print(f"角色:{msg.role} | 内容:{msg.content[0].text.value}")
print("\n")
# ---------------------- 第二步:再次 run 线程(追加消息) ----------------------
# 4.1 追加第二条用户消息(衔接上下文提问)
message_2 = client.beta.threads.messages.create(
thread_id=THREAD_ID,
role="user",
content="那这个结果乘以 3 是多少?" # 助手会知道“这个结果”指的是 1+1=2
)
print(f"第二条用户消息 ID:{message_2.id}")
# 4.2 第二次运行助手(run_id 新生成,thread_id 不变)
run_2 = client.beta.threads.runs.create(
thread_id=THREAD_ID,
assistant_id=assistant.id
)
print(f"第二次 run ID:{run_2.id}(与第一次 run ID 不同)")
print(f"线程 ID 仍为:{THREAD_ID}(与之前一致)\n")
# 4.3 等待第二次 run 完成
wait_for_run_completion(client, THREAD_ID, run_2.id)
# 4.4 获取第二次 run 后的所有 messages(验证:追加了第二条用户消息 + 新的助手回复)
messages_after_run2 = client.beta.threads.messages.list(thread_id=THREAD_ID)
print("===== 第二次 run 后的 messages 列表(追加式) =====")
for msg in messages_after_run2.data:
print(f"角色:{msg.role} | 内容:{msg.content[0].text.value}")
print("\n")
# ---------------------- 第三步:演示修改 messages 后再次 run ----------------------
# 5.1 修改第一条用户消息(比如修正问题)
# 先找到第一条消息的 ID(就是 message_1.id)
updated_message = client.beta.threads.messages.update(
thread_id=THREAD_ID,
message_id=message_1.id,
content="请问 2+2 等于多少?" # 把原来的 1+1 改成 2+2
)
print(f"修改后的第一条消息内容:{updated_message.content[0].text.value}\n")
# 5.2 第三次运行助手(基于修改后的 messages 重新回答)
run_3 = client.beta.threads.runs.create(
thread_id=THREAD_ID,
assistant_id=assistant.id
)
print(f"第三次 run ID:{run_3.id}")
wait_for_run_completion(client, THREAD_ID, run_3.id)
# 5.3 获取修改后 run 的 messages(验证:助手会基于修改后的问题重新回答)
messages_after_run3 = client.beta.threads.messages.list(thread_id=THREAD_ID)
print("===== 修改消息后第三次 run 的 messages 列表 =====")
for msg in messages_after_run3.data:
print(f"角色:{msg.role} | 内容:{msg.content[0].text.value}")
# 清理测试资源(可选)
# client.beta.assistants.delete(assistant.id)
# client.beta.threads.delete(THREAD_ID)
代码运行结果说明
假设你运行代码后,会看到以下核心结果(示例):
- 线程 ID 不变:全程
THREAD_ID都是同一个值,三次 run 的run_id各不相同; - Messages 追加存储:
- 第一次 run 后:messages 包含「用户:1+1等于多少?」+「助手:1+1等于2」;
- 第二次 run 后:messages 追加了「用户:那这个结果乘以3是多少?」+「助手:2×3=6」;
- 修改 messages 生效:
- 第三次 run 后,助手会基于修改后的「2+2等于多少?」重新回答(结果为4),且后续的「乘以3」会变成 4×3=12。
细节解释
- run_id vs thread_id:
thread_id:线程的唯一标识,代表一个“对话会话”,创建后永不改变;run_id:每次调用threads.runs.create都会生成新的 ID,代表“一次助手的执行过程”,用于查询执行状态、取消执行等。
- Messages 排序:
threads.messages.list返回的消息默认是倒序(最新的在前),但助手生成回复时会按正序读取所有历史消息; - 修改/删除 Messages:除了
update,你也可以用client.beta.threads.messages.delete(message_id)删除某条消息,修改/删除后再次 run,助手会基于最新的消息列表生成回复。
要点
- 线程 ID 固定:
thread_id一旦创建不会变化,多次 run 仅生成新的run_id; - Messages 追加式存储:同一线程内的
messages会保留所有用户/助手消息,再次 run 会自动衔接上下文; - Messages 可灵活修改:在 run 之前可以更新/删除已有消息,修改后的内容会影响后续 run 的回复结果。
更多推荐

所有评论(0)