LangGraph实战:构建高效Agentic工作流,解锁AI应用开发新范式

用 Agentic 框架构建 AI 工作流

LangGraph实战:构建高效Agentic工作流,解锁AI应用开发新范式

随着 GPT-5、Gemini 2.5 Pro 等强大 AI 模型的涌现,旨在高效利用这些模型的 Agentic 框架也日益增多。这类框架通过抽象化诸多复杂环节,极大地简化了与 AI 模型的协作,例如处理工具调用、管理智能体状态以及集成人工反馈循环。

本文将深入探讨其中一个可用的 Agentic AI 框架:LangGraph。我们将用它来开发一个简单的智能体应用,通过具体步骤展示此类框架的优势,并在文末讨论使用 LangGraph 及其他类似框架的优缺点。

市面上有多种 Agentic 框架可供选择,例如:
* LangChain
* LlamaIndex
* CrewAI

LangGraph实战:构建高效Agentic工作流,解锁AI应用开发新范式

为什么需要 Agentic 框架?

尽管市面上存在许多旨在简化应用开发的库,但它们有时反而会使代码变得晦涩、影响生产环境性能,并增加调试难度。

关键在于找到那些能通过抽象样板代码来真正简化应用的库。这一原则在创业领域常被概括为:专注于解决你的核心问题,而将那些已被解决的问题交给成熟的工具去处理。

Agentic 框架的价值在于它能够抽象掉开发者通常不愿处理的复杂性:
* 状态管理:不仅管理对话历史,还包括在执行 RAG 等任务时收集的所有相关信息。
* 工具使用:开发者无需编写调用工具的具体逻辑,只需定义好工具,框架便能负责如何调用,尤其擅长处理并行与异步的工具调用。

因此,使用 Agentic 框架可以剥离大量底层细节,让开发者能够将精力集中于产品的核心逻辑。

LangGraph 基础

LangGraph 的核心思想是基于“图”来构建工作流。在每次处理请求时,系统都会执行这个图。图中主要包含三个要素:
* 状态:保存在内存中的当前信息。
* 节点:通常是执行特定操作的单元,例如调用 LLM 判断意图或调用工具执行任务。
* :定义节点之间的流转逻辑,通常基于条件判断来决定下一步执行哪个节点。

这些概念均源自基础的图论。

实现一个工作流

LangGraph实战:构建高效Agentic工作流,解锁AI应用开发新范式

我们将通过一个简单的文档处理应用来实践 LangGraph。该应用允许用户执行以下操作:
* 创建带文本的文档
* 删除文档
* 在文档中搜索

为此,我们将构建一个包含两个主要步骤的工作流:
1. 识别用户意图:判断用户是想创建、删除还是搜索文档。
2. 路由执行:根据识别的意图,将请求路由到对应的处理流程。

虽然也可以直接让智能体自由调用所有工具,但先进行意图分类的路由设计,能为后续根据意图执行更复杂的操作序列提供清晰的架构。

加载依赖与 LLM

首先,加载必要的库并初始化 LLM。本例中使用 AWS Bedrock 的 Claude 模型,你也可以替换为其他服务商。

from typing_extensions import TypedDict, Literal
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command, interrupt
from langchain_aws import ChatBedrockConverse
from langchain_core.messages import HumanMessage, SystemMessage
from pydantic import BaseModel, Field
from IPython.display import display, Image
from dotenv import load_dotenv
import os
load_dotenv()

aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID") or ""
aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY") or ""

os.environ["AWS_ACCESS_KEY_ID"] = aws_access_key_id
os.environ["AWS_SECRET_ACCESS_KEY"] = aws_secret_access_key

llm = ChatBedrockConverse(
    model_id="us.anthropic.claude-3-5-haiku-20241022-v1:0",
    region_name="us-east-1",
    aws_access_key_id=aws_access_key_id,
    aws_secret_access_key=aws_secret_access_key,
)

# 使用字典模拟文档数据库,生产环境应替换为真实数据库
document_database: dict[str, str] = {}

