LangChain 多任务应用开发全解析

一、LangChain 基础核心

1.1 核心定位与价值

LangChain 是一套用于简化大语言模型(LLM)应用开发的框架,通过提供工具、组件和接口,降低 LLM 与外部系统(如工具、知识库)集成的复杂度,支持快速构建多任务、多流程的 AI 应用。

1.2 七大核心组件

组件 功能描述 典型应用场景
Models(模型) 集成各类 LLM(如 GPT-4o、通义千问 qwen-turbo),提供统一调用接口 文本生成、推理决策
Prompts(提示) 管理提示词模板、优化提示逻辑、支持提示序列化 标准化问答、动态生成提示
Memory(记忆) 存储对话上下文,实现 LLM 的 "短期记忆" 能力 多轮对话、上下文关联任务
Indexes(索引) 处理文档结构化(加载、切割、向量计算、存储),支持知识库交互 本地知识问答(RAG)
Chains(链) 串联多个组件(如 Prompt→LLM→Parser),定义固定执行流程 线性任务(翻译→分析→总结)
Agents(代理) 基于 LLM 自主决策工具调用顺序,支持 "思考 - 行动 - 观察" 循环 复杂任务(故障诊断、多工具组合)
Tools(工具) 集成外部能力(搜索、计算、文件操作等),作为 Agent 的执行单元 实时信息查询、数学计算、API 调用

1.3 基础代码示例(Prompt 与 Chain)

通过PromptTemplate定义提示模板,结合 LLM 形成可运行的链式结构,简化任务执行流程:

from langchain.prompts import PromptTemplate
from langchain_community.llms import Tongyi
import os
import dashscope
​
# 初始化API密钥与LLM
api_key = os.environ.get('DASHSCOPE_API_KEY')
dashscope.api_key = api_key
llm = Tongyi(model_name="qwen-turbo", dashscope_api_key=api_key)
​
# 定义提示模板
prompt = PromptTemplate(
    input_variables=["product"],
    template="What is a good name for a company that makes {product}?"
)
​
# 构建链式结构(LCEL风格,|表示数据流传递)
chain = prompt | llm
​
# 执行任务
result = chain.invoke({"product": "colorful socks"})
print(result)  # 输出:如"Vibrant Sock Co."等公司名称建议

二、关键工具(Tools)解析

2.1 常用工具类型与功能

LangChain 支持内置工具与自定义工具,核心工具包括:

工具名称 核心功能 依赖与安装 典型使用场景
SerpAPI 集成 Google、百度等搜索引擎,获取实时信息 pip install google-search-results+SerpAPI 密钥 日期查询、名人诞辰、实时数据获取
llm-math 基于 LLM 实现数学计算(支持复杂公式) 依赖 LLM(如 GPT-4、通义千问) 温度换算、次方计算、数据统计
File System Tools 本地文件读写、目录操作 无额外依赖(需文件权限) 文档加载、结果保存
Python REPL 执行 Python 代码片段,支持数据处理 无额外依赖(需代码执行权限) 数据分析、公式计算

2.2 工具调用示例(SerpAPI+llm-math)

 核心工具参考我的另外博文

LangChain 50个核心工具完全指南https://blog.csdn.net/chenchihwen/article/details/150392604?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228735e3240eecad389915c17acde7c8d4%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8735e3240eecad389915c17acde7c8d4&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-150392604-null-null.nonecase&utm_term=langchain&spm=1018.2226.3001.4450LangChain Community 50个核心工具https://blog.csdn.net/chenchihwen/article/details/150391920?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228735e3240eecad389915c17acde7c8d4%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8735e3240eecad389915c17acde7c8d4&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-3-150391920-null-null.nonecase&utm_term=langchain&spm=1018.2226.3001.4450

需求:查询当前北京温度(华氏度),并计算该温度的 1/4:

from langchain.agents import load_tools, initialize_agent
from langchain_community.llms import Tongyi
import os
import dashscope
​
# 初始化LLM
api_key = os.environ.get('DASHSCOPE_API_KEY')
dashscope.api_key = api_key
llm = Tongyi(model_name="qwen-turbo", dashscope_api_key=api_key)
​
# 加载工具(SerpAPI用于搜索温度,llm-math用于计算)
tools = load_tools(["serpapi", "llm-math"], llm=llm)
​
# 初始化Agent(Zero-Shot ReAct模式,自主决策工具调用)
agent = initialize_agent(
    tools, 
    llm, 
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, 
    verbose=True  # 打印执行详情
)
​
# 执行任务
agent.run("当前北京的温度是多少华氏度?这个温度的1/4是多少?")

