Message 与 Part
读取 Active 与 Segment 历史,理解 Message、Part 和 sequence
Message 与 Part
session.messages() 返回当前 Active 的全部持久化 Message,不需要 limit。实时变化由 subscribe() 补充。
const page = await session.messages();
for (const message of page.items) {
render_message(message);
}返回结果为:
interface SessionMessagePage {
items: SessionMessage[];
total: number;
source: "active" | "segment";
start_sequence?: number;
end_sequence?: number;
next_before_sequence?: number;
has_more: boolean;
}total 是 Session 已创建的真实 Message 总数,不是当前 items.length。source 说明本次结果来自当前 Active 还是一个已关闭 Segment。
四类顶层 Message
type | 表示什么 | 常见展示 |
|---|---|---|
user | 用户输入或运行中 steering | 用户气泡、附件 |
assistant | 模型生成的一次连续回复 | 文本、推理、工具、文件 |
action | 模型切换、压缩等运行时操作 | 状态通知 |
error | Session 或 Turn 的可展示错误 | 错误提示与重试入口 |
Session 内只有一条按 sequence 排序的顶层 Message 序列。工具调用不是另一条 Timeline,也不是顶层 Message;它属于 Assistant Message 的 parts,因此 text -> tool -> text 会以真实生成顺序保存。
普通 Tool Loop、Provider continuation 和恢复重试不会创建新的 Assistant Message;它们继续向当前 Assistant 追加有序 Part。只有新的 User Message(包括运行中 steer)真正进入消息序列时,才结束之前的 Assistant Message,后续输出再创建下一条 Assistant Message。模型 step 的边界由 step-start Part 表达,不需要额外的 segment 字段。
SessionMessage 是持久化数据结构,AI SDK UIMessage 是 Executor 与 UI 边界格式。User / Assistant 的可序列化 UI parts 可以转换成 Session parts,再恢复为等价的 UIMessage;action 和 error 是 Downcity Session 自己的顶层类型,不会送入模型。
Turn 在产生任何 Assistant 内容前失败时,只写入一条 error Message,不会用错误文本伪造 Assistant 回复。若失败前已经产生部分内容,该 Assistant 会以 status: "failed" 保留,随后再写入结构化 error Message。
Assistant Part
type | 关键字段 | 说明 |
|---|---|---|
text | text, state | 用户可见输出,streaming -> done |
reasoning | text, state | 推理输出,是否展示由产品决定 |
tool | tool_call_id, tool_name, state, metadata | 工具输入、等待、运行和结果 |
interaction | interaction_id, interaction_type, status, request, response | 审批、问题等用户异步交互 |
file | url, media_type, provider_metadata | Assistant 生成或引用的文件 |
source | source_type, source_id, URL 或文档字段 | AI SDK URL / document source |
data | data_type, data, data_id | 可持久化的结构化 UI 数据 |
step-start | 无业务字段 | 多 step 回复的边界 |
每个 Assistant Part 有稳定的 part_id 和创建后不可变的 sequence。每个模型 step 的 UI chunks 是 canonical 顺序的唯一来源;step 最终快照只校验顺序并补充 metadata,不会创建、删除或重排 Part。工具状态变化更新同一个 Part,不应在 UI 中创建“请求”和“结果”两个重复行。
Tool Provider metadata
Tool Part 会保留 AI SDK Provider 附着在工具调用和工具结果上的 metadata:
interface SessionAssistantToolPart {
title?: string;
tool_metadata?: JsonObject;
dynamic?: boolean;
call_provider_metadata?: ProviderMetadata;
result_provider_metadata?: ProviderMetadata;
provider_executed?: boolean;
preliminary?: boolean;
}这些字段是 Provider continuation 等能力所需的透明数据。应用可以读取并原样转存,但不应修改其中的 Provider 专属字段。旧 Session 没有这些可选字段时仍可正常读取。
文本、推理、文件和 source 同样保留各自的 provider_metadata。相同 data_id 或 source_id 的流式更新会替换原 Part 并保留 sequence;带 transient: true 的 AI SDK data chunk 只用于实时 UI,不写入 Session。
Tool 状态不保存审批副本。用户参与使用独立 Interaction Part:
interface SessionAssistantInteractionPart {
interaction_id: string;
interaction_type: "approval" | "question";
status: "pending" | "resolved" | "expired" | "cancelled";
request: SessionInteractionRequest;
response?: SessionInteractionResponse;
}关联 Tool 使用 ready -> waiting-user -> running -> completed,拒绝、超时或取消进入
failed。Tool 与 Interaction 在同一 Assistant revision 中提交;同一 revision 出现两条
Part Mutation 表示一次原子快照中的两个变更,不是两个独立事务。
顺序、版本与可见性
每条 Message 都有稳定 message_id、创建后不变的 sequence,以及随完整快照更新递增的 revision。客户端不能用较低 revision 覆盖较高 revision。
SDK 在每个模型 step 完成时校验最终 UIMessage 与 canonical chunks 的 Part 数量、类型、顺序和稳定标识。AI SDK 可能为只有 start/end、没有 delta 的 Text 或 Reasoning 生成空占位 Part;这类没有用户可见内容的 Part 会在对账前忽略,不进入 canonical history。其余最终快照不一致或 canonical part 写入失败都会使 Turn 失败,不会通过正文内容猜测 identity,也不会发布历史不完整的 completed Turn。客户端收到更高 revision 的完整 message Mutation 时,应替换整条 Message,而不是保留旧 revision 的 parts 数组。
默认 messages() 只返回 visibility: "visible" 的 Message。调试或审计时可传 include_internal: true。Compact Summary 不是 Message,不占用 sequence,也不会出现在 messages() 结果中。
加载更早历史
第一次调用不传参数,得到 Active 全量。若 has_more 为 true,把 next_before_sequence 原样传回,会得到紧邻的前一个完整 Segment:
let page = await session.messages();
render_messages(page.items);
while (page.has_more && page.next_before_sequence !== undefined) {
page = await session.messages({
before_sequence: page.next_before_sequence,
});
prepend_messages(page.items);
}before_sequence 必须是正整数。每次历史读取返回一个完整 Segment,不支持 limit、cursor、offset 或 Segment 内切片。这样 UI 边界与物理 Compact 边界一致,也不会重复读取累计 Summary。
与实时 Mutation 配合
- 用
messages()建立 Active 初始状态。 - 用
message/partMutation 替换对应快照。 - 用
delta追加当前文本 Part 的新文本或 Tool Part 的输入原文。 - 向上滚动时按
next_before_sequence逐个加载 Segment。 - 断线后重新读取 Active,并按
message_id + revision合并。