47.AI Agent-Langchain及检索
LangChain是一个简化大型语言模型(LLM)应用开发的开源框架。它提供多模态应用、代码辅助等场景支持,核心组件包括模型接口、提示模板、代理等。框架特别设计了ChatPromptTemplate会话模板系统,支持多轮对话管理,通过SystemMessage、HumanMessage和AIMessage实现角色区分和上下文维护。相比传统字符串拼接,ChatPromptTemplate具有多轮对话
文章目录
LangChain
LangChain 是一个开源框架,专门用来简化大型语言模型(LLM)应用的开发,帮你快速把模型能力接 入实际业务。它的核心价值在于降低集成成本,让开发者无需从零搭建就能实现复杂功能。
发展历程
2022年10月:作为 Python 工具发布。
2023年2月:支持 TypeScript。
2023年4月:扩展至多种 JavaScript 环境。
2025年11月:OceanBase AI 数据库兼容 LangChain。
典型应用场景
自动化任务 :邮件分类、合同审查、会议纪要生成。
多模态应用 :图像描述、语音助手。
代码辅助 :生成、解释或调试代码。
个性化推荐 :定制化内容生成。
核心组件
- 模型接口 :支持 OpenAI、Anthropic 等主流 LLM,方便切换和扩展。
- 提示模板 :预设结构化提示词,提升生成效果和一致性。
- 链(Chains) :串联多个步骤,支持条件和循环逻辑,适合复杂任务。
- 代理(Agents) :动态调用工具(如搜索、API),实现自动化操作。
- 记忆模块 :短期和长期记忆结合,增强上下文理解。
- 数据索引 :连接外部数据源(如数据库、PDF),提升生成准确性。
会话模板
LangChain 提供了灵活的会话模板系统,支持多轮对话和上下文管理。
ChatPromptTemplate
在 LangChain 里:
- PromptTemplate:用于单轮文本提示(prompt)
- ChatPromptTemplate:用于多轮对话(system / human / ai)
👉 ChatPromptTemplate = 对话剧本 + 可替换变量
1.导入 ChatPromptTemplate
from langchain_core.prompts import ChatPromptTemplate
这是 LangChain 最核心的 Prompt 组件之一,不依赖具体模型(OpenAI、Claude、Qwen 都能用)。
2.定义 messages(对话模板)
messages = [
('system','你是一位智者,你的名字是{name}'),
('human','你好{name},你感觉如何?'),
('ai','你好!我的状态非常好!'),
('human','你叫什么名字呢?'),
('ai','我叫{name}'),
('human','{user_input}')
]
这里是重点 👇
(1)消息角色说明
| 角色 | 含义 |
|---|---|
| system | 系统设定(人设、规则) |
| human | 用户输入 |
| ai | AI 的历史回答 |
(2)这是一个「已存在历史对话」的模板
你不是只写一句 prompt,而是:
-
AI 已经有人设
-
已经有历史对话
-
当前用户再继续说一句话 {user_input}
👉 非常适合 对话机器人 / Agent / Chat 记忆场景
(3) {} 是占位符(变量)
{name}
{user_input}
这些变量会在 运行时替换。
3.用 messages 创建会话模板
prompt = ChatPromptTemplate.from_messages(messages)
这一句的作用:
把“对话结构 + 变量占位符”变成一个 可复用的 Prompt 对象
4.传入参数,生成最终消息
print(prompt.format_messages(
name='guo',
user_input='你的朋友是谁呢?'
))
发生了什么?
-
{name} → ‘guo’
-
{user_input} → ‘你的朋友是谁呢?’
最终输出的是:
👉 一组 Message 对象列表(不是普通字符串)
运行后的实际效果
等价于下面这段“真实对话”:
System: 你是一位智者,你的名字是 guo
Human: 你好 zhang, 你感觉如何?
AI: 你好!我的状态非常好!
Human: 你叫什么名字呢?
AI: 我叫 guo
Human: 你的朋友是谁呢?
⚠ 这正是 大模型最喜欢的输入格式
from langchain_core.prompts import ChatPromptTemplate
message = [
('system','你是一位智者,你的名字是{name}'),
('human','你好{name},你感觉如何?'),
('ai','你好!我的状态非常好!'),
('human','你叫什么名字呢?'),
('ai','我叫{name}'),
('human','{user_input}')
]
prompt=ChatPromptTemplate(message)
print(prompt.format_messages(name='guo',user_input='你的朋友是谁'))
[SystemMessage(content='你是一位智者,你的名字是guo', additional_kwargs={}, response_metadata={}), HumanMessage(content='你好guo,你感觉如何?', additional_kwargs={}, response_metadata={}), AIMessage(content='你好!我的状态非常好!', additional_kwargs={}, response_metadata={}, tool_calls=[], invalid_tool_calls=[]), HumanMessage(content='你叫什么名字呢?', additional_kwargs={}, response_metadata={}), AIMessage(content='我叫guo', additional_kwargs={}, response_metadata={}, tool_calls=[], invalid_tool_calls=[]), HumanMessage(content='你的朋友是谁', additional_kwargs={}, response_metadata={})]
为什么要用 ChatPromptTemplate?(而不是字符串拼接)
传统方式(不推荐)
prompt = f"""
你是一位智者,你的名字是 guo
你好 guo,你感觉如何?
...
"""
问题:
- 不支持多角色
- 不利于 Agent / Memory
- 不可组合
ChatPromptTemplate 的优势
| 优点 | 说明 |
|---|---|
| 多轮对话 | 原生支持 system/human/ai |
| 变量安全 | 自动校验缺失变量 |
| 可组合 | 可与 Memory / Chain / Agent 结合 |
| 模型无关 | OpenAI / Claude / 本地模型都能用 |
示例
from langchain_core.prompts import ChatPromptTemplate
message = [
('system','你是一位智者,你的名字是{name}'),
('human','你好{name},你感觉如何?'),
('ai','你好!我的状态非常好!'),
('human','你叫什么名字呢?'),
('ai','我叫{name}'),
('human','{user_input}')
]
# #会话模板
prompt=ChatPromptTemplate(message)
#参数传入
print(prompt.format_messages(name='guo',user_input='你的朋友是谁'))
LangChain Message
这是 LangChain 体系里最底层、最稳定的抽象。
底层原理
SystemMessage —— 系统指令(人格 + 规则)
sy=SystemMessage(
content="你是一个智能体",
additonal_kwargs={"智能体名字": 'guo'}
)
SystemMessage 的作用
-
给模型设定身份 / 行为规则
-
权重最高(一般模型不会轻易违背)
等价于对模型说:
“接下来你必须按这个身份来回答
additional_kwargs 是什么?
additional_kwargs={'智能体名字':'guo'}
这是一个 扩展字段:
-
不一定会被模型“直接理解”
-
但会在 Agent / Tool / 自定义模型 中非常有用
-
常用于:
-
存 metadata
-
标注角色
-
业务标识
-
👉 模型输入 = content
👉 系统/程序用 = additional_kwargs
HumanMessage —— 用户输入
hu=HumanMessage(
content='请问你叫什么名字'
)
代表真实用户的一次提问。
在聊天模型里等价于:
User: 请问你叫什么名字
AIMessage —— AI 的历史回复
ai=AIMessage(
content="我的名字叫guo"
)
表示:
-
这是 已经发生过的一次 AI 回答
-
常用于:
-
构造对话上下文
-
回放历史
-
测试 / 回放日志
-
打印结果说明了什么?
for msg in[sy,hu,ai]:
print(msg.type,msg.content)
你打印的是:
一个标准的 Chat Messages List
👉 这是 ChatModel / Agent / Memory 的通用输入
Message vs PromptTemplate(非常关键)
❓ 什么时候用
Message?
| 场景 | 推荐 |
|---|---|
| 精细控制历史对话 | ✅ Message |
| Agent / Tool | ✅ Message |
| 手动构造上下文 | ✅ Message |
❓ 什么时候用ChatPromptTemplate?
| 场景 | 推荐 |
|---|---|
| 带变量的模板 | ✅ Prompt |
| 批量生成 | ✅ Prompt |
| 少写重复代码 | ✅ Prompt |
实际项目中:二者结合
PromptTemplate → 生成 messages
Message → 直接拼历史 / 插 Memory
Message 在真实项目里的典型用法
1.构造一段“完整对话历史”
messages = [
SystemMessage(content='你是一个智能体'),
HumanMessage(content='你好'),
AIMessage(content='你好,有什么可以帮你?'),
HumanMessage(content='你叫什么名字?')
]
👉 直接喂给模型即可
2.从 PromptTemplate 转成 Message(底层一致)
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
('system', '你是一个智能体'),
('human', '{question}')
])
msgs = prompt.format_messages(question='你叫什么名字?')
Message 的 type 属性(调试必会)
for msg in [sy, hm, ai]:
print(msg.type, msg.content)
输出类似
system 你是一个智能体
human 请问智能体叫什么名字?
ai 我叫guo
👉 模型内部就是靠 type 区分角色
示例
from langchain_core.messages import SystemMessage,HumanMessage,AIMessage
sy=SystemMessage(
content="你是一个智能体",
additonal_kwargs={"智能体名字": 'guo'}
)
hu=HumanMessage(
content='请问你叫什么名字'
)
ai=AIMessage(
content="我的名字叫guo"
)
for msg in[sy,hu,ai]:
print(msg.type,msg.content)
下面我只围绕你这段代码,给你写一份从“为什么存在 → 怎么用 → 什么时候用”的完整教程。
这一步其实已经进入 LangChain Prompt 体系里很高级、但非常有价值的部分了。
ChatMessagePromptTemplate
创建一条“自定义角色”的消息模板,并在运行时把变量填进去,生成一条 Message
这不是普通的 system / human / ai,而是你自己定义的角色。
导入
from langchain_core.prompts import (
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
AIMessagePromptTemplate,
ChatMessagePromptTemplate,
)
这里导入了 四种 MessagePromptTemplate:
| 类名 | 用途 |
|---|---|
| SystemMessagePromptTemplate | system 角色模板 |
| HumanMessagePromptTemplate | human 角色模板 |
| AIMessagePromptTemplate | ai 角色模板 |
| ChatMessagePromptTemplate | 自定义角色模板 ⭐ |
定义带变量的模板字符串
prompt = '有一种英雄主义就是在认清{subject}的真相后,依然热爱{subject}。'
这里:
-
{subject} 是 占位符
-
同一个变量可以出现多次
-
会在 format 时统一替换
创建“自定义角色”的消息模板(重点)
chat_message_prompt=ChatMessagePromptTemplate.from_template(
template=prompt,
role='张无忌'
)
这是本示例的核心
ChatMessagePromptTemplate 的意义
它允许你:
-
自定义角色名(不局限 system / human / ai)
-
构造 多智能体 / 剧情 / 旁白 / 工具角色
这里的角色是:
role='张无忌'
这在 Message 层会表现为:
type ='张无忌'
运行时传参,生成 Message
print(chat_message_prompt.format(subject='江湖'))
这一步发生了什么?
-
{subject} → ‘江湖’
-
返回的是 一条 Message 对象
Message 的结构大致是:
role: 张无忌
content: 有一种英雄主义就是在认清江湖的真相后,依然热爱江湖。
示例:
from langchain_core.prompts import ChatMessagePromptTemplate
prompt='有一种英雄主义就是在认清{subject}的真相后,依然热爱{subject}。'
chat_message_prompt=ChatMessagePromptTemplate.from_template(
template=prompt,
role='张无忌'
)
print(chat_message_prompt.format(subject='江湖'))
为什么要用 ChatMessagePromptTemplate?(核心价 值)
❓ 不能用 AIMessagePromptTemplate 吗?
可以,但不够强。
| 方式 | 限制 |
|---|---|
| AIMessagePromptTemplate | 只能是 ai |
| ChatMessagePromptTemplate | 任意角色名 ⭐ |
把它放进真实对话里(进阶用法)
现在只生成了一条 Message,
但真实项目一定是“多条Message组合”。
示例:和 system / human 一起用
from langchain_core.prompts import(
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
ChatPromptTemplate
)
chat_prompt=ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(
'你正在扮演一部{subject}世界'
),
HumanMessagePromptTemplate.from_template(
"请发表一句{subject}感悟"
),
])
messages=chat_prompt.format_messages(subject='江湖')
for m in messages:
print(m.type,":",m.content)
和 Message 的关系(必须搞清)
PromptTemplate → Message 的转化关系
ChatMessagePromptTemplate
↓ format()
ChatMessage (Message)
↓
ChatModel / Agent
PromptTemplate 永远不会直接喂模型
喂模型的永远是 Message
好,这一步我们就真的把「梅长苏」交给模型。
目标只有一个:
你构造的自定义角色 Message,如何真正驱动大模型说话
我会用最清晰、最短路径带你走完这一跳。
模型(完整实战)
整体流程
字符串(固化)->模版(参数)->斗轮对话(角色)->会话(字符串)->消息模版
ChatMessagePromptTemplate
↓ format()
Message(role=梅长苏)
↓
ChatModel(如 ChatOpenAI)
↓
AIMessage(模型输出)
你前面已经完成了 80%,现在只补最后 20%。
准备工作:需要的包
如果你用 OpenAI / 兼容接口:
pip install langchain-core langchain-openai
完整示例代码
导入(全部是最新路径)
from langchain_core.prompts import(
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
ChatPromptTemplate
)
构造完整对话 Prompt
chat_prompt=ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(
'你正在扮演一部{subject}世界'
),
HumanMessagePromptTemplate.from_template(
"请发表一句{subject}感悟"
),
])
⚠ SystemMessage 在这里起的是**“锁人格”**的作用
没有它,模型容易跑偏
把 Prompt 变成真正的 Message
messages=chat_prompt.format_messages(subject='江湖')
你此刻得到的是类似:
system : 你正在扮演一部江湖世界
human : 请发表一句江湖感悟
初始化模型(真正“交给模型”的瞬间)
llm = ChatOpenAI(
model="gpt-4o-mini", # 示例,可换
temperature=0.7
)
调用模型,获取回复
response = llm.invoke(messages)
print(response.content)
增强检索
增强检索(Enhanced Retrieval)通常指在信息检索或大模型应用中,通过更聪明的检索+ 更好的上下文组织,显著提升答案的准确性、相关性和可解释性。最常见的落地形态就是 RAG(Retrieval Augmented Generation,检索增强生成)。
1.为什么需要增强检索?
传统大模型的问题:
-
❌ 只靠参数记忆,不掌握你自己的数据
-
❌ 容易“幻觉”
-
❌ 无法实时更新知识
增强检索的目标:
先把“对的资料”找出来,再让模型基于资料回答
2.典型架构(RAG标准流程)
用户问题
↓
Query 改写 / 扩展(可选)
↓
向量化(Embedding)
↓
向量数据库检索(Top-K)
↓
结果重排(Re-ranking)
↓
上下文拼接(Context Window)
↓
LLM 生成答案
3.关键“增强”点
查询增强(Query Enhancement)
让“问题”更适合被检索:
- 同义词扩展
- 多语言转换
- 多 Query 并行检索(Multi-Query)
示例:
用户: K8s 网络问题
→ 扩展为:
- Kubernetes CNI
- Pod 网络通信
- Service 网络原理
检索增强(Hybrid Retrieval)
不要只用向量检索
常见组合:
- 向量检索(语义)
- 关键词检索(BM25)
- 结构化过滤(时间 / 标签 / 权限)
👉 Hybrid Search = 真实生产必选
重排增强(Re-ranking)
第一轮检索只是“可能相关”
第二轮用更强模型排序:
- Cross-Encoder
- LLM Re-ranker
效果:
👉 Top-5 质量 ≫ Top-50 原始结果
上下文增强(Context Engineering)
不是“拼越多越好”:
- Chunk 粒度控制(200–500 tokens)
- 去重
- 保留标题 / 来源
- 加引用标记(source id)
生成约束(Grounded Generation)
强制模型:
- 只基于提供内容回答
- 找不到就说“不知道”
Prompt 示例:
仅根据以下资料回答问题,
如果资料中没有答案,请明确说明“资料不足”。
4.常见技术栈
- Embedding:OpenAI / BGE / E5
- Vector DB:FAISS / Milvus / Weaviate
- Keyword:Elasticsearch / OpenSearch
- Orchestration:LangChain / LlamaIndex
- API:FastAPI + Redis
- 部署:Docker + K8s
5.增强检索 ≠ 只为大模型
同样适用于:
- 企业知识库
- 代码搜索
- 运维故障定位
- 日志 / 文档问答
LangChain 文本切分
从本地 txt 文件 → 自动识别编码 → 使用 LangChain 进行文本切分
这是 RAG / AI Agent 的第一步。
1.、为什么要做文本切分?
在 RAG(检索增强生成) 中,大模型不能直接吃下整篇长文档:
- LLM 有 上下文长度限制
- 检索是 按“文本块(chunk)”进行
- 文本过大 → 检索不准 / 成本高
👉 所以必须先 切分文档
2.环境准备
安装依赖(新版 LangChain)
pip install -U langchain langchain-text-splitters chardet
正确导入(重点)
from langchain_text_splitters import RecursiveCharacterTextSplitter
import chardet
⚠ 旧写法已废弃:
from langchain.text_splitter import RecursiveCharacterTextSplitter # 不推荐
3.完整示例代码
from langchain_text_splitters import RecursiveCharacterTextSplitter
import chardet
# 文件路径
file_path= 'text2/story.txt'
# 获取文本字符集
def get_encoding(file_path):
with open(file_path, 'rb') as f:
data=f.read()
encoding = chardet.detect(data)['encoding']
return encoding
# 获取数据
with open(file_path, 'r',encoding=get_encoding(file_path)) as f:
data=f.read()
# 文件切分器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=50, # 每个文本块最大长度(字符数)
chunk_overlap=20, # 相邻文本块的重叠长度
length_function=len, # 长度计算函数(中文用 len 即可)
add_start_index=True # 是否记录原文起始位置
)
# 执行文本切分
docs=text_splitter.create_documents([data])
# 输出切分结果
for i in docs:
print(i)
4.切分器参数逐个解释
chunk_size
chunk_size=50
含义:
- 每个文本块的 最大字符数
- 太大 → 检索不精确
- 太小 → 上下文碎片化
📌 经验值:
| 场景 | 推荐 |
|---|---|
| 中文文档 | 300-800 |
| 英文文档 | 500-1000 |
| 代码 | 200-400 |
chunk_overlap
chunk_overlap=20
含义:
- 相邻文本块 重复的字符数
- 防止语义被“切断”
示意:
chunk1: ABCDEFGHIJ
chunk2: HIJKLMNOP
📌 一般设置为 chunk_size 的 10%~30%
length_function
length_function=len
- len :按字符数(中文推荐)
- 英文 + token 场景可换成 tokenizer
add_start_index=True
add_start_index=True
切分后 Document 会包含:
doc.metadata["start_index"]
📌 用途:
溯源(告诉用户“答案来自哪一段”)
很好,这一步非常关键 👍
你已经从 递归字符切分 走到了 字符切分(CharacterTextSplitter),这是理解 RAG 切分原理的分水岭。
下面我接着你的代码,把 CharacterTextSplitter 字符切割写成一篇完整教程,并且会 对比RecursiveCharacterTextSplitter,让你真正“懂为什么要用哪个”。
LangChain 字符切割
使用 CharacterTextSplitter 按指定分隔符(中文逗号)切分文本
适合结构规则、格式统一的文本。
1.什么是 CharacterTextSplitter?
CharacterTextSplitter 是“最基础、最可控”的文本切分器。
一句话理解:
按你指定的分隔符切文本,再控制每块长度和重叠
它不会智能判断语义,而是 严格按规则执行。
2.什么时候该用 CharacterTextSplitter?
✅ 适合场景:
- 文本结构非常规则
- 有明确分隔符:
- 中文:
,。 - 英文:
.\n
- 中文:
- 日志、配置、对话、FAQ
❌ 不适合:
- 长文档
- 自然语言段落复杂
- 小说 / 技术文档
3.环境准备
pip install -U langchain-text-splitters chardet
导入:
from langchain_text_splitters import CharacterTextSplitter
import chardet
4.完整示例代码(逐行讲解)
from langchain_text_splitters import CharacterTextSplitter
import chardet
# 文件路径
file_path='./text2/story.txt'
# 获取文本字符集
def get_encoding(file_path):
with open(file_path, 'rb') as f:
data=f.read()
encoding = chardet.detect(data)['encoding']
return encoding
# 获取数据
with open(file_path, 'r',encoding=get_encoding(file_path)) as f:
data=f.read()
# 文件切分器
text_splitter = CharacterTextSplitter(
separator='.', # 指定分隔符(英文句号)
chunk_size=50, # 每个文本块最大长度
chunk_overlap=20, # 相邻文本块重叠部分
length_function=len, # 长度计算方式
add_start_index=True, # 记录原文起始索引
is_separator_regex=False # 分隔符是否为正则
)
# 执行切分
docs=text_splitter.create_documents([data])
# 查看切分结果
for i in docs:
print(i)
5.CharacterTextSplitter
参数详解
separator
separator='.'
含义:
- 用什么字符来“切”
- 不包含在结果文本中
常见示例:
| 文本类型 | separator |
|---|---|
| 中文句子 | 。 |
| 中文短句 | , |
| 英文句子 | . |
| 段落 | \n\n |
chunk_size
chunk_size=50
-
控制单个 chunk 最大长度
-
超过会继续拆分
📌 字符切分时:
chunk_size 是硬约束
chunk_overlap
chunk_overlap=20
作用:
-
防止上下文断裂
-
保留语义连续性
📌 一般设置为 chunk_size 的 20% 左右
add_start_index
add_start_index=True
切分后 Document.metadata 会包含:
{'start_index': 123}
👉 RAG 溯源 / 高亮定位必备
is_separator_regex
is_separator_regex=False
❌ False(当前)
-
separator被当作普通字符串 -
','就是中文逗号
✅ True(高级)
separator=r'[,。;]'
is_separator_regex=True
👉 可用正则 多分隔符切割
6.与 RecursiveCharacterTextSplitter 对比(非常重要)
| 项目 | Character | Recursive |
|---|---|---|
| 是否智能 | ❌ | ✅ |
| 是否可控 | ✅ | ⚠ |
| 语义完整性 | 一般 | 好 |
| 使用场景 | 规则文本 | 自然语言 |
| RAG 推荐度 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
👉 真实项目中 90% 用 Recursive
太好了 👍
你已经进入 RAG / Agent 中“最容易拉开差距的一块”——代码文档切割。
下面我承接你这段代码,写一篇完整、工程向的「代码文档切割教程」,并结合 为什么它对程序员Agent 非常重要。
LangChain 代码文档切割
使用 RecursiveCharacterTextSplitter.from_language 按编程语言结构切割代码
这是构建 代码问答 / Code Agent / 自动代码分析 的基础。
1.为什么“代码”不能用普通文本切割?
如果用普通文本切割代码,会出现问题:
❌ 语义被破坏
for i in range(1,
11):
❌ 函数 / 类被切散
❌ Agent 无法理解上下文
👉 代码有结构,必须“按语言规则切"
2.LangChain 是怎么支持“代码切割”的?
LangChain 内置了 多语言代码解析规则,核心就是:
RecursiveCharacterTextSplitter.from_language(...)
它会根据语言,优先按:
- 函数
- 类
- 代码块
- 空行
- 缩进
进行 递归切割。
3.支持的编程语言
from langchain_text_splitters import Language
print([e.value for e in Language])
常见输出(示例):
['python', 'java', 'js', 'go', 'cpp', 'html', 'markdown', 'sql', ...]
📌 熟悉的 Python / Java / Go。
4.完整示例代码
from langchain_text_splitters import(
Language,
RecursiveCharacterTextSplitter
)
# print([i.value for i in Language])
PYTHON_CODE='''
def sum()
a=0
for i in range(1,11):
a+=i
print(a)
sum()
'''
# 创建分割器
code_splitter=RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON, # 指定语言
chunk_size=50, # 每个代码块最大长度
chunk_overlap=10, # 代码块重叠
)
python_docs=code_splitter.create_documents([PYTHON_CODE])
# 解析
for i in python_docs:
print(i)
5.切割结果
输出是 Document 对象列表:
page_content='def sum()
a=0
for i in range(1,11):'
page_content='a+=i
print(a)
sum()'
👉 函数整体被保留,不会被乱切。
6.from_language()
以 Language.PYTHON 为例,优先级类似:
\n\n → def → class → 缩进块 → 行
👉 优先保留:
- 一个完整函数
- 一个完整类
- 一个逻辑代码块
这对:
- 代码搜索
- 错误定位
- 自动重构
- Code Review Agent
极其重要。
7.chunk_size / overlap 在代码里的经验值
推荐配置
| 场景 | chunk_size | overlap |
|---|---|---|
| 小函数 | 200 | 20 |
| 中等模块 | 400 | 40 |
| 大文件 | 800 | 100 |
📌 代码切割不宜太小
8.与普通 RecursiveCharacterTextSplitter 的区别
| 项目 | 普通文本 | 代码语言版 |
|---|---|---|
| 语义完整性 | 一般 | ⭐⭐⭐⭐⭐ |
| 是否识别函数 | ❌ | ✅ |
| 是否识别类 | ❌ | ✅ |
| Code Agent 适用 | ❌ | ✅ |
👉 代码一定要用 from_language()
9.真实工程中的典型应用
-
代码问答 Agent
“这个函数是干嘛的?”
→ 检索对应函数代码 → LLM 解释
-
自动代码审查
“有没有性能问题?”
→ 定位 for / while / IO 逻辑
-
DevOps / CI Agent
- 解析 Helm / YAML
- 解析 Terraform
- 解析 K8s 配置
Language.YAML
Language.JSON
10.进阶
多语言代码切割(仓库级)
splitter_map = {
".py": Language.PYTHON,
".go": Language.GO,
".java": Language.JAVA
}
自动选择切割器。
很好,这一步已经进入 RAG / Agent 工程里非常“专业”的层级—— Token 级切割 👍
很多人只会“字符切割”,真正做线上系统的一定会用 token 切割。
下面我承接你这段代码,写一篇完整的 Token 切割(tiktoken)教程,并把它和前面的字符 / 递归切割彻底对比清楚。
LangChain Token 切割
使用 tiktoken 按「模型 token 数」切分文本
这是构建 真实可上线 RAG / Agent 系统 的关键步骤。
1.为什么一定要学 Token 切割?
大模型真正关心的不是:
-
❌ 字符数
-
❌ 行数
而是:
✅ Token 数
如果你只按字符切割:
-
中文 / 英文 token 比例不同
-
实际 token 数可能超上下文
-
上线后 直接报错 or 成本失控
👉 Token 切割 = 工程级切割
2.什么是 tiktoken?
tiktoken 是 OpenAI 官方的 Tokenizer:
-
把文本 → token
-
精确对齐模型的计费 & 上下文限制
-
LangChain 内置支持
安装:
pip install tiktoken
3.代码在做什么?
现在用的是
CharacterTextSplitter.from_tiktoken_encoder(...)
含义是:
用 tiktoken 来计算长度,而不是 len()
也就是说:
- 切分 :token
- 而不是字符
4.完整示例代码
from langchain_text_splitters import CharacterTextSplitter, RecursiveCharacterTextSplitter
import chardet,tiktoken
# 文件路径
file_path='./text2/story.txt'
# 获取文本字符集
def get_encoding(file_path):
with open(file_path, 'rb') as f:
data=f.read()
encoding = chardet.detect(data)['encoding']
return encoding
# 获取数据
with open(file_path, 'r',encoding=get_encoding(file_path)) as f:
data=f.read()
# 创建分割器
token_splitter = CharacterTextSplitter.from_tiktoken_encoder(
separator='。', # 指定分隔符:按句号切分(中文小说自然句子)
chunk_size=10, # 每个 chunk 最大 token 数(实验值,可调
chunk_overlap=2, # 相邻 chunk 重叠 token 数,保证上下文连续
)
token_docs = token_splitter.create_documents([data])
# 获取token解析器
enc=tiktoken.get_encoding('cl100k_base')
for i,doc in enumerate(token_docs):
print(f"Chunk {i}") # chunk 序号
print(f"Token count: {len(enc.encode(doc.page_content))}") # 实际 token 数
print(doc.page_content) # 文本内容
print("="*50)
5.Token切割参数详解
chunk_size
chunk_size=50
含义:
每个文本块最多 50 个 token
不是字符!
示例对比:
| 文本 | 字符数 | Token 数 |
|---|---|---|
| Hello world | 11 | 2 |
| 我爱自然语言处理 | 8 | 6~8 |
chunk_overlap
chunk_overlap=5
- 相邻 chunk 共享 5 个 token
- 防止语义被截断
📌 推荐比例:10%~20%
6.Token 切割 vs 字符切割 vs 递归切割
| 维度 | 字符切割 | 递归切割 | Token 切割 |
|---|---|---|---|
| 是否语义优先 | ❌ | ✅ | ⚠ |
| 是否对齐模型 | ❌ | ❌ | ✅ |
| 是否防超长 | ❌ | ⚠ | ✅ |
| 上线安全性 | ❌ | ⚠ | ⭐⭐⭐⭐⭐ |
👉 **上线系统:**Token 切割必选
7.为什么不用 Recursive + token 一步完成?
原因:
-
Recursive:保证语义
-
Token:保证安全
👉 工程上是“组合拳”
示例:
# 第一次:语义切割
semantic_docs = recursive_splitter.create_documents(texts)
# 第二次:token 切割
safe_docs = token_splitter.split_documents(semantic_docs)
8.如何验证 token 数?(进阶)
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
print(len(enc.encode(doc.page_content)))
9.真实生产环境推荐配置
通用文档(中文)
chunk_size = 300
chunk_overlap = 50
GPT-4 / GPT-4o
- 上下文:8k / 32k
- 检索 TopK:3~5
LangChain 文档加载器
示如何使用 LangChain 和 community loaders 加载各种格式文档
这是 RAG / AI Agent 的第一步:把原始数据读入为 Document 对象列表。
1.为什么需要 Document Loader?
在 RAG / Agent 流程中:
- LLM 无法直接读取文件
- 文件格式多样:Markdown / CSV / JSON / HTML / PDF
- Document Loader 能:
- 解析不同文件格式
- 返回 Document 对象(包含文本 + metadata)
- 与后续 切割、Embedding、向量数据库 无缝对接
2.Markdown 文件加载
from langchain_community.document_loaders import TextLoader
# 加载markdown文件
loader=TextLoader('./text/MD示例.md',encoding='utf-8')
docs=loader.load()
for i in docs:
print(i.page_content)
要点:
- TextLoader 是最基础 loader
- 适合 .md 、 .txt 等纯文本
- 每个 Document 包含:
- page_content : 文本内容
- metadata : 文件信息(路径、来源等)
3.目录加载(批量文件)
pip install unstructured
pip install "unstructured[csv]"
pip install python-magic-bin 系统依赖库结合Python实现文件类型检索功能
from langchain_community.document_loaders import TextLoader
# 检索text目录中有几个csv文件
loader=TextLoader('./text/MD示例.md',encoding='utf-8')
docs=loader.load()
for i in docs:
print(i.page_content)
要点:
- glob 支持通配符,例如:
- *.md → 所有 Markdown
- *.txt → 所有文本
- 注意:
- .html 和 .rst 默认不加载
- 可以结合 unstructured 或 python-magic 增强识别能力
4.CSV 文件加载
# 获取csv列数据
from langchain_community.document_loaders import CSVLoader
import chardet
# 文件路径
file_path='./text/score.csv'
# 获取文本字符集
def get_encoding(file_path):
with open(file_path, 'rb') as f:
data=f.read()
encoding = chardet.detect(data)['encoding']
return encoding
loader=CSVLoader(file_path,encoding=get_encoding(file_path),source_column='英语')
docs=loader.load()
for i in docs:
print(i.metadata)
要点:
- 支持:
- 全文加载(每行一个 Document)
- 指定列加载
- Document.metadata 默认包含行号和文件名
5.HTML 文件加载
from langchain_community.document_loaders import BSHTMLLoader
loader = BSHTMLLoader('./text/index.html')
docs=loader.load()
for i in docs:
print(i.page_content)
要点:
- 自动提取 HTML 中的文本内容
- 支持保留标签或纯文本
- 适合网页抓取 / 爬虫场景
6.JSON 文件加载
pip install jq
from langchain_community.document_loaders import JSONLoader
loader=JSONLoader(
file_path='./text/simple_prompt.json',
jq_schema='.template',
text_content=False
)
docs=loader.load()
print(docs)
要点:
- jq_schema 是 JSONPath 风格选择器
- text_content :
- False → 保留原始 JSON 结构
- True → 全部转换为字符串
- 可用于配置文件、Prompt 模板、API 数据
7.PDF 文件加载
from langchain_community.document_loaders import PyPDFLoader
loader=PyPDFLoader('./text/pulsar.pdf')
pages=loader.load_and_split()
for i in pages:
print(i)
要点:
- 每页一个 Document
- load() → 整个 PDF 当作一个 Document
- load_and_split() → 按页拆分
- 可以和 RecursiveCharacterTextSplitter 或 token_splitter 配合使用
8.文档加载器使用经验总结
| 类型 | Loader | 推荐使用场景 | 注意事项 |
|---|---|---|---|
| Markdown / TXT | TextLoader | 文本文件、笔 记 | 支持 encoding |
| CSV | CSVLoader | 表格 / 数据列 | 可指定列,需注意编码 |
| HTML | BSHTMLLoader | 网页 / 抓取 | 可保留标签,需依赖 BeautifulSoup4 |
| JSON | JSONLoader | 配置 / API 数 据 | jq_schema 提取字段 |
| PyPDFLoader | 文档 / 论文 | load_and_split 按页拆分 | |
| 目录批量 | DirectoryLoader | 批量文件 | glob 支持通配符 |
更多推荐


所有评论(0)