文章
· 十月 24 阅读大约需 14 分钟

构建 AI 智能体:从小白到大神

学习如何使用 LangGraph 设计结合了推理、矢量搜索和工具集成的可扩缩自主 AI 智能体。

cover

概括

  • AI 智能体是一种超越简单的聊天机器人的自主系统,它结合了记忆库、上下文,并具有自动完成任务的主动性。
  • LangGraph 是一种框架,它使我们能够利用具有内置状态管理的节点(任务)和边缘(连接),构建复杂的 AI 工作流。
  • 本指南将指导您构建 AI 赋能的客户支持智能体,该智能体可以划分优先级,识别相关主题,并确定是上报还是自动回复。

那么,AI 智能体究竟是什么?

让我们直面它吧 —“AI 智能体”听起来就像可以接管会议室的机器人。 实际上,它们是您得力的助手,可以简化复杂的工作流,消除重复性任务。 您可以把它们看作是聊天机器人的下一个进化阶段:它们不只是简单地等待提示;它们可以发起行动,协调多个步骤,并随时进行调整。

过去,打造一个“智能”系统意味着兼顾语言理解、代码生成、数据查找等各种不同的模型,然后将它们粘合在一起。 您的一半时间花在了集成上,另一半时间则花在了调试上。

智能体彻底颠覆了这一切。 它们将上下文、主动性和适应性融合在一个精心编排的流程中。 它们不仅实现了自动化,更是肩负使命的智者。 借助 LangGraph 之类的框架,我相信,组建一支自己的智能体团队实际上会很有趣。

image

LangGraph 究竟是什么?

LangGraph 是一种创新型框架,它彻底改变了我们构建涉及大语言模型 (LLM) 的复杂应用程序的方式。

想象一下,您正在指挥一支管弦乐队:每种乐器(或“节点”)都需要知道何时演奏,声音有多大,顺序如何。 这种情况下,LangGraph 就是您的指挥棒,为您提供以下内容:

  • 图结构:它采用具有节点和边缘的图结构,使开发者能够设计适应各种分支和循环的灵活非线性工作流。 它可以反映复杂的决策过程,类似于神经通路的运作方式。
  • 状态管理:LangGraph 提供了各种内置工具,可以实现状态保持和错误恢复,简化了应用程序中各个阶段的上下文数据的维护。 借助 Zep 等工具,它可以在短期记忆和长期记忆之间高效切换,提高了交互质量。
  • 工具集成:借助 LangGraph,LLM 智能体可以轻松与外部服务或数据库协作,获取真实的数据,从而改进应用程序的功能和响应性。
  • 人机协同:除了可以实现自动化外,LangGraph 还可以适应工作流中的人为干预,这对于需要分析监督或伦理考虑的决策过程至关重要。

无论您是在构建具有真实记忆的聊天机器人、交互式故事引擎,还是能够处理复杂问题的智能体团队,LangGraph 都可以将令人头疼的管道工程转变成简单明了、直观的状态机。

开始

要开始使用 LangGraph,您需要进行基本的设置,通常包括安装 langgraph 和 langchain-openai 等必要的库。 然后,您可以定义图中的节点(任务)和边缘(连接),有效地实现短期记忆的检查点,并利用 Zep 满足更持久的记忆需求。

操作 LangGraph 时,请记住以下几点:

  • 具有灵活性的设计:利用强大的图结构来解释并非严格线性的潜在工作流分支和交互。
  • 以深思熟虑的方式与工具交互:利用外部工具增强 LLM 功能,而不是取而代之。 为每个工具提供全面的描述,以实现精确使用。
  • 采用丰富的记忆解决方案:高效地使用记忆库,留意 LLM 的上下文窗口,并考虑集成外部解决方案,以实现自动事实管理。

现在,我们已经介绍 LangGraph 的基础知识,我们来看一个实例。 为此,我们将开发一个用于提供客户支持的 AI 智能体。

这个智能体将接收电子邮件请求,分析电子邮件正文中的问题描述,然后确定请求的优先级和适当的主题/类别/部门。

系好安全带,我们开始吧!

buckle up

首先,我们需要定义什么是“工具”。 可以把它看作是智能体的专属“助理”,使其能够与外部功能进行交互。