执行流程

  1. Thought:需先获取北京当前华氏度,再计算 1/4

  2. Action:调用 SerpAPI,输入 "当前北京温度 华氏度"

  3. Observation:返回温度(如 "77°F")

  4. Action:调用 llm-math,输入 "77 / 4"

  5. Observation:返回计算结果("19.25")

  6. Final Answer:当前北京温度 77°F,其 1/4 为 19.25°F

三、记忆(Memory)机制

3.1 核心作用

解决 LLM "无状态" 问题,通过存储对话历史,让 AI 记住前序交互内容,支持多轮上下文关联(如 "上一个问题中的产品价格是多少?")。

3.2 四种常用 Memory 类型

Memory 类型 功能特点 适用场景
BufferMemory 完整存储所有对话历史,无压缩 短对话、需完整上下文的场景
BufferWindowMemory 仅存储最近 K 组对话(如最近 3 轮),减少 token 消耗 长对话、仅需近期上下文的场景
ConversationMemory 对对话历史进行摘要压缩,存储关键信息 超长对话、需控制 token 成本的场景
VectorStore-backed Memory 将对话历史向量化存储,通过相似度匹配召回相关上下文 对话历史量大、需精准关联旧内容的场景

3.3 Memory 代码示例(ConversationChain)

from langchain.chains import ConversationChain
from langchain_community.llms import Tongyi
import os
import dashscope
​
# 初始化LLM
api_key = os.environ.get('DASHSCOPE_API_KEY')
dashscope.api_key = api_key
llm = Tongyi(model_name="qwen-turbo", dashscope_api_key=api_key)
​
# 构建带Memory的对话链
conversation = ConversationChain(llm=llm, verbose=True)
​
# 多轮对话
output1 = conversation.predict(input="Hi there! I'm Jim.")
print(output1)  # 输出:"Hi Jim! It's nice to meet you. How can I help today?"
​
output2 = conversation.predict(input="Do you remember my name?")
print(output2)  # 输出:"Yes, I remember your name is Jim! Is there something specific you'd like to talk about?"

四、核心案例实战

4.1 案例 1:本地知识智能客服(ReAct 范式)

4.1.1 核心目标

基于自有知识库(如特斯拉产品数据),构建能自主调用工具、分步推理的客服系统,回答用户关于产品的问题(如 "Model 3 怎么样?")。

4.1.2 实现步骤
  1. 定义工具:

    • 「查询产品名称」:根据产品名获取描述(如 Model 3 的设计、价格)

    • 「公司相关信息」:获取品牌背景(如特斯拉的核心技术)

  2. 设计 ReAct 提示模板:

    AGENT_TMPL = """按照以下格式回答:
    Question: 需回答的问题
    Thought: 下一步要做什么
    Action: 工具名称(从{tools}中选)
    Action Input: 工具输入参数
    Observation: 工具返回结果(可重复多轮)
    Thought: 已获取足够信息
    Final Answer: 最终答案
    ​
    Question: {input}
    {agent_scratchpad}
    """
  3. 初始化 Agent 与执行:

    from langchain.agents import AgentExecutor, LLMSingleActionAgent
    from langchain.chains import LLMChain
    from langchain.prompts import CustomPromptTemplate
    from langchain.schema import AgentOutputParser
    ​
    # 定义提示模板与解析器
    agent_prompt = CustomPromptTemplate(template=AGENT_TMPL, input_variables=["input", "intermediate_steps"])
    output_parser = AgentOutputParser()  # 解析Agent输出的Action与Input
    ​
    # 构建LLM Chain与Agent
    llm_chain = LLMChain(llm=llm, prompt=agent_prompt)
    agent = LLMSingleActionAgent(
        llm_chain=llm_chain,
        output_parser=output_parser,
        allowed_tools=["查询产品名称", "公司相关信息"],
        stop=["\nObservation:"]
    )
    ​
    # 初始化执行器
    agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)
    ​
    # 运行客服问答
    agent_executor.run("Model 3怎么样?")
  4. 执行流程:

    • Thought:需先查 Model 3 的产品描述,再查特斯拉公司信息

    • Action:调用「查询产品名称」,Input:"Model 3"

    • Observation:返回 "Model 3 设计简洁,定价 23.19-33.19 万"

    • Action:调用「公司相关信息」,Input:"特斯拉"

    • Observation:返回 "特斯拉专注电动汽车,有驾驶辅助功能"

    • Final Answer:整合结果,输出 Model 3 的详细信息

