学习笔记 · Obsidian
LangChain TypeScript:状态、流式与结构化输出
三个不要混淆的概念
- State 是一次 thread 中 agent 执行所需的数据;messages 只是其中一个 channel。
- Streaming 是执行过程中如何观察增量,不会自动让 state 持久化。
- Structured output 是最终回答的契约,不保证中间工具结果或所有 provider 原始内容都符合该契约。
生产设计通常三者并用:checkpointer 保持 thread;stream 把 token、工具和状态更新送到客户端;最终 structuredResponse 经过 schema 验证后进入业务流程。
短期记忆:thread-scoped state
给 agent 配置 checkpointer 后,使用稳定的 configurable: { thread_id } 调用,LangGraph 会在每一步保存 state 并在下一轮恢复。MemorySaver 只适合本地和测试;生产应选择 PostgreSQL 等持久 checkpointer,并验证连接池、migration、备份、TTL、并发写入和灾难恢复。
自定义 state 使用 StateSchema/Zod 描述字段和 reducer。关键规则:
messages使用消息 reducer,追加新消息并理解RemoveMessage;- 普通字段若有并发更新必须明确归并规则,不能依赖对象覆盖顺序;
- 请求级、不可序列化的依赖放 runtime context,不放 checkpoint;
- state 中的 PII、工具结果和文件引用会被持久化,必须有数据分级和清理策略。
控制消息增长
长对话不能无限追加:
- trim:在模型调用前只取 token/window 内的有效历史;
- delete:用
RemoveMessage删除特定消息,或REMOVE_ALL_MESSAGES重置; - summarize:把旧历史压缩成摘要并保留近期消息;
- custom strategy:按任务、租户、模型 window 和工具协议定制。
裁剪时必须保持消息合法:不能留下孤立的 ToolMessage,也不能删掉 tool call 却保留其结果。摘要是有损压缩,需要测试关键事实、权限和未完成任务是否仍可恢复。
两套流式接口
传统 stream
agent.stream(input, { streamMode }) 支持:
| mode | 输出 | 适合 |
|---|---|---|
updates | 每一步的 state delta | 展示 agent 进度、工具阶段 |
messages | 模型 token/message chunks + metadata | 打字机效果、实时 TTS |
custom | 工具或节点通过 writer 发出的自定义事件 | 进度、业务指标、文件生成状态 |
可以同时请求多个 mode。客户端需要用 mode/event 判别联合类型,不能假设所有 chunk 都是文本。
Event Streaming v3
新应用优先评估 agent.streamEvents(input, { version: "v3" })。它提供类型化 projection,而不是让调用方遍历低层 callback 事件:
stream.messages:消息流;每条消息还能迭代.text、.reasoning、.toolCalls、.output、.usage;- root
.toolCalls:所有工具调用; .values:完整 state snapshot;.output:最终输出;.subagents/.subgraphs:嵌套执行;.extensions:由 middleware 注入的扩展投影。
若同时消费多个 projection,应并发 drain;只等待一个而让另一个无人消费,可能造成缓冲、背压或遗漏 UI 更新。断线重连、重复事件、取消和慢消费者也要在协议层设计。
TypeScript 特有版本边界
- middleware 注册的 stream transformer 要求 `[email protected]+`;Python 文档对应版本不同,不能照抄。
- 页面个别说明使用 Python 名称
stream_events,实际 JavaScript 方法是streamEvents。 - 禁止模型流式时使用
streaming: false;fallback 场景的disableStreaming: true是不同开关。文档出现streaming=False是语言模板残留。 - 传
thread_id只有在 agent 配置 checkpointer 后才会保存历史;LangSmith Deployment 会自动配置,本地需显式提供。
Structured output
responseFormat 可接受 Zod、其他 Standard Schema、JSON Schema,或显式 strategy。成功后结果位于 structuredResponse。
两种策略:
| 策略 | 机制 | 优点 | 风险 |
|---|---|---|---|
| Provider strategy | 使用 provider 原生 structured output | 通常约束更强、少一次工具语义 | 依赖 provider/model 支持与 schema 子集 |
| Tool strategy | 把 schema 表示为工具调用 | 兼容面广、可定制错误反馈 | 多一层 tool-call 语义,模型可能参数错误 |
传入 schema 时,LangChain 会根据 model profile 自动选 provider strategy;该 profile 推断要求 langchain >= 1.1,且 profile 仍属 beta。高风险系统应显式选 strategy 并在部署目标模型上验证,不把自动推断当永久契约。
toolStrategy 可设置 toolMessageContent 和 handleError。验证失败时,默认可把错误反馈给模型重试;应限制重试次数和总耗时,避免无效 schema 造成循环。即使框架已解析成功,进入数据库、支付或权限逻辑前仍应再次做业务校验。
TypeScript schema 实践
- Zod 同时提供运行时验证和类型推断,是默认首选;
- Standard Schema 方便复用 Valibot 等生态;
- JSON Schema 适合跨语言/协议,但 TypeScript 类型需另行生成或绑定;
- schema 应避免 provider 不支持的递归、过深 union 或松散 additional properties;
- 不把 prompt 中“请返回 JSON”当 structured output。
前端消费原则
- 先按 event/projection 做类型收窄,再更新 UI;
- partial tool args、partial JSON、partial Markdown 都是不可信中间态;
- UI 可乐观展示,但持久业务状态只由服务端确认事件推进;
- disconnect、cancel、rejoin 是三种不同语义;
- usage、reasoning 和 provider metadata 可能缺失,组件要有无数据分支。
逐页覆盖
| 页面 | 学习结论 |
|---|---|
| Short-term memory | checkpointer + thread、StateSchema、trim/delete/summarize 与生产持久化。 |
| Streaming | updates/messages/custom、并行模式、工具自定义 writer 和持久 thread。 |
| Event streaming | v3 typed projections、messages/toolCalls/values/output/subagents/extensions 与 transformer。 |
| Structured output | Zod/Standard/JSON Schema,provider/tool strategy、自动选择、错误反馈与验证。 |