关键设计决策和原因
用决策表归纳 LangChain JS 为何这样设计;实现细节见 01–06,图编排见 langgraphjs。
1. 为何 Runnable 是核心抽象
| 考量 | 选择 |
|---|---|
| 组件类型杂(Prompt、Model、Parser) | 统一 invoke/stream/batch |
| 组合 | pipe 无需继承树 |
| 观测 | config.callbacks 贯穿子调用 |
2. 为何 Monorepo
| 考量 | 选择 |
|---|---|
| core 稳定、provider 常变 | 分包发布 |
| 树摇 | 只装用到的 @langchain/openai |
| 版本对齐 | workspace 统一发版 |
3. 为何 TypeScript
生态与 LangGraph 一致;Message/Tool 类型减少运行时 schema 错误。
4. 为何复杂 Agent 用 LangGraph 而非 while
| while 循环 | LangGraph |
|---|---|
| 难测、难可视化 | 图 + checkpoint |
| 难 HITL | interrupt |
| 状态散落 | Channel + reducer |
5. 为何消息支持多模态
模型能力扩展(视觉、文件);用 content 块数组 扩展,而不改 HumanMessage 类层次。
6. 为何 Zod / Schema
Tool 参数与 structured output 需运行时校验;与 TS 类型同源。
7. 为何全异步
LLM 与网络 I/O 为主;同步 API 会阻塞 Node 事件循环。
8. 为何 RunnableConfig 而非长参数列表
跨层透传 callbacks、signal、metadata,子 Runnable 自动继承。
9. 为何多环境(Node / 浏览器 / Edge)
同一套链在服务端与边缘部署;差异在 fetch 适配与包入口。
10. 为何中间件 / 拦截
不改核心即可加限流、审计、RAG;符合开闭原则。
11. 为何 LangGraph 用 Reducer
多节点同超步写同一 state 字段需合并语义(如 messages append)。
12. 消息模型演进(摘要)
早期 string prompt → 统一 Message 数组 → 多模态 ContentBlock;旧 API 保留兼容层,新代码用 ChatModel + Message。
总结
主轴:Runnable 组合 + Message 对话 + Schema 约束 + LangGraph 管状态。