LangChain实战:快速开发LLM智能体
LangChain实战:快速开发LLM智能体
作者:15年经验资深软件架构师 | 发布时间:2024年9月 | 阅读时长:45分钟 | 适合人群:初中级Python开发者、大模型应用开发从业者
前言
2024年被行业称为「LLM智能体元年」,原生大模型的知识 cutoff、无法调用外部工具、推理能力有限等痛点,正在被智能体(Agent)技术彻底解决。想象一下:你只需要说一句“帮我查下2024年巴黎奥运会中国获得的金牌数,换算成我司的绩效分(1块金牌=10分),然后发邮件给所有部门同步”,智能体就能自动完成搜索、计算、邮件发送全流程,不需要你手动操作任何步骤。
而LangChain作为当前全球最流行的LLM应用开发框架,已经把智能体的开发门槛降到了极低的水平,你不需要自己实现复杂的推理循环、工具调度、记忆管理逻辑,只需要几十行代码就能搭建出一个功能完整的生产级智能体。
本文将从核心概念、算法原理、实战开发、最佳实践等维度,带你从零到一掌握LangChain智能体的开发全流程,读完本文你就能独立开发出适合自己业务场景的智能体。
一、核心概念与问题背景
1.1 我们为什么需要LLM智能体?
原生大模型(比如GPT-4o、LLaMA3)本质上是一个「概率预测机器」,它的所有知识都来自训练数据,存在四个无法解决的原生痛点:
| 痛点 | 具体表现 | 影响 |
|---|---|---|
| 知识 cutoff | 训练数据有时间截止点,比如GPT-4o的训练数据截止到2023年10月,不知道之后发生的事件 | 无法回答实时性问题,比如最新的新闻、公司内部的新文档 |
| 无法调用外部工具 | 不能访问互联网、数据库、操作系统、企业API等外部系统 | 无法完成需要交互的任务,比如发邮件、查库存、算复杂数学题 |
| 记忆能力有限 | 上下文窗口有限,无法记住长时间的对话历史,也无法存储个人/企业的私有知识 | 会话一多就忘记之前的约定,无法适配需要长期记忆的场景 |
| 复杂推理能力弱 | 无法自主拆解复杂任务,多步骤任务容易出错 | 比如需要先搜索再计算再整理的任务,原生大模型很容易出现幻觉 |
而LLM智能体就是为了解决这些痛点而生的:它以大模型为核心大脑,具备**规划(Planning)、记忆(Memory)、工具使用(Tool Use)**三大核心能力,可以自主完成用户交给的复杂动态任务。
1.2 核心概念定义
1.2.1 LLM智能体的核心三要素
- 规划能力:智能体可以把用户的复杂任务拆解成多个子步骤,自主安排执行顺序,执行完成后还能反思结果是否符合要求,有问题就重试。
- 记忆能力:分为三类:
- 短期记忆:存储当前会话的对话历史,用来理解上下文
- 长期记忆:存储用户的私有数据(比如文档、聊天记录),一般用向量数据库存储
- 工作记忆:存储当前任务执行过程中的中间结果,比如搜索到的信息、计算出来的数值
- 工具使用能力:智能体可以根据当前任务自主选择合适的工具调用,不需要预先定义执行流程。
1.2.2 LangChain核心组件定义
LangChain提供了一整套智能体开发的组件,你只需要组装这些组件就能完成开发:
| 组件 | 作用 |
|---|---|
| LLM | 智能体的核心大脑,负责推理、决策、生成内容,支持OpenAI、Anthropic、Ollama等几乎所有主流大模型 |
| Tool | 工具封装,每个工具包含名称、描述、执行函数三个核心属性,大模型通过工具的描述判断什么时候调用这个工具 |
| Memory | 记忆模块,用来存储对话历史和私有知识,支持几十种不同的记忆类型 |
| Agent | 智能体的核心逻辑,负责根据用户输入和历史上下文,决定下一步要做什么(回答用户还是调用工具) |
| AgentExecutor | 智能体调度器,负责执行Agent的决策、调用工具、处理错误、管理循环,避免死循环 |
| Vector Store | 向量数据库,用来存储长期记忆的私有数据,支持Chroma、PGVector、Milvus等主流向量库 |
1.2.3 普通Chain vs 智能体的对比
很多初学者会混淆Chain和Agent的区别,我们用一个表格明确两者的差异:
| 对比维度 | 普通Chain | LLM智能体(Agent) |
|---|---|---|
| 执行逻辑 | 固定流程,预先定义好每一步的执行顺序 | 动态流程,大模型自主决定每一步要做什么 |
| 工具使用 | 只能调用预先定义好的固定工具,没有选择能力 | 可以根据当前任务自主选择合适的工具调用 |
| 记忆能力 | 一般只支持简单的会话传递,没有长期记忆能力 | 支持短期对话记忆、长期向量记忆、工作记忆 |
| 推理能力 | 只能处理预先定义好的简单任务,复杂任务无法适配 | 可以自主拆解复杂任务,多步推理完成目标 |
| 适用场景 | 简单的、流程固定的场景,比如单轮RAG问答、文本生成 | 复杂的、动态的场景,比如个人助理、客户服务、研发效能 |
| 开发难度 | 低,只需要定义好Chain的步骤即可 | 中高,需要调试Prompt、工具、记忆的配合 |
| 可靠性 | 高,流程固定结果可控 | 中低,大模型的决策可能出现幻觉、错误调用工具 |
1.2.4 实体关系ER图
二、算法原理与数学模型
2.1 主流智能体算法介绍
目前工业界最常用的智能体算法有三种:
- ReAct:最通用的算法,核心逻辑是「思考(Thought)→行动(Action)→观察(Observation)」循环,适合大部分场景,也是LangChain默认支持的算法
- Self-Ask:适合需要多步检索的场景,智能体不断问自己问题,自己回答,直到拿到足够的信息
- Plan-and-Execute:适合非常复杂的任务,先做整体规划,再一步步执行每个子任务,适合多步骤的复杂工作流
我们重点讲解ReAct算法,这是当前应用最广泛的智能体算法。
2.2 ReAct算法流程
整个流程是一个循环,直到智能体判断不需要调用工具,就可以输出最终回答。
2.3 数学模型
ReAct算法的本质是对推理轨迹的序列建模,我们可以用下面的公式表示:
p(τ∣q)=∏t=1Tp(at∣q,h1:t−1)⋅p(ot∣at)⋅p(rt∣q,h1:t−1,ot) p(\tau|q) = \prod_{t=1}^T p(a_t|q, h_{1:t-1}) \cdot p(o_t|a_t) \cdot p(r_t|q, h_{1:t-1}, o_t) p(τ∣q)=t=1∏Tp(at∣q,h1:t−1)⋅p(ot∣at)⋅p(rt∣q,h1:t−1,ot)
其中:
- qqq是用户输入的查询
- τ\tauτ是整个推理轨迹,包含所有的思考、行动、观察步骤
- h1:t−1h_{1:t-1}h1:t−1是前t-1步的历史上下文
- ata_tat是第t步要采取的行动(调用哪个工具,参数是什么)
- oto_tot是第t步调用工具得到的观察结果
- rtr_trt是第t步的思考结果,用来判断下一步要做什么
工具选择的评分函数可以表示为:
score(ti∣q,h)=LLM(fprompt(q,h,ti)) score(t_i|q, h) = \text{LLM}(f_{\text{prompt}}(q, h, t_i)) score(ti∣q,h)=LLM(fprompt(q,h,ti))
其中tit_iti是第i个工具,fpromptf_{\text{prompt}}fprompt是提示模板,LLM输出一个0-1的分数表示当前场景下是否应该调用这个工具,分数最高的工具会被选中调用。
三、项目实战:开发个人工作助理智能体
我们将开发一个功能完整的个人工作助理智能体,具备以下功能:
- 实时搜索互联网信息
- 本地私有知识库问答(查工作日志、需求文档)
- 复杂数学计算
- 发送邮件
- 日程管理
3.1 系统架构设计
3.2 开发环境搭建
3.2.1 环境要求
- Python 3.10+
- 大模型API密钥(OpenAI API Key,或者本地部署Ollama LLaMA3)
- Serper API Key(用来做互联网搜索,免费注册即可获取:https://serper.dev)
- 邮箱SMTP授权码(用来发送邮件)
3.2.2 依赖安装
# 安装LangChain核心依赖
pip install langchain langchain-openai langchain-community langchain-chroma python-dotenv
# 安装工具相关依赖
pip install serperapi ics smtplib email
3.2.3 环境变量配置
创建.env文件,填入以下内容:
OPENAI_API_KEY=你的OpenAI API Key
SERPER_API_KEY=你的Serper API Key
SMTP_SERVER=smtp.qq.com # 你的邮箱SMTP服务器
SMTP_PORT=465 # SMTP端口
SMTP_USER=你的邮箱地址
SMTP_PASSWORD=你的邮箱授权码
3.3 核心代码实现
3.3.1 初始化依赖与环境变量
import os
import smtplib
from email.mime.text import MIMEText
from datetime import datetime
from ics import Calendar, Event
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.tools import Tool, SerperDevTool, CalculatorTool
from langchain_community.document_loaders import DirectoryLoader, MarkdownLoader
from langchain.text_splitter import MarkdownTextSplitter
from langchain_chroma import Chroma
from langchain.memory import ConversationBufferMemory
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
# 加载环境变量
load_dotenv()
3.3.2 初始化大模型
# 初始化大模型,用gpt-4o效果最好,也可以换成本地模型
llm = ChatOpenAI(
model="gpt-4o",
temperature=0, # 温度设为0,减少幻觉,让决策更稳定
api_key=os.getenv("OPENAI_API_KEY")
)
# 如果用本地LLaMA3模型的话,注释上面的代码,用下面的
# from langchain_community.chat_models import ChatOllama
# llm = ChatOllama(model="llama3:70b", temperature=0)
3.3.3 实现本地知识库RAG工具
def init_rag_tool(data_dir: str = "./docs") -> Tool:
"""初始化本地知识库工具,data_dir是存放本地markdown文档的目录"""
# 加载本地所有markdown文档
loader = DirectoryLoader(data_dir, glob="**/*.md", loader_cls=MarkdownLoader)
documents = loader.load()
# 切分文档,每块1000字符,重叠200字符,保证语义完整性
text_splitter = MarkdownTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(documents)
# 初始化向量数据库,持久化到本地
vectorstore = Chroma.from_documents(
documents=splits,
embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory="./chroma_db"
)
# 创建检索器,返回最相关的3个结果
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 定义检索函数
def rag_query(query: str) -> str:
"""查询本地知识库的内容,返回相关的文档片段"""
docs = retriever.get_relevant_documents(query)
return "\n\n".join([doc.page_content for doc in docs])
# 封装成Tool,重点是description要写清楚,大模型靠这个判断什么时候调用这个工具
return Tool(
name="local_knowledge_base",
description="当需要查询本地文档、工作日志、公司内部资料的时候使用这个工具,输入是用户的问题,输出是相关的文档内容",
func=rag_query
)
rag_tool = init_rag_tool()
3.3.4 实现邮件发送工具
def send_email(to: str, subject: str, content: str) -> str:
"""发送邮件,参数是收件人邮箱、邮件主题、邮件内容"""
try:
msg = MIMEText(content, "plain", "utf-8")
msg["From"] = os.getenv("SMTP_USER")
msg["To"] = to
msg["Subject"] = subject
server = smtplib.SMTP_SSL(os.getenv("SMTP_SERVER"), os.getenv("SMTP_PORT"))
server.login(os.getenv("SMTP_USER"), os.getenv("SMTP_PASSWORD"))
server.sendmail(os.getenv("SMTP_USER"), to, msg.as_string())
server.quit()
return f"邮件发送成功,收件人:{to},主题:{subject}"
except Exception as e:
return f"邮件发送失败:{str(e)}"
email_tool = Tool(
name="send_email",
description="当需要发送邮件给其他人的时候使用这个工具,输入参数是三个,用逗号分隔:收件人邮箱,邮件主题,邮件内容。比如输入:zhangsan@example.com,会议通知,明天下午2点开会",
func=lambda x: send_email(*x.split(","))
)
3.3.5 实现日程管理工具
def manage_schedule(input_str: str) -> str:
"""管理日程,支持查询、添加日程,输入是操作类型和参数,比如:add,2024-09-10 14:00,开会,公司会议室 或者 query,2024-09-10"""
parts = input_str.split(",")
op = parts[0].strip()
cal_path = "./schedule.ics"
# 加载现有日历
if os.path.exists(cal_path):
with open(cal_path, "r") as f:
cal = Calendar(f.read())
else:
cal = Calendar()
if op == "add":
try:
start_time = datetime.fromisoformat(parts[1].strip())
name = parts[2].strip()
location = parts[3].strip() if len(parts) >3 else ""
event = Event()
event.name = name
event.begin = start_time
event.location = location
cal.events.add(event)
with open(cal_path, "w") as f:
f.write(str(cal))
return f"日程添加成功:{start_time} {name} {location}"
except Exception as e:
return f"添加日程失败:{str(e)}"
elif op == "query":
try:
date = datetime.fromisoformat(parts[1].strip()).date()
events = [e for e in cal.events if e.begin.date() == date]
if not events:
return f"{date} 没有日程安排"
res = [f"{e.begin.time()} {e.name} {e.location}" for e in sorted(events, key=lambda x: x.begin)]
return f"{date} 的日程:\n" + "\n".join(res)
except Exception as e:
return f"查询日程失败:{str(e)}"
else:
return "不支持的操作,支持add和query"
schedule_tool = Tool(
name="schedule_management",
description="当需要查询或者添加日程的时候使用这个工具,输入格式:操作类型,参数。比如添加日程输入add,2024-09-10 14:00,开会,公司会议室;查询日程输入query,2024-09-10",
func=manage_schedule
)
3.3.6 组装智能体
# 初始化所有工具
tools = [
SerperDevTool(), # 互联网搜索工具
CalculatorTool(), # 数学计算器工具
rag_tool, # 本地知识库工具
email_tool, # 邮件发送工具
schedule_tool # 日程管理工具
]
# 初始化记忆模块,存储对话历史
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# 拉取ReAct的官方Prompt模板,也可以自己定制
prompt = hub.pull("hwchase17/react-chat")
# 创建ReAct Agent
agent = create_react_agent(llm, tools, prompt)
# 创建AgentExecutor,负责调度执行
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
memory=memory,
verbose=True, # 开启verbose,打印整个思考过程,方便调试
max_iterations=10, # 最大迭代次数,防止死循环
handle_parsing_errors=True # 自动处理解析错误,比如大模型输出格式不对的时候自动重试
)
3.3.7 运行智能体
if __name__ == "__main__":
print("=== 个人工作助理智能体已启动,输入exit退出 ===")
while True:
query = input("你:")
if query.lower() in ["退出", "exit", "quit"]:
print("=== 智能体已关闭 ===")
break
res = agent_executor.invoke({"input": query})
print(f"助理:{res['output']}")
3.4 运行效果演示
我们测试一个复杂任务:
你:帮我查下2024年苹果秋季发布会发布了哪些新品,整理成3点核心信息,然后添加到我明天下午3点的日程里作为汇报内容,再发邮件给lisi@example.com同步这个信息
运行时的verbose输出如下:
> Entering new AgentExecutor chain...
Thought: 用户需要我先查2024苹果秋季发布会的新品,整理成3点,然后添加日程,再发邮件。首先我需要用搜索工具查相关信息。
Action: serper_dev_tool
Action Input: 2024年苹果秋季发布会发布的新品
Observation: 2024年9月10日苹果秋季发布会发布了iPhone 16系列、Apple Watch Series 10、AirPods 4三款核心新品,iPhone 16搭载A18芯片,支持AI拍照功能,Apple Watch Series 10支持血糖监测,AirPods 4支持主动降噪和空间音频。
Thought: 我已经拿到了搜索结果,现在整理成3点核心信息。接下来需要添加日程,时间是明天下午3点,内容是苹果发布会新品汇报。
Action: schedule_management
Action Input: add,2024-09-11 15:00,苹果秋季发布会新品汇报,内容:1. iPhone 16系列:搭载A18芯片,支持AI拍照;2. Apple Watch Series 10:新增血糖监测功能;3. AirPods 4:支持主动降噪和空间音频
Observation: 日程添加成功:2024-09-11 15:00 苹果秋季发布会新品汇报 内容:1. iPhone 16系列:搭载A18芯片,支持AI拍照;2. Apple Watch Series 10:新增血糖监测功能;3. AirPods 4:支持主动降噪和空间音频
Thought: 日程已经添加成功,接下来需要发邮件给lisi@example.com,主题是2024苹果秋季发布会新品信息,内容就是整理的3点。
Action: send_email
Action Input: lisi@example.com,2024苹果秋季发布会新品信息,你好,2024年苹果秋季发布会核心新品信息如下:1. iPhone 16系列:搭载A18芯片,支持AI拍照;2. Apple Watch Series 10:新增血糖监测功能;3. AirPods 4:支持主动降噪和空间音频。请知悉。
Observation: 邮件发送成功,收件人:lisi@example.com,主题:2024苹果秋季发布会新品信息
Thought: 我已经完成了所有任务:查到了新品信息,整理成3点,添加了日程,发送了邮件。现在可以给用户返回结果了。
Final Answer: 已经为你完成以下操作:
1. 查询到2024年苹果秋季发布会核心新品信息:
- iPhone 16系列:搭载A18芯片,支持AI拍照
- Apple Watch Series 10:新增血糖监测功能
- AirPods 4:支持主动降噪和空间音频
2. 已经将该内容添加到你2024-09-11 15:00的日程中,主题为苹果秋季发布会新品汇报
3. 已经将该信息发送给lisi@example.com,邮件发送成功。
> Finished chain.
可以看到智能体完全自主完成了所有步骤,不需要任何人工干预。
四、最佳实践与常见问题解决
4.1 最佳实践Tips
- 工具数量越少越好:不要给智能体太多无关的工具,工具越多大模型选择越容易出错,只给当前场景需要的工具即可。
- 工具描述要清晰准确:大模型完全靠工具的description判断什么时候调用工具,最好在描述里给出输入输出的示例,减少错误。
- 温度设为0:智能体需要稳定的决策,不需要创造性,temperature设为0可以大幅降低幻觉和决策错误的概率。
- 加最大迭代次数限制:一定要设置max_iterations,防止智能体陷入死循环,比如一直搜索不到结果就无限调用搜索工具。
- 敏感操作加人工确认:比如发邮件、删除数据、操作生产环境等敏感操作,一定要加人类确认步骤,LangChain提供了HumanApprovalCallbackHandler可以直接使用:
from langchain.callbacks import HumanApprovalCallbackHandler
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
callbacks=[HumanApprovalCallbackHandler(approve_tools=["send_email", "schedule_management"])],
...
)
- 用LangSmith做调试:LangSmith是LangChain官方的调试工具,可以记录每一步的思考、工具调用、结果,方便排查问题,比如智能体为什么调用了错误的工具。
4.2 常见问题解决
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 智能体总是调用错误的工具 | 工具描述不清晰,或者大模型能力不够 | 优化工具的description,加入输入输出示例;换能力更强的大模型;在Prompt里加入Few Shot示例 |
| 智能体陷入死循环 | 任务太复杂,或者工具返回的结果不足以回答问题 | 调大max_iterations;优化Prompt,明确告诉智能体什么时候停止;优化工具返回的结果,包含更多信息 |
| 智能体输出格式错误 | 大模型没有按照ReAct的格式输出 | 开启handle_parsing_errors=True;优化Prompt,明确要求输出格式;用支持原生工具调用的大模型(比如GPT-4o、LLaMA3) |
| 智能体忘记之前的对话内容 | 记忆模块配置错误 | 确认memory_key和Prompt里的变量名一致;如果上下文太长,改用ConversationSummaryMemory,自动总结对话历史 |
五、实际应用场景与未来趋势
5.1 实际应用场景
- 企业内部知识库智能助理:整合企业内部的文档、代码、日志、工单等数据,员工可以用自然语言查询任何信息,智能体可以自主查文档、查日志、提单,大幅降低员工的检索成本。
- 客户服务智能体:整合客服知识库、订单系统、物流系统,智能体可以自主查询订单状态、物流信息、处理退换货请求,解决80%以上的常见客服问题,大幅降低客服成本。
- 研发效能智能体:整合Git、Jenkins、监控系统、日志系统,研发人员可以用自然语言查询报错率、回滚版本、查日志、生成代码,大幅提升研发效率。
- 个人学习助理:整合学习资料、笔记、在线课程,智能体可以自主制定学习计划、解答疑问、生成练习题、检查学习效果,提升学习效率。
5.2 行业发展历史
| 时间 | 事件 | 影响 |
|---|---|---|
| 2022年10月 | LangChain 首个版本开源 | 首次提出将大模型与外部工具、记忆结合的开发范式,降低LLM应用开发门槛 |
| 2023年3月 | ReAct 论文正式发布 | 提出“思考-行动-观察”的智能体推理框架,成为现在大部分Agent的基础算法 |
| 2023年8月 | LangChain 正式推出 AgentExecutor 组件 | 封装了Agent的调度、循环、错误处理逻辑,开发者无需自己实现推理循环 |
| 2023年12月 | OpenAI 推出原生工具调用(Function Calling)能力 | 大模型可以直接输出结构化的工具调用参数,大幅降低Agent的解析错误率 |
| 2024年2月 | LangChain 推出 LangGraph 组件 | 支持多智能体协作、状态化的Agent流程,满足更复杂的企业级场景需求 |
| 2024年6月 | 各大开源模型(LLaMA3、Qwen2等)均原生支持工具调用 | 开发者可以基于开源模型构建完全本地化的Agent,无需依赖闭源API |
5.3 未来发展趋势与挑战
发展趋势
- 多智能体协作:未来复杂任务会由多个智能体协作完成,比如规划Agent、执行Agent、审核Agent,各自负责自己擅长的部分,效率更高,结果更可靠。
- 小模型智能体:现在的智能体大多需要大模型驱动,未来专门微调的小模型就能很好的完成工具调用和决策,成本更低,速度更快。
- 端侧智能体:未来智能体可以直接运行在手机、电脑等端侧设备上,不需要调用云端API,隐私性更好,响应速度更快。
- 可解释性智能体:现在的智能体决策过程是黑盒,未来可以解释每一步决策的原因,方便排查问题,提升可靠性。
面临的挑战
- 幻觉问题:大模型的幻觉仍然存在,智能体可能会做出错误的决策,调用错误的工具,输出错误的结果。
- 效率问题:复杂任务需要多次调用大模型,成本高,速度慢,无法适配高并发场景。
- 安全问题:智能体调用外部工具可能会导致数据泄露、系统被攻击等安全问题,需要完善的权限控制和审核机制。
- 评估问题:智能体的好坏很难量化评估,目前还没有统一的评估标准,需要更完善的评估体系。
六、工具和资源推荐
- 官方文档:LangChain官方文档(https://python.langchain.com/)是最好的学习资料,更新及时,示例丰富。
- LangChain Cookbook:官方提供的示例仓库(https://github.com/langchain-ai/langchain/tree/master/cookbook),包含上百个智能体开发的示例代码。
- LangSmith:LangChain官方的调试和监控工具(https://www.langchain.com/langsmith),开发智能体必备,可以大大提升调试效率。
- LangServe:LangChain官方的部署工具,可以把智能体一键部署成API接口,方便集成到其他系统。
- LangGraph:LangChain官方的多智能体开发框架,适合开发复杂的多智能体协作场景。
本章小结
LangChain已经成为当前开发LLM智能体的首选框架,它的组件化设计让开发者不需要关心底层的推理循环、工具调度、记忆管理逻辑,只需要专注于业务逻辑的实现,几十行代码就能搭建出功能完整的智能体。
未来5年,智能体将会成为下一代软件的主流交互方式,所有的软件都会有智能体入口,用户只需要用自然语言就能完成所有操作,而LangChain作为智能体开发的基础设施,将会扮演越来越重要的角色。现在开始学习LangChain智能体开发,刚好赶上这一波技术浪潮的红利。
如果你在开发过程中有任何问题,欢迎在评论区留言交流。
更多推荐
所有评论(0)