langchain应用-agent

什么是 LangChain Agent

在 LangChain 中,Agent 是一个代理,接收用户的输入,采取相应的行动然后返回行动的结果。Agent 可以看作是一个自带路由消费 Chains 的代理,基于 MRKL 和 ReAct 的基本原理,Agent 可以使用工具和自然语言处理问题。官方也提供了对应的 Agent,包括 OpenAI Functions Agent、Plan-and-execute Agent、Self Ask With Search 类 AutoGPT 的 Agent 等。Agent 的作用是代表用户或其他系统完成任务,例如数据收集、数据处理、决策支持等。Agent 可以是自主的,具备一定程度的智能和自适应性,以便在不同的情境中执行任务。我们今天主要了解基于 ReAct 原理来实现的 Agent。

ReAct

ReAct 是一个结合了推理和行动的语言模型。虽然 LLM 在语言理解和交互决策制定方面展现出了令人印象深刻的能力,但它们的推理(例如链式思考提示)和行动(例如行动计划生成)的能力主要被视为两个独立的主题。ReAct 的目标是探索如何使用 LLM 以交错的方式生成推理痕迹和特定任务的行动,从而在两者之间实现更大的协同作用。

想象一下,你有一个智能助手机器人,名叫小明。你给小明一个任务:去厨房为你做一杯咖啡。小明不仅要完成这个任务,还要告诉你他是如何一步步完成的。

没有 ReAct 的小明:

  1. 小明直接跑到厨房。

  2. 你听到了一些声音,但不知道小明在做什么。

  3. 过了一会儿,小明回来给你一杯咖啡。

这样的问题是,你不知道小明是怎么做咖啡的,他是否加了糖或奶,或者他是否在过程中遇到了任何问题。

有 ReAct 的小明:

  1. 小明告诉你:"我现在去厨房。"

  2. 小明再说:"我找到了咖啡粉和咖啡机。"

  3. "我现在开始煮咖啡。"

  4. "咖啡煮好了,我要加点糖和奶。"

  5. "好了,咖啡做好了,我现在给你拿过去。"

这次,你完全知道小明是怎么做咖啡的,知道他的每一个步骤和决策。

ReAct 就是这样的原理。它不仅执行任务(行动),还会告诉你它是如何思考和决策的(推理)。这样,你不仅知道任务完成了,还知道为什么这样做,如果有问题,也更容易找出原因。

更多关于 ReAct 的内容可以查看这篇文章[1]。

提示词模板

要实现 Agent,我们需要先定义一套基于 ReAct 的提示词模板,示例中的 Agent 就是基于 ReAct 原理来实现的,为了方便理解,我们将官方的英文提示词模板换成中文和去掉一些没必要的内容,修改后的提示词模板内容如下:

bash 复制代码
template = """尽你所能回答以下问题,你可以使用以下工具:
{tools}
请按照以下格式:
问题:你必须回答的输入问题
思考:你应该始终考虑该怎么做
行动:要采取的行动,应该是[{tool_names}]中的一个
行动输入:行动的输入
观察:行动的结果
... (这个思考/行动/行动输入/观察可以重复N次)
思考:我现在知道最终答案了
最终答案:对原始输入问题的最终答案
开始吧!
问题:{input}
{agent_scratchpad}"""

简单介绍一下这个提示词模板,首先提示词模板中会引用到一些工具,可以看到模板中有 2 个变量:toolstool_names,tools 变量是一个列表,包含了所有的工具,列表中的每个元素包含了工具的名称和描述,而 tool_names 变量是工具名称的列表,传入具体的工具后,会生成对应的工具列表,比如我们有如下 2 个工具,解析后如下所示:

bash 复制代码
尽你所能回答以下问题,你可以使用以下工具:
search: 实时联网搜索的工具
math: 数学计算的工具
请按照以下格式:
......
行动:要采取的行动,应该是[search, math]中的一个
......

这样 LLM 在执行任务时就知道要使用哪些工具,以及在提取信息时可以提取到正确的工具名。

模板中下面的思考/行动/行动输入/观察就是标准的 ReAct 流程,思考是指思考如何解决问题,行动是具体的工具,行动输入是工具用到的参数,观察是工具执行完成后得到的结果,这个流程可以重复多次,直到最终得到最终答案。

模板最后还有 2 个变量:inputagent_scratchpad,input 是用户输入的问题,agent_scratchpad 是之前的思考过程(下面解析代码时会讲),包括了思考、行动、行动输入和观察等,这个变量在 Agent 执行过程中会被更新,代入具体的值后,模板会生成如下的提示词:

bash 复制代码
......
问题:北京的天气怎么样
思考: 我们需要通过 search 工具查找北京天气。
行动: search
行动输入: "北京天气"
观察: 6日(今天). 多云转晴. 32/22℃. <3级
思考:

从问题下面一句到最后结束就是agent_scratchpad的值。

Langchain 组件

Langchain Agent 相关有下面几个关键组件:

代理

