agent实战专栏|第11篇:LangChain入门:构建Agent的瑞士军刀
引言
在Agent开发领域,LangChain无疑是最受欢迎的框架之一。它提供了一套完整的工具链,帮助开发者快速构建强大的AI应用。LangChain的核心优势在于它的模块化设计和丰富的集成能力,能够轻松连接各种LLM、数据源和外部工具。
本章将详细介绍LangChain的核心概念,包括Chain、Prompt、Tool等,并通过实战案例演示如何使用LangChain构建Agent。
一、LangChain概述
1.1 什么是LangChain
LangChain是一个用于构建基于LLM的应用程序的框架。它提供了一套工具和抽象,使得开发者能够快速构建复杂的AI应用,包括聊天机器人、问答系统、代码生成器等。
1.2 LangChain的核心优势
| 优势 | 说明 |
|---|---|
| 模块化设计 | 将复杂的AI应用分解为可组合的模块 |
| 丰富的集成 | 支持多种LLM、向量数据库、API等 |
| 易于扩展 | 提供插件系统,方便添加自定义功能 |
| 文档完善 | 提供详细的文档和丰富的示例 |
| 社区活跃 | 拥有庞大的开发者社区和生态系统 |
1.3 LangChain的架构
┌─────────────────────────────────────────────────────────────┐
│ LangChain │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ LLM层 │ │ 工具层 │ │ 数据层 │ │
│ │ OpenAI │ │ Search │ │ Vector DB │ │
│ │ Anthropic │ │ Calculator │ │ SQL DB │ │
│ │ Google │ │ API调用 │ │ 文件系统 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └────────┬────────┴────────┬────────┘ │
│ │ │ │
│ ┌────────▼────────┐ ┌─────▼─────┐ │
│ │ Chain层 │ │ Memory层 │ │
│ │ SequentialChain│ │ Buffer │ │
│ │ RouterChain │ │ Summary │ │
│ │ TransformChain│ │ Vector │ │
│ └──────┬─────────┘ └────┬──────┘ │
│ │ │ │
│ └────────┬────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Agent层 │ │
│ │ ReAct Agent │ │
│ │ Conversational │ │
│ │ PlanAndExecute │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
1.4 安装LangChain
# 安装基础版本
pip install langchain
# 安装完整版本(包含所有依赖)
pip install langchain[all]
# 安装特定模块
pip install langchain[openai] # OpenAI集成
pip install langchain[chromadb] # Chroma向量数据库
pip install langchain[anthropic] # Anthropic集成
二、核心概念:Prompt
2.1 Prompt的定义
Prompt是指用户输入给LLM的文本,用于引导LLM生成特定的输出。一个好的Prompt能够让LLM更准确地理解用户意图,生成高质量的响应。
2.2 Prompt模板
LangChain提供了Prompt模板功能,允许开发者定义可复用的Prompt结构。
2.2.1 基础Prompt模板
from langchain.prompts import PromptTemplate
# 创建基础Prompt模板
prompt = PromptTemplate(
input_variables=["topic"],
template="请介绍一下{topic}。"
)
# 使用模板
result = prompt.format(topic="人工智能")
print(result)
2.2.2 ChatPrompt模板
from langchain.prompts import ChatPromptTemplate
# 创建ChatPrompt模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名专业的技术顾问。"),
("human", "请介绍一下{topic}。")
])
# 使用模板
result = prompt.format(topic="LangChain")
print(result)
2.2.3 带示例的Prompt模板
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
# 创建示例模板
example_prompt = PromptTemplate(
input_variables=["question", "answer"],
template="问题: {question}\n答案: {answer}"
)
# 创建示例
examples = [
{"question": "什么是Python?", "answer": "Python是一种高级编程语言。"},
{"question": "什么是机器学习?", "answer": "机器学习是一种人工智能技术。"},
{"question": "什么是深度学习?", "answer": "深度学习是机器学习的一个分支。"}
]
# 创建FewShotPrompt模板
prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="请根据以下示例回答问题:",
suffix="问题: {input}\n答案:",
input_variables=["input"]
)
# 使用模板
result = prompt.format(input="什么是LangChain?")
print(result)
2.3 Prompt最佳实践
2.3.1 明确任务描述
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", """你是一名专业的代码审查员。请仔细审查以下代码,并提供详细的改进建议。
审查标准:
1. 代码可读性:命名规范、注释清晰
2. 代码质量:避免重复、遵循DRY原则
3. 安全性:防止注入攻击、敏感信息保护
4. 性能:避免不必要的计算、优化算法
输出格式:
## 问题列表
1. [问题描述] - [严重程度] - [改进建议]
## 优化建议
1. [建议内容]"""),
("human", "{code}")
])
2.3.2 使用结构化输出
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", """请分析以下文本,并以JSON格式输出结果。
输出格式:
{{
"sentiment": "positive|negative|neutral",
"entities": [{"name": "实体名称", "type": "实体类型"}],
"summary": "文本摘要"
}}"""),
("human", "{text}")
])
2.3.3 限制输出长度
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", """请回答以下问题,限制在100字以内。"""),
("human", "{question}")
])
三、核心概念:Chain
3.1 Chain的定义
Chain是LangChain中最核心的概念之一。它是一系列组件的组合,用于处理输入并生成输出。Chain可以将多个组件串联起来,形成一个完整的处理流程。
3.2 基础Chain
3.2.1 LLMChain
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import ChatPromptTemplate
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建Prompt
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名助手。"),
("human", "{input}")
])
# 创建Chain
chain = LLMChain(llm=llm, prompt=prompt)
# 运行Chain
result = chain.run(input="请介绍一下LangChain。")
print(result)
3.2.2 异步Chain
# 异步运行Chain
result = await chain.arun(input="请介绍一下LangChain。")
print(result)
3.3 组合Chain
3.3.1 SequentialChain
SequentialChain将多个Chain按顺序连接起来,前一个Chain的输出作为后一个Chain的输入。
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain, SequentialChain
from langchain.prompts import ChatPromptTemplate
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建第一个Chain:生成主题
prompt1 = ChatPromptTemplate.from_messages([
("system", "你是一名创意写作助手。"),
("human", "请为一篇技术文章生成一个主题。")
])
chain1 = LLMChain(llm=llm, prompt=prompt1, output_key="topic")
# 创建第二个Chain:生成大纲
prompt2 = ChatPromptTemplate.from_messages([
("system", "你是一名技术编辑。"),
("human", "请为主题'{topic}'生成文章大纲。")
])
chain2 = LLMChain(llm=llm, prompt=prompt2, output_key="outline")
# 创建第三个Chain:生成文章
prompt3 = ChatPromptTemplate.from_messages([
("system", "你是一名技术作家。"),
("human", "请根据大纲'{outline}'写一篇详细的文章。")
])
chain3 = LLMChain(llm=llm, prompt=prompt3, output_key="article")
# 组合Chain
overall_chain = SequentialChain(
chains=[chain1, chain2, chain3],
input_variables=[],
output_variables=["topic", "outline", "article"],
verbose=True
)
# 运行组合Chain
result = overall_chain.run({})
print(f"主题: {result['topic']}")
print(f"大纲: {result['outline']}")
print(f"文章: {result['article']}")
3.3.2 RouterChain
RouterChain根据输入内容选择不同的Chain进行处理。
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain, RouterChain, MultiPromptChain
from langchain.prompts import ChatPromptTemplate
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建不同领域的Prompt
physics_prompt = ChatPromptTemplate.from_messages([
("system", "你是一名物理学专家。"),
("human", "{input}")
])
math_prompt = ChatPromptTemplate.from_messages([
("system", "你是一名数学专家。"),
("human", "{input}")
])
computer_prompt = ChatPromptTemplate.from_messages([
("system", "你是一名计算机科学专家。"),
("human", "{input}")
])
# 创建Prompt字典
prompt_infos = [
{
"name": "physics",
"description": "处理物理学相关问题",
"prompt_template": physics_prompt
},
{
"name": "math",
"description": "处理数学相关问题",
"prompt_template": math_prompt
},
{
"name": "computer",
"description": "处理计算机科学相关问题",
"prompt_template": computer_prompt
}
]
# 创建RouterChain
destination_chains = {}
for p_info in prompt_infos:
name = p_info["name"]
prompt = p_info["prompt_template"]
chain = LLMChain(llm=llm, prompt=prompt)
destination_chains[name] = chain
# 创建路由Prompt
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(
destinations="\n".join([f"{p['name']}: {p['description']}" for p in prompt_infos])
)
router_prompt = ChatPromptTemplate.from_messages([
("system", router_template),
("human", "{input}")
])
# 创建RouterChain
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
# 创建MultiPromptChain
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=destination_chains["computer"]
)
# 运行Chain
result = chain.run("什么是量子力学?")
print(result)
result = chain.run("什么是深度学习?")
print(result)
3.3.3 TransformChain
TransformChain允许开发者自定义输入输出转换逻辑。
from langchain.chains import TransformChain
from langchain.chains import SequentialChain
# 创建TransformChain
def transform_func(inputs):
text = inputs["text"]
# 转换逻辑:将文本转换为大写
transformed_text = text.upper()
return {"transformed_text": transformed_text}
transform_chain = TransformChain(
input_variables=["text"],
output_variables=["transformed_text"],
transform=transform_func
)
# 创建LLMChain
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import ChatPromptTemplate
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名助手。"),
("human", "请分析以下文本:{transformed_text}")
])
llm_chain = LLMChain(llm=llm, prompt=prompt, output_key="analysis")
# 组合Chain
overall_chain = SequentialChain(
chains=[transform_chain, llm_chain],
input_variables=["text"],
output_variables=["transformed_text", "analysis"],
verbose=True
)
# 运行Chain
result = overall_chain.run(text="Hello, LangChain!")
print(f"转换后的文本: {result['transformed_text']}")
print(f"分析结果: {result['analysis']}")
四、核心概念:Tool
4.1 Tool的定义
Tool是Agent与外部世界交互的桥梁。通过Tool,Agent可以执行各种操作,如搜索、计算、文件操作等。
4.2 内置工具
LangChain提供了许多内置工具,可以直接使用。
4.2.1 搜索工具
from langchain.tools import DuckDuckGoSearchRun
# 创建搜索工具
search = DuckDuckGoSearchRun()
# 使用搜索工具
result = search.run("LangChain最新版本")
print(result)
4.2.2 计算器工具
from langchain.tools import CalculatorTool
# 创建计算器工具
calculator = CalculatorTool()
# 使用计算器工具
result = calculator.run("25 × 48 + 120")
print(result)
4.2.3 文件操作工具
from langchain.tools import FileWriteTool, FileReadTool
# 创建文件写入工具
write_tool = FileWriteTool()
# 使用文件写入工具
result = write_tool.run({"file_path": "output.txt", "content": "Hello, LangChain!"})
print(result)
# 创建文件读取工具
read_tool = FileReadTool()
# 使用文件读取工具
result = read_tool.run("output.txt")
print(result)
4.3 自定义工具
开发者可以根据需要创建自定义工具。
4.3.1 基础自定义工具
from langchain.tools import BaseTool
from typing import Optional, Type
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(description="搜索查询")
class CustomSearchTool(BaseTool):
name = "custom_search"
description = "使用自定义搜索引擎搜索信息"
args_schema: Type[BaseModel] = SearchInput
def _run(self, query: str) -> str:
# 自定义搜索逻辑
return f"搜索结果: {query} - 这是模拟的搜索结果"
async def _arun(self, query: str) -> str:
# 异步搜索逻辑
return f"搜索结果: {query} - 这是模拟的异步搜索结果"
# 使用自定义工具
tool = CustomSearchTool()
result = tool.run("LangChain教程")
print(result)
4.3.2 带参数验证的工具
from langchain.tools import BaseTool
from typing import Optional, Type
from pydantic import BaseModel, Field, ValidationError
class CalculatorInput(BaseModel):
expression: str = Field(description="数学表达式")
@classmethod
def validate_expression(cls, v):
# 验证表达式只包含数字和运算符
allowed_chars = "0123456789+-*/(). "
for char in v:
if char not in allowed_chars:
raise ValueError(f"表达式包含非法字符: {char}")
return v
class SafeCalculatorTool(BaseTool):
name = "safe_calculator"
description = "安全计算器,只接受合法的数学表达式"
args_schema: Type[BaseModel] = CalculatorInput
def _run(self, expression: str) -> str:
try:
# 安全计算
result = eval(expression)
return f"计算结果: {result}"
except Exception as e:
return f"计算错误: {e}"
# 使用工具
tool = SafeCalculatorTool()
# 合法表达式
result = tool.run("25 * 48")
print(result)
# 非法表达式(包含注入攻击)
result = tool.run("__import__('os').system('ls')")
print(result)
4.4 工具集成
4.4.1 集成API
from langchain.tools import BaseTool
from typing import Optional, Type
from pydantic import BaseModel, Field
import requests
class WeatherInput(BaseModel):
city: str = Field(description="城市名称")
class WeatherTool(BaseTool):
name = "weather"
description = "查询指定城市的天气"
args_schema: Type[BaseModel] = WeatherInput
def _run(self, city: str) -> str:
# 调用天气API
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid=your-api-key"
response = requests.get(url)
data = response.json()
if response.status_code == 200:
weather = data["weather"][0]["description"]
temp = data["main"]["temp"] - 273.15
return f"{city}的天气: {weather}, 温度: {temp:.1f}°C"
else:
return f"查询失败: {data.get('message', '未知错误')}"
# 使用工具
tool = WeatherTool()
result = tool.run("Beijing")
print(result)
4.4.2 集成数据库
from langchain.tools import BaseTool
from typing import Optional, Type
from pydantic import BaseModel, Field
import sqlite3
class DatabaseQueryInput(BaseModel):
query: str = Field(description="SQL查询语句")
class DatabaseTool(BaseTool):
name = "database_query"
description = "查询SQLite数据库"
args_schema: Type[BaseModel] = DatabaseQueryInput
def __init__(self, db_path: str):
super().__init__()
self.db_path = db_path
def _run(self, query: str) -> str:
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 限制只能执行SELECT查询
if not query.strip().upper().startswith("SELECT"):
return "只允许执行SELECT查询"
cursor.execute(query)
results = cursor.fetchall()
conn.close()
return f"查询结果: {results}"
except Exception as e:
return f"查询错误: {e}"
# 使用工具
tool = DatabaseTool(db_path="example.db")
result = tool.run("SELECT * FROM users")
print(result)
五、核心概念:Memory
5.1 Memory的定义
Memory允许Agent在对话过程中记住之前的交互内容,从而实现上下文理解和多轮对话。
5.2 内置Memory类型
5.2.1 ConversationBufferMemory
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建Memory
memory = ConversationBufferMemory()
# 创建对话Chain
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)
# 进行对话
response1 = conversation.predict(input="你好,我叫张三。")
print(response1)
response2 = conversation.predict(input="我是一名程序员。")
print(response2)
response3 = conversation.predict(input="我擅长Python编程。")
print(response3)
response4 = conversation.predict(input="你知道我叫什么名字吗?")
print(response4)
5.2.2 ConversationBufferWindowMemory
from langchain.memory import ConversationBufferWindowMemory
# 创建带窗口的Memory,只保留最近2轮对话
memory = ConversationBufferWindowMemory(k=2)
# 创建对话Chain
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)
# 进行多轮对话
conversation.predict(input="你好,我叫张三。")
conversation.predict(input="我是一名程序员。")
conversation.predict(input="我擅长Python编程。")
conversation.predict(input="我喜欢打篮球。")
# 由于窗口大小为2,只会记住最近2轮对话
response = conversation.predict(input="你知道我叫什么名字吗?")
print(response)
5.2.3 ConversationSummaryMemory
from langchain.memory import ConversationSummaryMemory
# 创建带摘要的Memory
memory = ConversationSummaryMemory(llm=llm)
# 创建对话Chain
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)
# 进行多轮对话
conversation.predict(input="你好,我叫张三。")
conversation.predict(input="我是一名程序员,在一家科技公司工作。")
conversation.predict(input="我擅长Python和Java编程,已经有5年工作经验了。")
conversation.predict(input="我喜欢打篮球和看电影。")
# 查看摘要
print(memory.buffer)
5.2.4 ConversationKGMemory
from langchain.memory import ConversationKGMemory
# 创建知识图谱Memory
memory = ConversationKGMemory(llm=llm)
# 创建对话Chain
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)
# 进行对话
conversation.predict(input="张三是一名程序员,他在阿里巴巴工作。")
conversation.predict(input="李四是一名设计师,她在腾讯工作。")
# 查看知识图谱
print(memory.kg)
5.3 自定义Memory
from langchain.memory import BaseMemory
from typing import Dict, List
from pydantic import BaseModel
class CustomMemory(BaseMemory, BaseModel):
history: List[Dict[str, str]] = []
max_length: int = 10
@property
def memory_variables(self) -> List[str]:
return ["history"]
def load_memory_variables(self, inputs: Dict) -> Dict[str, str]:
# 将历史记录转换为字符串
history_str = "\n".join([f"{item['role']}: {item['content']}" for item in self.history])
return {"history": history_str}
def save_context(self, inputs: Dict, outputs: Dict) -> None:
# 保存对话历史
self.history.append({"role": "user", "content": inputs["input"]})
self.history.append({"role": "assistant", "content": outputs["response"]})
# 如果超过最大长度,删除最早的记录
while len(self.history) > self.max_length:
self.history.pop(0)
def clear(self) -> None:
# 清空历史记录
self.history = []
# 使用自定义Memory
memory = CustomMemory(max_length=5)
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)
# 进行对话
conversation.predict(input="你好!")
conversation.predict(input="请介绍一下LangChain。")
六、核心概念:Agent
6.1 Agent的定义
Agent是LangChain中最高级别的抽象,它能够自主决策并执行任务。Agent通常包含以下组件:
- LLM:核心大脑,用于推理和决策
- Tools:工具集,用于与外部世界交互
- Memory:记忆系统,用于存储对话历史
- Prompt:提示模板,用于引导LLM
6.2 Agent类型
6.2.1 ZeroShotAgent
ZeroShotAgent不需要示例,直接根据工具描述进行决策。
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.tools import DuckDuckGoSearchRun, CalculatorTool
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建工具
tools = [
DuckDuckGoSearchRun(name="搜索"),
CalculatorTool(name="计算器")
]
# 创建Agent
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# 运行Agent
result = agent.run("搜索Python最新版本并计算发布至今的天数")
print(result)
6.2.2 ConversationalAgent
ConversationalAgent适用于对话场景,能够保持对话历史。
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.tools import DuckDuckGoSearchRun, CalculatorTool
from langchain.memory import ConversationBufferMemory
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建工具
tools = [
DuckDuckGoSearchRun(name="搜索"),
CalculatorTool(name="计算器")
]
# 创建Memory
memory = ConversationBufferMemory(memory_key="chat_history")
# 创建Agent
agent = initialize_agent(
tools,
llm,
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
memory=memory,
verbose=True
)
# 进行多轮对话
result1 = agent.run("你好,我叫张三。")
print(result1)
result2 = agent.run("搜索今天北京的天气。")
print(result2)
result3 = agent.run("我明天要去北京出差,需要穿什么衣服?")
print(result3)
6.2.3 SelfAskWithSearchAgent
SelfAskWithSearchAgent会先进行自我提问,然后根据需要进行搜索。
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.tools import DuckDuckGoSearchRun
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建工具
tools = [DuckDuckGoSearchRun(name="搜索")]
# 创建Agent
agent = initialize_agent(
tools,
llm,
agent=AgentType.SELF_ASK_WITH_SEARCH,
verbose=True
)
# 运行Agent
result = agent.run("LangChain是什么时候发布的?")
print(result)
6.2.4 ReActAgent
ReActAgent结合了推理和行动,是最常用的Agent类型。
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.tools import DuckDuckGoSearchRun, CalculatorTool
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建工具
tools = [
DuckDuckGoSearchRun(name="搜索"),
CalculatorTool(name="计算器")
]
# 创建Agent
agent = initialize_agent(
tools,
llm,
agent=AgentType.REACT_DOCSTORE,
verbose=True
)
# 运行Agent
result = agent.run("计算圆周率的前10位")
print(result)
6.3 自定义Agent
from langchain.agents import AgentExecutor, BaseSingleActionAgent
from langchain.tools import DuckDuckGoSearchRun, CalculatorTool
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from typing import List, Tuple, Any, Optional
class CustomAgent(BaseSingleActionAgent):
llm: ChatOpenAI
tools: List
@property
def input_keys(self) -> List[str]:
return ["input"]
def plan(
self,
intermediate_steps: List[Tuple[Any, str]],
**kwargs: Any
) -> Any:
# 获取输入
input_text = kwargs["input"]
# 使用LLM决定下一步行动
prompt = ChatPromptTemplate.from_messages([
("system", f"""你是一名Agent,拥有以下工具:
{[tool.name for tool in self.tools]}
请根据输入决定使用哪个工具或直接回答。
输出格式:
- 如果使用工具:TOOL_NAME: 参数
- 如果直接回答:直接输出答案"""),
("human", input_text)
])
response = self.llm.predict(prompt.format())
# 解析响应
if ":" in response:
tool_name, tool_input = response.split(":", 1)
tool_name = tool_name.strip()
tool_input = tool_input.strip()
# 找到对应的工具
for tool in self.tools:
if tool.name == tool_name:
return tool, tool_input
# 如果不使用工具,返回None
return None, response
async def aplan(
self,
intermediate_steps: List[Tuple[Any, str]],
**kwargs: Any
) -> Any:
return self.plan(intermediate_steps, **kwargs)
# 创建工具
tools = [
DuckDuckGoSearchRun(name="搜索"),
CalculatorTool(name="计算器")
]
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建自定义Agent
agent = CustomAgent(llm=llm, tools=tools)
# 创建Agent执行器
agent_executor = AgentExecutor.from_agent_and_tools(
agent=agent,
tools=tools,
verbose=True
)
# 运行Agent
result = agent_executor.run("搜索今天的天气")
print(result)
七、核心概念:文档加载与处理
7.1 文档加载器
LangChain提供了多种文档加载器,可以从不同来源加载文档。
7.1.1 文本文件加载器
from langchain.document_loaders import TextLoader
# 创建加载器
loader = TextLoader("example.txt")
# 加载文档
documents = loader.load()
# 查看文档内容
for doc in documents:
print(doc.page_content)
7.1.2 PDF文件加载器
# 安装依赖
pip install pypdf
from langchain.document_loaders import PyPDFLoader
# 创建加载器
loader = PyPDFLoader("example.pdf")
# 加载文档(按页加载)
documents = loader.load_and_split()
# 查看文档内容
for i, doc in enumerate(documents):
print(f"第{i+1}页: {doc.page_content[:200]}")
7.1.3 Word文件加载器
# 安装依赖
pip install python-docx
from langchain.document_loaders import Docx2txtLoader
# 创建加载器
loader = Docx2txtLoader("example.docx")
# 加载文档
documents = loader.load()
# 查看文档内容
for doc in documents:
print(doc.page_content)
7.1.4 网页加载器
from langchain.document_loaders import WebBaseLoader
# 创建加载器
loader = WebBaseLoader("https://langchain.com/")
# 加载文档
documents = loader.load()
# 查看文档内容
for doc in documents:
print(doc.page_content[:500])
7.2 文档分割器
当文档过大时,需要将其分割成小块。
7.2.1 字符分割器
from langchain.text_splitter import CharacterTextSplitter
# 创建分割器
text_splitter = CharacterTextSplitter(
separator="\n",
chunk_size=1000,
chunk_overlap=200
)
# 分割文本
text = """LangChain是一个用于构建基于LLM的应用程序的框架。
它提供了一套工具和抽象,使得开发者能够快速构建复杂的AI应用。
LangChain支持多种LLM提供商,包括OpenAI、Anthropic、Google等。
它还提供了丰富的工具集,包括搜索、计算、文件操作等。
LangChain的核心概念包括Chain、Prompt、Tool、Memory、Agent等。
通过这些概念,开发者可以构建各种类型的AI应用,如聊天机器人、问答系统、代码生成器等。"""
chunks = text_splitter.split_text(text)
# 查看分割结果
for i, chunk in enumerate(chunks):
print(f"块{i+1}: {chunk}")
7.2.2 递归字符分割器
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 创建分割器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", " ", ""]
)
# 分割文本
chunks = text_splitter.split_text(text)
# 查看分割结果
for i, chunk in enumerate(chunks):
print(f"块{i+1}: {chunk}")
7.2.3 文档分割器
from langchain.text_splitter import CharacterTextSplitter
from langchain.document_loaders import TextLoader
# 加载文档
loader = TextLoader("example.txt")
documents = loader.load()
# 创建分割器
text_splitter = CharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
# 分割文档
split_docs = text_splitter.split_documents(documents)
# 查看分割结果
for i, doc in enumerate(split_docs):
print(f"文档块{i+1}: {doc.page_content[:200]}")
八、核心概念:向量数据库集成
8.1 向量数据库简介
向量数据库用于存储和检索嵌入向量,是构建RAG(Retrieval-Augmented Generation)应用的关键组件。
8.2 Chroma集成
# 安装依赖
pip install chromadb
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
# 加载文档
loader = TextLoader("example.txt")
documents = loader.load()
# 分割文档
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
split_docs = text_splitter.split_documents(documents)
# 创建向量数据库
embeddings = OpenAIEmbeddings()
vector_db = Chroma.from_documents(
documents=split_docs,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 持久化向量数据库
vector_db.persist()
# 加载向量数据库
vector_db = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
# 进行相似性搜索
query = "LangChain的核心概念是什么?"
results = vector_db.similarity_search(query, k=3)
# 查看搜索结果
for i, result in enumerate(results):
print(f"结果{i+1}: {result.page_content}")
8.3 Pinecone集成
# 安装依赖
pip install pinecone-client
from langchain.vectorstores import Pinecone
from langchain.embeddings import OpenAIEmbeddings
import pinecone
# 初始化Pinecone
pinecone.init(
api_key="your-api-key",
environment="us-west1-gcp"
)
# 创建索引(如果不存在)
index_name = "langchain-demo"
if index_name not in pinecone.list_indexes():
pinecone.create_index(
name=index_name,
dimension=1536,
metric="cosine"
)
# 获取索引
index = pinecone.Index(index_name)
# 创建向量数据库
embeddings = OpenAIEmbeddings()
vector_db = Pinecone(
index=index,
embedding_function=embeddings.embed_query,
text_key="text"
)
# 添加文档
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
loader = TextLoader("example.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
split_docs = text_splitter.split_documents(documents)
vector_db.add_documents(split_docs)
# 进行相似性搜索
query = "LangChain的核心概念是什么?"
results = vector_db.similarity_search(query, k=3)
# 查看搜索结果
for i, result in enumerate(results):
print(f"结果{i+1}: {result.page_content}")
8.4 FAISS集成
# 安装依赖
pip install faiss-cpu # CPU版本
# pip install faiss-gpu # GPU版本
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
# 加载文档
loader = TextLoader("example.txt")
documents = loader.load()
# 分割文档
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
split_docs = text_splitter.split_documents(documents)
# 创建向量数据库
embeddings = OpenAIEmbeddings()
vector_db = FAISS.from_documents(split_docs, embeddings)
# 保存向量数据库
vector_db.save_local("faiss_index")
# 加载向量数据库
vector_db = FAISS.load_local("faiss_index", embeddings)
# 进行相似性搜索
query = "LangChain的核心概念是什么?"
results = vector_db.similarity_search(query, k=3)
# 查看搜索结果
for i, result in enumerate(results):
print(f"结果{i+1}: {result.page_content}")
九、核心概念:评估
9.1 评估概述
评估是衡量Agent性能的重要环节。LangChain提供了多种评估工具和方法。
9.2 使用LLM评估
from langchain.evaluation import load_evaluator
from langchain.chat_models import ChatOpenAI
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建评估器
evaluator = load_evaluator("qa", llm=llm)
# 定义问题和答案
question = "什么是LangChain?"
predicted_answer = "LangChain是一个用于构建AI应用的框架。"
reference_answer = "LangChain是一个用于构建基于LLM的应用程序的框架,它提供了一套工具和抽象,使得开发者能够快速构建复杂的AI应用。"
# 进行评估
result = evaluator.evaluate(
prediction=predicted_answer,
reference=reference_answer,
input=question
)
# 查看评估结果
print(f"评估得分: {result['score']}")
print(f"评估理由: {result['reasoning']}")
9.3 使用标准评估数据集
from langchain.evaluation import load_evaluator
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import ChatPromptTemplate
# 创建LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建Chain
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名助手。"),
("human", "{question}")
])
chain = LLMChain(llm=llm, prompt=prompt)
# 创建评估器
evaluator = load_evaluator("qa", llm=llm)
# 定义测试数据集
test_cases = [
{
"question": "什么是Python?",
"reference": "Python是一种高级、通用、解释型的编程语言,由Guido van Rossum于1991年创建。"
},
{
"question": "什么是机器学习?",
"reference": "机器学习是人工智能的一个分支,它使计算机能够从数据中学习并做出预测或决策。"
},
{
"question": "什么是深度学习?",
"reference": "深度学习是机器学习的一个子领域,它使用多层神经网络来学习数据的复杂特征。"
}
]
# 进行评估
total_score = 0
for i, test_case in enumerate(test_cases):
# 获取预测答案
predicted_answer = chain.run(question=test_case["question"])
# 进行评估
result = evaluator.evaluate(
prediction=predicted_answer,
reference=test_case["reference"],
input=test_case["question"]
)
total_score += result["score"]
print(f"测试用例{i+1}:")
print(f" 问题: {test_case['question']}")
print(f" 预测答案: {predicted_answer}")
print(f" 参考答案: {test_case['reference']}")
print(f" 得分: {result['score']}")
print(f" 理由: {result['reasoning']}")
print()
# 计算平均得分
average_score = total_score / len(test_cases)
print(f"平均得分: {average_score}")
9.4 自定义评估器
from langchain.evaluation import EvaluatorType, load_evaluator
from langchain.evaluation.schema import Evaluator
from typing import Optional, Dict, Any
class CustomEvaluator(Evaluator):
def evaluate(
self,
prediction: str,
reference: Optional[str] = None,
input: Optional[str] = None,
**kwargs: Any
) -> Dict[str, Any]:
# 自定义评估逻辑
# 这里简单比较预测答案和参考答案的相似度
if reference is None:
return {"score": 0.5, "reasoning": "没有参考答案"}
# 计算相似度(简单实现)
prediction_words = set(prediction.lower().split())
reference_words = set(reference.lower().split())
if len(reference_words) == 0:
return {"score": 0.0, "reasoning": "参考答案为空"}
overlap = len(prediction_words & reference_words)
similarity = overlap / len(reference_words)
return {
"score": similarity,
"reasoning": f"预测答案与参考答案的相似度为{similarity:.2f}"
}
# 使用自定义评估器
evaluator = CustomEvaluator()
# 进行评估
result = evaluator.evaluate(
prediction="LangChain是一个用于构建AI应用的框架。",
reference="LangChain是一个用于构建基于LLM的应用程序的框架。",
input="什么是LangChain?"
)
print(f"评估得分: {result['score']}")
print(f"评估理由: {result['reasoning']}")
十、实战案例:构建知识库问答系统
10.1 需求分析
构建一个基于文档的知识库问答系统,用户可以上传文档并进行问答。
10.2 系统架构
┌─────────────────────────────────────────────────────────────┐
│ 知识库问答系统 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 文档上传 │───▶│ 文档处理 │───▶│ 向量存储 │ │
│ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 用户查询 │ │
│ │ ┌───────────────────────────┐ │ │
│ │ │ 问题解析 → 向量检索 → LLM生成 │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
10.3 代码实现
from langchain.document_loaders import TextLoader, PyPDFLoader, Docx2txtLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
import os
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
class KnowledgeBaseQA:
"""知识库问答系统"""
def __init__(self, persist_directory="./chroma_db"):
self.persist_directory = persist_directory
self.embeddings = OpenAIEmbeddings()
self.vector_db = None
self.qa_chain = None
self._initialize()
def _initialize(self):
"""初始化向量数据库"""
try:
# 尝试加载现有的向量数据库
self.vector_db = Chroma(
persist_directory=self.persist_directory,
embedding_function=self.embeddings
)
print("已加载现有的向量数据库")
except:
# 如果不存在,创建新的向量数据库
self.vector_db = Chroma(
persist_directory=self.persist_directory,
embedding_function=self.embeddings
)
print("创建了新的向量数据库")
def load_document(self, file_path):
"""加载文档"""
# 根据文件类型选择加载器
if file_path.endswith(".txt"):
loader = TextLoader(file_path)
elif file_path.endswith(".pdf"):
loader = PyPDFLoader(file_path)
elif file_path.endswith(".docx"):
loader = Docx2txtLoader(file_path)
else:
raise ValueError(f"不支持的文件类型: {file_path}")
# 加载文档
documents = loader.load()
print(f"已加载文档: {file_path}, 共{len(documents)}页")
# 分割文档
text_splitter = CharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
split_docs = text_splitter.split_documents(documents)
print(f"已分割为{len(split_docs)}个文档块")
# 添加到向量数据库
self.vector_db.add_documents(split_docs)
self.vector_db.persist()
print("文档已添加到向量数据库")
def setup_qa_chain(self):
"""设置问答Chain"""
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建自定义Prompt
prompt = ChatPromptTemplate.from_messages([
("system", """你是一名专业的知识库问答助手。请根据提供的文档内容回答问题。
要求:
1. 仅基于提供的文档内容回答问题
2. 如果文档中没有相关信息,请明确说明
3. 回答要简洁明了,避免冗长
4. 对于复杂问题,提供详细的解释
文档内容:
{context}"""),
("human", "{question}")
])
# 创建检索器
retriever = self.vector_db.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# 创建问答Chain
self.qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": prompt},
return_source_documents=True
)
def query(self, question):
"""查询知识库"""
if self.qa_chain is None:
self.setup_qa_chain()
# 执行查询
result = self.qa_chain({"query": question})
# 返回结果
return {
"answer": result["result"],
"sources": [doc.metadata.get("source", "") for doc in result["source_documents"]]
}
# 使用示例
if __name__ == "__main__":
# 创建知识库问答系统
kb_qa = KnowledgeBaseQA()
# 加载文档
kb_qa.load_document("example.txt")
kb_qa.load_document("example.pdf")
# 查询知识库
questions = [
"LangChain的核心概念是什么?",
"如何使用LangChain构建Agent?",
"LangChain支持哪些LLM提供商?"
]
for question in questions:
print(f"\n问题: {question}")
result = kb_qa.query(question)
print(f"答案: {result['answer']}")
print(f"来源: {result['sources']}")
10.4 运行结果分析
运行上述代码,系统会:
- 加载指定的文档(支持txt、pdf、docx格式)
- 将文档分割成小块并存储到向量数据库
- 根据用户查询进行相似性检索
- 将检索到的相关文档作为上下文输入给LLM
- LLM基于上下文生成答案
十一、常见问题与解决方案
11.1 问题1:LLM调用失败
现象:调用LLM时出现错误。
原因分析:
- API密钥未正确配置
- 网络连接问题
- API调用频率超限
解决方案:
import os
from dotenv import load_dotenv
from langchain.chat_models import ChatOpenAI
# 加载环境变量
load_dotenv()
# 检查API密钥
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError("OPENAI_API_KEY环境变量未设置")
# 创建LLM,增加重试次数
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0,
max_retries=3,
request_timeout=60
)
# 测试调用
try:
response = llm.predict("Hello")
print(response)
except Exception as e:
print(f"调用失败: {e}")
11.2 问题2:文档分割不合理
现象:文档分割后,相关内容被分割到不同的块中。
原因分析:
- 分割器配置不当
- 分割粒度不合适
解决方案:
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 使用递归分割器,按优先级选择分隔符
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=[
"\n\n", # 优先按段落分割
"\n", # 其次按行分割
"。", # 中文句号
"!", # 中文感叹号
"?", # 中文问号
".", # 英文句号
" ", # 空格
"" # 最后按字符分割
]
)
# 分割文档
split_docs = text_splitter.split_documents(documents)
11.3 问题3:向量检索结果不准确
现象:检索到的文档与查询不相关。
原因分析:
- 嵌入模型选择不当
- 检索参数配置不合理
- 文档质量差
解决方案:
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
# 使用合适的嵌入模型
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
# 创建向量数据库
vector_db = Chroma.from_documents(
documents=split_docs,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 调整检索参数
retriever = vector_db.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={
"k": 5,
"score_threshold": 0.7
}
)
# 进行检索
results = retriever.get_relevant_documents("查询内容")
11.4 问题4:Agent工具调用失败
现象:Agent无法正确调用工具。
原因分析:
- 工具描述不清晰
- Prompt模板不合适
- LLM无法理解工具用途
解决方案:
from langchain.tools import BaseTool
from typing import Optional, Type
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(description="搜索查询,用于查找相关信息")
class CustomSearchTool(BaseTool):
name = "web_search"
description = """用于搜索互联网上的信息。
当用户的问题需要最新的信息、事实性知识或外部数据时,应该使用此工具。
例如:查询天气、搜索新闻、查找技术文档等。
输入参数:query - 搜索关键词"""
args_schema: Type[BaseModel] = SearchInput
def _run(self, query: str) -> str:
# 搜索逻辑
return f"搜索结果: {query}"
11.5 问题5:Memory占用过大
现象:随着对话进行,Memory占用越来越大,导致性能下降。
原因分析:
- 对话历史不断累积
- 没有设置存储上限
解决方案:
from langchain.memory import ConversationBufferWindowMemory
from langchain.memory import ConversationSummaryMemory
from langchain.chat_models import ChatOpenAI
# 方法1:使用窗口Memory,限制历史记录数量
memory = ConversationBufferWindowMemory(k=10)
# 方法2:使用摘要Memory,将历史记录压缩为摘要
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
memory = ConversationSummaryMemory(llm=llm)
# 方法3:定期清理Memory
memory.clear()
十二、总结
本章详细介绍了LangChain的核心概念,包括:
- Prompt:引导LLM生成特定输出的文本模板
- Chain:一系列组件的组合,用于处理输入并生成输出
- Tool:Agent与外部世界交互的桥梁
- Memory:存储对话历史,实现上下文理解
- Agent:最高级别的抽象,能够自主决策并执行任务
- 文档加载与处理:从不同来源加载和处理文档
- 向量数据库集成:存储和检索嵌入向量,构建RAG应用
- 评估:衡量Agent性能的方法和工具
通过实战案例,我们展示了如何使用LangChain构建一个完整的知识库问答系统,涵盖了文档加载、向量存储、检索和生成等核心功能。
LangChain的模块化设计和丰富的集成能力使其成为构建Agent应用的首选框架。掌握LangChain的核心概念,将为后续的Agent开发打下坚实的基础。
练习题
- 基础题:使用LangChain创建一个简单的对话Chain,能够回答用户问题。
- 进阶题:使用LangChain创建一个多步骤的SequentialChain,完成文章生成任务。
- 实战题:创建一个工具调用Agent,能够使用搜索和计算器工具回答问题。
- 挑战题:构建一个完整的RAG系统,支持文档上传、向量存储和问答功能。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)