@tool 装饰器在这里必不可少。 LangChain 简化了自定义工具的创建,这意味着首先,定义一个 Python 函数,然后应用 @tool 装饰器。

tools

为了进行说明,我们来创建第一个工具。 这个工具将帮助智能体根据电子邮件内容划分 IT 支持工单的优先级:

    from langchain_core.tools import tool

    @tool
    def classify_priority(email_body: str) -> str:
        """Classify the priority of an IT support ticket based on email content."""
        prompt = ChatPromptTemplate.from_template(
            """Analyze this IT support email and classify its priority as High, Medium, or Low.

            High: System outages, security breaches, critical business functions down
            Medium: Non-critical issues affecting productivity, software problems
            Low: General questions, requests, minor issues

            Email: {email}

            Respond with only: High, Medium, or Low"""
        )
        chain = prompt | llm
        response = chain.invoke({"email": email_body})
        return response.content.strip()

太棒了! 现在,我们有一个提示,指示 AI 接收电子邮件正文,对其进行分析,并将其优先级分为“高”、“中”或“低”。

就是这样! 您刚刚编写了一个智能体可以调用的工具!

接下来,我们创建一个类似的工具来识别支持请求的主要主题(或类别):


@tool def identify_topic(email_body: str) -> str: """Identify the main topic/category of the IT support request.""" prompt = ChatPromptTemplate.from_template( """Analyze this IT support email and identify the main topic category. Categories: password_reset, vpn, software_request, hardware, email, network, printer, other Email: {email} Respond with only the category name (lowercase with underscores).""" ) chain = prompt | llm response = chain.invoke({"email": email_body}) return response.content.strip()

现在,我们需要创建一个状态,在 LangGraph 中,这一小部分非常重要。

把它想象成图的中枢神经系统。 这就是节点之间的通信方式,就像优等生在课堂上传递纸条一样。

文档中显示:

“状态是表示应用程序当前快照的共享数据结构。”

在实践中呢? 状态是在节点之间移动的结构化消息。 它将一个步骤的输出作为下一个步骤的输入。 基本上,它是将整个工作流粘合在一起的粘合剂。

因此,在构建图之前,我们必须先定义我们的状态结构。 本例中,我们的状态包括以下内容:

  • 用户请求(电子邮件正文)
  • 指定的优先级
  • 确定的主题(类别)

它简单明了,因此您可以像专业人士一样浏览图。

    from typing import TypedDict

    # Define the state structure
    class TicketState(TypedDict):
        email_body: str
        priority: str
        topic: str


    # Initialize state
    initial_state = TicketState(
        email_body=email_body,
        priority="",
        topic=""
    )

节点与 边缘:LangGraph 的关键组成部分

LangGraph 的基本要素包括节点边缘

  • 节点:它们是图中的操作单元,执行实际工作。 节点通常由可以执行任何逻辑(从计算到与语言模型 (LLM) 或外部集成交互)的 Python 代码组成。 从本质上讲,节点就像传统编程中的个别函数或智能体。
  • 边缘:边缘定义节点之间的执行流,决定接下来会发生什么。 它们充当连接器,允许状态根据预定义条件从一个节点转换到另一个节点。 在 LangGraph 中,边缘在协调节点之间的序列和决策流方面至关重要。

为了掌握边缘的功能,我们来看一个消息传递应用程序的简单类比:

  • 节点类似于积极参与对话的用户(或他们的设备)。
  • 边缘代表着用户之间能够促进沟通的聊天主题或连接。

当用户选择一个聊天主题来发送消息时,会有效地创建一个边缘,将他们与另一个用户连接在一起。 与 LangGraph 状态的结构化模式类似,每次交互(无论是发送文本、语音还是视频消息)都遵循预定义的顺序。 它确保了沿边缘传递的数据的一致性和可解释性。

不同于事件驱动型应用程序的动态特性,LangGraph 采用在整个执行过程中保持一致的静态模式。 它简化了节点之间的通信,使开发者可以依赖稳定的状态格式,从而确保无缝的边缘通信。

设计基本工作流

