使用AI学习AI - LangChain - Quickstart
langchain
学习[[Langchain]]中,根据官方demo让Claude Code生成的详细注释和教程,以便更好的学习AI。
完整代码示例
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.tools import tool, ToolRuntime
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents.structured_output import ToolStrategy
from pprint import pprint
import os
SYSTEM_PROMPT = """你是一位擅长使用双关语的专业天气预报员。
你可以使用两种工具:
- get_weather_for_location:用于获取指定地点的天气信息
- get_user_location: 使用此方法获取用户位置
如果用户向你询问天气,请务必明确其所在位置。若从问题中能判断出对方指的是其当前所在地,需调用get_user_location工具来定位其位置。"""
@tool
def get_weather_for_location(city: str) -> str:
"""
获取指定城市的天气
参数:
city: 城市名称
返回:
天气描述字符串
"""
return f'{city} 今天是晴天。'
@dataclass
class Context:
"""
自定义运行时上下文模式
这个类定义了在工具调用时可以传递给 Agent 的额外数据。
例如:用户ID、会话信息、认证信息等。
"""
user_id: str # 用户ID,用于识别当前对话属于哪个用户
@tool
def get_user_location(runtime: ToolRuntime[Context]) -> str:
"""
根据用户ID获取用户信息
参数:
runtime: 工具运行时对象,包含 context 上下文
返回:
用户所在的城市名称
"""
user_id = runtime.context.user_id
return "罗马" if user_id == "1" else "西安"
load_dotenv()
api_key = os.getenv("GLM_API_KEY")
base_url = os.getenv("GLM_BASE_URL")
model = ChatOpenAI(
model="glm-4.7-flash",
api_key=api_key,
base_url=base_url,
temperature=0.5,
timeout=10,
max_tokens=1000
)
@dataclass
class ResponseFormat:
"""
智能体的响应模式
这个类定义了 Agent 最终返回的数据结构。
使用 @dataclass 也可以用 Pydantic 模型(更强大的验证功能)
"""
punny_response: str
weather_conditions: str | None = None
checkpointer = InMemorySaver()
agent = create_agent(
model=model,
system_prompt=SYSTEM_PROMPT,
tools=[get_user_location, get_weather_for_location],
context_schema=Context,
response_format=ToolStrategy(ResponseFormat),
checkpointer=checkpointer
)
config = {"configurable": {"thread_id": "1"}}
response = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "外面天气怎么样?"
}
]
},
config=config,
context=Context(user_id="1")
)
pprint(response['structured_response'])
# 预期输出类似:
# ResponseFormat(
# punny_response="罗马今天是个'日'不落的好天气!阳光普照,让你心情'阳光'起来!",
# weather_conditions="罗马 今天是晴天。"
# )
# 注意:我们可以使用相同的 thread_id 继续对话
response = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "thank you!"
}
]
},
config=config, # 使用相同的 thread_id,Agent 会"记住"之前的对话
context=Context(user_id="1") # 传递相同的上下文
)
pprint(response['structured_response'])
# 预期输出类似:
# ResponseFormat(
# punny_response="不客气!很高兴能为你'预报'一个阳光灿烂的心情!",
# weather_conditions=None # 这次没有天气信息
# )
详细注释版代码
"""
LangChain Agent 进阶示例 - 详细注释版
========================================
这个示例演示了如何创建一个具有以下功能的 AI Agent:
1. 使用工具(tools)来扩展 AI 的能力
2. 自定义上下文(context)来传递运行时数据
3. 结构化输出(structured response)来规范响应格式
4. 记忆功能(checkpointer)来支持多轮对话
核心概念:
-----------
Agent: 智能体,是一个使用 LLM 来决定采取什么行动的系统
Tool: 工具,是 Agent 可以调用的函数,用于扩展 LLM 的能力
Context: 上下文,用于在工具调用时传递额外的运行时数据
Checkpointer: 检查点保存器,用于存储对话历史,实现记忆功能
Structured Output: 结构化输出,强制 Agent 返回特定格式的响应
"""
# ============================================================================
# 1. 导入必要的库
# ============================================================================
from dataclasses import dataclass # Python标准库,用于创建简单的数据类
from langchain.agents import create_agent # LangChain核心函数,用于创建Agent
from langchain.tools import tool, ToolRuntime # @tool装饰器和工具运行时上下文
from langchain_openai import ChatOpenAI # LangChain对OpenAI兼容API的封装
from dotenv import load_dotenv # 用于加载.env环境变量文件
from langgraph.checkpoint.memory import InMemorySaver # 内存检查点保存器,用于记忆
from langchain.agents.structured_output import ToolStrategy # 结构化输出策略
from pprint import pprint # 用于美观打印
import os # Python标准库,用于读取环境变量
# ============================================================================
# 2. 定义系统提示词(System Prompt)
# ============================================================================
SYSTEM_PROMPT = """你是一位擅长使用双关语的专业天气预报员。
你可以使用两种工具:
- get_weather_for_location:用于获取指定地点的天气信息
- get_user_location: 使用此方法获取用户位置
如果用户向你询问天气,请务必明确其所在位置。若从问题中能判断出对方指的是其当前所在地,需调用get_user_location工具来定位其位置。"""
# 【原理】系统提示词(System Prompt)是给 LLM 的"角色设定"和"行为指导"
# 在这里,我们告诉 Agent:
# 1. 它的角色:擅长双关语的天气预报员
# 2. 它可以使用哪些工具
# 3. 什么时候使用哪个工具
# ============================================================================
# 3. 定义工具(Tools)
# ============================================================================
# 【工具1】获取天气的工具
@tool # @tool 装饰器将普通函数转换为 LangChain 可以识别的工具
def get_weather_for_location(city: str) -> str:
"""
获取指定城市的天气
参数:
city: 城市名称
返回:
天气描述字符串
"""
# 在实际应用中,这里可能会调用真实的天气API
return f'{city} 今天是晴天。'
# 【原理】@tool 装饰器的作用:
# 1. 将函数的文档字符串(docstring)转换为工具的描述
# 2. 将函数的参数类型提示转换为工具的参数schema
# 3. 让 Agent 能够理解如何调用这个工具
# ============================================================================
# 4. 定义自定义上下文(Context)
# ============================================================================
@dataclass # @dataclass 自动生成 __init__, __repr__ 等方法
class Context:
"""
自定义运行时上下文模式
这个类定义了在工具调用时可以传递给 Agent 的额外数据。
例如:用户ID、会话信息、认证信息等。
"""
user_id: str # 用户ID,用于识别当前对话属于哪个用户
# 【原理】Context 的作用:
# 1. 允许在运行时向工具传递额外的数据
# 2. 这些数据不会出现在 Agent 的思维链中(更安全)
# 3. 工具可以通过 runtime.context 访问这些数据
# 【工具2】获取用户位置的工具(使用Context)
@tool
def get_user_location(runtime: ToolRuntime[Context]) -> str:
"""
根据用户ID获取用户信息
参数:
runtime: 工具运行时对象,包含 context 上下文
返回:
用户所在的城市名称
"""
# 从运行时上下文中获取 user_id
user_id = runtime.context.user_id
# 根据 user_id 返回对应的位置(实际应用中可能查询数据库)
return "罗马" if user_id == "1" else "西安"
# 【原理】这里演示了工具如何访问 Context:
# 1. 函数参数声明为 runtime: ToolRuntime[Context]
# 2. 通过 runtime.context.user_id 获取用户ID
# 3. 这样可以根据不同的用户返回不同的位置信息
# ============================================================================
# 5. 加载环境变量并初始化模型
# ============================================================================
# 加载 .env 文件中的环境变量
load_dotenv()
# 从环境变量中读取API配置
api_key = os.getenv("GLM_API_KEY") # API密钥
base_url = os.getenv("GLM_BASE_URL") # API基础URL
# 创建 ChatOpenAI 模型实例
model = ChatOpenAI(
model="glm-4.7-flash", # 使用智谱AI的 glm-4.7-flash 模型
api_key=api_key, # API密钥
base_url=base_url, # API基础URL
temperature=0.5, # 控制输出的随机性(0-1,越低越确定)
timeout=10, # 请求超时时间(秒)
max_tokens=1000 # 最大输出token数
)
# 【原理】ChatOpenAI 是 LangChain 对 OpenAI 兼容 API 的封装
# 智谱AI的 API 兼容 OpenAI 的格式,所以可以用同一个类
# temperature 参数:
# - 0: 更加确定、一致的输出
# - 0.5: 平衡确定性和创造性
# - 1: 更加随机、有创造性的输出
# ============================================================================
# 6. 定义结构化输出格式
# ============================================================================
@dataclass
class ResponseFormat:
"""
智能体的响应模式
这个类定义了 Agent 最终返回的数据结构。
使用 @dataclass 也可以用 Pydantic 模型(更强大的验证功能)
"""
# 一个巧妙的双关回应(必不可少)
punny_response: str
# 如有相关的任何有趣天气信息,请提供
weather_conditions: str | None = None # None 表示可选字段
# 【原理】结构化输出的作用:
# 1. 强制 Agent 返回符合特定格式的数据
# 2. 方便程序处理 Agent 的响应(解析、验证、使用)
# 3. 避免返回不可预测的文本格式
# ============================================================================
# 7. 创建检查点保存器(实现记忆功能)
# ============================================================================
checkpointer = InMemorySaver()
# 【原理】Checkpointer(检查点保存器)的作用:
# 1. 存储对话的历史记录
# 2. 每次调用 Agent 时,会自动加载该 thread_id 的历史
# 3. 这样 Agent 可以"记住"之前的对话内容
# 4. InMemorySaver 将历史保存在内存中(程序运行期间有效)
# 实际应用中可能用 PostgresSaver、RedisSaver 等持久化存储
# ============================================================================
# 8. 创建 Agent
# ============================================================================
agent = create_agent(
model=model, # 使用的 LLM 模型
system_prompt=SYSTEM_PROMPT, # 系统提示词
tools=[get_user_location, get_weather_for_location], # 可用工具列表
context_schema=Context, # 上下文的数据结构定义
response_format=ToolStrategy(ResponseFormat), # 响应格式策略
checkpointer=checkpointer # 检查点保存器
)
# 【原理】create_agent 创建的是一个"有状态的 Agent":
# 1. 它包含一个 LLM 用于推理
# 2. 它有权访问定义的工具
# 3. 它可以根据用户输入决定调用哪个工具
# 4. 它可以记住对话历史(通过 checkpointer)
# 5. 它可以接收运行时上下文
# 6. 它可以返回结构化的响应
# 【Agent 的工作流程】(简化版):
# 用户输入 → Agent 思考 → 需要信息? → 调用工具 → 获取结果 → 继续思考 → 返回响应
# ↓
# 不需要 → 直接返回响应
# ============================================================================
# 9. 配置对话线程ID
# ============================================================================
# thread_id 是特定对话的唯一标识符
# 相同的 thread_id 会共享相同的对话历史
config = {"configurable": {"thread_id": "1"}}
# 【原理】thread_id 的作用:
# 1. 每个唯一的 thread_id 代表一个独立的对话会话
# 2. 使用相同的 thread_id 可以继续之前的对话
# 3. 使用不同的 thread_id 会开始新的对话
# 4. 类似于聊天应用中的"会话ID"
# ============================================================================
# 10. 第一次调用 Agent
# ============================================================================
response = agent.invoke(
{
"messages": [
{
"role": "user", # 消息角色:user(用户)
"content": "外面天气怎么样?" # 用户的问题
}
]
},
config=config, # 使用之前定义的配置(包含thread_id)
context=Context(user_id="1") # 传递上下文:用户ID为1
)
# 【第一次调用的执行流程】:
# 1. Agent 接收到用户问题:"外面天气怎么样?"
# 2. Agent 思考:用户没有指定位置
# 3. Agent 查看系统提示词:需要调用 get_user_location 获取位置
# 4. Agent 调用 get_user_location 工具,传入 Context(user_id="1")
# 5. 工具返回:"罗马"(因为 user_id="1")
# 6. Agent 现在知道位置是罗马
# 7. Agent 调用 get_weather_for_location 工具,city="罗马"
# 8. 工具返回:"罗马 今天是晴天。"
# 9. Agent 将天气信息包装成双关语回应
# 10. Agent 按照 ResponseFormat 格式化输出
# 打印结构化响应
pprint(response['structured_response'])
# 预期输出类似:
# ResponseFormat(
# punny_response="罗马今天是个'日'不落的好天气!阳光普照,让你心情'阳光'起来!",
# weather_conditions="罗马 今天是晴天。"
# )
# ============================================================================
# 11. 第二次调用 Agent(继续对话)
# ============================================================================
# 注意:我们可以使用相同的 thread_id 继续对话
response = agent.invoke(
{
"messages": [
{
"role": "user", # 消息角色
"content": "thank you!" # 用户感谢
}
]
},
config=config, # 使用相同的 thread_id,Agent 会"记住"之前的对话
context=Context(user_id="1") # 传递相同的上下文
)
# 【第二次调用的执行流程】:
# 1. Agent 接收到用户输入:"thank you!"
# 2. Agent 从 checkpointer 加载历史:知道之前查询了罗马的天气
# 3. Agent 思考:用户在表示感谢,不需要调用工具
# 4. Agent 直接生成双关语回应
# 5. Agent 按照 ResponseFormat 格式化输出
# 6. 注意:weather_conditions 为 None,因为这次没有查询天气
# 打印结构化响应
pprint(response['structured_response'])
# 预期输出类似:
# ResponseFormat(
# punny_response="不客气!很高兴能为你'预报'一个阳光灿烂的心情!",
# weather_conditions=None # 这次没有天气信息
# )
# ============================================================================
# 总结:完整的数据流
# ============================================================================
"""
用户输入
↓Agent(LLM) → 思考需要什么信息
↓需要位置信息?
↓ Yes调用 get_user_location(runtime.context.user_id="1") → 返回 "罗马"
↓调用 get_weather_for_location(city="罗马") → 返回 "罗马 今天是晴天。"
↓Agent 生成双关语回应
↓按照 ResponseFormat 格式化输出
↓返回给用户 ResponseFormat(punny_response, weather_conditions)
对话历史被 checkpointer 保存,下次对话可以继续...
"""
# ============================================================================
# 扩展:如何测试不同的用户
# ============================================================================
# 如果想测试不同用户,只需改变 user_id:
# context = Context(user_id="2") # 会返回 "西安" 而不是 "罗马"
# response = agent.invoke(
# {"messages": [{"role": "user", "content": "天气怎么样?"}]},
# config=config,
# context=Context(user_id="2")
# )
Demo 的核心概念和工作原理:
核心概念图解
┌─────────────────────────────────────────────────────────────────┐
│ LangChain Agent 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 用户输入 ──→ Agent ──→ LLM ──→ 思考 │
│ ↓ │
│ 需要外部信息? │
│ ↓ Yes │
│ 调用 Tool ──→ 获取结果 │
│ ↓ │
│ 继续思考 ──→ 生成响应 │
│ ↓ │
│ 结构化输出 ──→ 返回给用户 │
│ │
│ 伴随过程: │
│ ├─ Context: 传递运行时数据(如 user_id) │
│ ├─ Checkpointer: 保存对话历史 │
│ └─ ResponseFormat: 规范输出格式 │
└─────────────────────────────────────────────────────────────────┘
关键组件说明
┌────────────────┬────────────────────────────────┬───────────┐
│ 组件 │ 作用 │ 类比 │
├────────────────┼────────────────────────────────┼───────────┤
│ Agent │ 决定调用什么工具、如何响应用户 │ 指挥官 │
├────────────────┼────────────────────────────────┼───────────┤
│ Tool │ Agent 可以调用的函数 │ 士兵/工具 │
├────────────────┼────────────────────────────────┼───────────┤
│ LLM │ 进行推理的"大脑" │ 智能大脑 │
├────────────────┼────────────────────────────────┼───────────┤
│ Context │ 传递运行时数据(不暴露给LLM) │ 秘密情报 │
├────────────────┼────────────────────────────────┼───────────┤
│ Checkpointer │ 保存对话历史 │ 记忆本 │
├────────────────┼────────────────────────────────┼───────────┤
│ ResponseFormat │ 规范输出格式 │ 表格模板 │
└────────────────┴────────────────────────────────┴───────────┘
代码执行流程
第一次调用:"外面天气怎么样?"
├─ Agent 思考:用户没说位置
├─ 调用 get_user_location(user_id="1") → "罗马"
├─ 调用 get_weather_for_location("罗马") → "罗马 今天是晴天"
├─ Agent 生成双关语 + 格式化输出
└─ 返回 ResponseFormat(punny_response, weather_conditions)
第二次调用:"thank you!"(相同 thread_id)
├─ Agent 从记忆加载历史:知道之前问了罗马天气
├─ Agent 思考:用户感谢,不需要调用工具
├─ Agent 直接生成双关语
└─ 返回 ResponseFormat(punny_response, weather_conditions=None)
为什么要这样设计?
1. 工具化(Tools):LLM 不能实时获取数据(如天气),通过工具扩展能力
2. 上下文(Context):传递敏感数据(如用户ID)给工具,但不暴露给LLM
3. 记忆(Checkpointer):支持多轮对话,Agent 能"记住"之前的交流
4. 结构化输出:便于程序处理响应,而不是解析自然语言文本
关键点学习
ChatOpenAI
概览
ChatOpenAI 是 LangChain 提供的核心类,用于与兼容 OpenAI API 格式的大语言模型(LLM)进行交互。该类封装了所有与 LLM 通信所需的逻辑,包括请求构建、响应解析、错误处理等。
架构示意
┌─────────────────────────────────────────────────────────────────┐
│ ChatOpenAI 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 应用层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ChatOpenAI 实例 │ │
│ │ ├─ 配置参数 (model, temperature, etc.) │ │
│ │ ├─ 请求构建 │ │
│ │ └─ 响应解析 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ 协议层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HTTP/HTTPS 请求 (OpenAI API 格式) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ 服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LLM 服务提供商 │ │
│ │ ├─ OpenAI / 智谱AI / 通义千问 / DeepSeek / ... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
语法
ChatOpenAI(
model: str, # 模型名称 (必填)
api_key: Optional[str] = None, # API认证密钥
base_url: Optional[str] = None, # API基础URL
temperature: float = 0.7, # 输出随机性(0-2),值越低越确定,越高越发散
max_tokens: Optional[int] = None, # 最大输出token数
timeout: Optional[float] = None, # 请求超时时间(秒)
max_retries: int = 2, # 失败重试次数
api_version: Optional[str] = None, # API版本
organization: Optional[str] = None, # 组织ID
n: int = 1, # 生成候选数量
top_p: float = 1.0, # nucleus采样参数(0-1)
presence_penalty: float = 0, # 存在惩罚(-2到2)
frequency_penalty: float = 0, # 频率惩罚(-2到2)
streaming: bool = False, # 是否启用流式输出
stop: Optional[Union[str, List[str]]] = None, # 停止序列
stop_sequences: Optional[List[str]] = None, # 停止序列(别名)
model_kwargs: Optional[Dict[str, Any]] = None, # 额外模型参数
seed: Optional[int] = None, # 随机种子
tiktoken_model_name: Optional[str] = None, # token计数模型名称
cache: Optional[BaseCache] = None, # 缓存实例
) -> ChatOpenAI
create_agent()
概览
create_agent() 是 LangChain 提供的函数,用来创建一个智能代理(Agent)。Agent 是一个可以自动决定调用哪些工具来解决问题的程序。
参数说明
create_agent(
llm: BaseLanguageModel, # 大模型实例(必填)
tools: list[BaseTool], # 工具列表(必填)
prompt: ChatPromptTemplate = None, # 提示词模板(可选)
**kwargs # 扩展参数
) -> Runnable:
agent.invoke()
概述
agent.invoke() 是 LangChain Agent 的核心调用方法,用于与 Agent 交互并执行任务。它接收用户输入,让 Agent 使用工具进行推理和执行,最后返回结果。
参数说明
agent.invoke(
input: Union[str, Dict[str, Any]],
config: Optional[AgentExecutorConfig] = None,
*,
**kwargs: Any
) -> AgentExecutorOutput

浙公网安备 33010602011771号