4.2 案例 2:工具链组合设计(多工具串联)

4.2.1 核心目标

通过组合「文本分析」「数据转换」「文本处理」三种工具,解决复杂任务(如情感分析 + 行数统计、CSV 转 JSON)。

4.2.2 工具定义
工具名称 功能 输入参数 输出结果
文本分析工具(TextAnalysisTool) 提取文本字数、字符数、情感倾向 文本内容 {"word_count": 34, "sentiment": "positive"}
数据转换工具(DataConversionTool) 实现 CSV 与 JSON 互转 输入数据、输入格式、输出格式 转换后的 JSON/CSV 字符串
文本处理工具(TextProcessingTool) 统计行数、查找 / 替换文本 操作类型(如 count_lines)、文本内容 {"line_count": 3}
4.2.3 任务示例(情感分析 + 行数统计)
# 定义工具链(LCEL风格)
from langchain.schema.runnable import RunnableLambda
​
# 初始化工具实例
text_analysis = TextAnalysisTool()
text_processing = TextProcessingTool()
​
# 构建工具链(先分析情感,再统计行数)
tools_chain = {
    "analysis": RunnableLambda(lambda x: text_analysis.run(x["text"])),
    "line_count": RunnableLambda(lambda x: text_processing.run("count_lines", x["text"]))
}
​
# 执行任务
text = "这个产品非常好用,我很喜欢它的设计!\n价格合理,推荐购买。"
result = tools_chain.invoke({"text": text})
print(result) 
# 输出:{"analysis": {"sentiment": "positive"}, "line_count": {"line_count": 2}}

4.3 案例 3:网络故障诊断 Agent

4.3.1 核心目标

构建能自主串联工具、分步诊断网络问题的 Agent(如 "无法访问www.example.com"),支持工具输出作为下一个工具的输入。

