黄朝忆的个人博客
登录|注册

8. LangChain框架-2

创建 2026-10-06 14:0716579 字8 浏览0 评论

Agent代理

    Agent代理的核心思想是使用语言模型来选择要采取的一系列动作。在链中,动作序列是硬编码的。

    在代理中,语言模型用作推理引擎来确定要采取哪些动作以及按什么顺序进行。

    因此,在LangChain中,Agent代理就是使用语言模型作为推理引擎,让模型自主判断、调用工具和决定下一步行动。

    Agent代理像是一个多功能接口,能够使用多种工具,并根据用户输入决定调用哪些工具,同时能够将一个工具的输出数据作为另一个工具的输入数据。


Tavily在线搜索

    构建一个具有两种工具的代理:一种用于在线查找,另一种用于查找加载到索引中的特定数据。

    在LangChain中有一个内置的工具,可以方便地使用Tavily搜索引擎作为工具。

    访问Tavily(用于在线搜索)注册账号并登录,获取API 密钥

    TAVILY_API_KEY申请:https://tavily.com/

    

最简单的示例

import os
from langchain_community.tools.tavily_search import TavilySearchResults
from dotenv import load_dotenv

load_dotenv()

# 查询 Tavily 搜索 API 并返回 json 的工具
search = TavilySearchResults(tavily_api_key=os.getenv("tavily_key"))
# 执行查询
res = search.invoke("目前市场上苹果手机16的售价是多少?")
print(res)

创建检索器

    根据上述查询结果中的某个URL中,获取一些数据创建一个检索器。


# 加载所需的库
import os

from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv

load_dotenv()

# 查询 Tavily 搜索 API 并返回 json 的工具
# search = TavilySearchResults()
# # 执行查询
# res = search.invoke("目前市场上苹果手机16的售价是多少?")
# print(res)


# 创建索引器根据上述查询的结果

# 加载HTML内容为一个文档对象
loader = WebBaseLoader("https://news.qq.com/rain/a/20240920A07Y5Y00")
# 读取文档
docs = loader.load()
# print(docs)

# 分割文档
documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)

# 向量化
vector = FAISS.from_documents(documents, DashScopeEmbeddings(dashscope_api_key=os.getenv('api_key')))

# 创建检索器
retriever = vector.as_retriever()

# 测试检索结果
print(retriever.get_relevant_documents("目前市场上苹果手机16的售价是多少?"))


对接大模型

    创建Agent,这里使用LangChain中一个叫OpenAI functions的代理,然后得到一个AgentExecutor代理执行器


# 加载所需的库
import os

from langchain import hub
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_openai import ChatOpenAI
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv
from langchain.tools.retriever import create_retriever_tool
load_dotenv()

# 查询 Tavily 搜索 API 并返回 json 的工具
search = TavilySearchResults(tavily_api_key=os.getenv("tavily_key"))
# # 执行查询
# res = search.invoke("目前市场上苹果手机16的售价是多少?")
# print(res)


# 创建索引器根据上述查询的结果

# 加载HTML内容为一个文档对象
loader = WebBaseLoader("https://news.qq.com/rain/a/20240920A07Y5Y00")
# 读取文档
docs = loader.load()
# print(docs)

# 分割文档
documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)

# 向量化
vector = FAISS.from_documents(documents, DashScopeEmbeddings(dashscope_api_key=os.getenv('api_key')))

# 创建检索器
retriever = vector.as_retriever()

# 测试检索结果
# print(retriever.get_relevant_documents("目前市场上苹果手机16的售价是多少?"))


# 创建一个工具来检索文档
retriever_tool = create_retriever_tool(
    retriever,
    "iPhone_price_search",
    "搜索有关 iPhone 16 的价格信息。对于iPhone 16的任何问题,您必须使用此工具!",
)

# 创建将在下游使用的工具列表
tools = [search, retriever_tool]

# 初始化大模型
llm = ChatOpenAI(api_key=os.getenv("api_key"),
                 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                 model='qwen-plus', temperature=0)



# https://smith.langchain.com/hub
# 获取要使用的提示
prompt = hub.pull("hwchase17/openai-functions-agent")
# 打印Prompt
# print(prompt)

# 使用OpenAI functions代理
from langchain.agents import create_openai_functions_agent

# 构建OpenAI函数代理:使用 LLM、提示模板和工具来初始化代理
agent = create_openai_functions_agent(llm, tools, prompt)

