轻松一刻:程序员最怕听到的四句话,按恐怖程度排序:“这个需求很简单。”,“用户不会那样操作的。”,“明天能上线吗?”,“你这代码我看不懂,重写一遍吧。”
在学习LangChain框架之前,先看一段不用框架调取大模型的代码demo.py(去掉了界面搭建部分,觉得太长可以先跳过,在后面与LangChain框架对比时会再分部分提及):
import gradio as gr
import json
import time
from datetime import datetime
import os
from typing import List, Dict, Optional
import requests
from dotenv import load_dotenv
load_dotenv() # .env文件里面的 DEEPSEEK_API_KEY = "sk******"
class DeepSeekChat:
def __init__(self, api_key: str = None, base_url: str = "https://api.deepseek.com"):
"""
初始化DeepSeek聊天类
Args:
api_key: DeepSeek API密钥
base_url: API基础URL
"""
self.api_key = api_key or os.getenv("DEEPSEEK_API_KEY")
if not self.api_key:
raise ValueError("请设置DEEPSEEK_API_KEY环境变量或传入api_key参数")
self.base_url = base_url
self.conversation_history: List[Dict] = []
self.system_prompt = "你是一个有帮助的AI助手。"
self.temperature = 0.7
self.max_tokens = 2000
self.top_p = 0.95
def chat(self, message: str, stream: bool = False) -> str:
"""
发送聊天消息
Args:
message: 用户消息
stream: 是否使用流式输出
Returns:
AI响应内容
"""
# 构建消息历史
messages = [{"role": "system", "content": self.system_prompt}]
messages.extend(self.conversation_history)
messages.append({"role": "user", "content": message})
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
data = {
"model": "deepseek-chat",
"messages": messages,
"temperature": self.temperature,
"max_tokens": self.max_tokens,
"top_p": self.top_p,
"stream": stream
}
try:
response = requests.post(
f"{self.base_url}/v1/chat/completions",
headers=headers,
json=data,
stream=stream,
timeout=60
)
if stream:
return self._handle_stream_response(response)
else:
return self._handle_normal_response(response)
except requests.exceptions.RequestException as e:
return f"请求失败: {str(e)}"
except Exception as e:
return f"发生错误: {str(e)}"
def _handle_normal_response(self, response) -> str:
"""处理普通响应"""
if response.status_code == 200:
result = response.json()
assistant_message = result["choices"][0]["message"]["content"]
# 更新对话历史
self.conversation_history.append({"role": "assistant", "content": assistant_message})
return assistant_message
else:
return f"API错误: {response.status_code} - {response.text}"
def _handle_stream_response(self, response) -> str:
"""处理流式响应"""
full_response = ""
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
data = line[6:]
if data != '[DONE]':
try:
chunk = json.loads(data)
if 'choices' in chunk and len(chunk['choices']) > 0:
delta = chunk['choices'][0].get('delta', {})
if 'content' in delta:
content = delta['content']
full_response += content
yield content
except json.JSONDecodeError:
continue
# 保存完整响应到历史
self.conversation_history.append({"role": "assistant", "content": full_response})
def clear_history(self):
"""清空对话历史"""
self.conversation_history = []
def get_history(self) -> List[Dict]:
"""获取对话历史"""
return self.conversation_history
def set_system_prompt(self, prompt: str):
"""设置系统提示"""
self.system_prompt = prompt
def set_parameters(self, temperature: float = None, max_tokens: int = None, top_p: float = None):
"""设置生成参数"""
if temperature is not None:
self.temperature = max(0, min(2, temperature))
if max_tokens is not None:
self.max_tokens = max(1, min(8192, max_tokens))
if top_p is not None:
self.top_p = max(0, min(1, top_p))
1. 为什么要学 LangChain
在demo中聊天流程靠request手动搭建
# 手动拼 messages
messages = [{"role":"system","content":prompt}]
messages.extend(history)
messages.append({"role":"user","content":msg})
# 手动发请求
response = requests.post(url, headers=headers, json=data)
reply = response.json()["choices"][0]["message"]["content"]
# 手动存历史
self.conversation_history.append({"role":"assistant","content":reply})
这段写法用LangChain写的话如下:
# 模板自动拼
prompt = ChatPromptTemplate.from_messages([
("system", "你是AI助手"),
MessagesPlaceholder("history"),
("human", "{input}"),
])
# 管道符串联,一行搞定
chain = prompt | llm
# 自动管历史
reply = chain_with_memory.invoke(
{"input": msg},
config={"configurable": {"session_id": sid}}
)
LangChain 的核心价值
- 标准化:换模型只改
base_url和model,其余代码不用动 - 可组合:用
|管道符串联组件,像搭积木一样 - 自动记忆:
RunnableWithMessageHistory替代手动管conversation_history - 生态丰富:RAG、向量库、工具调用、文档加载等都有现成封装
- 面试加分:JD 里写 "熟悉 LangChain" 的比比皆是
2. 核心概念速览
LangChain 的所有功能围绕 Runnable(可运行组件)构建。每个组件都是一个 Runnable,可以用 | 串联:

