langgraph
这个库的作用是编排节点构成的有向图 一般用于构建多Agent应用(尤其是多分支/成环的复杂编排)
简单理解:state-对象;节点-接受state输出state的函数;边-根据state决定走向;langgraph在执行过程中负责给state赋值 节点调度等功能
demo
ts
import {
Annotation,
Command,
type GraphNode,
MemorySaver,
Send,
START,
StateGraph,
interrupt,
INTERRUPT,
isInterrupted,
} from '@langchain/langgraph'
const GraphState = Annotation.Root({
arr: Annotation<string[], string[]>({ default: () => [], reducer: (cur, v) => [...cur, ...v] }),
input: Annotation<string | undefined>(),
})
type GraphStateType = typeof GraphState.State
type GraphNodeType = GraphNode<typeof GraphState>
type GraphNodeNameType = (typeof graph)['~NodeType']
const GraphSend = Send<GraphNodeNameType, Partial<GraphStateType>>
const GraphCommand = Command<boolean, Partial<GraphStateType>, GraphNodeNameType>
const node1: GraphNodeType = (state) => {
console.log('node1', state)
// interrupt中断图 参数是对外的提问
// 首次调用会抛出异常
// 直到恢复图时 第二次interrupt才会赋值 resume才会作为其返回值
const approved = interrupt<string, boolean>('a question')
if (approved && state.arr.length !== 0) {
return new GraphCommand({
// node1结束后 先变更主state
update: { input: 'aa', arr: ['a'] },
// 然后 Send 并行派发:创建新的state 但reducer会同步至已有state
goto: state.arr.map((v) => new GraphSend('node2', { input: v })),
// goto不会覆盖原有的边 所有还会额外执行一次node2 同样是并行
// 执行顺序:update-goto-edge
// reducer顺序 udpate-edge-goto
})
}
return {}
}
const node2: GraphNodeType = (state) => {
console.log('node2:', state)
if (state.input) return { arr: [state.input] }
return {}
}
const graph = new StateGraph(GraphState)
.addNode('node1', node1)
.addNode('node2', node2)
.addEdge(START, 'node1')
.addEdge('node1', 'node2')
// checkpointer保存在内存里 在resume时使用
.compile({ checkpointer: new MemorySaver() })
const cfg = {
configurable: {
// 读写缓存的key
thread_id: 'demo',
},
}
// 第一次执行:invoke 触发 interrupt,图暂停并返回 __interrupt__ 字段
const paused = await graph.invoke({ arr: ['x', 'y', 'z'] }, cfg)
console.log('paused:', JSON.stringify(paused, null, 2))
if (isInterrupted(paused)) {
// 可能同时处于多个中断
// 需要逐个resume
const interrupts = paused[INTERRUPT]
// 第二次执行:Command({ resume: true }) 将 true 作为 interrupt() 的返回值注入,
const result = await graph.invoke(new GraphCommand({ resume: true }), cfg)
console.log('result:', JSON.stringify(result, null, 2))
}
对interrupt的说明
interrupt支持异步/条件调用 但是不应在循环中调用 不应将动态远程数据作为interrupt的条件
其实现原理是 首次调用抛出异常 等resume后 不抛出异常 而是改为返回reusme的值 应当确保state相同时 interrupt的执行路径不变