自定义工作流 (Custom workflow)
在自定义工作流 (custom workflow) 架构中,你使用 LangGraph 定义专属于你自己的定制化执行流。你可以完全掌控图结构——包括顺序步骤、条件分支、循环以及并行执行。
核心特性
- 完全掌控图结构
- 将确定性逻辑与智能体行为相结合
- 支持顺序步骤、条件分支、循环以及并行执行
- 可将其他架构模式作为节点嵌入到你的工作流中
何时使用
当标准模式(子智能体、技能等)无法满足你的需求、你需要将确定性逻辑与智能体行为相结合,或者你的用例需要复杂的路由或多阶段处理时,请使用自定义工作流。
工作流中的每个节点都可以是一个简单的函数、一次 LLM 调用,或者是包含工具的完整智能体。你还可以在自定义工作流中组合其他架构——例如,将多智能体系统作为单个节点嵌入其中。
有关自定义工作流的完整示例,请参见下方教程。
路由器模式是自定义工作流的一个示例。本教程将引导你构建一个并行查询 GitHub、Notion 和 Slack 并对结果进行综合汇总的路由器。
基础实现
其核心理念在于,你可以直接在任何 LangGraph 节点内部调用 LangChain 智能体,从而将自定义工作流的灵活性与预构建智能体的便利性结合起来:
typescript
import { z } from "zod";
import { createAgent } from "langchain";
import { StateGraph, START, END, StateSchema, MessagesValue } from "@langchain/langgraph";
const agent = createAgent({ model: "openai:gpt-5.5", tools: [...] });
const AgentState = new StateSchema({
messages: MessagesValue,
query: z.string(),
});
const agentNode: GraphNode<typeof AgentState> = (state) => {
// 调用 LangChain 智能体的 LangGraph 节点
const result = await agent.invoke({
messages: [{ role: "user", content: state.query }]
});
return { answer: result.messages.at(-1)?.content };
}
// 构建一个简单的工作流
const workflow = new StateGraph(State)
.addNode("agent", agentNode)
.addEdge(START, "agent")
.addEdge("agent", END)
.compile();示例:RAG 流水线
一个常见的用例是将检索与智能体相结合。本示例构建了一个 WNBA 数据统计助手,它可以从知识库中检索信息并能够获取实时新闻。
自定义 RAG 工作流
该工作流演示了三种类型的节点:
- 模型节点 (Rewrite):使用结构化输出重写用户查询以实现更好的检索效果。
- 确定性节点 (Retrieve):执行向量相似度搜索——不涉及 LLM。
- 智能体节点 (Agent):基于检索到的上下文进行推理,并可通过工具获取额外信息。
TIP
你可以使用 LangGraph 状态在工作流各个步骤之间传递信息。这使得工作流的每个部分都可以读取并更新结构化字段,从而轻松在各节点间共享数据与上下文。
typescriptimport { StateGraph, Annotation, START, END } from "@langchain/langgraph"; import { createAgent, tool } from "langchain"; import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai"; import { MemoryVectorStore } from "@langchain/classic/vectorstores/memory"; import * as z from "zod"; const State = Annotation.Root({ question: Annotation<string>(), rewrittenQuery: Annotation<string>(), documents: Annotation<string[]>(), answer: Annotation<string>(), }); // 包含球员名单、比赛结果与球员统计数据的 WNBA 知识库 const embeddings = new OpenAIEmbeddings(); const vectorStore = await MemoryVectorStore.fromTexts( [ // 球员名单 "New York Liberty 2024 roster: Breanna Stewart, Sabrina Ionescu, Jonquel Jones, Courtney Vandersloot.", "Las Vegas Aces 2024 roster: A'ja Wilson, Kelsey Plum, Jackie Young, Chelsea Gray.", "Indiana Fever 2024 roster: Caitlin Clark, Aliyah Boston, Kelsey Mitchell, NaLyssa Smith.", // 比赛结果 "2024 WNBA Finals: New York Liberty defeated Minnesota Lynx 3-2 to win the championship.", "June 15, 2024: Indiana Fever 85, Chicago Sky 79. Caitlin Clark had 23 points and 8 assists.", "August 20, 2024: Las Vegas Aces 92, Phoenix Mercury 84. A'ja Wilson scored 35 points.", // 球员统计数据 "A'ja Wilson 2024 season stats: 26.9 PPG, 11.9 RPG, 2.6 BPG. Won MVP award.", "Caitlin Clark 2024 rookie stats: 19.2 PPG, 8.4 APG, 5.7 RPG. Won Rookie of the Year.", "Breanna Stewart 2024 stats: 20.4 PPG, 8.5 RPG, 3.5 APG.", ], [{}, {}, {}, {}, {}, {}, {}, {}, {}], embeddings ); const retriever = vectorStore.asRetriever({ k: 5 }); const getLatestNews = tool( async ({ query }) => { // 此处调用你的新闻 API return "Latest: The WNBA announced expanded playoff format for 2025..."; }, { name: "get_latest_news", description: "Get the latest WNBA news and updates", schema: z.object({ query: z.string() }), } ); const agent = createAgent({ model: "openai:gpt-5.5", tools: [getLatestNews], }); const model = new ChatOpenAI({ model: "gpt-5.5" }); const RewrittenQuery = z.object({ query: z.string() }); async function rewriteQuery(state: typeof State.State) { const systemPrompt = `Rewrite this query to retrieve relevant WNBA information. The knowledge base contains: team rosters, game results with scores, and player statistics (PPG, RPG, APG). Focus on specific player names, team names, or stat categories mentioned.`; const response = await model.withStructuredOutput(RewrittenQuery).invoke([ { role: "system", content: systemPrompt }, { role: "user", content: state.question }, ]); return { rewrittenQuery: response.query }; } async function retrieve(state: typeof State.State) { const docs = await retriever.invoke(state.rewrittenQuery); return { documents: docs.map((doc) => doc.pageContent) }; } async function callAgent(state: typeof State.State) { const context = state.documents.join("\n\n"); const prompt = `Context:\n${context}\n\nQuestion: ${state.question}`; const response = await agent.invoke({ messages: [{ role: "user", content: prompt }], }); return { answer: response.messages.at(-1)?.contentBlocks }; } const workflow = new StateGraph(State) .addNode("rewrite", rewriteQuery) .addNode("retrieve", retrieve) .addNode("agent", callAgent) .addEdge(START, "rewrite") .addEdge("rewrite", "retrieve") .addEdge("retrieve", "agent") .addEdge("agent", END) .compile(); const result = await workflow.invoke({ question: "Who won the 2024 WNBA Championship?", }); console.log(result.answer);NOTE
在生产环境中,请使用持久化向量存储,例如 Weaviate、Pinecone 或 MongoDB Atlas,而非
MemoryVectorStore。参见所有向量存储。