| 组件 | 作用 | 你的对照 |
|---|---|---|
| ChatOpenAI | 调用大模型 | 替代 requests.post() |
| ChatPromptTemplate | 管理提示词模板 | 替代手动拼 messages |
| OutputParser | 解析模型输出 | 替代手动取 response["choices"][0] |
| Memory | 管理对话历史 | 替代 conversation_history 列表 |
| Retriever | 从向量库检索 | RAG 的核心组件 |
| Tool | 包装工具函数 | Function Calling |
一句话理解:LangChain = 把 "调用大模型" 这件事拆成标准化组件,用 | 管道符串联,像流水线一样运转。
3. ChatOpenAI —— 模型调用层
3.1 基本用法
ChatOpenAI 是 LangChain 对 OpenAI API 的包装。因为 DeepSeek 和通义千问都兼容 OpenAI API 格式,所以同一个类可以调三个不同的模型——只改参数就行。
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os
load_dotenv()
# 创建 LLM 实例 —— 对比你 use_deepseek_test.py 里的写法
llm = ChatOpenAI(
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com", # DeepSeek
model="deepseek-chat",
temperature=0.7,
max_tokens=2000,
)
# 最简单的调用
result = llm.invoke("你好,你是谁?")
print(result.content) # .content 就是回复文本
print(result.usage_metadata) # token 用量
返回值说明:llm.invoke() 返回一个 AIMessage 对象,不是字符串。要取回复文本用 .content。这和 OpenAI SDK 的 response.choices[0].message.content 作用一样,但更简洁。
3.2 切换模型 —— 只改参数
# DeepSeek
llm_deepseek = ChatOpenAI(
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com",
model="deepseek-chat",
)
# 通义千问 (阿里云 DashScope)
llm_qwen = ChatOpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
model="qwen-plus",
)
# OpenAI 原版
llm_openai = ChatOpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o",
)
# 三个模型用法完全一样,因为都返回 AIMessage
for llm in [llm_deepseek, llm_qwen, llm_openai]:
print(llm.invoke("1+1=?").content)
注意:ChatOpenAI 这个类名容易误解。它不是只能调 OpenAI,而是"兼容 OpenAI API 格式的模型都能调"。DeepSeek、通义千问、Moonshot 都兼容这个格式。
3.3 常用参数
| 参数 | 作用 | 推荐值 |
|---|---|---|
temperature | 随机性,越高越有创造力 | 聊天 0.7 / 代码 0.1 / 分类 0 |
max_tokens | 最大生成 token 数 | 2000-4096 |
model | 模型名称 | deepseek-chat / qwen-plus / gpt-4o |
streaming | 是否流式输出 | True 时用 stream() 而非 invoke() |
4. PromptTemplate —— 提示词模板
在 demo.py 里手动拼 messages:先加 system,再遍历 history,最后加 user。ChatPromptTemplate 把这件事模板化了。
4.1 基本模板
from langchain_core.prompts import ChatPromptTemplate
# 用 {变量名} 占位,调用时传值
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个{role},请用{style}的语气回答问题。"),
("human", "{question}"),
])
# 填充变量 —— 返回格式化后的 ChatPromptValue
formatted = prompt.invoke({
"role": "翻译官",
"style": "正式",
"question": "Hello World",
})
print(formatted) # 看看格式化后的 messages 长什么样
4.2 带对话历史的模板
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages([
# 1. 系统提示 —— 定义 AI 的人设
("system", "你是一个有帮助的AI助手。用简洁的中文回答。"),
# 2. 历史对话占位符 —— 自动插入之前的对话
MessagesPlaceholder(variable_name="history"),
# 3. 用户当前输入
("human", "{input}"),
])
# MessagesPlaceholder 等价于 demo.py 里的:
# messages.extend(self.conversation_history)
# 但它是自动的,不用手动 extend
4.3 Few-Shot 模板(给几个例子让模型学)
from langchain_core.prompts import FewShotChatMessagePromptTemplate
# 例子:教模型做情感分析
examples = [
{"input": "这个产品太好用了!", "output": "正面"},
{"input": "质量很差,退货了", "output": "负面"},
{"input": "还可以吧,一般般", "output": "中性"},
]
example_prompt = ChatPromptTemplate.from_messages([
("human", "{input}"),
("ai", "{output}"),
])
few_shot_prompt = FewShotChatMessagePromptTemplate(
examples=examples,
example_prompt=example_prompt,
)
# 组合成完整 prompt
final_prompt = ChatPromptTemplate.from_messages([
("system", "判断用户评价的情感倾向,只回答: 正面/负面/中性"),
few_shot_prompt,
("human", "{input}"),
])
什么时候用 Few-Shot?:当模型对任务理解不够好(比如自定义分类、特定格式输出),给 2-5 个例子比写一堆规则更有效。这是 prompt engineering 的核心技巧。
5. LCEL 管道符 —— 核心语法
LCEL(LangChain Expression Language)是 LangChain 的设计核心。| 管道符把多个 Runnable 串联成一条链,上一步的输出自动喂给下一步。
5.1 最简单的链
# prompt 的输出 → 喂给 llm
chain = prompt | llm
# 调用
result = chain.invoke({"input": "什么是RAG?"})
print(result.content)
5.2 加 OutputParser
from langchain_core.output_parsers import StrOutputParser
# 再加一个 |,链就变长了
chain = prompt | llm | StrOutputParser()
# StrOutputParser 把 AIMessage 对象变成纯字符串
result = chain.invoke({"input": "什么是RAG?"})
print(result) # 直接是字符串,不用 .content 了