from langchain.agents import AgentExecutor
# 将代理与AgentExecutor工具结合起来
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 执行代理 进行对比
# agent_executor.invoke({"input": "目前市场上苹果手机16的各个型号的售价是多少?如果我在此基础上加价5%卖出,应该如何定价?"})
agent_executor.invoke({"input": "美国2024年谁胜出了总统的选举?"})


OpenAI Functions Agent

    在LangChain中,create_openai_functions_agent是一个便捷的函数,用于创建能够与OpenAI提供的函数交互的代理。这使得开发人员可以创建智能应用程序,通过代理与用户进行更自然、更有效的对话。


from langchain.agents import create_openai_functions_agent, AgentExecutor
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain_core.tools import Tool
import os
from dotenv import load_dotenv

load_dotenv()


# 定义查询订单状态的函数
def query_order_status(order_id):
    if order_id == "1024":
        return "订单 1024 的状态是:已发货,预计送达时间是 3-5 个工作日。"
    else:
        return f"未找到订单 {order_id} 的信息,请检查订单号是否正确。"


# 定义退款政策说明函数
def company_refund_policy(company_name):
    print(company_name)
    if company_name == "tom公司":
        return "tom公司的退款政策是:在购买后7天内可以申请全额退款,需提供购买凭证。"
    else:
        print('输入有误')


# 查询年龄
def get_age(name):
    if name == "tom":
        print(name)
        return "我的年龄是56岁!"
    else:
        print('输入有误')


# 初始化工具
tools = [
    TavilySearchResults(max_results=1, tavily_api_key=os.getenv("tavily_key")),
    Tool(
        name="queryOrderStatus",
        func=query_order_status,
        description="根据订单ID查询订单状态",
        args={"order_id": "订单的ID"}
    ),
    Tool(
        name="companyRefundPolicy",
        func=company_refund_policy,
        description="查询某某公司退款政策详细内容",
        args={"company_name": "公司名称"}
    ),
    Tool(
        name="getAge",
        func=get_age,
        description="查询tom年龄大小",
        args={"name": "查询tom年龄大小"}
    ),
]

# 获取使用的提示
prompt = ChatPromptTemplate.from_messages([
    ("system",
        "你是一个客服助手,使用工具回答问题。传递给工具的内容必须是准确的json数据不结尾的括号多加一个,如果是字符串数据必须和输入的保持一致要完整,不是篡改**重要规则**, "
    ),
    # 用户输入变量
    ("user", "{input}"),
    # 关键点:添加代理中间步骤占位符
    MessagesPlaceholder(variable_name="agent_scratchpad")
])
# print(prompt)
# 选择将驱动代理的LLM
llm = ChatOpenAI(api_key=os.getenv("api_key"),
                 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                 model='qwen-plus')

# 构建OpenAI函数代理
agent = create_openai_functions_agent(llm, tools, prompt)

# 通过传入代理和工具创建代理执行器
agent_executor = AgentExecutor(agent=agent, tools=tools, handle_parsing_errors=True, verbose=True)

# 定义一些测试询问
queries = [
    "请问订单1024的状态是什么?",
    "请问tom公司退款政策是什么?",
    "2024年谁胜出了美国总统的选举"
]

# 运行代理并输出结果
for input in queries:
    response = agent_executor.invoke({"input": input})
    print(f"客户提问:{input}")
    print(f"代理回答:{response}\n")

# response = agent_executor.invoke({"input": "2024年谁胜出了美国总统的选举"})
# print(f"代理回答:{response}\n")


ReAct Agent

    ReAct (Reflective Agent) 是 LangChain 中的一种聊天代理(Agent)类型。它具有以下独特的特点:

  • 反思能力:ReAct 代理在给出响应之前,会先对自己的行为和预测进行深入的反思和评估。它会检查自己是否遵循了预先设定的规则和指令,是否达到了预期的目标。
  • 自我纠错:如果ReAct代理在反思过程中发现自己存在问题或疏漏,它会主动尝试对自己的行为进行纠正和改正,以修复错误,提高自身的表现。
  • 迭代学习:通过不断的反思和自我纠错,ReAct 代理可以在与用户的交互中逐步学习和优化自己的行为方式,不断提高回答的质量和准确性。
  • 可解释性:ReAct 代理在给出最终响应时,会同时提供自己的思考过程和决策依据,使得它的行为更加透明和可解释。

    这种具备反思和自我纠错能力的 ReAct 代理,在需要较高可靠性和稳定性的应用场景中很有优势,例如智能客服、问答系统、任务执行等。它可以通过持续的自我学习和优化,为用户提供更加智能和可信的交互体验。


    Google搜索API:访问 SerpApi ,注册账号,选择相应的订阅计划(Free),然后获取API Key,利用这个API为大模型提供Google搜索工具。

    SerpApi:https://serpapi.com/