可以将 LangGraph 中的流工程理解为设计一个状态机。 在这个情境中,每个节点代表一个不同的状态或处理步骤,而边缘定义了这些状态之间的转换。 这种方式对想要在 AI 的确定性任务序列与动态决策能力之间取得平衡的开发者特别有用。 我们来使用前面定义的 TicketState 类初始化 StateGraph,开始构建流程。

    from langgraph.graph import StateGraph, START, END

    workflow = StateGraph(TicketState)

节点添加:节点是基本要素,用于执行划分工单优先级或识别其主题等特定任务。

每个节点函数均接收当前状态,执行其操作,并返回一个字典以更新状态:

   def classify_priority_node(state: TicketState) -> TicketState:
        """Node to classify ticket priority."""
        priority = classify_priority.invoke({"email_body": state["email_body"]})
        return {"priority": priority}

    def identify_topic_node(state: TicketState) -> TicketState:
        """Node to identify ticket topic."""
        topic = identify_topic.invoke({"email_body": state["email_body"]})
        return {"topic": topic}


    workflow.add_node("classify_priority", classify_priority_node)
    workflow.add_node("identify_topic", identify_topic_node)

classify_priority_node 和 identify_topic_node 方法将更改 TicketState 并发送参数输入。

边缘创建:定义连接节点的边缘:


workflow.add_edge(START, "classify_priority") workflow.add_edge("classify_priority", "identify_topic") workflow.add_edge("identify_topic", END)

classify_priority 确定起点,而 identify_topic 确定到目前为止工作流的终点。

编译与执行:配置节点和边缘后,编译并执行该工作流。


graph = workflow.compile() result = graph.invoke(initial_state)

太好了! 您还可以生成 LangGraph 流的可视化表示。

graph.get_graph().draw_mermaid_png(output_file_path="graph.png")

如果将代码运行到此点,您就会看到一个与下面类似的图:

first_graph.png

这幅图直观地显示了一次顺序执行:开始,然后划分优先级,接着确定主题,最后结束。

LangGraph 最强大的一个方面是它的灵活性,这让我们可以创建更复杂的流程和应用程序。 例如,我们可以修改工作流,使用以下行将 START 中的边缘添加到两个节点:

    workflow.add_edge(START, "classify_priority")
    workflow.add_edge(START, "identify_topic")

这一更改意味着智能体将同时执行 classify_priority 和 identify_topic。

LangGraph 中另一个非常有用的功能是能够使用条件边缘。 它们允许工作流根据对当前状态的评估进行分支,实现任务的动态路由。

我们来增强工作流。 我们将创建一个新工具,分析请求的内容、优先级和主题,以确定它是否为需要上报(例如,为人工团队提交工单)的高优先级问题。 如果不需要,将为用户生成一个自动响应。


@tool def make_escalation_decision(email_body: str, priority: str, topic: str) -> str: """Decide whether to auto-respond or escalate to IT team.""" prompt = ChatPromptTemplate.from_template( """Based on this IT support ticket, decide whether to: - "auto_respond": Send an automated response for simple/common or medium priority issues - "escalate": Escalate to the IT team for complex/urgent issues Email: {email} Priority: {priority} Topic: {topic} Consider: High priority items usually require escalation, while complex technical issues necessitate human review. Respond with only: auto_respond or escalate""" ) chain = prompt | llm response = chain.invoke({ "email": email_body, "priority": priority, "topic": topic }) return response.content.strip()

此外,如果请求被确定为低优先级或中等优先级(导致“auto_respond”决策),我们将执行矢量搜索来检索历史回答。 然后,将使用此信息来生成适当的自动回答。 不过,这需要两个额外的工具:


@tool def retrieve_examples(email_body: str) -> str: """Retrieve relevant examples from past responses based on email_body.""" try: examples = iris.cls(__name__).Retrieve(email_body) return examples if examples else "No relevant examples found." except: return "No relevant examples found." @tool def generate_reply(email_body: str, topic: str, examples: str) -> str: """Generate a suggested reply based on the email, topic, and RAG examples.""" prompt = ChatPromptTemplate.from_template( """Generate a professional IT support response based on: Original Email: {email} Topic Category: {topic} Example Response: {examples} Create a helpful, professional response that addresses the user's concern. Keep it concise and actionable.""" ) chain = prompt | llm response = chain.invoke({ "email": email_body, "topic": topic, "examples": examples }) return response.content.strip()