5.3 LCEL 的三种调用方式
# 1. invoke —— 普通调用,等完整结果
result = chain.invoke({"input": "你好"})
# 2. stream —— 流式调用,逐块返回(打字机效果)
for chunk in chain.stream({"input": "你好"}):
print(chunk, end="", flush=True)
# 3. batch —— 批量调用,并行处理多个输入
results = chain.batch([
{"input": "什么是AI"},
{"input": "什么是ML"},
{"input": "什么是DL"},
])
print(results) # 返回 3 个结果
5.4 用 RunnablePassthrough 传额外数据
from langchain_core.runnables import RunnablePassthrough
# RunnablePassthrough 像一条"直通管道",把输入原样传到输出
# 在 RAG 中非常有用:一边检索,一边把用户问题透传给 prompt
chain = RunnablePassthrough.assign(
context=lambda x: retriever.invoke(x["question"])
) | prompt | llm | StrOutputParser()
# 上面等价于:
# 1. 接收 {"question": "..."}
# 2. 用 question 去检索,结果存到 context 字段
# 3. 把 {question, context} 传给 prompt
# 4. prompt 输出喂给 llm
# 5. llm 输出喂给 parser
理解 LCEL 的关键:| 左边的组件输出,必须和右边的组件输入兼容。比如 prompt 输出的是 ChatPromptValue,llm 接收的就是 ChatPromptValue,所以能接上。llm 输出的是 AIMessage,StrOutputParser 接收 AIMessage,所以也能接上。
6. OutputParser —— 输出解析
LLM 的原始输出是 AIMessage 对象,但实际使用时可能要:取纯文本、解析 JSON、解析列表。OutputParser 负责这些。
6.1 StrOutputParser —— 取纯文本
from langchain_core.output_parsers import StrOutputParser
chain = prompt | llm | StrOutputParser()
# 输出直接是字符串,不需要 .content
6.2 JsonOutputParser —— 解析 JSON
from langchain_core.output_parsers import JsonOutputParser
parser = JsonOutputParser()
# 在 prompt 里告诉模型输出 JSON 格式
prompt = ChatPromptTemplate.from_messages([
("system", "你是信息提取助手。输出JSON格式:\n{name, age, city}"),
("human", "{input}"),
])
chain = prompt | llm | parser
result = chain.invoke({"input": "张三,25岁,在北京工作"})
print(result)
# {'name': '张三', 'age': 25, 'city': '北京'}
print(type(result)) # <class 'dict'>
6.3 PydanticOutputParser —— 结构化解析(推荐)
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
# 1. 定义你想要的数据结构
class MovieInfo(BaseModel):
title: str = Field(description="电影名称")
rating: float = Field(description="评分 0-10")
genre: list[str] = Field(description="类型标签")
# 2. 创建 parser,它会自动生成格式说明
parser = PydanticOutputParser(pydantic_object=MovieInfo)
# 3. 把格式说明塞进 prompt
prompt = ChatPromptTemplate.from_messages([
("system", "你是电影信息提取助手。\n{format_instructions}"),
("human", "{input}"),
])
prompt = prompt.partial(format_instructions=parser.get_format_instructions())
chain = prompt | llm | parser
result = chain.invoke({"input": "我不是药神,评分9.0,剧情/现实题材"})
print(result)
# MovieInfo(title='我不是药神', rating=9.0, genre=['剧情', '现实题材'])
print(result.title) # 我是药神
print(result.rating) # 9.0
为什么要用 PydanticOutputParser?:它做了一件很聪明的事:自动把你的数据结构变成 JSON Schema 格式的提示词,让模型知道该输出什么格式。比手写"请输出JSON格式"靠谱得多。
7. Memory —— 对话记忆
你在 demo.py 里手动维护 conversation_history 列表。LangChain 用 RunnableWithMessageHistory 自动做这件事。
7.1 基本用法
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 存储每个 session 的历史
chat_histories = {}
def get_history(session_id: str):
if session_id not in chat_histories:
chat_histories[session_id] = InMemoryChatMessageHistory()
return chat_histories[session_id]
# 把 chain 包装成"有记忆的 chain"
chain_with_memory = RunnableWithMessageHistory(
chain, # 你之前的 chain (prompt | llm)
get_history, # 获取历史的函数
input_messages_key="input", # 用户输入的字段名
history_messages_key="history", # prompt 里 history 占位符的名字
)
# 调用时传入 session_id
reply1 = chain_with_memory.invoke(
{"input": "我叫小明"},
config={"configurable": {"session_id": "user_001"}}
)
# LangChain 自动: 把 "我叫小明" 存入 user_001 的历史
reply2 = chain_with_memory.invoke(
{"input": "我叫什么名字?"},
config={"configurable": {"session_id": "user_001"}}
)
# LangChain 自动: 从历史里读出 "我叫小明",模型知道你叫小明
与demo.py对比
demo.py 手动管理
# 手动存
self.conversation_history.append(
{"role":"user",
"content":message}
)
self.conversation_history.append(
{"role":"assistant",
"content":reply}
)
# 手动读
messages.extend(
self.conversation_history
)
# 手动清
def clear_history(self):
self.conversation_history = []
LangChain 自动管理
# 自动存 + 自动读
chain_with_memory.invoke(
{"input": msg},
config={"configurable":
{"session_id": sid}}
)
# 手动清
get_history(sid).clear()
# 不同用户互不干扰
# session_id 就是隔离键
7.2 多用户隔离
# user_A 和 user_B 的历史互不干扰
chain_with_memory.invoke(
{"input": "我叫张三"},
config={"configurable": {"session_id": "user_A"}}
)
chain_with_memory.invoke(
{"input": "我叫李四"},
config={"configurable": {"session_id": "user_B"}}
)
# user_A 问 "我叫什么"
chain_with_memory.invoke(
{"input": "我叫什么名字?"},
config={"configurable": {"session_id": "user_A"}}
)
# 回答 "张三",因为只读了 user_A 的历史
7.3 历史过长怎么办?—— 总结式记忆
# 对话太长时,历史会占满 token。
# 思路:把旧对话让 LLM 总结成一段摘要,只保留摘要 + 最近几轮
# 在实际项目中可以用 RunnableWithMessageHistory + 自定义缩减逻辑
# 或者用 langchain-community 里的 ConversationSummaryBufferMemory
# 核心思路是:当 history 超过阈值时,用 llm 把旧消息压缩成摘要
# 简单版:手动限制历史长度
def get_history(session_id: str):
if session_id not in chat_histories:
chat_histories[session_id] = InMemoryChatMessageHistory()
history = chat_histories[session_id]
# 只保留最近 10 条消息
if len(history.messages) > 10:
history.messages = history.messages[-10:]
return history
生产环境注意:InMemoryChatMessageHistory 存在内存里,重启就没了。生产环境要用 Redis 或数据库。LangChain 也支持 RedisChatMessageHistory,用法一样,只是换一个 get_history 函数。
8. 常用 Chain 模式
8.1 基础对话链
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"input": "你好"})
8.2 带记忆的对话链
chain = prompt | llm
chain_with_memory = RunnableWithMessageHistory(
chain, get_history,
input_messages_key="input",
history_messages_key="history",
)
8.3 结构化输出链
chain = prompt | llm | PydanticOutputParser(pydantic_object=MyModel)
result = chain.invoke({"input": "..."})
# result 是 MyModel 实例,不是字符串
8.4 RAG 链(检索 + 生成)
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
8.5 条件分支链 —— 根据输入走不同路径
from langchain_core.runnables import RunnableLambda
# 根据输入类型选择不同的处理方式
def route(input):
if "代码" in input["input"]:
return "code"
return "chat"
chain = RunnableLambda(route).with_fallbacks(...)
# 或者用 RunnableBranch 做更复杂的分支
8.6 并行链 —— 同时做多件事
from langchain_core.runnables import RunnableParallel
# 同时翻译成英文和日文
parallel = RunnableParallel(
english=english_chain,
japanese=japanese_chain,
)
result = parallel.invoke({"input": "你好世界"})
print(result["english"]) # Hello World
print(result["japanese"]) # こんにちは世界
9. RAG —— 检索增强生成
RAG 是 LangChain 最常用的场景。核心思路:先从知识库里检索相关内容,再把内容塞进 prompt 让 LLM 基于它回答。

