LangChain createAgent 源码(中):Middleware 与图编译
《Agent 大模型 0 到 1 系统课》 · 程序员 Sunday
你正在阅读本节的免费试读。完整课程 ¥499,可在文末添加作者微信购买。
上一小节最后,咱们顺着 createAgent() 一直追到了 ReactAgent,并且看到了下面这段代码:
const workflow = new StateGraph(state, {
input,
output,
context: this.options.contextSchema
})
state、input、output 和 context 分别负责什么,前面已经讲过了。
但是!
代码执行到这里以后,Agent 其实还不能运行。
因为 new StateGraph() 只是创建了一个 Graph Builder。他更像是先准备好一张空白的流程图,并且规定这张图里的数据应该长什么样。
至于流程图里有哪些 Node、Node 之间怎么连接、模型什么时候调用 Tool、Tool 执行完以后又回到哪里,现在都还没有确定。
所以,ReactAgent 构造函数接下来还需要完成三件事:
- 把模型、Tool 和 Middleware 加入 Graph
- 为这些 Node 连接 Edge 和 Conditional Edge
- 调用 compile(),把 Graph Builder 编译成可执行 Graph
这一小节,咱们就继续沿着 ReactAgent.ts 往下看,把这三件事捋清楚。
先看看最终要构建出什么
暂时先不看源码,咱们想一下一个最基础的 Agent 应该怎样运行。
模型收到用户问题以后,可能直接给出最终回答,也可能返回 Tool Call。
如果返回 Tool Call,应用程序就执行 Tool,再把 Tool Result 交还给模型。模型拿到结果以后继续判断,直到不再调用 Tool 为止。
因此,最基础的 Graph 其实就长这样:
这里的 model_request 负责调用模型,tools 负责执行 Tool。
从 model_request 出发有两个方向,所以这里不能写死一条普通 Edge,而是需要根据模型返回的结果动态选择路径。
这个地方用到的,就是第 07 小节讲过的 Conditional Edge。
不过,真实 Agent 里还可能加入 Middleware。
例如咱们第 06 小节使用过下面这些 Hook:
这些 Hook 加进来以后,Graph 还是不是刚才那张图?
答案是:不一定。
有些 Middleware Hook 会直接变成 Graph Node,有些则只是包住模型或者 Tool 的调用过程,根本不会出现在 Graph 里。
这就是接下来阅读源码时,最需要弄明白的第一件事。
Middleware 是怎么进入 Graph 的
继续打开上一小节已经定位到的文件:libs/langchain/src/agents/ReactAgent.ts
找到刚才的 new StateGraph(),然后继续往下看。
紧接着会看到几个数组:
这里先不要管源码里的泛型,只看变量名就可以了。
简化之后的代码,大概就是这样:
// 为有钩子的中间件节点生成节点名
const beforeAgentNodes = []
const beforeModelNodes = []
const afterModelNodes = []
const afterAgentNodes = []
const wrapModelCallHookMiddleware = []
前四个数组负责记录生命周期 Hook 对应的 Graph Node,最后一个数组则专门保存实现了 wrapModelCall 的 Middleware。
然后,源码会遍历咱们传给 createAgent() 的全部 Middleware:
const middleware = this.options.middleware ?? []
for (let i = 0; i < middleware.length; i++) {
const m = middleware[i]
if (m.beforeAgent) {
const node = new BeforeAgentNode(m)
const name = `${m.name}.before_agent`
beforeAgentNodes.push({ index: i, name })本节试读已结束
继续学习,解锁完整课程
购买《Agent 大模型 0 到 1 系统课》,
继续阅读本节剩余内容与后续课程。