4.3.2 核心工具与串联逻辑
工具名称 功能 输入 输出(供下一个工具使用)
DNS 解析工具(DNSTool) 将域名解析为 IP 域名(如www.example.com IP 地址(如 93.184.216.34)
网络连通性工具(PingTool) 检查 IP 连通性与延迟 IP 地址 连通状态(成功 / 超时)、延迟
本地接口检查工具(InterfaceCheckTool) 检查本地网络配置 接口名称(可选) 接口状态、IP / 子网掩码
日志分析工具(LogAnalysisTool) 搜索网络错误日志 关键词(如 timeout)、时间范围 匹配的错误日志条目
4.3.3 诊断流程示例
  1. 用户问题:"无法访问www.example.com"

  2. Agent 推理:

    • Thought:需先解析域名 IP,再检查连通性

    • Action1:DNS 解析工具,Input:www.example.com → Observation:IP=93.184.216.34

    • Action2:Ping 工具,Input:93.184.216.34 → Observation:连接超时

    • Action3:本地接口检查工具 → Observation:接口正常

    • Action4:日志分析工具,Input:关键词 timeout → Observation:发现路由错误日志

  3. Final Answer:诊断为 "路由问题或目标服务器不可用",建议检查路由配置

五、LCEL(LangChain Expression Language)

5.1 核心定位

LCEL 是 LangChain 推出的链式表达式语言,通过|(管道符)实现组件间的数据流传递,支持灵活的任务编排(串联、分支、并行)。

5.2 核心优势

  • 简洁性:用A | B | C表示 "A 的输出作为 B 的输入,B 的输出作为 C 的输入",代码逻辑清晰

  • 灵活性:支持分支({"x": A, "y": B})、条件判断、并行执行

  • 可调试性:每个组件可单独测试,便于定位问题

5.3 典型用法示例

5.3.1 多步串联(翻译→分析→回译)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
​
# 定义子任务
translate_to_en = ChatPromptTemplate.from_template("Translate to English: {input}") | llm | StrOutputParser()
process_text = ChatPromptTemplate.from_template("Analyze sentiment: {text}") | llm | StrOutputParser()
translate_to_cn = ChatPromptTemplate.from_template("Translate to Chinese: {output}") | llm | StrOutputParser()
​
# 组合任务链
workflow = {"text": translate_to_en} | process_text | translate_to_cn
​
# 执行(流式输出)
for chunk in workflow.stream({"input": "北京有哪些好吃的地方,简略回答不超过200字"}):
    print(chunk, end="", flush=True)
5.3.2 条件分支(根据文本长度选择处理方式)
def lcel_conditional_chain(text):
    # 定义工具
    text_analysis_chain = ChatPromptTemplate.from_template("Analyze: {text}") | llm | StrOutputParser()
    line_count_chain = ChatPromptTemplate.from_template("Count lines: {text}") | llm | StrOutputParser()
    
    # 条件判断:文本长度>20则分析,否则统计行数
    if len(text) > 20:
        return text_analysis_chain.invoke({"text": text})
    else:
        return line_count_chain.invoke({"text": text})
​
# 测试
print(lcel_conditional_chain("短文本"))  # 输出:行数统计结果
print(lcel_conditional_chain("这是一段超过20个字符的长文本,用于测试情感分析功能"))  # 输出:情感分析结果

六、AI Agent 工具对比

6.1 主流 Agent 工具核心差异

工具 核心定位 架构特点 适用场景 优势 局限性
LangChain 开源 LLM 应用开发框架 基于 Chain 的线性 / 分支工作流,支持 Agent 模式 快速构建 RAG、对话系统、简单工具调用 组件丰富、文档完善、生态成熟 复杂流程(循环 / 多 Agent 协作)支持较弱
LangGraph LangChain 扩展,专注复杂工作流 基于图(Graph)的循环、条件逻辑,支持多 Agent 协作 自适应 RAG、多轮决策、多 Agent 系统 动态流程调整、状态管理强 学习成本高,需理解图结构
Qwen-Agent 通义千问 AI Agent 框架 基于阿里云大模型,支持多模态、MCP 调用 开源项目、多模态交互、工具集成 与通义千问深度适配、支持三级索引 生态相对较小,依赖阿里云资源
Coze 字节跳动无代码 AI Bot 平台 可视化拖拽界面,内置知识库、多模态插件 快速部署社交机器人、轻量级工作流 无代码 / 低代码、部署便捷 灵活性低,定制化能力弱
Dify 开源 LLM 应用开发平台 API 优先,支持 Prompt 工程、灵活编排 企业私有化部署、定制化 LLM 应用 开源可控、支持深度集成 需开发者具备一定编码能力

6.2 选择建议

  • 无代码开发:优先 Coze(可视化界面,快速出成果)

  • 快速原型(线性任务):LangChain(组件丰富,开发效率高)

  • 复杂系统(多 Agent / 循环流程):LangGraph(图结构支持动态逻辑)

  • 企业私有化部署:Dify(开源可控)或 Qwen-Agent(适配国产大模型)

七、总结

LangChain 作为 LLM 应用开发的核心框架,通过 "组件化 + 流程化" 的设计,降低了 AI 应用的开发门槛。其核心价值在于:

  1. 工具集成:无缝对接搜索、计算、文件操作等外部能力,扩展 LLM 边界;

  2. 流程编排:通过 Chain(固定流程)和 Agent(自主决策),支持从简单到复杂的任务;

  3. 生态丰富:支持多模型(GPT、通义千问等)、多记忆类型、多部署方式,适配不同场景需求。

无论是构建本地知识库客服、网络故障诊断系统,还是复杂的多工具任务链,LangChain 都能提供灵活的解决方案,是当前 AI 应用开发的核心工具之一。

smple python

CASE-LangChain使用 product_llm.py

import re
from typing import List, Union
# Python内置模块,用于格式化和包装文本
import textwrap
import time

from langchain.agents import (
    Tool,          # 可用工具
    AgentExecutor, # Agent执行
    LLMSingleActionAgent, # 定义Agent
    AgentOutputParser, # 输出结果解析
)
from langchain.prompts import StringPromptTemplate
# LLMChain,包含一个PromptTemplate和一个LLM
from langchain_community.llms import Tongyi  # 导入通义千问Tongyi模型
from langchain import LLMChain
# Agent执行,Agent结束
from langchain.schema import AgentAction, AgentFinish
# PromptTemplate: 管理LLMs的Prompts
from langchain.prompts import PromptTemplate
from langchain.llms.base import BaseLLM

# 定义了LLM的Prompt Template
CONTEXT_QA_TMPL = """
根据以下提供的信息,回答用户的问题
信息:{context}

问题:{query}
"""
CONTEXT_QA_PROMPT = PromptTemplate(
    input_variables=["query", "context"],
    template=CONTEXT_QA_TMPL,
)

# 输出结果显示,每行最多60字符,每个字符显示停留0.1秒(动态显示效果)
def output_response(response: str) -> None:
    if not response:
        exit(0)
    # 每行最多60个字符
    for line in textwrap.wrap(response, width=60):
        for word in line.split():
            for char in word:
                print(char, end="", flush=True)
                time.sleep(0.1)  # Add a delay of 0.1 seconds between each character
            print(" ", end="", flush=True)  # Add a space between each word
        print()  # Move to the next line after each line is printed
    # 遇到这里,这个问题的回答就结束了
    print("----------------------------------------------------------------")

# 模拟公司产品和公司介绍的数据源
class TeslaDataSource:
    def __init__(self, llm: BaseLLM):
        self.llm = llm

    # 工具1:产品描述
    def find_product_description(self, product_name: str) -> str:
        """模拟公司产品的数据库"""
        product_info = {
            "Model 3": "具有简洁、动感的外观设计,流线型车身和现代化前脸。定价23.19-33.19万",
            "Model Y": "在外观上与Model 3相似,但采用了更高的车身和更大的后备箱空间。定价26.39-36.39万",
            "Model X": "拥有独特的翅子门设计和更加大胆的外观风格。定价89.89-105.89万",
        }
        # 基于产品名称 => 产品描述
        return product_info.get(product_name, "没有找到这个产品")

    # 工具2:公司介绍
    def find_company_info(self, query: str) -> str:
        """模拟公司介绍文档数据库,让llm根据信息回答问题"""
        context = """
        特斯拉最知名的产品是电动汽车,其中包括Model S、Model 3、Model X和Model Y等多款车型。
        特斯拉以其技术创新、高性能和领先的自动驾驶技术而闻名。公司不断推动自动驾驶技术的研发,并在车辆中引入了各种驾驶辅助功能,如自动紧急制动、自适应巡航控制和车道保持辅助等。
        """
        # prompt模板 = 上下文context + 用户的query
        prompt = CONTEXT_QA_PROMPT.format(query=query, context=context)
        # 使用LLM进行推理
        return self.llm(prompt)


AGENT_TMPL = """按照给定的格式回答以下问题。你可以使用下面这些工具:

{tools}

回答时需要遵循以下用---括起来的格式:

---
Question: 我需要回答的问题
Thought: 回答这个上述我需要做些什么
Action: "{tool_names}" 中的一个工具名
Action Input: 选择这个工具所需要的输入
Observation: 选择这个工具返回的结果
...(这个 思考/行动/行动输入/观察 可以重复N次)
Thought: 我现在知道最终答案
Final Answer: 原始输入问题的最终答案
---

现在开始回答,记得在给出最终答案前,需要按照指定格式进行一步一步的推理。

Question: {input}
{agent_scratchpad}
"""


class CustomPromptTemplate(StringPromptTemplate):
    template: str  # 标准模板
    tools: List[Tool]  # 可使用工具集合

    def format(self, **kwargs) -> str:
        """
        按照定义的 template,将需要的值都填写进去。
        Returns:
            str: 填充好后的 template。
        """
        # 取出中间步骤并进行执行
        intermediate_steps = kwargs.pop("intermediate_steps")  
        print('intermediate_steps=', intermediate_steps)
        print('='*30)
        thoughts = ""
        for action, observation in intermediate_steps:
            thoughts += action.log
            thoughts += f"\nObservation: {observation}\nThought: "
        # 记录下当前想法 => 赋值给agent_scratchpad
        kwargs["agent_scratchpad"] = thoughts  
        # 枚举所有可使用的工具名+工具描述
        kwargs["tools"] = "\n".join(
            [f"{tool.name}: {tool.description}" for tool in self.tools]
        )  
        # 枚举所有的工具名称
        kwargs["tool_names"] = ", ".join(
            [tool.name for tool in self.tools]
        )  
        cur_prompt = self.template.format(**kwargs)
        #print(cur_prompt)
        return cur_prompt

"""
    对Agent返回结果进行解析,有两种可能:
    1)还在思考中 AgentAction
    2)找到了答案 AgentFinal
"""
class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
        """
        解析 llm 的输出,根据输出文本找到需要执行的决策。
        Args:
            llm_output (str): _description_
        Raises:
            ValueError: _description_
        Returns:
            Union[AgentAction, AgentFinish]: _description_
        """
        # 如果句子中包含 Final Answer 则代表已经完成
        if "Final Answer:" in llm_output:  
            return AgentFinish(
                return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
                log=llm_output,
            )

        # 需要进行 AgentAction
        regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"  # 解析 action_input 和 action
        match = re.search(regex, llm_output, re.DOTALL)
        if not match:
            raise ValueError(f"Could not parse LLM output: `{llm_output}`")
        action = match.group(1).strip()
        action_input = match.group(2)
        # Agent执行
        return AgentAction(
            tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output
        )



if __name__ == "__main__":
    # 定义LLM
    llm = Tongyi(model_name="qwen-turbo", dashscope_api_key=DASHSCOPE_API_KEY)  # 使用通义千问qwen-turbo模型
    # 自有数据
    tesla_data_source = TeslaDataSource(llm)
    # 定义的Tools
    tools = [
        Tool(
            name="查询产品名称",
            func=tesla_data_source.find_product_description,
            description="通过产品名称找到产品描述时用的工具,输入的是产品名称",
        ),
        Tool(
            name="公司相关信息",
            func=tesla_data_source.find_company_info,
            description="当用户询问公司相关的问题,可以通过这个工具了解公司信息",
        ),
    ]
    
    # 用户定义的模板
    agent_prompt = CustomPromptTemplate(
        template=AGENT_TMPL,
        tools=tools,
        input_variables=["input", "intermediate_steps"],
    )
    # Agent返回结果解析
    output_parser = CustomOutputParser()
    # 最常用的Chain, 由LLM + PromptTemplate组成
    llm_chain = LLMChain(llm=llm, prompt=agent_prompt)
    # 定义的工具名称
    tool_names = [tool.name for tool in tools]
    # 定义Agent = llm_chain + output_parser + tools_names
    agent = LLMSingleActionAgent(
        llm_chain=llm_chain,
        output_parser=output_parser,
        stop=["\nObservation:"],
        allowed_tools=tool_names,
    )
    # 定义Agent执行器 = Agent + Tools
    agent_executor = AgentExecutor.from_agent_and_tools(
        agent=agent, tools=tools, verbose=True
    )

    # 主过程:可以一直提问下去,直到Ctrl+C
    while True:
        try:
            user_input = input("请输入您的问题:")
            response = agent_executor.run(user_input)
            output_response(response)
        except KeyboardInterrupt:
            break

代码说明

代码逻辑详解

product_llm.py 文件实现了一个基于 LangChain 的智能问答系统,专门用于回答关于特斯拉产品和公司信息的问题。下面是详细的代码逻辑分析:

1. 导入模块和依赖

这部分导入了构建 Agent 所需的各种组件,包括:

  • 正则表达式模块用于解析 LLM 输出

  • LangChain 的核心组件:工具、Agent 执行器、Agent 类等

  • 通义千问模型接口

  • 提示模板相关类

import re
from typing import List, Union
import textwrap
import time
​
from langchain.agents import (
    Tool,
    AgentExecutor,
    LLMSingleActionAgent,
    AgentOutputParser,
)
from langchain.prompts import StringPromptTemplate
from langchain_community.llms import Tongyi
from langchain import LLMChain
from langchain.schema import AgentAction, AgentFinish
from langchain.prompts import PromptTemplate
from langchain.llms.base import BaseLLM

2. 上下文问答模板定义

定义了一个用于基于上下文回答问题的提示模板,将在公司信息查询工具中使用。

CONTEXT_QA_TMPL = """
根据以下提供的信息,回答用户的问题
信息:{context}
​
问题:{query}
"""
CONTEXT_QA_PROMPT = PromptTemplate(
    input_variables=["query", "context"],
    template=CONTEXT_QA_TMPL,
)

3. 输出显示函数

这个函数用于以动态打字效果显示回答结果,增强用户体验。

def output_response(response: str) -> None:
    if not response:
        exit(0)
    # 每行最多60个字符
    for line in textwrap.wrap(response, width=60):
        for word in line.split():
            for char in word:
                print(char, end="", flush=True)
                time.sleep(0.1)
            print(" ", end="", flush=True)
        print()
    print("----------------------------------------------------------------")

4. 数据源类 TeslaDataSource

TeslaDataSource 类模拟了一个特斯拉产品和公司信息的数据库:

  1. find_product_description 方法:根据产品名称返回产品描述信息

  2. find_company_info 方法:基于公司背景信息和用户问题,使用 LLM 生成回答

class TeslaDataSource:
    def __init__(self, llm: BaseLLM):
        self.llm = llm
​
    # 工具1:产品描述
    def find_product_description(self, product_name: str) -> str:
        """模拟公司产品的数据库"""
        product_info = {
            "Model 3": "具有简洁、动感的外观设计,流线型车身和现代化前脸。定价23.19-33.19万",
            "Model Y": "在外观上与Model 3相似,但采用了更高的车身和更大的后备箱空间。定价26.39-36.39万",
            "Model X": "拥有独特的翅子门设计和更加大胆的外观风格。定价89.89-105.89万",
        }
        # 基于产品名称 => 产品描述
        return product_info.get(product_name, "没有找到这个产品")
​
    # 工具2:公司介绍
    def find_company_info(self, query: str) -> str:
        """模拟公司介绍文档数据库,让llm根据信息回答问题"""
        context = """
        特斯拉最知名的产品是电动汽车,其中包括Model S、Model 3、Model X和Model Y等多款车型。
        特斯拉以其技术创新、高性能和领先的自动驾驶技术而闻名。公司不断推动自动驾驶技术的研发,并在车辆中引入了各种驾驶辅助功能,如自动紧急制动、自适应巡航控制和车道保持辅助等。
        """
        # prompt模板 = 上下文context + 用户的query
        prompt = CONTEXT_QA_PROMPT.format(query=query, context=context)
        # 使用LLM进行推理
        return self.llm(prompt)

5. Agent 模板定义

定义了 Agent 的提示模板,指导 LLM 如何按照 ReAct(Reasoning and Action)模式进行思考和行动。

AGENT_TMPL = """按照给定的格式回答以下问题。你可以使用下面这些工具:
​
{tools}
​
回答时需要遵循以下用---括起来的格式:
​
---
Question: 我需要回答的问题
Thought: 回答这个上述我需要做些什么
Action: "{tool_names}" 中的一个工具名
Action Input: 选择这个工具所需要的输入
Observation: 选择这个工具返回的结果
...(这个 思考/行动/行动输入/观察 可以重复N次)
Thought: 我现在知道最终答案
Final Answer: 原始输入问题的最终答案
---
​
现在开始回答,记得在给出最终答案前,需要按照指定格式进行一步一步的推理。
​
Question: {input}
{agent_scratchpad}
"""

6. 自定义提示模板类

CustomPromptTemplate 类用于动态生成提示模板,将工具信息和中间步骤填充到模板中。

class CustomPromptTemplate(StringPromptTemplate):
    template: str  # 标准模板
    tools: List[Tool]  # 可使用工具集合
​
    def format(self, **kwargs) -> str:
        """
        按照定义的 template,将需要的值都填写进去。
        Returns:
            str: 填充好后的 template。
        """
        # 取出中间步骤并进行执行
        intermediate_steps = kwargs.pop("intermediate_steps")
        print('intermediate_steps=', intermediate_steps)
        print('='*30)
        thoughts = ""
        for action, observation in intermediate_steps:
            thoughts += action.log
            thoughts += f"\nObservation: {observation}\nThought: "
        # 记录下当前想法 => 赋值给agent_scratchpad
        kwargs["agent_scratchpad"] = thoughts
        # 枚举所有可使用的工具名+工具描述
        kwargs["tools"] = "\n".join(
            [f"{tool.name}: {tool.description}" for tool in self.tools]
        )
        # 枚举所有的工具名称
        kwargs["tool_names"] = ", ".join(
            [tool.name for tool in self.tools]
        )
        cur_prompt = self.template.format(**kwargs)
        #print(cur_prompt)
        return cur_prompt

7. 自定义输出解析器

CustomOutputParser 类用于解析 LLM 的输出,判断是继续执行工具还是返回最终答案。

class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
        """
        解析 llm 的输出,根据输出文本找到需要执行的决策。
        """
        # 如果句子中包含 Final Answer 则代表已经完成
        if "Final Answer:" in llm_output:
            return AgentFinish(
                return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
                log=llm_output,
            )
​
        # 需要进行 AgentAction
        regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"  # 解析 action_input 和 action
        match = re.search(regex, llm_output, re.DOTALL)
        if not match:
            raise ValueError(f"Could not parse LLM output: `{llm_output}`")
        action = match.group(1).strip()
        action_input = match.group(2)
        # Agent执行
        return AgentAction(
            tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output
        )

8. 主程序逻辑

主程序逻辑包括以下步骤:

  1. 初始化通义千问 LLM 模型

  2. 创建 TeslaDataSource 实例

  3. 定义两个工具:产品查询工具和公司信息查询工具

  4. 创建自定义提示模板和输出解析器

  5. 构建 LLMChain

  6. 创建 LLMSingleActionAgent

  7. 创建 AgentExecutor 来执行 Agent 和工具

  8. 进入循环,接收用户输入并执行 Agent

if __name__ == "__main__":
    # 定义LLM
    llm = Tongyi(model_name="qwen-turbo", dashscope_api_key=DASHSCOPE_API_KEY)
    # 自有数据
    tesla_data_source = TeslaDataSource(llm)
    # 定义的Tools
    tools = [
        Tool(
            name="查询产品名称",
            func=tesla_data_source.find_product_description,
            description="通过产品名称找到产品描述时用的工具,输入的是产品名称",
        ),
        Tool(
            name="公司相关信息",
            func=tesla_data_source.find_company_info,
            description="当用户询问公司相关的问题,可以通过这个工具了解公司信息",
        ),
    ]
    
    # 用户定义的模板
    agent_prompt = CustomPromptTemplate(
        template=AGENT_TMPL,
        tools=tools,
        input_variables=["input", "intermediate_steps"],
    )
    # Agent返回结果解析
    output_parser = CustomOutputParser()
    # 最常用的Chain, 由LLM + PromptTemplate组成
    llm_chain = LLMChain(llm=llm, prompt=agent_prompt)
    # 定义的工具名称
    tool_names = [tool.name for tool in tools]
    # 定义Agent = llm_chain + output_parser + tools_names
    agent = LLMSingleActionAgent(
        llm_chain=llm_chain,
        output_parser=output_parser,
        stop=["\nObservation:"],
        allowed_tools=tool_names,
    )
    # 定义Agent执行器 = Agent + Tools
    agent_executor = AgentExecutor.from_agent_and_tools(
        agent=agent, tools=tools, verbose=True
    )
​
    # 主过程:可以一直提问下去,直到Ctrl+C
    while True:
        try:
            user_input = input("请输入您的问题:")
            response = agent_executor.run(user_input)
            output_response(response)
        except KeyboardInterrupt:
            break

工作流程总结

  1. 用户输入问题(如"Model 3怎么样?")

  2. Agent 使用自定义提示模板生成提示,包含可用工具信息

  3. LLM 根据提示决定采取什么行动

  4. 如果需要查询产品信息,Agent 会调用 find_product_description 工具

  5. 如果需要查询公司信息,Agent 会调用 find_company_info 工具

  6. 工具返回结果后,Agent 将结果作为观察信息,继续决定下一步行动

  7. 当收集到足够信息后,Agent 生成最终答案

  8. 最终答案通过动态显示函数呈现给用户

这个系统展示了如何使用 LangChain 构建一个基于 ReAct 模式的智能问答 Agent,能够根据用户问题自主决定调用哪些工具来获取信息,并最终生成答案。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