LangChain 能力详解
·
LangChain 能力详解
📑 目录
环境配置
Python环境
- Python 3.13
所需依赖包
# 第一部分:核心依赖
langchain-openai==0.3.33
langchain==0.3.27
langchain-deepseek==0.1.4
langchain-ollama==0.3.6
langchain-tavily==0.2.12
langchain-chroma==0.2.5
langchain-community==0.3.22
nltk==3.9.2
langchain-redis==0.2.4
unstructured==0.18.15
markdown==3.9
redisvl==0.10.0
# 第二部分:Pinecone(注意安装顺序问题)
pinecone==7.3.0
langchain-pinecone==0.2.12
⚠️ 注意事项:
- Pinecone相关包需要在学习到向量存储部分再安装
- 安装langchain-pinecone会影响Redis的MMR搜索功能
- 高版本Pinecone可能存在numpy兼容性问题
安装命令:
pip install -r requirements.txt
快速上手
为什么要用LangChain?
原生LLM开发面临的问题:
- 提示词不规范,结果容易出现幻觉
- 模型切换困难
- 输出非结构化,难以与程序接口对接
- 知识陈旧,无法获取实时信息
- 难以连接外部工具和系统
LangChain的核心目标就是解决这些问题,将NLP流程拆解为标准化组件。
最简单的对话示例
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
# 1. 定义大模型
model = ChatOpenAI(model="gpt-4o-mini")
# 2. 定义消息列表
messages = [
SystemMessage(content="Translate the following from English into Chinese"),
HumanMessage(content="hi!")
]
# 3. 定义输出解析器
parser = StrOutputParser()
# 4. 定义链
chain = model | parser
# 5. 执行链
result = chain.invoke(messages)
print(result) # 输出:你好!
关键概念引出
Runnable接口:LangChain组件的基础接口,提供:
- Invoked:单个输入转换为输出
- Batched:批量处理
- Streamed:流式传输
- Inspected:检查功能
- Composed:组合能力
LCEL (LangChain Expression Language):声明式编程方式,通过|操作符合并Runnable对象。
# 以下三种写法等价
chain = model | parser
chain = RunnableSequence(first=model, last=parser)
chain = model.pipe(parser)
聊天模型核心能力
1. 定义聊天模型
方式1:ChatOpenAI(明确指定)
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="gpt-4o-mini",
temperature=0, # 控制随机性,0最保守
max_tokens=None, # 最大生成token数
timeout=None, # 超时时间
max_retries=2, # 最大重试次数
# api_key="your-key", # 可从环境变量读取
# base_url="...", # API请求基础URL
)
方式2:init_chat_model(工厂函数)
from langchain.chat_models import init_chat_model
# 基本用法
gpt_model = init_chat_model("gpt-4o-mini", model_provider="openai")
deepseek_model = init_chat_model("deepseek-chat", model_provider="deepseek")
# 可配置模型
configurable_model = init_chat_model(
model="gpt-4o-mini",
temperature=0,
configurable_fields=("model", "temperature"),
config_prefix="first"
)
# 动态配置
result = configurable_model.invoke(
"what's your name",
config={
"configurable": {
"first_model": "deepseek-chat",
"first_temperature": 0.5
}
}
)
方式3:本地部署模型(ChatOllama)
from langchain_ollama import ChatOllama
ollama_model = ChatOllama(
model="deepseek-r1:70b",
base_url='http://192.168.100.220:11434',
num_ctx=2048, # 上下文窗口大小
num_gpu=1 # GPU数量
)
2. 工具调用
工具调用让LLM能够与外部世界交互,扩展能力边界。
创建工具的多种方式
方式1:@tool装饰器(最常用)
from langchain_core.tools import tool
from typing_extensions import Annotated
# 需要文档字符串
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers."""
return a * b
# 使用Annotated提供参数描述
@tool
def add(
a: Annotated[int, ..., "First integer"],
b: Annotated[int, ..., "Second integer"]
) -> int:
"""Add two integers."""
return a + b
方式2:依赖Pydantic类
from pydantic import BaseModel, Field
class AddInput(BaseModel):
"""Add two integers."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
@tool(args_schema=AddInput)
def add(a: int, b: int) -> int:
return a + b # 无需文档字符串
方式3:StructuredTool.from_function
from langchain_core.tools import StructuredTool
def multiply(a: int, b: int) -> int:
return a * b
calculator_tool = StructuredTool.from_function(
func=multiply,
name="Calculator",
description="两数相乘",
args_schema=CalculatorInput,
response_format="content_and_artifact" # 返回内容和原始数据
)
绑定工具
# 绑定工具到模型
tools = [add, multiply]
model_with_tools = model.bind_tools(tools)
# 或强制调用工具
model_with_tools = model.bind_tools(tools, tool_choice="any")
完整工具调用流程
from langchain_core.messages import HumanMessage
# 1. 定义消息
messages = [HumanMessage("9乘6等于多少?5加3等于多少?")]
# 2. 第一次调用:获取工具调用指令
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)
# 3. 执行工具调用
for tool_call in ai_msg.tool_calls:
selected_tool = {"add": add, "multiply": multiply}[tool_call["name"].lower()]
tool_msg = selected_tool.invoke(tool_call)
messages.append(tool_msg)
# 4. 第二次调用:获取最终答案
result = model.invoke(messages)
print(result.content) # 输出:9乘6等于54,5加3等于8。
LangChain内置工具示例:Tavily搜索
from langchain_tavily import TavilySearch
# 配置环境变量 TAVILY_API_KEY
tool = TavilySearch(max_results=4)
model_with_tools = model.bind_tools([tool])
messages = [HumanMessage("中国西安今天的天气怎么样?")]
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)
for tool_call in ai_msg.tool_calls:
tool_msg = tool.invoke(tool_call)
messages.append(tool_msg)
result = model_with_tools.invoke(messages)
print(result.content)
3. 结构化输出
将模型输出转换为结构化格式(JSON、Pydantic对象等)。
from pydantic import BaseModel, Field
from typing import Optional
class Joke(BaseModel):
"""给用户讲一个笑话。"""
setup: str = Field(description="这个笑话的开头")
punchline: str = Field(description="这个笑话的妙语")
rating: Optional[int] = Field(default=None, description="评分1-10")
# 绑定结构化输出
structured_model = model.with_structured_output(Joke)
result = structured_model.invoke("给我讲一个关于唱歌的笑话")
print(result) # 输出Pydantic对象
嵌套结构示例:
class Data(BaseModel):
"""获取关于笑话的数据。"""
jokes: List[Joke]
structured_model = model.with_structured_output(Data)
result = structured_model.invoke("分别讲一个关于唱歌和跳舞的笑话")
4. 流式传输
# 同步流式
chunks = []
for chunk in model.stream("讲一个50字笑话"):
chunks.append(chunk)
print(chunk.content, end="|", flush=True)
# 异步流式
import asyncio
async def async_stream():
async for chunk in model.astream("讲一个50字笑话"):
print(chunk.content, end="", flush=True)
asyncio.run(async_stream())
# 带解析器的流式
chain = model | StrOutputParser()
for chunk in chain.stream("写一段关于爱情的歌词"):
print(chunk, end="|", flush=True)
自定义流式解析器
from typing import Iterator
def split_into_list(input: Iterator[str]) -> Iterator[List[str]]:
buffer = ""
for chunk in input:
buffer += chunk
while "。" in buffer:
stop_index = buffer.index("。")
yield [buffer[:stop_index].strip()]
buffer = buffer[stop_index + 1:]
yield [buffer.strip()]
chain = model | StrOutputParser() | split_into_list
5. 使用LangSmith追踪
# 配置环境变量
# LANGSMITH_TRACING="true"
# LANGSMITH_API_KEY="你的LangSmith API Key"
# 任意代码执行后,在LangSmith平台查看追踪信息
消息机制
消息类型
| 类型 | 对应角色 | 描述 |
|---|---|---|
| SystemMessage | system | 系统指令,设定对话基调 |
| HumanMessage | user | 用户输入 |
| AIMessage | assistant | 模型响应 |
| ToolMessage | tool | 工具调用结果 |
多轮对话与内存
from langchain_core.messages import HumanMessage, AIMessage
# 手动维护历史消息
messages = [
HumanMessage(content="Hi! I'm Bob"),
AIMessage(content="Hello Bob! How can I assist you today?"),
HumanMessage(content="What's my name?"),
]
model.invoke(messages).pretty_print()
消息裁剪
from langchain_core.messages import trim_messages
trimmer = trim_messages(
max_tokens=65, # 最大token数
strategy="last", # 保留最后的消息
token_counter=model, # token计数方法
include_system=True, # 保留系统消息
allow_partial=False, # 不允许拆分消息
start_on="human", # 确保第一条消息是human
)
chain = trimmer | model
消息过滤
from langchain_core.messages import filter_messages
# 按类型过滤
filter_messages(messages, include_types="human")
# 按类型+ID过滤
filter_messages(messages, include_types=[HumanMessage, AIMessage], exclude_ids=["3"])
消息合并
from langchain_core.messages import merge_message_runs
merged = merge_message_runs(messages)
chain = merge_message_runs() | model
提示词模板
字符串模板
from langchain_core.prompts import PromptTemplate
# 方式1:from_template
prompt_template = PromptTemplate.from_template("Translate the following into {language}")
# 方式2:直接初始化
prompt_template = PromptTemplate(
input_variables=["language"],
template="Translate the following into {language}",
)
result = prompt_template.invoke({"language": "Chinese"})
聊天消息模板
from langchain_core.prompts import ChatPromptTemplate
prompt_template = ChatPromptTemplate([
("system", "Translate the following into {language}."),
("user", "{text}")
])
# 实例化
messages = prompt_template.invoke({
"language": "Chinese",
"text": "what is your name?"
}).to_messages()
# 链式调用
chain = prompt_template | model | StrOutputParser()
消息占位符
from langchain_core.prompts import MessagesPlaceholder
prompt_template = ChatPromptTemplate([
("system", "你是一个聊天助手"),
MessagesPlaceholder("msgs") # 或 ("placeholder", "{msgs}")
])
messages_to_pass = [
HumanMessage(content="中国首都是哪里?"),
AIMessage(content="中国首都是北京。"),
HumanMessage(content="那法国呢?")
]
result = prompt_template.invoke({"msgs": messages_to_pass})
使用LangChain Hub
from langsmith import Client
client = Client()
prompt = client.pull_prompt("hardkothari/prompt-maker", include_model=True)
chain = prompt | model
while True:
task = input("你的任务是什么?")
lazy_prompt = input("你当前的提示是什么?")
chain.invoke({'lazy_prompt': lazy_prompt, 'task': task}).pretty_print()
少样本提示
基本用法
from langchain_core.prompts import FewShotChatMessagePromptTemplate, ChatPromptTemplate
# 定义示例
examples = [
{"input": "2 2", "output": "4"},
{"input": "2 3", "output": "5"},
]
# 定义示例模板
example_prompt = ChatPromptTemplate([
("human", "{input}"),
("ai", "{output}"),
])
# 创建少样本提示
few_shot_prompt = FewShotChatMessagePromptTemplate(
example_prompt=example_prompt,
examples=examples,
)
# 组合最终提示
final_prompt = ChatPromptTemplate([
("system", "你是一个神奇的数学奇才。"),
few_shot_prompt,
("human", "{input}"),
])
chain = final_prompt | model
chain.invoke({"input": "What is 2 9?"}).pretty_print() # 输出:11
推理引导示例
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 字符串模板
example_prompt = PromptTemplate.from_template("Question: {question}\n{answer}")
# 示例集(包含推理过程)
examples = [
{
"question": "李白和杜甫,谁更长寿?",
"answer": """是否需要后续问题:是的。
后续问题:李白享年多少岁?
中间答案:李白享年61岁。
后续问题:杜甫享年多少岁?
中间答案:杜甫享年58岁。
所以最终答案是:李白"""
},
# ... 更多示例
]
prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
suffix="Question: {input}",
input_variables=["input"],
)
prompt_messages = prompt.invoke({
"input": "《教父》和《星球大战》的导演来自同一个国家吗?"
}).to_messages()
示例选择器
按长度选择
from langchain_core.example_selectors import LengthBasedExampleSelector
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=25,
)
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
按语义相似性选择
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
Chroma,
k=1,
)
按MMR选择(最大边际相关性)
from langchain_core.example_selectors import MaxMarginalRelevanceExampleSelector
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
Chroma,
k=2,
)
输出解析器
文本解析器
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
chain = model | parser
结构化对象解析器
from langchain_core.output_parsers import PydanticOutputParser
parser = PydanticOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | model | parser
JSON解析器
from langchain_core.output_parsers import JsonOutputParser
parser = JsonOutputParser()
# 或带Pydantic验证
parser = JsonOutputParser(pydantic_object=Joke)
其他解析器:XMLOutputParser、YamlOutputParser、CommaSeparatedListOutputParser、EnumOutputParser等。
文档加载器
Document对象
from langchain_core.documents import Document
documents = [
Document(
page_content="狗是很好的伴侣,以忠诚和友好而闻名。",
metadata={"source": "mammal-pets-doc"},
),
# ...
]
加载PDF
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("./Docs/PDF/文档.pdf")
docs = loader.load() # 每页一个Document
print(f"总页数:{len(docs)}")
print(f"第一页内容:{docs[0].page_content[:200]}")
print(f"第一页元数据:{docs[0].metadata}")
加载Markdown
from langchain_community.document_loaders import UnstructuredMarkdownLoader
# single模式:整个文档作为一个Document
loader = UnstructuredMarkdownLoader("./Docs/Markdown/文档.md", mode="single")
data = loader.load()
# elements模式:拆分为多个元素
loader = UnstructuredMarkdownLoader("./Docs/Markdown/文档.md", mode="elements")
data = loader.load() # 包含Title、ListItem、NarrativeText等类型
文本分割器
基于字符长度拆分
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
separator="\n\n", # 分隔符
chunk_size=100, # 目标块大小
chunk_overlap=20, # 块重叠大小
length_function=len, # 长度计算函数
is_separator_regex=False,
)
texts = text_splitter.split_documents(documents)
基于Token长度拆分
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base", # GPT-4、GPT-3.5-turbo的编码方式
chunk_size=200,
chunk_overlap=50,
)
硬约束长度拆分
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base",
chunk_size=100,
chunk_overlap=0,
)
代码文档拆分
from langchain_text_splitters import PythonCodeTextSplitter
python_splitter = PythonCodeTextSplitter(chunk_size=50, chunk_overlap=0)
python_docs = python_splitter.create_documents([PYTHON_CODE])
文本向量与向量存储
嵌入模型
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
# 嵌入文档列表
texts = [doc.page_content for doc in documents]
documents_vector = embeddings.embed_documents(texts)
print(f"向量维度:{len(documents_vector[0])}") # 3072维
# 嵌入单个查询
query_vector = embeddings.embed_query("项目中遇到了哪些挑战?")
内存向量存储
from langchain_core.vectorstores import InMemoryVectorStore
# 初始化
vector_store = InMemoryVectorStore(embedding=embeddings)
# 添加文档
ids = vector_store.add_documents(documents=documents)
# 获取文档
docs = vector_store.get_by_ids(ids[:3])
# 相似性搜索
search_docs = vector_store.similarity_search(query="数据库表怎么设计的?", k=2)
# 元数据过滤
def filter_function(doc: Document) -> bool:
return doc.metadata.get("source") == "期望的source"
search_docs = vector_store.similarity_search(
query="数据库表怎么设计的?",
k=2,
filter=filter_function
)
# 删除文档
vector_store.delete(ids=ids[:3])
Redis向量存储
from langchain_redis import RedisConfig, RedisVectorStore
from redisvl.query.filter import Tag, Num
# 配置
config = RedisConfig(
index_name="qa",
redis_url="redis://localhost:6379",
metadata_schema=[
{"name": "category", "type": "tag"},
{"name": "num", "type": "numeric"},
],
)
# 初始化
vector_store = RedisVectorStore(embeddings, config=config)
# 添加文档时添加元数据
for i, doc in enumerate(documents, start=1):
doc.metadata["category"] = "QA"
doc.metadata["num"] = i
ids = vector_store.add_documents(documents=documents)
# 相似性搜索带分数
scored_results = vector_store.similarity_search_with_score(
query="数据库表怎么设计的?",
k=4
)
# 元数据过滤
filter_condition = (Tag("category") == "qa") & (Num("num") < 50)
scored_results = vector_store.similarity_search_with_score(
query="数据库表怎么设计的?",
k=2,
filter=filter_condition
)
# MMR搜索
mmr_results = vector_store.max_marginal_relevance_search(
query="数据库表怎么设计的?",
k=2,
fetch_k=10,
filter=filter_condition
)
Pinecone向量存储
from pinecone import Pinecone, ServerlessSpec
from langchain_pinecone import PineconeVectorStore
# 初始化Pinecone
pc = Pinecone()
index_name = "qa"
# 创建索引
if not pc.has_index(index_name):
pc.create_index(
name=index_name,
dimension=3072,
metric="cosine",
spec=ServerlessSpec(
cloud="aws",
region="us-east-1"
)
)
# 获取索引
index = pc.Index(index_name)
# 初始化向量存储
vector_store = PineconeVectorStore(embedding=embeddings, index=index)
# 添加文档
ids = vector_store.add_documents(documents=documents)
# 相似性搜索
search_docs = vector_store.similarity_search(
query="数据库表怎么设计的?",
k=2,
filter={"category": "QA"}
)
# 删除
vector_store.delete(delete_all=True) # 全量删除
vector_store.delete(ids=delete_ids) # 指定ID删除
检索器
从向量存储创建检索器
# 基本检索器
retriever = vector_store.as_retriever()
docs = retriever.invoke("数据库表怎么设计的?")
# 配置参数
retriever = vector_store.as_retriever(
search_type="similarity", # 或 "mmr", "similarity_score_threshold"
search_kwargs={"k": 2}
)
# MMR配置
retriever = vector_store.as_retriever(
search_type="mmr",
search_kwargs={
"k": 2,
"fetch_k": 10
}
)
自定义检索器
from langchain_core.runnables import chain
from typing import List
from langchain_core.documents import Document
@chain
def custom_retriever(query: str) -> List[Document]:
return vector_store.similarity_search(query, k=2)
docs = custom_retriever.invoke("数据库表怎么设计的?")
RAG实战案例
完整RAG流程
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_redis import RedisConfig, RedisVectorStore
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
# 1. 初始化模型
model = ChatOpenAI(model="gpt-4o-mini")
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
# 2. 配置向量存储
config = RedisConfig(
index_name="qa",
redis_url="redis://192.168.100.238:6379",
metadata_schema=[
{"name": "category", "type": "tag"},
{"name": "num", "type": "numeric"},
],
)
vector_store = RedisVectorStore(embeddings, config=config)
retriever = vector_store.as_retriever()
# 3. 定义提示词模板
prompt = ChatPromptTemplate.from_messages([
("human", """你是负责回答问题的助手。使用以下检索到的上下文片段来回答问题。
如果你不知道答案,就说你不知道。最多只用三句话,回答要简明扼要。
Question: {question}
Context: {context}
Answer:""")
])
# 4. 文档格式化函数
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# 5. 构建RAG链
rag_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| prompt
| model
| StrOutputParser()
)
# 6. 流式执行
for chunk in rag_chain.stream("数据库表怎么设计的?"):
print(chunk, end="", flush=True)
交互式RAG问答
while True:
question = input("\n请输入您的问题(输入'退出'结束):").strip()
if question.lower() in ["退出", "quit"]:
break
if not question:
continue
print("回答:", end="", flush=True)
for chunk in rag_chain.stream(question):
print(chunk, end="", flush=True)
print()
📌 核心概念总结
| 组件 | 作用 | 常用类/方法 |
|---|---|---|
| 聊天模型 | 与LLM交互 | ChatOpenAI, init_chat_model |
| 消息 | 通信单位 | SystemMessage, HumanMessage, AIMessage |
| 提示词模板 | 动态生成提示词 | PromptTemplate, ChatPromptTemplate |
| 输出解析器 | 结构化输出 | StrOutputParser, PydanticOutputParser |
| 工具调用 | 扩展LLM能力 | @tool, bind_tools() |
| 文档加载器 | 加载各类文档 | PyPDFLoader, UnstructuredMarkdownLoader |
| 文本分割器 | 切分文档 | CharacterTextSplitter, RecursiveCharacterTextSplitter |
| 嵌入模型 | 文本转向量 | OpenAIEmbeddings |
| 向量存储 | 存储和检索向量 | InMemoryVectorStore, RedisVectorStore |
| 检索器 | 统一检索接口 | as_retriever(), 自定义@chain |
| LCEL | 声明式链式编程 | | 操作符, RunnableSequence |
个人学习心得:
- LangChain的核心思想是组件化和可组合性,每个组件都实现Runnable接口
- LCEL让构建复杂流程变得简单直观
- RAG是当前最实用的LLM应用模式,掌握好文档加载→分割→嵌入→存储→检索→生成的完整流程
- 遇到问题时,查看官方文档和源码是最好的学习方式
更多推荐


所有评论(0)