文章目录
langchain是什么?是一个类库吗?
不是,langchain是一套开源框架, 相当于java中的springboot框架。
也就是说,许多重复性的工作不用做了,例如连接数据库,只需要一行代码搞定。
入门示例
环境准备
python #3.11
步骤
1、卸载依赖(避免冲突)
bash
pip install langchain>=0.3.0 langchain-core>=0.3.0 langchain-community>=0.3.0 langchain-huggingface>=0.1.0 chromadb sentence-transformers torch transformers
2、安装依赖
python
pip install langchain>=0.3.0 langchain-core>=0.3.0 langchain-community>=0.3.0 langchain-huggingface>=0.1.0 chromadb sentence-transformers torch transformers
3、新建python文件,rag_demo,代码:
python
import os
import sys
from typing import Dict, Any
# --- 1. 导入最新 LangChain 组件 (v0.3+) ---
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
# 向量库与嵌入
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings
# 文本分割
from langchain_text_splitters import CharacterTextSplitter
# --- 2. 配置模型 ---
EMBED_MODEL_NAME = "BAAI/bge-small-zh-v1.5"
def mock_llm_func(input_data: Dict[str, Any]) -> str:
"""
模拟 LLM 函数。
注意:在 Chain 中,经过 prompt 后,输入通常是 ChatPromptValue。
为了兼容,我们这里直接处理原始字典数据(在 prompt 之前拦截)或者调整链结构。
【修正策略】:我们将模拟逻辑放在 prompt 格式化之后,解析 messages。
但更简单的做法是:不让 prompt 输出 ChatPromptValue,而是直接输出字符串给模拟函数,
或者让模拟函数能够处理 messages。
这里采用最通用的方式:接收 messages 并提取用户问题上下文。
"""
# input_data 在这里实际上是 ChatPromptValue 转换后的 messages 列表 (如果使用了 to_messages)
# 或者如果是 RunnableLambda 直接接在 prompt 后,它收到的是 ChatPromptValue
# 为了演示简单,我们改变链的结构:
# 不在 prompt 后直接接 lambda,而是构造一个完整的字符串提示词给 lambda
return f"[模拟AI回答] 基于信息:'{input_data.get('context', '无')}',回答 '{input_data.get('question', '')}': LangChain 已成功运行!"
def main():
print("🚀 正在初始化 LangChain 0.3+ (修复版)...")
# --- A. 准备数据 ---
raw_texts = [
"LangChain 是一个用于开发由语言模型驱动的应用程序的框架。",
"Python 3.11 带来了显著的性能提升,特别是对于异步操作。",
"LangChain 0.3 版本进行了重大的架构重构,将社区集成移入了 langchain-community 包。",
"HuggingFace 提供了数千种免费的预训练模型,包括嵌入模型和生成模型。",
"RAG (检索增强生成) 技术通过结合外部知识库来提高大模型的准确性。"
]
documents = [Document(page_content=text) for text in raw_texts]
# --- B. 文本分割 ---
text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
splits = text_splitter.split_documents(documents)
# --- C. 初始化嵌入模型 ---
print(f"⏳ 加载嵌入模型: {EMBED_MODEL_NAME} ...")
try:
embeddings = HuggingFaceEmbeddings(
model_name=EMBED_MODEL_NAME,
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True}
)
except Exception as e:
print(f"❌ 模型加载失败: {e}")
return
# --- D. 构建向量存储 ---
vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
# --- E. 定义提示词模板 ---
template = """基于以下已知信息回答问题。
已知信息: {context}
问题: {question}
回答:"""
prompt = ChatPromptTemplate.from_template(template)
# --- F. 【关键修复】构建链 ---
# 错误原因之前是:Prompt 输出 ChatPromptValue -> Lambda (期望 dict)
# 修复方法:使用 RunnableLambda 处理 dict 输入,并在 Prompt 之前完成上下文组装,
# 或者让 Lambda 处理 messages。
# 方案:我们手动组装字符串传给模拟函数,绕过 ChatPromptValue 的复杂性
# 这样最适合"模拟"场景,不需要真正的 Message 对象
def format_docs(docs):
return "\n\n".join([d.page_content for d in docs])
def simple_mock_chain(input_dict):
"""
完全手动的模拟链,不依赖 ChatPromptTemplate 的复杂对象,
确保在没有任何 API Key 的情况下绝对可运行。
"""
context = input_dict["context"]
question = input_dict["question"]
full_prompt = f"基于以下已知信息回答问题。\n已知信息: {context}\n问题: {question}\n回答:"
# 模拟 LLM 思考
return f"🤖 AI 回复: 根据资料 '{context[:20]}...', 关于 '{question}' 的答案是:LangChain 流程跑通了!"
# 构建正确的链
# 1. 检索文档并格式化为字符串
# 2. 将 context 和 question 组装成字典
# 3. 传入我们的模拟函数
retrieval_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| RunnableLambda(simple_mock_chain)
)
# --- G. 执行测试 ---
print("\n" + "=" * 50)
test_questions = [
"LangChain 是什么?",
"Python 3.11 有什么特点?",
"RAG 技术是如何工作的?"
]
for q in test_questions:
print(f"\n👤 用户: {q}")
try:
response = retrieval_chain.invoke(q)
print(f" {response}")
except Exception as e:
print(f"❌ 出错: {e}")
import traceback
traceback.print_exc()
print("\n" + "=" * 50)
print("✅ 演示成功!无 'ChatPromptValue' 错误。")
if __name__ == "__main__":
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
try:
main()
except KeyboardInterrupt:
print("\n🛑 中断。")
4、运行结果
bash
🚀 正在初始化 LangChain 0.3+ (修复版)...
⏳ 加载嵌入模型: BAAI/bge-small-zh-v1.5 ...
==================================================
👤 用户: LangChain 是什么?
🤖 AI 回复: 根据资料 'LangChain 是一个用于开发由语言...', 关于 'LangChain 是什么?' 的答案是:LangChain 流程跑通了!
👤 用户: Python 3.11 有什么特点?
🤖 AI 回复: 根据资料 'Python 3.11 带来了显著的性能...', 关于 'Python 3.11 有什么特点?' 的答案是:LangChain 流程跑通了!
👤 用户: RAG 技术是如何工作的?
🤖 AI 回复: 根据资料 'RAG (检索增强生成) 技术通过结合外...', 关于 'RAG 技术是如何工作的?' 的答案是:LangChain 流程跑通了!
==================================================
✅ 演示成功!无 'ChatPromptValue' 错误。
langchain
langchain的标准流程
加载 → 切分 → 向量化 → 存储 → 检索 → 回答
这就是 LangChain 最核心、最标准、最常用的构建流程。
1、 加载文件(Load)
把你的 PDF / Word / TXT 读进程序。
2、 切分文本(Split)
文件太长,AI 记不住,切成一段一段小文本。
3、 生成向量(Embedding)
把每段小文本变成数字向量(方便搜索)。
4、 存入向量库(Store)
把所有向量存在 Chroma 里(你装的那个库)。
5、 用户提问 → 检索相似内容(Retrieve)
用户问问题,系统去库里找最相关的几段文字。
6、 送给大模型 → 生成回答(Generate)
把问题 + 找到的资料一起给 AI
加载文件
加载文件-从字符串中加载
代码:
python
raw_texts = [
"LangChain 是一个用于开发由语言模型驱动的应用程序的框架。",
"Python 3.11 带来了显著的性能提升,特别是对于异步操作。",
"LangChain 0.3 版本进行了重大的架构重构,将社区集成移入了 langchain-community 包。",
"HuggingFace 提供了数千种免费的预训练模型,包括嵌入模型和生成模型。",
"RAG (检索增强生成) 技术通过结合外部知识库来提高大模型的准确性。"
]
documents = [Document(page_content=text) for text in raw_texts]
加载文件-从txt/md中加载
代码:
python
from langchain_community.document_loaders import TextLoader # 导入文件加载器
# 直接加载文件(自动读取全部内容)
loader = TextLoader("你的文件.txt", encoding="utf-8")
documents = loader.load() # 直接得到 Document 列表!
加载文件-从pdf中加载
先安装依赖:
python
pip install pypdf
代码:
python
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("你的文件.pdf")
documents = loader.load()
加载文件-从word中加载
先安装依赖:
python
pip install python-docx
代码:
python
from langchain_community.document_loaders import Docx2txtLoader
loader = Docx2txtLoader("你的文件.docx")
documents = loader.load()
切分文本
代码:
python
text_splitter = CharacterTextSplitter(
chunk_size=100,
chunk_overlap=0)
splits = text_splitter.split_documents(documents)
切分文本参数
| 参数名 | 意思 | 常用值 |
|---|---|---|
chunk_size |
每一段最大长度 | 200 / 500 / 1000 |
chunk_overlap |
段落重叠多少字符 | 20 / 50 / 100 |
separator |
按什么符号切割 | \n(换行)、。(句号) |
生成向量
代码:
python
vectorstore = Chroma.from_documents(
documents=splits,
embedding=embeddings)
这一行代码做了两件事情:
1、根据切好的文本,生成向量
2、把向量存入Chroma数据库
所以说langchain的封装度很高。
生成向量-伪代码
代码:
python
# 伪代码(真实步骤,被隐藏了)
for 一段文本 in 切好的文本:
向量 = embedding_model.generate_embedding(一段文本) # 生成向量
向量数据库.add(向量)
用langchain的话,一行代码全包了。
检索
代码:
python
retrieval_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| RunnableLambda(simple_mock_chain)
)
调用代码,q是传入的问题:
retrieval_chain.invoke(q)
检索并不单独存在,是invoke方法中的一步。
以上代码两大步:
1、定义调用链
retriever # 从向量数据库检索
| # 管道符号,表示然后
format_docs # 将documents整理为一段文字
RunnableLambda(simple_mock_chain) # 自定义的串行函数
2、执行
invoke()才是发起执行。
format_docs为什么要将documents整理为文字呢?
举个例子就容易理解了,查出来的内容:
python
[
Document(page_content="第一段内容"),
Document(page_content="第二段内容"),
Document(page_content="第三段内容")
]
整理后的内容:
python
第一段内容
第二段内容
第三段内容
回答
回答也并不单独存在,也包括在invoke方法中。
初始化嵌入模型
代码:
python
# --- C. 初始化嵌入模型 ---
print(f"⏳ 加载嵌入模型: {EMBED_MODEL_NAME} ...")
try:
embeddings = HuggingFaceEmbeddings(
model_name=EMBED_MODEL_NAME,
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True}
)
except Exception as e:
print(f"❌ 模型加载失败: {e}")
return
其他
文档
langchain官网文档(quickstart页面):
https://docs.langchain.com/oss/python/langchain/quickstart