如何从字符串反序列化 OpenAI ChatCompletion 对象

本文介绍通过 `eval()` 配合显式导入 openai 类型模块,将 `str(chat_completion)` 生成的字符串重新构造为原始 `chatcompletion` 对象的方法,并强调其仅适用于临时迁移场景,不可用于生产环境。

OpenAI Python SDK(v1.0+)中,client.chat.completions.create() 返回的是一个结构化的 Pydantic 模型对象(如 ChatCompletion),而非纯字典。若历史数据误用 str(response) 存入数据库(例如 "ChatCompletion(id='chatcmpl-...', choices=[Choice(...)], usage=CompletionUsage(...))"),则无法直接用 json.loads() 还原——因为 str() 输出的是可读性优先的调试字符串,并非标准序列化格式。

此时,一种临时可行(但需谨慎)的反实例化方式是结合 eval() 与显式类型导入:

# ✅ 必须提前导入所有可能出现在 str() 输出中的类
from openai.types.chat import ChatCompletion, ChatCompletionMessage
from openai.types.chat.chat_completion import Choice
from openai.types import CompletionUsage

# 假设已从数据库读取原始字符串
response_obj_string = "ChatCompletion(id='chatcmpl-abc123', choices=[Choice(finish_reason='stop', index=0, message=ChatCompletionMessage(content='Hello!', role='assistant'))], usage=CompletionUsage(completion_tokens=5, prompt_tokens=12, total_tokens=17))"

# ⚠️ 仅限受控、可信、一次性迁移场景!
response_obj = eval(response_obj_string)
print(type(response_obj))  # 
print(response_obj.choices[0].message.content)  # "Hello!"

⚠️ 关键注意事项:

  • eval() 会动态执行字符串代码,若字符串来源不可信(如用户输入、未校验日志),将导致任意代码执行风险,绝对禁止在生产环境使用
  • str(obj) 的输出格式属于内部实现细节,OpenAI SDK 未承诺其稳定性,未来版本可能变更结构,导致 eval() 失败;
  • 正确做法应始终使用 .model_dump()(Pydantic v2)或 .dict()(v1)序列化为 JSON 字典,再存入数据库;还原时用 ChatCompletion.model_validate(json_dict) 安全重建;
  • 若已存在大量 str() 形式的历史数据,建议编写一次性迁移脚本:先用上述 eval + 导入方式加载,再立即转存为标准 JSON 格式,后续统一走 model_validate() 流程。

总结:eval() + 显式导入是一种“救急不救穷”的临时技术手段,核心价值在于辅助版本迁移过渡;长期方案必须回归结构化序列化(JSON)与 Pydantic 官方验证机制,确保类型安全、可维护性与安全性。