OpenAI Assistants API架构:运动员threads有装备assistants,比赛run时,由messages.list记事,由runs.list裁判
前文“OpenAI Assistants工作”中我们学习了threads、assistants 、run,本文我们进一步从架构角度去学习它们。
为了学习OpenAI Assistants API 的实际工作机制,我们做个有趣的类比:把threads视为运动员,assistants就是运动装备,让其比赛就是run起来,那么client.beta.threads.messages.list则是赛事记录员,而messages则是运动员的履历,比赛会让履历更丰富。
即:
threads= 运动员(独立个体,有专属履历);assistants= 专属教练+装备包(定战术、给工具,而非单纯工具);run= 一次完整的比赛/训练回合(上场执行并产生结果);threads.messages.list= 赛事记录员(主动调取并查看运动员的所有履历);threads.runs.list= 赛事裁判员(监督执行、判定结果、维护规则)
这个类比能完美覆盖 API 的核心特性:线程独立性、助手的规则约束、run 的执行过程、messages 的追加式存储,以及 list 接口的查询能力。
类比关系
为了让角色关系更清晰,先把所有核心 API 和运动场景的类比汇总:
| API 组件 | 运动赛事类比 | 核心职责 |
|---|---|---|
threads |
运动员 | 独立的对话主体,有专属的比赛履历(messages) |
assistants |
教练+装备包 | 定战术(instructions)、提供工具,指导运动员的每一轮比赛 |
run |
单轮比赛/训练回合 | 运动员在教练指导下完成一次完整的执行过程 |
threads.messages.list |
赛事记录员 | 记录运动员的所有对话/动作(messages),只记“内容”不判“结果” |
threads.runs.list |
赛事裁判员 | 监督/记录/判定每一轮比赛(run)的执行状态、结果、合规性,核心管“执行过程” |
一、 threads = 运动员:独立的个体,有专属履历
每个 thread 对应一个独立的运动员,运动员的核心特征是:
- 独立性:运动员A(线程1)是100米短跑选手,运动员B(线程2)是跳远选手,二者的比赛过程、成绩记录完全分开;
- 有专属履历:每个运动员都有自己的“比赛记录本”(
messages),记录了他每一次的上场动作(用户消息)和比赛结果(助手回复)。
代码+场景例子:
# 创建2个独立的“运动员”(线程):短跑运动员A、跳远运动员B
thread_sprinter = client.beta.threads.create() # 短跑运动员A(线程ID固定)
thread_jumper = client.beta.threads.create() # 跳远运动员B(线程ID固定)
这两个线程(运动员)的“比赛记录”(messages)完全隔离,就算用同一个教练(assistant),也不会互相干扰。
二、assistants = 专属教练 + 定制装备包:定规则、给工具
你把 assistants 比作“运动工具”偏单薄,它更像为特定运动员定制的教练+装备:
- 教练的角色:通过
instructions给运动员定“战术规则”(比如“短跑要先起跑后冲刺,步频不低于180”); - 装备的角色:给运动员提供比赛所需的工具(比如模型版本、代码解释器、文件检索能力)。
一个教练(assistant)可以指导多个运动员(threads),但所有被指导的运动员都会遵循这个教练的战术规则。
代码+场景例子:
# 创建“短跑专属教练”(assistant):定战术+给装备
sprinter_coach = client.beta.assistants.create(
name="短跑教练",
# 教练的战术规则(instructions)
instructions="""你是100米短跑教练,指导运动员时要做到:
1. 每次指导必须先分析运动员上一次的成绩;
2. 给出的调整建议要具体(比如“起跑延迟减少0.1秒”);
3. 只回答短跑相关问题,不聊其他项目。""",
model="gpt-4o", # 教练的“基础装备”(模型)
tools=[{"type": "code_interpreter"}] # 教练的“辅助装备”(成绩计算工具)
)
这个教练(assistant)可以指导所有短跑运动员(threads),比如运动员A和运动员C都是短跑选手,都会遵循这个教练的战术规则。
三、run = 完成一次比赛/训练回合:上场执行并产生结果
run 不是单纯“让运动员上场”,而是运动员在教练指导下,结合自己的过往履历(messages),完成一次完整的比赛/训练,产生新的结果。
每次 run 都会:
- 读取运动员的历史履历(
messages); - 遵循教练的战术规则(
instructions); - 完成一次动作,生成新的结果(助手回复),并追加到履历中。
代码+场景例子:
# 1. 给短跑运动员A(线程)添加“上场动作”(用户消息:第一次训练请求)
client.beta.threads.messages.create(
thread_id=thread_sprinter.id,
role="user",
content="我刚才跑100米用了10.8秒,怎么提升?" # 运动员的“上场动作/成绩反馈”
)
# 2. 让运动员A完成一次训练回合(run):教练指导+结合履历出建议
run_1 = client.beta.threads.runs.create(
thread_id=thread_sprinter.id,
assistant_id=sprinter_coach.id
)
# 等待训练完成,教练给出调整建议(助手回复),并追加到运动员A的履历中
wait_for_run_completion(client, thread_sprinter.id, run_1.id)
这次 run 后,运动员A的履历(messages)会新增一条“教练回复”:比如“你的起跑延迟0.2秒,建议调整起跑姿势,目标把延迟降到0.1秒”。
四、threads.messages.list = 赛事记录员:调取并查看完整履历
“旁观者”的类比偏被动,而 list 接口是主动的“记录员” —— 它能调取并查看某一位运动员(线程)的所有比赛/训练记录,包括运动员的每一次动作(用户消息)和教练的每一次指导(助手回复),且能按时间顺序完整呈现。
代码+场景例子:
# 记录员调取短跑运动员A的所有履历(messages)
all_records = client.beta.threads.messages.list(thread_id=thread_sprinter.id)
# 打印所有记录(按时间倒序,最新的在前)
print("短跑运动员A的训练履历:")
for record in all_records.data:
role = "运动员" if record.role == "user" else "教练"
content = record.content[0].text.value
print(f"{role}:{content}")
输出结果(模拟):
短跑运动员A的训练履历:
教练:你的起跑延迟0.2秒,建议调整起跑姿势,目标把延迟降到0.1秒
运动员:我刚才跑100米用了10.8秒,怎么提升?
记录员(list)不仅能“看”,还能完整获取所有记录,这是比“旁观者”更精准的定位。
五、threads.runs.list = 赛事裁判员
「裁判员(runs.list)」的功能定位——它不关心“运动员说了什么(messages)”,只关心“运动员的每一轮比赛(Run)是否合规、是否完成、结果如何”,这正是裁判员和记录员最核心的区别。
「裁判员(runs.list)」的核心价值:
- 状态监督:实时监控线程下所有 Run 的执行状态(进行中/完成/失败),对应裁判员“看住比赛过程”;
- 结果判定:识别 Run 的成功/失败状态,并记录失败原因,对应裁判员“判定比赛有效性”;
- 历史追溯:保存所有 Run 的执行记录(时间、关联助手、状态),对应裁判员“存档比赛记录”;
- 规则维护:识别并等待活跃 Run 完成,避免并行执行,对应裁判员“维护赛事规则”。
client.beta.threads.runs.list(thread_id=thread.id) 的核心是查询指定线程下所有 Run 的完整执行信息,对应裁判员的上述4个核心职责。
作用1:监督所有比赛回合(Run)的执行状态
裁判员的首要职责是“看住每一轮比赛的进行情况”——runs.list 能列出线程下所有 Run 的状态(进行中/完成/失败/取消),确保没有“违规并行比赛”(同一运动员同时参加多轮比赛)。
场景类比:短跑运动员(线程)正在进行第一轮100米比赛(Run1,状态in_progress),裁判员会实时监控,禁止他同时参加第二轮比赛(Run2),直到第一轮结束。
代码示例:
import openai
import time
client = openai.OpenAI(api_key="你的API Key")
# 1. 创建运动员(线程)和教练(助手)
thread = client.beta.threads.create()
assistant = client.beta.assistants.create(
name="短跑教练",
instructions="指导运动员完成100米训练",
model="gpt-4o"
)
# 2. 发起第一轮比赛(Run1,模拟执行中)
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="我要跑100米,指导我起跑"
)
run1 = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id)
# 3. 裁判员(runs.list)检查所有Run的状态
runs = client.beta.threads.runs.list(thread_id=thread.id).data
print("=== 裁判员检查比赛状态 ===")
for run in runs:
print(f"比赛ID(Run ID):{run.id}")
print(f"比赛状态:{run.status}") # 输出:in_progress(进行中)
print(f"关联教练(Assistant ID):{run.assistant_id}")
# (等待Run1完成后,裁判员状态会更新为succeeded)
while client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run1.id).status != "succeeded":
time.sleep(2)
runs_after_complete = client.beta.threads.runs.list(thread_id=thread.id).data
print("\n=== 比赛完成后,裁判员更新状态 ===")
print(f"Run1 最终状态:{runs_after_complete[0].status}") # 输出:succeeded
作用2:判定比赛结果的有效性(成功/失败/违规)
裁判员的核心职责是“判定比赛是否有效”——runs.list 能获取 Run 的最终状态(succeeded/failed/cancelled),甚至失败原因,就像裁判员判定“运动员犯规(Run失败)”并说明违规原因。
场景类比:运动员比赛时踩线(Run执行出错,比如工具调用失败),裁判员判定该轮比赛无效,并记录“踩线违规”(失败原因)。
代码示例:
# 模拟一个会失败的Run(比如绑定不存在的工具)
assistant_bad = client.beta.assistants.create(
name="错误教练",
instructions="指导训练",
model="gpt-4o",
tools=[{"type": "invalid_tool"}] # 无效工具,导致Run失败
)
# 发起失败的Run
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="帮我分析训练数据"
)
run_failed = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant_bad.id
)
# 等待Run失败
time.sleep(5)
# 裁判员(runs.list)判定结果并记录失败原因
runs = client.beta.threads.runs.list(thread_id=thread.id).data
run_failed_detail = [r for r in runs if r.id == run_failed.id][0]
print("=== 裁判员判定比赛结果 ===")
print(f"比赛状态:{run_failed_detail.status}") # 输出:failed
# 查看失败原因(裁判员记录的违规详情)
if run_failed_detail.last_error:
print(f"失败原因:{run_failed_detail.last_error.message}")
# 输出示例:Invalid tool type 'invalid_tool'. Valid types are: code_interpreter, retrieval, function
作用3:追溯所有比赛的历史记录(复盘/申诉)
裁判员会“存档所有比赛的判定记录”——runs.list 能查询线程下所有 Run 的完整历史(执行时间、状态、关联助手/消息),就像裁判员保留比赛录像,供后续复盘或申诉。
场景类比:教练对比赛结果有异议,裁判员调出历史记录(所有Run的执行信息),复盘每一轮比赛的判定过程。
代码示例:
# 给线程发起多个Run,模拟多轮比赛
# 第1轮:成功的Run
client.beta.threads.messages.create(thread_id=thread.id, role="user", content="第一轮训练")
run1 = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id)
time.sleep(3)
# 第2轮:失败的Run
client.beta.threads.messages.create(thread_id=thread.id, role="user", content="第二轮训练")
run2 = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant_bad.id)
time.sleep(3)
# 第3轮:取消的Run
client.beta.threads.messages.create(thread_id=thread.id, role="user", content="第三轮训练")
run3 = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id)
client.beta.threads.runs.cancel(thread_id=thread.id, run_id=run3.id) # 取消Run
# 裁判员(runs.list)调出所有比赛记录复盘
all_runs = client.beta.threads.runs.list(thread_id=thread.id).data
print("=== 裁判员复盘所有比赛记录 ===")
for i, run in enumerate(all_runs, 1):
print(f"\n第{i}轮比赛:")
print(f" ID:{run.id}")
print(f" 状态:{run.status}")
print(f" 执行时间:{time.ctime(run.created_at)}")
print(f" 关联教练:{run.assistant_id}")
# 输出示例:
# 第1轮比赛:
# ID:run_123
# 状态:succeeded
# 执行时间:Mon Jan 12 10:00:00 2026
# 关联教练:asst_456
# 第2轮比赛:
# ID:run_456
# 状态:failed
# 执行时间:Mon Jan 12 10:00:05 2026
# 关联教练:asst_789
# 第3轮比赛:
# ID:run_789
# 状态:cancelled
# 执行时间:Mon Jan 12 10:00:10 2026
# 关联教练:asst_456
作用4:制止违规的并行比赛(维护赛事规则)
裁判员会“禁止运动员同时参加多轮比赛”——runs.list 能识别出状态为 in_progress 的 Run,确保同一线程(运动员)同一时间只有一个 Run(比赛)在执行,这也是你之前封装的 submit_message_wait_completion 函数的核心逻辑。
场景类比:裁判员发现运动员同时报名了两轮比赛,会制止第二轮,直到第一轮结束。
代码示例(对应你之前的函数逻辑):
def wait_for_runs_complete(thread):
"""裁判员检查并等待所有进行中的比赛完成"""
runs = client.beta.threads.runs.list(thread_id=thread.id).data
for run in runs:
if run.status == "in_progress":
print(f"裁判员制止并行比赛:等待Run {run.id} 完成...")
while True:
run_status = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id).status
if run_status in ["succeeded", "failed", "cancelled"]:
print(f"Run {run.id} 完成,状态:{run_status}")
break
time.sleep(3)
# 测试:发起一个执行中的Run,调用函数让裁判员制止并行
client.beta.threads.messages.create(thread_id=thread.id, role="user", content="并行训练请求")
run_in_progress = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id)
# 裁判员检查并等待
wait_for_runs_complete(thread)
# 输出:
# 裁判员制止并行比赛:等待Run run_xxx 完成...
# Run run_xxx 完成,状态:succeeded
把上述这个类比记住了,那么,OpenAI Assistants API 的架构你就学会了,值得点赞吧。
更多推荐

所有评论(0)