Functional API 允许你以最小的代码改动,为你的应用程序添加 LangGraph 的关键特性(持久化、记忆、人机协同和流式处理)。它旨在将这些特性集成到现有代码中,这些代码可能使用标准语言原语进行分支和控制流,例如 if 语句、for 循环和函数调用。与许多需要将代码重构为显式管道或 DAG 的数据编排框架不同,Functional API 允许你在不强制使用刚性执行模型的情况下,集成这些能力。Functional API 使用两个关键构建块:
entrypoint:入口点封装了工作流逻辑并管理执行流程,包括处理长时间运行的任务和中断。
task:表示一个离散的工作单元,例如 API 调用或数据处理步骤,可以在入口点内异步执行。任务返回一个类似 future 的对象,可以异步等待或同步解析。
import { MemorySaver, entrypoint, task, interrupt } from "@langchain/langgraph";const writeEssay = task("writeEssay", async (topic: string) => { // A placeholder for a long-running task. await new Promise((resolve) => setTimeout(resolve, 1000)); return `An essay about topic: ${topic}`;});const workflow = entrypoint( { checkpointer: new MemorySaver(), name: "workflow" }, async (topic: string) => { const essay = await writeEssay(topic); const isApproved = interrupt({ // Any json-serializable payload provided to interrupt as argument. // It will be surfaced on the client side as an Interrupt when streaming data // from the workflow. essay, // The essay we want reviewed. // We can add any additional information that we need. // For example, introduce a key called "action" with some instructions. action: "Please approve/reject the essay", }); return { essay, // The essay that was generated isApproved, // Response from HIL }; });
import { v7 as uuid7 } from "uuid";import { MemorySaver, entrypoint, task, interrupt } from "@langchain/langgraph";const writeEssay = task("writeEssay", async (topic: string) => { // This is a placeholder for a long-running task. await new Promise(resolve => setTimeout(resolve, 1000)); return `An essay about topic: ${topic}`;});const workflow = entrypoint( { checkpointer: new MemorySaver(), name: "workflow" }, async (topic: string) => { const essay = await writeEssay(topic); const isApproved = interrupt({ // Any json-serializable payload provided to interrupt as argument. // It will be surfaced on the client side as an Interrupt when streaming data // from the workflow. essay, // The essay we want reviewed. // We can add any additional information that we need. // For example, introduce a key called "action" with some instructions. action: "Please approve/reject the essay", }); return { essay, // The essay that was generated isApproved, // Response from HIL }; });const threadId = uuid7();const config = { configurable: { thread_id: threadId }};for await (const item of workflow.stream("cat", config)) { console.log(item);}
import { Command } from "@langchain/langgraph";// Get review from a user (e.g., via a UI)// In this case, we're using a bool, but this can be any json-serializable value.const humanReview = true;const stream = await workflow.stream( new Command({ resume: humanReview }), config);for await (const item of stream) { console.log(item);}
import { entrypoint } from "@langchain/langgraph";const myWorkflow = entrypoint( { checkpointer, name: "workflow" }, async (someInput: Record<string, any>): Promise<number> => { // some logic that may involve long-running tasks like API calls, // and may be interrupted for human-in-the-loop return result; });
import { entrypoint, getPreviousState } from "@langchain/langgraph";const myWorkflow = entrypoint( { checkpointer, name: "workflow" }, async (number: number) => { const previous = getPreviousState<number>() ?? 0; // This will return the previous value to the caller, saving // 2 * number to the checkpoint, which will be used in the next invocation // for the `previous` parameter. return entrypoint.final({ value: previous, save: 2 * number, }); });const config = { configurable: { thread_id: "1", },};await myWorkflow.invoke(3, config); // 0 (previous was undefined)await myWorkflow.invoke(1, config); // 6 (previous was 3 * 2 from the previous invocation)
幂等性确保多次运行相同的操作会产生相同的结果。这有助于防止在某个步骤因故障而重新运行时出现重复的 API 调用和冗余处理。始终将 API 调用放在任务函数中进行检查点,并将它们设计为幂等的,以防重新执行。
这对于导致数据写入的操作尤其重要。
当工作流恢复时,LangGraph 从检查点重放已完成的任务结果。一个已开始但未完成的任务可能会在该次恢复时再次运行,因此请将副作用设计为幂等的。使用幂等键或验证现有结果以避免意外的重复。
import { entrypoint, interrupt } from "@langchain/langgraph";import fs from "fs";const myWorkflow = entrypoint( { checkpointer, name: "workflow }, async (inputs: Record<string, any>) => { // This code will be executed a second time when resuming the workflow. // Which is likely not what you want. fs.writeFileSync("output.txt", "Side effect executed"); const value = interrupt("question"); return value; });
在此示例中,副作用被封装在任务中,确保了恢复时执行的一致性。
import { entrypoint, task, interrupt } from "@langchain/langgraph";import * as fs from "fs";const writeToFile = task("writeToFile", async () => { fs.writeFileSync("output.txt", "Side effect executed");});const myWorkflow = entrypoint( { checkpointer, name: "workflow" }, async (inputs: Record<string, any>) => { // The side effect is now encapsulated in a task. await writeToFile(); const value = interrupt("question"); return value; });