定义图结构

接下来定义图。首先创建一个路由器,用于将用户输入分类为三种意图之一:add_documentdelete_documentask_document

# 定义状态结构
class State(TypedDict):
    input: str
    decision: str | None
    output: str | None

# 为路由逻辑定义结构化输出模式
class Route(BaseModel):
    step: Literal["add_document", "delete_document", "ask_document"] = Field(
        description="路由流程中的下一步"
    )

# 增强 LLM,使其支持结构化输出以用于路由
router = llm.with_structured_output(Route)

def llm_call_router(state: State):
    """将用户输入路由到适当的节点"""
    decision = router.invoke(
        [
            SystemMessage(
                content="""将用户输入路由到以下三种意图之一:
                - 'add_document'
                - 'delete_document'
                - 'ask_document'
                你只需返回意图,无需其他文本。
                """
            ),
            HumanMessage(content=state["input"]),
        ]
    )
    return {"decision": decision.step}

# 条件边函数,根据决策路由到对应节点
def route_decision(state: State):
    if state["decision"] == "add_document":
        return "add_document_to_database_tool"
    elif state["decision"] == "delete_document":
        return "delete_document_from_database_tool"
    elif state["decision"] == "ask_document":
        return "ask_document_tool"

这里定义了状态 State 来存储用户输入和路由决策。通过强制 LLM 进行结构化输出,我们确保了模型只会返回三种预定义的意图之一,从而实现了可靠的路由逻辑。

接着,我们定义本例中使用的三个工具,每个意图对应一个工具。

# 节点定义
def add_document_to_database_tool(state: State):
    """向数据库添加文档。根据用户查询,提取文档的文件名和内容。若未提供,则不添加。"""
    user_query = state["input"]
    # 从用户查询中提取文件名
    filename_prompt = f"根据以下用户查询,提取文档的文件名:{user_query}。只返回文件名,不要返回其他文本。"
    output = llm.invoke(filename_prompt)
    filename = output.content
    # 从用户查询中提取内容
    content_prompt = f"根据以下用户查询,提取文档的内容:{user_query}。只返回内容,不要返回其他文本。"
    output = llm.invoke(content_prompt)
    content = output.content
    # 将文档添加到数据库
    document_database[filename] = content
    return {"output": f"文档 {filename} 已添加到数据库"}

def delete_document_from_database_tool(state: State):
    """从数据库删除文档。根据用户查询,提取要删除的文档的文件名。若未提供,则不删除。"""
    user_query = state["input"]
    # 从用户查询中提取文件名
    filename_prompt = f"根据以下用户查询,提取要删除的文档的文件名:{user_query}。只返回文件名,不要返回其他文本。"
    output = llm.invoke(filename_prompt)
    filename = output.content
    # 如果文档存在则删除,否则返回失败信息
    if filename not in document_database:
        return {"output": f"数据库中未找到文档 {filename}"}
    document_database.pop(filename)
    return {"output": f"文档 {filename} 已从数据库删除"}

def ask_document_tool(state: State):
    """询问文档相关问题。根据用户查询,提取文档的文件名和问题。若未提供,则不提问。"""
    user_query = state["input"]
    # 从用户查询中提取文件名
    filename_prompt = f"根据以下用户查询,提取要提问的文档的文件名:{user_query}。只返回文件名,不要返回其他文本。"
    output = llm.invoke(filename_prompt)
    filename = output.content
    # 从用户查询中提取问题
    question_prompt = f"根据以下用户查询,提取要问文档的问题:{user_query}。只返回问题,不要返回其他文本。"
    output = llm.invoke(question_prompt)
    question = output.content
    # 对文档进行提问
    if filename not in document_database:
        return {"output": f"数据库中未找到文档 {filename}"}
    result = llm.invoke(f"文档:{document_database[filename]}nn问题:{question}")
    return {"output": f"文档查询结果:{result.content}"}

最后,我们通过添加节点和边来构建图:

