高级特性
掌握了基础之后,LangGraphGo 还提供了一系列高级特性,帮助你构建更复杂、更健壮、更易于维护的 AI 系统。
1. 并行执行 (Parallel Execution)
背景与功能
在许多任务中,多个步骤是可以同时进行的。例如,在写一篇文章时,可以同时搜索三个不同的主题。并行执行可以显著减少总的延迟。
实现原理
LangGraphGo 的运行时会自动检测并行机会。如果一个节点有多个出边指向不同的节点(Fan-out),或者多个节点没有依赖关系且都满足执行条件,运行时会将它们放入 Goroutine 中并发执行。
并发执行的结果需要合并。这就是为什么 State Schema 中的 Reducer 如此重要。Reducer 定义了如何安全地将并发产生的部分状态合并到主状态中。
代码展示
// 简单的 Fan-out 模式
g.AddEdge("start", "search_topic_a")
g.AddEdge("start", "search_topic_b")
g.AddEdge("start", "search_topic_c")
// Fan-in 模式:所有搜索完成后,汇总结果
g.AddEdge("search_topic_a", "aggregator")
g.AddEdge("search_topic_b", "aggregator")
g.AddEdge("search_topic_c", "aggregator")
// 运行时会自动并行执行 search_topic_a, b, c
// 只有当它们都完成后(或者根据具体逻辑),aggregator 才会执行
2. 子图 (Subgraphs)
背景与功能
随着应用变得复杂,单张大图会变得难以管理。子图允许你将图模块化。你可以像调用普通节点一样调用另一个编译好的图。
这不仅有助于代码组织,还支持多 Agent 协作模式,其中每个 Agent 都是一个独立的子图。
实现原理
编译后的图(CompiledGraph)实现了与节点函数相同的接口(接收 Context 和 State,返回 State)。因此,它可以直接被添加为父图的一个节点。
需要注意的是,父图和子图的状态 Schema 需要兼容,或者需要进行转换。
代码展示
// 1. 定义子图 (Researcher Agent)
researchGraph := graph.NewStateGraph(...)
// ... 构建 researchGraph ...
researchRunnable := researchGraph.Compile()
// 2. 定义父图 (Main Assistant)
mainGraph := graph.NewStateGraph(...)
// 3. 将子图作为节点添加到父图
mainGraph.AddNode("research_agent", researchRunnable)
// 4. 定义流程
mainGraph.AddEdge("classify_input", "research_agent")
3. 可视化 (Visualization)
背景与功能
“一图胜千言”。对于复杂的逻辑图,代码往往不如图形直观。LangGraphGo 内置了 Mermaid.js 格式的导出功能,让你能直接看到图的结构。
实现原理
图对象遍历其内部的节点和边结构,生成符合 Mermaid 语法的字符串描述。
代码展示
exporter := graph.NewExporter(g)
mermaidChart := exporter.DrawMermaid()
fmt.Println(mermaidChart)
// 输出示例:
// graph TD
// start --> node_a
// node_a --> node_b
// ...
你可以将输出复制到 Mermaid Live Editor 中查看图表。
4. 运行时配置 (Configuration)
背景与功能
在不改变图结构和代码的情况下,我们经常需要调整运行时的行为。例如,为不同的用户使用不同的 LLM 模型,或者传递 User ID 以便在日志中追踪。
Config 对象提供了一种标准的方式来传递这些元数据。
实现原理
Config 对象在图的执行上下文中传递。任何节点都可以通过 graph.GetConfig(ctx) 获取当前的配置。
代码展示
// 调用时传递配置
config := &graph.Config{
Configurable: map[string]interface{}{
"user_id": "123",
"model": "gpt-4",
},
}
runnable.InvokeWithConfig(ctx, input, config)
// 在节点中访问配置
func myNode(ctx context.Context, state interface{}) (interface{}, error) {
config := graph.GetConfig(ctx)
userID := config.Configurable["user_id"]
// ...
}