9.1 完整 RAG 代码
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# ① 加载文档
loader = TextLoader("my_doc.txt")
docs = loader.load()
# ② 切分文档 —— 把长文档切成小块
splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每块约 500 字符
chunk_overlap=50, # 块之间重叠 50 字符(避免切断语义)
)
chunks = splitter.split_documents(docs)
# ③ 向量化 + 存入 Chroma 向量库
embeddings = OpenAIEmbeddings(
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com", # DeepSeek 也支持 embedding
)
vectorstore = Chroma.from_documents(chunks, embeddings)
# ④ 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) # 每次检索 3 个最相关的块
# ⑤ 构建 RAG prompt
rag_prompt = ChatPromptTemplate.from_messages([
("system", """根据以下检索到的上下文回答用户问题。
如果上下文中没有相关信息,请说明"我没有找到相关信息"。
上下文:
{context}"""),
("human", "{question}"),
])
# ⑥ 构建 RAG 链
rag_chain = (
{
"context": retriever, # 检索相关文档
"question": RunnablePassthrough() # 原样透传用户问题
}
| rag_prompt
| llm
| StrOutputParser()
)
# ⑦ 使用
answer = rag_chain.invoke("这个文档讲了什么?")
print(answer)
RAG 链的数据流:用户输入字符串 → 并行做两件事:retriever 检索文档(存入 context),RunnablePassthrough 原样传递(存入 question) → rag_prompt 接收 {context, question} 格式化 → llm 生成 → parser 取纯文本。整条链用 | 串联,每一步各司其职。
9.2 文档加载器
| 加载器 | 用途 | 安装 |
|---|---|---|
TextLoader | 纯文本 | 内置 |
PyPDFLoader | PDF 文件 | pip install pypdf |
WebBaseLoader | 网页 | pip install bs4 |
CSVLoader | CSV 文件 | 内置 |
DirectoryLoader | 批量加载文件夹 | 内置 |
9.3 向量库对比
| 向量库 | 特点 | 适用场景 |
|---|---|---|
| Chroma | 纯 Python,内存/本地文件,零配置 | 学习/原型/小项目 |
| FAISS | Meta 出品,高性能,本地 | 大规模本地检索 |
| Milvus | 分布式,云原生 | 生产环境/百万级 |
| Pinecone | 云托管,免运维 | SaaS 产品 |
学习建议:先用 Chroma 跑通 RAG 全链路,理解原理后再考虑 FAISS/Milvus。Chroma 零配置,pip 装完就能用,对学习阶段最友好。
10. Agent —— 工具调用
在 上一篇文章里手写了 Agent 循环(for 循环 + tool_calls 判断 + 工具执行)。LangChain 把这个过程也封装了。
10.1 用 LangChain 创建工具
from langchain_core.tools import tool
# 用 @tool 装饰器把普通函数变成 LangChain 工具
# 函数的 docstring 会自动变成工具描述,给 LLM 看的
@tool
def get_weather(city: str) -> str:
"""查询指定城市的天气"""
# 你的实现
return f"{city}: 晴天 28°C"
@tool
def calculate(expression: str) -> str:
"""计算数学表达式"""
return str(eval(expression))
tools = [get_weather, calculate]
@tool 装饰器 vs 上一篇文章手写的 JSON Schema
在上一篇文章里手写了 TOOLS 列表,每个工具一大段 JSON。@tool 装饰器自动从函数签名和 docstring 生成 JSON Schema,省了很多模板代码。
10.2 创建 Agent
from langchain.agents import create_tool_calling_agent, AgentExecutor
# 1. 创建 Agent prompt
agent_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有用的助手。可以使用工具。"),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"), # Agent 的中间思考
])
# 2. 创建 Agent
agent = create_tool_calling_agent(llm, tools, agent_prompt)
# 3. 创建 AgentExecutor —— 自动处理循环
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 4. 使用
result = agent_executor.invoke({"input": "北京天气怎么样?"})
print(result["output"])
对比 上一篇文章手写的 Agent 循环
上一篇文章手写了 for i in range(max_iterations) 循环,手动判断 msg.tool_calls,手动执行工具,手动把结果喂回。AgentExecutor 把这些全封装了——你只需要传工具和 prompt,它自动跑循环。
10.3 工具从函数到 JSON Schema 的自动转换
# 你 Day1 手写的
TOOLS = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询城市天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"}
},
"required": ["city"]
}
}
}]
# LangChain 的写法 —— 一个装饰器搞定
@tool
def get_weather(city: str) -> str:
"""查询指定城市的天气"""
...
# 工具的 JSON Schema 自动生成
print(get_weather.args_schema.model_json_schema())
11. 流式输出
在 demo.py 里手写了 _handle_stream_response,手动解析 SSE 数据。LangChain 用 stream() 一行搞定。
# 普通链的流式
chain = prompt | llm | StrOutputParser()
for chunk in chain.stream({"input": "写一首诗"}):
print(chunk, end="", flush=True)
# 每个 chunk 是一小段文字,逐块返回
# 带记忆的链也能流式
for chunk in chain_with_memory.stream(
{"input": "写一首诗"},
config={"configurable": {"session_id": "user_001"}}
):
print(chunk, end="", flush=True)
配合 Gradio 实现打字机效果
import gradio as gr
def chat_stream(message, history):
# Gradio 6.0 的流式写法 —— 用 yield
partial = ""
for chunk in chain_with_memory.stream(
{"input": message},
config={"configurable": {"session_id": "user_001"}}
):
partial += chunk
yield partial # 每次返回累积的文本,Gradio 自动刷新
gr.ChatInterface(chat_stream).launch()
对比 demo.py 的手写流式
demo.py 的 _handle_stream_response 手动解析 data: 前缀、手动拼 full_response、手动处理 [DONE] 标记。LangChain 的 stream() 把这些全封装了,你只需要 for chunk in chain.stream()。
12. 速查表
常用导入
# 模型
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
# Prompt
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, FewShotChatMessagePromptTemplate
# Parser
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser, PydanticOutputParser
# Runnable
from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableLambda
# Memory
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 文档处理
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader, PyPDFLoader
# 向量库
from langchain_community.vectorstores import Chroma
# Agent
from langchain_core.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
常用模式速查
| 场景 | 代码 |
|---|---|
| 基础调用 | llm.invoke("你好").content |
| 基础链 | chain = prompt | llm | StrOutputParser() |
| 带记忆链 | RunnableWithMessageHistory(chain, get_history, ...) |
| RAG 链 | {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | parser |
| 流式 | for chunk in chain.stream(input): ... |
| 批量 | results = chain.batch([input1, input2, input3]) |
| 并行 | RunnableParallel(a=chain1, b=chain2).invoke(input) |
| 定义工具 | @tool def my_func(x: str) -> str: ... |
| Agent | AgentExecutor(agent=create_tool_calling_agent(llm, tools, prompt), tools=tools) |
学习路线建议
- 先跑通基础链:
prompt | llm | StrOutputParser(),理解数据怎么在链里流动 - 加记忆:用
RunnableWithMessageHistory,对比你 demo2.py 的手动管理 - 学 RAG:用 Chroma 跑通"加载→切分→向量化→检索→生成"全链路
- 学 Agent:用
@tool+create_tool_calling_agent,对比你 Day1 手写的循环 - 学流式:用
stream()+ Gradio 做打字机效果 - 学结构化输出:用
PydanticOutputParser让模型输出结构化数据
核心心法:LangChain 的一切都围绕 | 管道符。你不需要记住所有 API,只需要理解:左边组件的输出 = 右边组件的输入。遇到新组件,先看它接收什么、输出什么,就能知道怎么接进链里。


被折叠的 条评论
为什么被折叠?