from langchain_community.agent_toolkits.load_tools import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()

# 开启DEBUG 显示具体的日志信息
# langchain.debug = True
# langchain.verbose = True

# 初始化大模型:语言模型控制代理
llm = ChatOpenAI(
    api_key=os.getenv("api_key"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen-plus"
)

# 设置工具:加载使用的工具,serpapi:调用Google搜索引擎 
tools = load_tools(["serpapi"], llm=llm, SERPAPI_API_KEY=os.getenv("SERPAPI_API_KEY"))

# 初始化Agent:使用工具、语言模型和代理类型来初始化代理    ZERO_SHOT_REACT_DESCRIPTION 类型的代理可以在没有预先训练的情况下尝试解决新的问题
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

# 让代理来回答提出的问题
agent.invoke({"input": "目前市场上苹果手机16的售价是多少?用中文回答"})


Self-Ask with Search Agent

    Self-Ask with Search是一个通过搜索自我询问的代理,通过询问+答案的机制来帮助大模型寻找事实性问题的过渡性答案,从而引出最终答案。

import os

from dotenv import load_dotenv
from langchain import hub
from langchain.agents import AgentExecutor, create_self_ask_with_search_agent
from langchain_community.tools.tavily_search import TavilyAnswer
from langchain_openai import ChatOpenAI
load_dotenv()
# 将初始化工具,让它提供答案而不是文档
tools = [TavilyAnswer(name="Intermediate Answer", description="Answer Search")]

# 初始化大模型
llm = ChatOpenAI(
    api_key=os.getenv("api_key"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen-plus"
)

# 获取使用提示 可以修改此提示
prompt = hub.pull("hwchase17/self-ask-with-search")

# 使用搜索代理构建自助询问
agent = create_self_ask_with_search_agent(llm, tools, prompt)

# 通过传入代理和工具创建代理执行程序
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

# 运行代理
agent_executor.invoke({"input": "中国有哪些省份呢?"})

LangChain之Tools工具

1. 工具Tools

    工具是代理、链或LLM可以用来与世界互动的接口。它们结合了几个要素

  • 工具的名称
  • 工具的描述
  • 该工具输入的JSON模式
  • 要调用的函数
  • 是否应将工具结果直接返回给用户

    LangChain通过提供统一框架集成功能的具体实现。在框架内,每个功能被封装成一个工具,具有自己的输入输出及处理方法。代理接收任务后,通过大模型推理选择适合的工具处理任务。一旦选定,LangChain将任务输入传递给该工具,工具处理输入生成输出。输出经过大模型推理,可用于其他工具的输入或作为最终结果返回给用户。


    Langchain地址:https://python.langchain.com/api_reference/community/tools.html


2. 工具的初步认识

# pip install wikipedia
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from dotenv import load_dotenv
import os

load_dotenv()

# 初始化工具 可以根据需要进行配置
# 使用包装器WikipediaAPIWrapper进行搜索并获取页面摘要。默认情况下,它将返回前 k 个结果的页面摘要。它通过 doc_content_chars_max 限制文档内容。
api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)
tool = WikipediaQueryRun(api_wrapper=api_wrapper)

# 工具默认名称
print("name:", tool.name)
# 工具默认的描述
print("description:", tool.description)
# 输入内容 默认JSON模式
print("args:", tool.args)
# 是否直接返回工具的输出。
print("return_direct:", tool.return_direct)

# 可以用字典输入来调用这个工具
print(tool.run({"query": "langchain"}))
# 使用单个字符串输入来调用该工具。
print(tool.run("langchain"))
# 需要科学上网


3. 修改工具

    可以修改参数的内置名称、描述和JSON模式。

from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from pydantic import BaseModel, Field

# 初始化工具 可以根据需要进行配置
api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)


class WikiInputs(BaseModel):
    """维基百科工具的输入。"""

    query: str = Field(
        description="维基百科中的查询,字数应在3个字以内"
    )


tool = WikipediaQueryRun(
    name="wiki-tool",
    description="在维基百科中查找内容",
    args_schema=WikiInputs,
    api_wrapper=api_wrapper,
    return_direct=True,
)

