LLM 0.32a0 是一次重大的向后兼容重构
来源: Simon Willison’s Weblog | 作者: Simon Willison | 日期: 2026-04-29 原文链接: https://simonwillison.net/2026/Apr/29/llm/#atom-everything
一句话总结
LLM 0.32a0 通过两个核心架构变更——将输入建模为消息序列、将输出建模为带类型的部分流——突破了原有”文本进、文本出”的抽象限制,使其能够覆盖当今前沿模型的多样化输入输出能力。
速览
- 原有抽象已触及天花板——LLM 自 2023 年创建以来一直使用”文本提示词 → 文本响应”模型,但随着附件、schemas、tools 等功能堆叠,这个抽象已无法覆盖前沿模型的能力
- 输入重构为消息序列——新增
llm.user()和llm.assistant()构建器函数,支持通过messages=[]数组直接注入完整对话历史,解决了无法从外部预填充对话的痛点 - 输出重构为带类型的部分流——新增
stream_events()/astream_events()API,每个事件携带类型标识(text、tool_call_name、tool_call_args 等),可区分处理不同类型的输出内容 - 向后兼容——原有
prompt=接口仍然可用,LLM 在幕后自动升级为单元素 messages 数组 - CLI 支持推理 token 可视化——思考文本以不同颜色显示且输出到 stderr,不影响管道结果;新增
-R/--no-reasoning标志可抑制推理输出 - 新增响应序列化机制——
response.to_dict()/Response.from_dict()允许用户将响应存储到任意后端,摆脱对 SQLite 的绑定 - 对话可通过 reply() 延续——
response.reply()提供了一种比传统 conversation 对象更轻量的对话延续方式 - 下一步:SQLite 日志系统重设计——计划将对话存储建模为图结构,避免重复存储被不断扩展的对话
核心内容
原有抽象为什么不够用了
LLM 最初在 2023 年 4 月创建时,将世界简化为”发送文本提示词、获得文本响应”。这在当时是合理的。但三年间,LLM 先后叠加了附件(图像/音频/视频输入)、schemas(结构化 JSON 输出)、tools(工具调用)等功能。与此同时,前沿模型本身也在演进——推理能力、图像生成、混合类型输出不断涌现。
原来的”文本进、文本出”抽象已经无法表达这些能力。LLM 通过插件系统为数千个模型提供统一接口,抽象层的局限意味着整个插件生态都受到制约。
输入侧重构:消息序列
核心问题:旧的 conversation API 只能从头构建对话,无法从外部注入一段已有的对话历史。这使得构建模拟 OpenAI chat completions API 等场景变得过于困难。
旧的 CLI 工具通过 SQLite 持久化对话来绕过这个问题,但这从未成为稳定 API 的一部分,而且很多场景下用户不想绑定 SQLite。
新方案:
import llm
from llm import user, assistant
model = llm.get_model("gpt-5.5")
response = model.prompt(messages=[
user("Capital of France?"),
assistant("Paris"),
user("Germany?"),
])
llm.user() 和 llm.assistant() 是新的构建器函数,直接在 messages=[] 数组中使用。旧的 prompt= 参数仍然可用——LLM 在幕后将其升级为单元素的 messages 数组,实现向后兼容。
新增的 response.reply() 方法提供了对话延续的轻量替代方案:
response2 = response.reply("How about Hungary?")
输出侧重构:带类型的部分流
核心问题:当今模型返回的内容类型越来越多样——推理文本、普通文本、工具调用请求、工具输出、图像、音频片段可能交错出现在同一个流式响应中。旧的流式 API 只能逐块输出文本字符串,无法区分这些不同类型的内容。
新方案:stream_events() 和 astream_events() 返回带类型标识的事件流:
for event in response.stream_events():
if event.type == "text":
print(event.chunk, end="", flush=True)
elif event.type == "tool_call_name":
print(f"\nTool call: {event.chunk}(", end="", flush=True)
elif event.type == "tool_call_args":
print(event.chunk, end="", flush=True)
工具调用方面,响应结束后可以调用 response.execute_tool_calls() 直接运行函数,或用 response.reply() 让工具被调用后将返回值自动发送回模型。
CLI 层面的体现:思考文本(reasoning tokens)现在以灰色显示,与最终响应文本用颜色区分。思考文本输出到 stderr,不会污染管道结果。新增 -R/--no-reasoning 标志可完全抑制推理 token 的显示。这是本次发布唯一面向 CLI 的变更。
响应序列化机制
旧的 SQLite 持久化机制不够灵活。新增的 to_dict() / from_dict() 方法让用户可以将响应导出为 JSON 风格的字典(实际上是一个 TypedDict,定义在 llm/serialization.py 模块中),存储到任何后端,然后还原。
serializable = response.to_dict()
response = Response.from_dict(serializable)
下一步计划
这是一个 alpha 发布,目的是在真实环境中检验新设计。Simon 预计稳定版 0.32 将与 alpha 非常相似。
剩余大任务是重新设计 SQLite 日志系统,以捕获新抽象返回的更细粒度的细节。理想方案是将对话存储建模为图(graph)结构,支持 chat completions API 风格的对话扩展场景,避免在数据库中产生重复。尚未决定这是放在 0.32 还是 0.33。
名言金句
- “The original abstraction—of text input that returns text output—was no longer able to represent everything I needed it to.”
- “LLM needs to evolve to better handle the diversity of input and output types that can be processed by today’s frontier models.”
- “Surprisingly that ended up being the only CLI-facing change in this release.”
可行建议
- 尝试 alpha 版本:
pip install llm==0.32a0,在真实项目中测试新的 messages API 和 streaming events API,向 Simon 反馈设计问题 - 迁移 conversation 代码:如果你有使用
model.conversation()的代码,可以开始迁移到messages=[]模式以获得更大灵活性 - 利用序列化机制:如果你之前因为 SQLite 绑定而回避持久化对话,现在可以用
to_dict()/from_dict()接入自己的存储层 - 插件开发者注意:需要关注新的 streaming events 协议,参考 llm-anthropic 插件的更新