# 构建工作流
router_builder = StateGraph(State)

# 添加节点
router_builder.add_node("add_document_to_database_tool", add_document_to_database_tool)
router_builder.add_node("delete_document_from_database_tool", delete_document_from_database_tool)
router_builder.add_node("ask_document_tool", ask_document_tool)
router_builder.add_node("llm_call_router", llm_call_router)

# 添加边以连接节点
router_builder.add_edge(START, "llm_call_router")
router_builder.add_conditional_edges(
    "llm_call_router",
    route_decision,
    {  # route_decision 返回的名称 : 要访问的下一个节点名称
        "add_document_to_database_tool": "add_document_to_database_tool",
        "delete_document_from_database_tool": "delete_document_from_database_tool",
        "ask_document_tool": "ask_document_tool",
    },
)
router_builder.add_edge("add_document_to_database_tool", END)
router_builder.add_edge("delete_document_from_database_tool", END)
router_builder.add_edge("ask_document_tool", END)

# 编译工作流
memory = InMemorySaver()
router_workflow = router_builder.compile(checkpointer=memory)
config = {"configurable": {"thread_id": "1"}}

# 可视化工作流
display(Image(router_workflow.get_graph().draw_mermaid_png()))

最后的显示函数会渲染出如下图所示的图结构:

LangGraph实战:构建高效Agentic工作流,解锁AI应用开发新范式

现在,我们可以按不同意图来运行这个工作流了。

添加文档:

user_input = "Add the document 'test.txt' with content 'This is a test document' to the database"
state = router_workflow.invoke({"input": user_input}, config)
print(state["output"])

# -> Document test.txt added to database

查询文档:

user_input = "Give me a summary of the document 'test.txt'"
state = router_workflow.invoke({"input": user_input}, config)
print(state["output"])

# -> A brief, generic test document with a simple descriptive sentence.

删除文档:

user_input = "Delete the document 'test.txt' from the database"
state = router_workflow.invoke({"input": user_input}, config)
print(state["output"])

# -> Document test.txt deleted from database

可以看到,工作流在不同的路由路径下都能正确运行。你可以根据需要轻松地增加更多意图,或为每个意图添加更多节点,从而构建更复杂的工作流。

更强的 Agentic 用例

“Agentic Workflows” 与完全的 “Agentic Applications” 之间的区别有时会令人困惑。为了区分这两个术语,这里引用 Anthropic 在《Building effective agents》一文中的观点:

工作流 是通过预定义的代码路径来编排 LLM 和工具的系统;而 智能体 是由 LLM 动态地指挥其自身流程与工具使用、并持续控制任务完成方式的系统。

大多数使用 LLM 解决的问题更适合采用工作流模式,因为多数问题(根据经验)是预定义的,并且应该遵循一套预设的防护栏。例如,在上述添加/删除/查询文档的场景中,最佳实践就是先定义意图分类器,并基于不同意图设定好后续的固定流程。

但在某些场景下,你可能需要构建更具自主性的智能体应用。例如,一个能够在你代码库中搜索、在线查阅最新文档并直接修改代码的编程助手。这类应用的潜在场景极为多样,很难预先定义固定的工作流。

若想构建具备更高自主性的智能体系统,可以深入探索其核心概念。

LangGraph 的优缺点

优点

我认为 LangGraph 主要有以下三个优点:

  • 上手简单:安装和快速启动非常便捷。参考官方文档,或利用 AI 助手(如 Cursor)基于文档实现特定工作流,都能轻松开始。
  • 开源:其代码完全开源。这意味着无论背后的公司如何变化,你都可以确保项目代码的长期可运行性,这对于生产环境至关重要。
  • 代码简洁:它显著简化了大量样板代码,并抽象掉了许多原本需要手动处理的复杂逻辑,让开发更聚焦于业务本身。

缺点

