LangChain:构建 LLM 驱动应用的框架
bash pip install langchain langchain-openai
**API 密钥配置**
LangChain 本身不限制模型提供商。这里以 OpenAI 为例,你需要申请一个 API Key,并建议将其设置为环境变量:
```bash
export OPENAI_API_KEY="your-api-key-here"
在代码中也可以直接传入,但推荐使用环境变量以保持安全。
1.2 发起第一个 LLM 请求
用最少的代码体验 LangChain 的调用模式:
from langchain_openai import ChatOpenAI
# 初始化模型(默认使用 gpt-3.5-turbo)
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.9)
response = llm.invoke("你好,请用一句话介绍你自己。")
print(response.content)
invoke 方法是 LangChain 的标准执行接口,几乎适用于所有组件。你已经迈出了第一步
2. 核心概念:Prompt Template 与 Chain
直接拼接字符串会让代码难以维护。LangChain 提供了 Prompt Template 来系统化管理提示词。
2.1 使用 PromptTemplate
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("请将以下英文翻译成中文:{text}")
# 填充变量后生成一个消息对象
message = prompt.format_messages(text="The future belongs to those who believe in the beauty of their dreams.")
print(message)
2.2 构建第一个 Chain
Chain 是 LangChain 的核心抽象,它将多个步骤串联为一个可执行的整体:
chain = prompt | llm
result = chain.invoke({"text": "Artificial Intelligence is reshaping every industry."})
print(result.content)
| 操作符使用 LCEL(LangChain 表达式语言)将组件连接起来。数据从左向右流动:先经过 prompt 格式化成模型可理解的输入,再交给 llm 生成回复。
3. 让应用拥有记忆:ConversationBufferMemory
LLM 本身是无状态的,每次调用都是独立的。要让应用拥有对话记忆,LangChain 提供了多种记忆管理类。
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
memory = ConversationBufferMemory(return_messages=True)
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True # 打印中间过程,便于调试
)
# 连续对话
conversation.predict(input="我叫小明。")
conversation.predict(input="我叫什么名字?")
此时模型会回答“你叫小明”。ConversationBufferMemory 简单地将整个对话历史存储在列表中,并在每次调用时作为上下文注入 prompt。对于长对话,你可能需要更高级的记忆类型(如 ConversationSummaryMemory),以避免 token 超限。
4. 工具集成:让 LLM 与外部世界互动
LLM 的强项在于语言理解,但无法直接访问实时数据或执行操作。工具(Tool) 机制为模型提供了“手和眼”。
4.1 自定义一个天气工具
假设我们有一个天气查询函数。我们将其包装为 LangChain 的 Tool:
from langchain.tools import tool
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气(模拟)"""
# 实际项目可替换为真实 API 调用
weather_data = {
"北京": "晴,25°C",
"上海": "多云,28°C",
}
return weather_data.get(city, "暂无该城市天气数据。")
4.2 绑定工具到模型
llm_with_tools = llm.bind_tools([get_weather])
现在模型可以根据用户输入自动决定是否调用工具。但要真正执行工具并处理返回结果,我们需要一个完整的 Agent 流程。
5. Agent:自主推理与行动的智能体
Agent 是 LangChain 中的高级组件,它利用 LLM 的推理能力来决定“何时使用哪个工具,以及如何解读工具返回的结果”。
5.1 创建 Agent
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有用的助手,可以使用工具获取信息。"),
("user", "{input}"),
("placeholder", "{agent_scratchpad}"), # 必须包含,用于记录中间步骤
])
agent = create_tool_calling_agent(llm, [get_weather], prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=[get_weather],
verbose=True,
handle_parsing_errors=True,
)
5.2 运行 Agent
response = agent_executor.invoke({"input": "现在北京的天气怎么样?"})
print(response['output'])
在 verbose 模式下,你会看到模型的思考过程:它识别出需要调用 get_weather 工具,并将“北京”作为参数传入,拿到结果后生成自然语言回复。
5.3 多工具协同
你可以添加更多工具,例如计算器、搜索引擎或数据库查询工具。Agent 会根据上下文自动选择并组合使用它们:
@tool
def calculate(expression: str) -> str:
"""计算数学表达式,例如 '2 + 2'"""
try:
return str(eval(expression))
except:
return "计算错误"
tools = [get_weather, calculate]
# 后续创建 agent 时使用 tools 即可
当用户问“北京的温度除以2是多少?”时,Agent 会先查天气,再用计算器处理结果。
6. 构建完整的端到端应用:带记忆的天气助理
综合以上知识,我们创建一个能记住对话历史的天气助理。
6.1 初始化带记忆的 Agent
AgentExecutor 本身没有记忆。我们需要将对话历史手动传入 prompt。LangChain 提供了 MessagesPlaceholder 来注入历史消息。
from langchain_core.messages import AIMessage, HumanMessage
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 使用内存存储对话历史(生产环境可换为 Redis 等)
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 重构 prompt,加入历史消息占位
prompt = ChatPromptTemplate.from_messages([
("system", "你是个天气助手,可以使用工具获取天气信息。"),
("placeholder", "{chat_history}"),
("user", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 包装为带消息历史的 Runnable
conversational_agent = RunnableWithMessageHistory(
agent_executor,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history",
)
6.2 进行多轮对话
session_id = "user_123"
response1 = conversational_agent.invoke(
{"input": "我叫小明,北京天气如何?"},
config={"configurable": {"session_id": session_id}}
)
print(response1['output'])
response2 = conversational_agent.invoke(
{"input": "我刚才说我叫什么名字?"},
config={"configurable": {"session_id": session_id}}
)
print(response2['output']) # 会输出“小明”
记忆功能通过会话 ID 隔离,不同用户可以并发访问。
7. 进阶主题与最佳实践
7.1 流式输出
在 Web 应用中,通常需要逐词显示回复以提升体验。使用 astream_events 方法:
async for event in agent_executor.astream_events(
{"input": "北京天气如何?"}, version="v1"
):
if event["event"] == "on_chat_model_stream":
content = event["data"]["chunk"].content
print(content, end="", flush=True)
7.2 选择正确的记忆类型
- BufferMemory:适合短对话,简单直接。
- SummaryMemory:自动对历史做摘要,控制 token 消耗。
- ConversationTokenBufferMemory:按 token 数量限制历史长度。
- VectorStoreRetrieverMemory:将历史存入向量数据库,按相关度检索——适合需要长期记忆的场景。
7.3 结构化输出
利用 with_structured_output 可以让模型返回 JSON 等格式化数据,便于后续代码处理:
from pydantic import BaseModel, Field
class WeatherResponse(BaseModel):
city: str = Field(description="城市名")
summary: str = Field(description="天气梗概")
structured_llm = llm.with_structured_output(WeatherResponse)
res = structured_llm.invoke("今天北京的天气是晴天,25°C")
print(res) # WeatherResponse(city='北京', summary='晴天,25°C')