# 工具默认名称
print("name:", tool.name)
# 工具默认的描述
print("description:", tool.description)
print(tool.run("langchain"))


4. 自定义工具

  • 在LangChain中,自定义工具有多种方法
  • @tool装饰器
  • @tool装饰器是定义自定义工具的最简单方法。装饰器默认使用函数名称作为工具名称,但可以通过传递字符串作为第一个参数来覆盖此设置。此外,装饰器将使用函数的文档字符串作为工具的描述 - 因此必须提供文档字符串。
from langchain.tools import tool

@tool
def add_number(a: int, b: int) -> int:
    """add two numbers."""
    return a + b


print(add_number.name)
print(add_number.description)
print(add_number.args)

res = add_number.run({"a": 10, "b": 20})
print(res)


5. 更多Tools

    Tavily Search工具:Tavily的搜索API是一个专门为人工智能代理(llm)构建的搜索引擎,可以快速提供实时、准确和真实的结果。

    访问Tavily(https://tavily.com/)注册账号并登录,获取API 密钥


from langchain import hub
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_openai import ChatOpenAI
import os
from dotenv import load_dotenv

load_dotenv()
tool = TavilySearchResults(max_results=1)
#  使用Tavily搜索工具
tools = [TavilySearchResults(max_results=1, tavily_api_key=os.getenv("TAVILY_API_KEY"))]
# print(tool.run("目前市场上黄金的售价是多少?"))
# https://smith.langchain.com/hub
# 获取要使用的提示
prompt = hub.pull("hwchase17/openai-tools-agent")

# 初始化大模型
llm = ChatOpenAI(
    api_key=os.getenv("API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen-plus"
)

# 构建 OpenAI 工具代理
agent = create_openai_tools_agent(llm, tools, prompt)

# 通过传入代理和工具创建代理执行程序
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 运行代理
agent_executor.invoke({"input": "中国有多少个省份?"})



LangChain之Memory

    大多数的 LLM 应用程序都会有一个会话接口,允许我们和 LLM 进行多轮的对话,并有一定的上下文记忆能力。但实际上,模型本身是不会记忆任何上下文的,只能依靠用户本身的输入去产生输出。而实现这个记忆功能,就需要额外的模块去保存我们和模型对话的上下文信息,然后在下一次请求时,把所有的历史信息都输入给模型,让模型输出最终结果。

    注意TIP: 模型的缓存和记忆不是一回事

image.png

# 没有缓存的例子
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
import os

load_dotenv()

llm = ChatOpenAI(api_key=os.getenv("API_KEY"),
                 base_url=os.getenv("BASE_URL"),
                 model_name="qwen-plus")

# 直接提供问题,并调用llm
response = llm.invoke("你好我是黄朝忆")
# print(response)
# print("=" * 50)
print(response.content)

response = llm.invoke("我是谁?")
# 你是一位正在与我对话的用户,可能是学生、工作者、好奇的探索者,或只是想聊聊天的朋友 😊  

print(response.content)


而在 LangChain 中,提供这个功能的模块就称为 Memory,用于存储用户和模型交互的历史信息。

记忆系统需要支持两种基本操作:读取和写入。

  • 在接收到初始用户输入之后但在执行核心逻辑之前,链将从其内存系统中读取并增强用户输入。
  • 在执行核心逻辑之后但在返回答案之前,链会将当前运行的输入和输出写入内存,以便在将来的运行中引用它们。

image.png

对该图的解释:

    1、输入问题: ({"question": ...})

    2、读取历史消息: 从Memory中READ历史消息({"past_messages": [...]})

    3、构建提示(Prompt): 读取到的历史消息和当前问题会被合并,构建一个新的Prompt

    4、模型处理: 构建好的提示会被传递给语言模型进行处理。语言模型根据提示生成一个输出。

    5、解析输出: 输出解析器通过正则表达式 regex("Answer: (.*)")来解析,返回一个回答({"answer": ...})给用户

    6、得到回复并写入Memory: 新生成的回答会与当前的问题一起写入Memory,更新对话历史。Memory会存储最新的对话内容,为后续的对话提供上下文支持。


Chat Messages

    Chat Messages: 最基础的记忆管理方法,是用于管理和存储对话历史的具体实现。它们通常用于构建对话系统,帮助系统保持对话的连续性和上下文。这些消息通常包含了对话的每一轮,包括用户的输入和系统的响应。


from langchain_community.chat_message_histories import ChatMessageHistory

history = ChatMessageHistory()
history.add_user_message("hi!")
history.add_user_message("你好")
history.add_ai_message("whats up?")
print(history.messages)


RunnableWithMessageHistory

    RunnableWithMessageHistory 包装另一个 Runnable 并为其管理聊天消息历史记录;它负责读取和更新聊天消息历史记录。


本地内存存储

import json
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain.schema import messages_from_dict, messages_to_dict
from dotenv import load_dotenv
import os

# 加载环境变量(需要包含API_KEY)
load_dotenv()

# 初始化大语言模型(通义千问)
llm = ChatOpenAI(
    api_key=os.getenv("API_KEY"),  # 从环境变量读取API密钥
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",  # 阿里云兼容端点
    model="qwen-plus"  # 使用qwen-turbo模型
)

# 创建对话提示模板
prompt = ChatPromptTemplate.from_messages([
    # 系统角色设定
    ("system", "你是一个友好的助手"),
    # 历史消息占位符(变量名必须与链配置中的history_messages_key一致)
    MessagesPlaceholder(variable_name="history"),
    # 用户输入占位符
    ("user", "{input}")
])

# 构建基础对话链(组合提示模板和语言模型)
base_chain = prompt | llm

# 全局会话存储字典(Key: session_id, Value: ChatMessageHistory实例)
store = {}


def get_session_history(session_id):
    """获取或创建会话历史存储对象
    Args:
        session_id: 会话唯一标识(用于多会话隔离)
    Returns:
        对应会话的聊天历史记录对象
    """
    if session_id not in store:
        store[session_id] = ChatMessageHistory()  # 初始化空历史记录
    return store[session_id]


# 创建支持历史记录的对话链
conversation = RunnableWithMessageHistory(
    base_chain,  # 基础对话链
    get_session_history=get_session_history,  # 历史记录获取方法
    input_messages_key="input",  # 输入文本的键名
    history_messages_key="history"  # 历史记录的键名(需与提示模板中的变量名一致)
)


def save_memory(filepath, session_id):
    """保存指定会话的历史记录到文件
    Args:
        filepath: 文件保存路径(建议使用.json扩展名)
        session_id: 要保存的会话ID(默认"default")
    """
    history = get_session_history(session_id)
    # 将消息对象列表转换为字典格式
    dicts = messages_to_dict(history.messages)
    # 写入JSON文件(UTF-8编码)
    with open(filepath, "w", encoding='utf-8') as f:
        json.dump(dicts, f, ensure_ascii=False)


def load_memory(filepath, session_id):
    """从文件加载历史记录到指定会话
    Args:
        filepath: 历史记录文件路径
        session_id: 要加载到的会话ID(默认"default")
    """
    with open(filepath, "r", encoding='utf-8') as f:
        dicts = json.load(f)
    # 将字典转换回消息对象列表
    messages = messages_from_dict(dicts)
    # 更新全局存储中的会话历史
    store[session_id] = ChatMessageHistory(messages=messages)


def legacy_predict(input_text: str, session_id: str = "default") -> str:
    """模拟旧版predict方法的调用接口
    Args:
        input_text: 用户输入文本
        session_id: 会话ID(默认"default")
    Returns:
        AI生成的回复文本
    """
    return conversation.invoke(
        {"input": input_text},  # 输入参数
        # 配置参数(必须包含session_id来关联历史记录)
        config={"configurable": {"session_id": session_id}}
    ).content


if __name__ == "__main__":
    # 使用默认会话ID
    SESSION_ID = "default"

    # 模拟连续对话(4轮)
    legacy_predict("你好", SESSION_ID)  # 问候
    legacy_predict("你是谁,我是黄朝忆", SESSION_ID)  # 身份确认
    legacy_predict("你的背后实现原理是什么", SESSION_ID)  # 技术原理询问

    # 查询对话历史(第4轮)
    last_response = legacy_predict('截止到现在我们聊了什么?', SESSION_ID)
    print("最后一次回答:", last_response)

    # 持久化保存对话历史(JSON格式)
    save_memory("./memory_new.json", SESSION_ID)

    # 模拟重新加载历史记录(清空当前会话后重新加载)
    load_memory("./memory_new.json", SESSION_ID)

    # 验证历史恢复效果(第5轮)
    reload_response = legacy_predict("我回来了,我们之前都聊了一些什么?", SESSION_ID)
    print("\n恢复后的回答:", reload_response)





评论(共 0 条)

编辑器加载中...