这是负责决定下一步采取什么动作的类。 这是由语言模型和提示驱动的。 该提示可以包括以下内容:

  1. 代理的个性(对于以某种方式响应很有用)

  2. 代理的背景上下文(对于给予其更多关于所要求完成的任务类型的上下文很有用)

  3. 调用更好推理的提示策略(最著名/广泛使用的是ReAct

工具

工具是代理调用的函数。 这里有两个重要的考虑因素:

  1. 给代理访问正确工具的权限

  2. 以对代理最有帮助的方式描述工具

如果没有这两者,您想要构建的代理将无法工作。 如果您不给代理访问正确工具的权限,它将永远无法完成目标。 如果您不正确描述工具,代理将不知道如何正确使用它们。

代理执行器

代理执行器是代理的运行时。 这是实际调用代理并执行其选择的动作的部分。 以下是此运行时的伪代码:

bash 复制代码
next_action = agent.get_action(...)
while next_action != AgentFinish:
    observation = run(next_action)
    next_action = agent.get_action(..., next_action, observation)
return next_action

虽然这看起来很简单,但此运行时为您处理了几个复杂性,包括:

  1. 处理代理选择不存在的工具的情况

  2. 处理工具发生错误的情况

  3. 处理代理生成无法解析为工具调用的输出的情况

  4. 在所有级别上记录和可观察性(代理决策,工具调用)-可以输出到stdout或LangSmith

代理运行时

AgentExecutor类是LangChain支持的主要代理运行时.

记忆体

大多数(如果不是全部)内存模块的核心实用类之一是 ChatMessageHistory 类。这是一个超轻量级的包装器,它公开了方便的方法来保存人类消息、AI 消息,然后获取它们全部。

如果您在链外管理内存,可能需要直接使用此类。

bash 复制代码
from langchain.memory import ChatMessageHistory history = ChatMessageHistory() history.add_user_message("hi!") history.add_ai_message("whats up?")


history.messages


[HumanMessage(content='hi!', additional_kwargs={}), AIMessage(content='whats up?', additional_kwargs={})]

langchain通过在历史信息基础支持上进行封装,提供了 带窗口大小的以及缓冲 ConversationBufferWindowMemory,

对回话记录进行摘要使用的 ConversationSummaryMemory。

代码案例

python 复制代码
from pydantic import BaseModel, Field
import datetime

from langserve import add_routes
from typing import Any, List, Union
from fastapi import status as fastapistatus

from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from fastapi import FastAPI, HTTPException
from langchain_ollama import ChatOllama
from langchain.agents import tool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents.format_scratchpad.openai_tools import (
    format_to_openai_tool_messages,
)
from langchain.agents import AgentExecutor
from langchain.agents.output_parsers.openai_tools import (
    OpenAIToolsAgentOutputParser
)

import logging
from langchain.memory import (
    ConversationSummaryBufferMemory,
    ConversationBufferWindowMemory
)

logger = logging.getLogger(__name__)


# 注册计算字符串长度的工具,当询问计算输入长度时即可通过agent_scratchpad 后的关键字
# 关联上该工具计算输出,从而在该类问题中使用工具计算结果回答
@tool
def get_word_length(word: str) -> int:
    """Returns the length of a word."""
    return len(word)


tools = [get_word_length]


class Agent:
  

    def __init__(self, name, agent_id, role_describe, model="qwen2:7b",
                 temperature=0.7, token_limit=75):
        self.name = name
        self.agent_id = agent_id

        # 使用 ollama 中的模型 "qwen2:7b" 作为llm model
        llm = ChatOllama(
            model=model,
            temperature=temperature,
            verbose=True
        )
        
        # 提示词构建
        prompt = ChatPromptTemplate.from_messages(
            [
                (
                    "system",
                    role_describe,
                ),
                ("user", "{input}"),
                # agent_scratchpad 记录了每步思考过程
                MessagesPlaceholder(variable_name="agent_scratchpad"),
                # chat_history 注入对话历史,使用对对话历史的管理可实现记忆
                MessagesPlaceholder(variable_name="chat_history"),
            ]
        )
        self.prompt = prompt
        
        # 窗口历史,即记忆只记录最近 token_limit 句对话
        memory = ConversationBufferWindowMemory(k=token_limit,
                                                memory_key="chat_history",
                                                return_messages=True)

        
        self.memory = memory
        # 绑定工具
        llm_with_tools = llm.bind_tools(tools)
        self.tools = llm_with_tools

        agent = (
            {
                "input": lambda x: x["input"],
                "agent_scratchpad": lambda x: format_to_openai_tool_messages(
                    x["intermediate_steps"]
                ),
                "chat_history": lambda x:
                memory.load_memory_variables({})["chat_history"],
            }
            | prompt
            | llm_with_tools
            | OpenAIToolsAgentOutputParser()
        )

        # 运行agent runner
        agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
        self.agent_executor = agent_executor

        

    def chat(self, input_text):   
        # 输入input, 进行对话 
        response = self.agent_executor.invoke({"input": input_text})  
        self.memory.save_context(inputs={'input': response['input']}, 
            outputs={'output': response['output']})
        if response:
           return response['output']
        else:
            return ('There are no result..')


def command_chat(agent):
    while True:
        user_input = input("input:")
        user_output = daxiong_agent.chat(user_input)
        print(user_output)


if __name__ == "__main__":
    daxiong_agent = AgentRole("daxiong", "1234567", "你是一个中二的智能助手")
    command_chat(daxiong_agent)
相关推荐
Neo很努力13 小时前
【deepseek】本地部署+RAG知识库挂载+对话测试
自然语言处理·chatgpt·langchain·aigc·llama
朴拙数科1 天前
Langchain vs. LlamaIndex:哪个在集成MongoDB并分析资产负债表时效果更好?
数据库·mongodb·langchain
静静的喝酒5 天前
langchain学习笔记之消息存储在内存中的实现方法
langchain·消息存储·内存存储
Rickie5 天前
DeepSeek-V3 解读:优化效率与规模
langchain·rag·deepseek
少林码僧5 天前
1.5 企业级AI大模型四阶技术全景解析:从Prompt到Pre-training的进化路径
人工智能·gpt·chatgpt·langchain·prompt
Clang's Blog7 天前
使用 LangChain 对接硅基流动(SiliconFlow)API:构建一个智能对话系统
langchain
海绵波波1077 天前
Langchain对管道操作符|的重构实现链式流程
python·重构·langchain
morning_sir_jking7 天前
【无标题】
ai·langchain
nmblr9 天前
LangChain基础篇 (06)
langchain