学习笔记 · Obsidian
TypeScript 工作流、Agentic RAG 与 SQL Agent 模式
Workflow 与 Agent
- Workflow:代码预先规定路径和顺序,强调可预测、可测试、可审计。
- Agent:模型动态决定工具与下一步,适合问题和解法无法事先完全确定的场景。
生产系统通常是混合体:外层工作流控制权限、预算、审批、数据库写入和终止条件;局部 agent 在受限工具集内规划。
LangGraph 为两者提供 persistence、streaming、HITL、debug、deployment 与 State/reducer,但不会自动提供业务安全边界。
六种通用编排模式
Prompt chaining
每个模型调用处理上一步输出。适合可拆解、每步可验证的任务,例如翻译后校验、生成后事实检查。
关键设计:
- 每步使用结构化输入输出;
- 在昂贵下游前设置质量 gate;
- 保存原始输入和每步结果,而非只保存最终文本;
- 上游失败不要继续放大。
Graph API 用顺序 edge 和 conditional edge;Functional API 用顺序 task 与 if。
Parallelization
多个独立子任务并行,或同一任务用不同视角重复执行再汇总。TypeScript Functional API 以 Promise.all 表达;Graph API 用同一节点的多个 outgoing edge。
风险:
- provider 并发和 rate limit;
- reducer 合并顺序;
- 多个 branch 写同一 key;
- 部分成功后的重放;
- 成本随分支数量线性增加。
Routing
先分类,再进入专用节点。分类通常用结构化输出约束为有限 enum;conditional router 把 enum 映射到节点名。
路由器不能决定它无权决定的资源 ID、租户或写权限。低置信度应进入澄清/保守路径,而不是随机选一个高权限工具。
Orchestrator-worker
orchestrator 动态拆分未知数量子任务,worker 并行处理,最后综合。
Graph API 使用 Send 为每个 worker 传专有 State,并用 ReducedValue 聚合结果;Functional API 可先让 task 规划,再 Promise.all 调用 worker task。
必须设置:
- 最大 worker 数与递归深度;
- 任务去重与稳定 ID;
- provider 并发 semaphore;
- 单 worker timeout/retry;
- 聚合 token/内存上限;
- 失败 worker 的 partial-result 策略。
Evaluator-optimizer
generator 生成,evaluator 给结构化 grade/feedback,不合格则带反馈重做。适合存在明确成功标准但需迭代的任务。
必须有业务终止条件:最大轮次、成本、时间、连续无改进次数。GRAPH_RECURSION_LIMIT 只是最后安全网。
evaluator 与 generator 使用同一模型会产生相关偏差;高风险任务应加入规则校验、不同模型或人工复核。
ReAct agent
模型 -> tool calls -> ToolNode -> ToolMessage -> 模型,直到模型不再调用工具。
ToolNode 负责并行工具执行、常见错误与 State/context 注入。工具只能读取传给 ToolNode 的完整 State;若手工只传 messages,自定义字段不会自动可见。
TypeScript 工具的第二参数可按 ToolRuntime 类型读取 graph State 与 run context,这是与 Python 注入参数写法不同的地方。
Graph 与 Functional 的模式对应
| 模式 | Graph API | Functional API |
|---|---|---|
| chain | static/conditional edge | 顺序 await + if |
| parallel | 多 edge + reducer | Promise.all |
| routing | ConditionalEdgeRouter | switch/if |
| dynamic worker | Send + ReducedValue | 动态 task 数组 + Promise.all |
| optimize loop | cycle + terminal router | while + task |
| ReAct | model node + ToolNode cycle | while + model/tool task |
选择 Graph API 时优先显式 State 和可视化;选择 Functional 时所有非确定性与副作用必须进入 task,保证 replay。
Agentic RAG 的完整回路
官方自定义 RAG agent 不是固定“检索后回答”,而是让模型先决定是否需要检索:
- 载入与预处理文档;
- 分块、embedding、写入 vector store;
- 用 retriever 创建工具;
- 模型直接回答或生成 retriever tool call;
- ToolNode 执行检索;
- grader 判断文档是否相关;
- 相关则基于上下文生成答案;
- 不相关则 rewrite question,再回到模型/检索路径。
State 与节点
示例使用消息型 State。核心节点/路由:
- generateQueryOrRespond;
- retrieve;
- gradeDocuments;
- rewriteQuestion;
- generateAnswer;
- toolsCondition 或自定义 conditional edge。
grader 用 Zod 结构化输出给 yes/no;文档还提供结构化解析失败时的普通文本 fallback。fallback 应被 trace 和监控,不能静默视为同等可靠。
检索质量
预处理要明确:
- source provenance 与更新时间;
- chunk size/overlap;
- embedding model 与维度;
- metadata filter 和租户 ACL;
- top-k、阈值、去重与 rerank;
- 索引重建和删除策略。
示例 vector store 与网页 loader 只说明流程,不构成生产数据管线。生产索引要有增量更新、失败重试、版本和可追溯 source ID。
Prompt injection 边界
官方 grader prompt 明确要求把 retrieved docs 当数据并忽略其中指令。这是必要提示,但不能单独防御提示注入。
还应:
- 把检索内容放在明确数据边界;
- 过滤/标注不可信来源;
- 工具权限与模型文本分离;
- 禁止文档内容修改 system policy;
- 对引用的 source ID 做服务端校验;
- 最终写操作仍需代码权限与人工审批。
循环控制
rewrite -> retrieve -> grade 可能无限循环。State 应记录 attempts、查询历史、命中文档 ID 和质量分;达到阈值后返回“不足以回答”或转人工,而不是持续花费 token。
答案与引用
最终 answer 应只基于允许的 context,并输出可验证的 source ID。不要让模型随意生成 URL;引用映射由服务端从检索结果构造。
SQL Agent 的受控图
模型生成 SQL 天生高风险。官方示例比通用 ReAct 更可控,将关键工具调用拆成专用节点:
- list tables;
- get schema;
- generate query;
- check query;
- execute query;
- 根据结果回答或继续;
- 可在 execute 前 interrupt 人工审核。
显式图可以强制“先看表、再看 schema、检查后才执行”,而不是只在 system prompt 中请求模型遵守。
数据库安全基线
官方强警告:示例数据库工具是最小演示封装,不可直接用于生产。最低要求:
- 使用只读数据库账号;
- 仅授权允许的 schema/table/view;
- 默认拒绝 INSERT、UPDATE、DELETE、DDL、PRAGMA/扩展调用;
- 设置 statement timeout、row limit、scan/cost limit;
- 禁止多 statement;
- 参数化应用提供的值;
- SQL AST/parser 校验,而不是只查字符串关键字;
- 在只读副本或隔离 analytics warehouse 执行;
- 每次执行记录 user、thread、query hash、审批和行数;
- 返回模型前做列级脱敏和结果大小限制。
最小权限只能降低风险,不能消除模型驱动 SQL 风险。
SQL 工具与消息协议
每个 AI tool call 必须有同 tool_call_id 的 ToolMessage。专用节点可强制调用指定工具,但仍需维护消息完整性。
query checker 是防线之一,不是数据库防火墙。它也由模型驱动,可能漏判;最终执行 adapter 必须执行确定性 allowlist 和资源限制。
数据库 error 可作为 ToolMessage 反馈模型修正,但:
- 不返回连接串、完整 stack 或数据库内部路径;
- 对语法错误限制重试次数;
- 对权限错误不重试;
- 对 timeout/大扫描先收紧 query,而不是盲目提高限制。
SQL HITL
执行前 interrupt payload 可包含:
- SQL 文本;
- 参数;
- 目标只读数据源;
- 预计影响/查询范围;
- 风险理由;
- approve/edit/reject 选项。
恢复时:
- approve:执行已审核 query;
- edit:用审核人修改的 args 更新 tool call,再执行;
- reject/feedback:返回模型重新生成或结束。
必须使用 checkpointer 与稳定 thread_id。审批接口还需用户身份、角色权限、一次性 interrupt ID 和重复提交防护。仅依靠 thread_id 不构成授权。
interrupt node 恢复时从开头执行,因此审批前不能已经执行真实 SQL 写操作。即使只读查询,昂贵预检也应幂等并有缓存/限制。
TypeScript 实现边界
- 使用 Zod/Standard Schema 给 router、grader 和工具输入做运行时校验;interface/type 只在编译期存在。
- Functional API 是 task/entrypoint 函数工厂,不是 Python decorator。
- 并行 worker/tool 使用 Promise;需要显式 semaphore 控制 provider/数据库并发。
- async generator 工具可以通过 tools stream mode 报告进度。
- browser 只显示流式 State、引用和审批;数据库 driver、vector store、模型 key 留在 Node/服务端。
- 文档示例中的模型名、loader、SQLite、内存 vector store 与 API 版本会变化,必须替换成项目锁定版本并端到端编译。
何时不用自定义 LangGraph agent
不要因为能画图就自行重写全部基础能力:
- 标准工具调用 agent:优先 LangChain createAgent;
- 标准 SQL 问答:先评估高层 SQL agent,并在外层加确定性安全 adapter;
- 普通固定 RAG:检索链可能比 agentic loop 更可控、更便宜;
- 没有恢复/HITL/复杂路由需求:普通函数或队列 job 足够。
自定义 LangGraph 的收益应来自显式控制、恢复、并发或审计,而不是增加节点数量。
模式验收清单
- 每个循环有业务终止、时间和成本上限。
- 每个并行分支有并发上限、timeout 和 reducer。
- router/grade 使用有限结构化 schema,并有失败路径。
- RAG 记录 source provenance、ACL、索引版本和引用映射。
- retrieved text 视为不可信数据,不能授予工具权限。
- SQL 使用只读隔离账号、AST allowlist、row/time/cost limit。
- 高风险 SQL 在执行前 HITL,审批绑定用户、thread 与 interrupt ID。
- ToolMessage 与 tool_call_id 完整配对。
- 外部副作用幂等,checkpoint/replay 不会重复真实动作。
- Node、SDK、provider、数据库和浏览器均做真实集成测试。