现在,我们为这些新工具定义相应的节点:


def decision_node(state: TicketState) -> TicketState: """Node to decide on escalation or auto-response.""" decision = make_escalation_decision.invoke({ "email_body": state["email_body"], "priority": state["priority"], "topic": state["topic"] }) return {"decision": decision} def rag_node(state: TicketState) -> TicketState: """Node to retrieve relevant examples using RAG.""" examples = retrieve_examples.invoke({"email_body": state["email_body"]}) return {"rag_examples": examples} def generate_reply_node(state: TicketState) -> TicketState: """Node to generate suggested reply.""" reply = generate_reply.invoke({ "email_body": state["email_body"], "topic": state["topic"], "examples": state["rag_examples"] }) return {"suggested_reply": reply} def execute_action_node(state: TicketState) -> TicketState: """Node to execute final action based on decision.""" if state["decision"] == "escalate": action = f"🚨 ESCALATED TO IT TEAM\nPriority: {state['priority']}\nTopic: {state['topic']}\nTicket created in system." print(f"[SYSTEM] Escalating ticket to IT team - Priority: {state['priority']}, Topic: {state['topic']}") else: action = f"✅ AUTO-RESPONSE SENT\nReply: {state['suggested_reply']}\nTicket logged for tracking." print(f"[SYSTEM] Auto-response sent to user - Topic: {state['topic']}") return {"final_action": action} workflow.add_node("make_decision", decision_node) workflow.add_node("rag", rag_node) workflow.add_node("generate_reply", generate_reply_node) workflow.add_node("execute_action", execute_action_node)

然后,条件边缘将使用 make_decision 节点的输出来定向流:

    workflow.add_conditional_edges(
        "make_decision",
        lambda x: x.get("decision"),
        {
            "auto_respond": "rag",
            "escalate": "execute_action"
        }
    )

如果 make_escalation_decision 工具(通过 decision_node)产生“auto_respond”,工作流将继续通过 RAG 节点(检索示例),然后是 generate_reply 节点(设计回答),最后是 execute_action 节点(记录 auto-response)。

相反,如果决策是“escalate”,流程将绕过 RAG 和生成步骤,直接转到 execute_action 来处理上报。 要添加剩余的标准边缘来完成图,请执行以下操作:

    workflow.add_edge("rag", "generate_reply")
    workflow.add_edge("generate_reply", "execute_action")
    workflow.add_edge("execute_action", END)

数据集注释:对于此项目,我们用于支持检索增强生成 (RAG) 的数据集来自 Hugging Face 上的 Customer Support Tickets 数据集。 对该数据集进行了筛选,以便只包含分类为技术支持并限制为英语的条目。 它确保 RAG 系统只为技术支持任务检索高度相关的特定领域的示例。

此时,我们的图应当与下图类似:

graph.png

当您使用一封电子邮件来执行此图,并导致高优先级分类和“escalate”决策时,您将看到以下响应:

image.png

同时,被分类为低优先级并导致“auto_respond”决策的请求将触发与下面类似的回复:

image.png

那么… 这一路都很顺利吗?

并不完全是。 有一些问题需要注意:

  • 数据隐私:小心敏感信息 — 这些智能体需要防护。
  • 计算成本:某些高级设置需要大量资源。
  • 幻觉:LLM 偶尔也会编造一些内容(不过仍然比大多数实习生聪明)。
  • 非确定性:相同的输入可能会返回不同的输出,这对创造力来说是好事,但对严格的流程来说却很棘手。

不过,大多数缺点都可以通过良好的规划、合适的工具和一点点思考加以控制。

LangGraph 将 AI 智能体从流行语变成实实在在的有效解决方案。 无论您是要自动执行客户支持,处理 IT 工单,还是构建自主应用程序,这个框架都让操作变得可行,甚至有趣。

您有任何问题或反馈吗? 请说出来。 AI 革命需要像您这样的建设者。

讨论 (0)1
登录或注册以继续