在实际使用中,我也发现了一些不足之处:

  • 仍有一定样板代码:在实现自定义工作流时,虽然比从零开始写代码量少,但仍需要编写一定量的配置和结构代码,以实现一个相对简单的流程。这部分源于 LangGraph 定位为一个比某些高层框架(如 LangChain)更底层的工具,旨在提供灵活性的同时避免过度抽象。
  • 特有的集成问题:如同许多第三方库,集成 LangGraph 时可能会遇到其特有的错误。例如,在尝试可视化工作流图时,我遇到了与 _draw_mermaid_png 函数相关的问题。使用外部库总是在“获得的便利抽象”和“可能遇到的特定 Bug”之间进行权衡。

总结

总体而言,我认为 LangGraph 是构建智能体系统的一个非常有用的工具。通过意图分类来路由到不同处理流程的方式,让我能够相对轻松地搭建所需的工作流。它在“避免过度抽象以保持代码透明和易于调试”与“封装不必要的复杂性”之间取得了良好的平衡。采用此类智能体框架有利有弊,而判断其是否适合你的最佳方式,就是亲自尝试实现几个简单的工作流。

原文地址:https://pub.towardsai.net/how-to-build-effective-agentic-systems-with-langgraph-e5433d7aa153


关注“鲸栖”小程序,掌握最新AI资讯

本文由鲸栖原创发布,未经许可,请勿转载。转载请注明出处:http://www.itsolotime.com/archives/13403

(0)
上一篇 2025年11月20日 下午12:37
下一篇 2025年11月21日 上午11:30

相关推荐

  • TritonForge:剖析引导+LLM协同,突破Triton内核优化瓶颈,成功率42.7%最高提速5倍

    TritonForge: Profiling-Guided Framework for Automated Triton Kernel Optimization https://arxiv.org/pdf/2512.09196 本文提出 TritonForge,一款基于剖析引导的自动化 Triton 内核优化框架,旨在解决现代机器学习中 GPU 内核优化耗时…

    12小时前
    700
  • 解锁Agentic AI并行化:14个核心模式提升系统可靠性与性能

    构建高效的智能体(Agentic)系统,离不开扎实的软件工程实践。其核心在于设计能够协调运作、并行执行,并能与外部系统高效交互的组件。例如,推测执行(Speculative Execution) 通过预先处理可预测的请求来降低延迟;冗余执行(Redundant Execution) 则通过同时运行同一智能体的多个副本来避免单点故障,提升系统韧性。除此之外,还…

    2025年11月27日
    700
  • LLM 大模型工程师:AI 时代的弄潮儿

    随着 LLM 技术的不断发展和突破,LLM 大模型工程师这一新兴职业应运而生,他们正成为推动 AI 进步的关键力量,对于传统软件工程师来说,了解并迈向这一领域,或许将开启一段充满机遇与挑战的职业新征程。

    2025年10月2日
    21100
  • 周末实战:5个能放进作品集的Agentic AI项目,助你求职脱颖而出

    人们常把“Agentic AI”描绘成只有大型实验室才能驾驭的高深技术。事实并非如此。 你完全可以在几天内,构建出真正能放进作品集的智能体项目。这些项目能解决实际问题,从而在求职时为你加分,而不是只会运行花哨提示词的玩具。 这里有五个你马上就可以动手实践的项目,即使你只有一台在卧室里、电量只剩一半的笔记本电脑。 我们将通过简单的示例逐一讲解,让你看清各个组件…

    2025年12月8日
    500
  • 从AI聊天到代理小队:如何用SCCR框架替代50%编码时间

    AI 生成的图片(概念与提示由作者撰写) 某个深夜,我几乎要关闭代码编辑器,开始质疑自己是否还属于这个行业。 我遵循了所有“正确”的实践:多年的经验、整洁的提交记录、扎实的代码评审。然而,我却目睹着更年轻的开发者以快我一倍的速度交付功能。原因在于,他们天生采用了一种“AI优先”的工作方式,而我仍将AI视为一个更聪明的搜索框。 他们在与“代理”结对编程。我却在…

    2025年11月20日
    400

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注