学习笔记 · Obsidian

TypeScript 工作流、Agentic RAG 与 SQL Agent 模式

LangChainLangGraphTypeScript

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 APIFunctional API
chainstatic/conditional edge顺序 await + if
parallel多 edge + reducerPromise.all
routingConditionalEdgeRouterswitch/if
dynamic workerSend + ReducedValue动态 task 数组 + Promise.all
optimize loopcycle + terminal routerwhile + task
ReActmodel node + ToolNode cyclewhile + model/tool task

选择 Graph API 时优先显式 State 和可视化;选择 Functional 时所有非确定性与副作用必须进入 task,保证 replay。

Agentic RAG 的完整回路

官方自定义 RAG agent 不是固定“检索后回答”,而是让模型先决定是否需要检索:

  1. 载入与预处理文档;
  2. 分块、embedding、写入 vector store;
  3. 用 retriever 创建工具;
  4. 模型直接回答或生成 retriever tool call;
  5. ToolNode 执行检索;
  6. grader 判断文档是否相关;
  7. 相关则基于上下文生成答案;
  8. 不相关则 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 更可控,将关键工具调用拆成专用节点:

  1. list tables;
  2. get schema;
  3. generate query;
  4. check query;
  5. execute query;
  6. 根据结果回答或继续;
  7. 可在 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、数据库和浏览器均做真实集成测试。