当前位置:首页 > AI > 正文内容

LangChain 应用开发实战指南:从零构建智能应用

廖万里10小时前AI0

LangChain 是当前最流行的 LLM 应用开发框架,它提供了一套完整的工具链,让开发者能够快速构建从简单到复杂的 AI 应用。本文将深入剖析 LangChain 的核心概念、关键组件,并通过实战案例演示如何从零构建一个智能问答系统。

一、核心概念

LangChain 的设计理念是将 LLM 应用的各个组件模块化,通过"链"的方式组合起来。理解以下核心概念是掌握 LangChain 的基础:

1.1 Chains(链)

链是 LangChain 的核心抽象,它将多个组件串联起来,形成一个完整的处理流程。比如一个简单的问答链可能是:接收用户问题 → 调用 LLM → 返回答案。而更复杂的链可能包含数据检索、多次 LLM 调用、条件判断等多个步骤。

链的优势在于:

  • 模块化设计:每个链负责一个明确的任务,易于理解和维护
  • 可复用性:同一个链可以在不同的应用中复用
  • 易于组合:多个简单链可以组合成复杂的处理流程

1.2 Prompts(提示词)

提示词是与 LLM 交互的关键。LangChain 提供了 PromptTemplate 类,让提示词管理更加规范和灵活:

from langchain.prompts import PromptTemplate

# 创建提示词模板
template = """你是一个专业的{role}。

用户问题: {question}

请用简洁专业的语言回答:"""

prompt = PromptTemplate(
    input_variables=["role", "question"],
    template=template
)

# 使用模板生成最终提示词
final_prompt = prompt.format(role="Python开发工程师", question="如何优化列表查询性能?")
print(final_prompt)

1.3 Memory(记忆)

LLM 本身是无状态的,但很多应用需要记住对话历史。LangChain 的 Memory 组件解决了这个问题:

from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain

# 创建带记忆的对话链
memory = ConversationBufferMemory()
conversation = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=True
)

# 多轮对话会自动保存历史
response1 = conversation.predict(input="你好,我是小明")
response2 = conversation.predict(input="你还记得我叫什么吗?")
# LLM会回答"记得,你是小明"

1.4 Agents(智能体)

智能体是 LangChain 中最强大的组件,它可以根据用户输入动态选择和执行工具。比如一个智能助手可能同时具备搜索、计算、代码执行等能力,智能体能够自主判断何时使用哪个工具。

二、核心组件详解

2.1 Models(模型层)

LangChain 支持多种 LLM 提供商,包括 OpenAI、Azure、Anthropic、本地模型等:

from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI

# 文本补全模型
llm = OpenAI(temperature=0.7, model_name="gpt-3.5-turbo-instruct")

# 对话模型(推荐)
chat_model = ChatOpenAI(
    temperature=0.7,
    model_name="gpt-4",
    max_tokens=2000
)

# 调用模型
response = chat_model.predict("用一句话解释什么是机器学习")
print(response)

2.2 Embeddings(嵌入)

嵌入是将文本转换为向量表示的关键技术,在语义搜索、文档检索等场景中至关重要:

from langchain.embeddings import OpenAIEmbeddings

embeddings = OpenAIEmbeddings()

# 将文本转换为向量
text = "LangChain是一个强大的LLM应用开发框架"
vector = embeddings.embed_query(text)

print(f"向量维度: {len(vector)}")  # 通常1536维
print(f"前5个值: {vector[:5]}")

# 批量嵌入
texts = ["这是第一句话", "这是第二句话", "这是第三句话"]
vectors = embeddings.embed_documents(texts)
print(f"生成了{len(vectors)}个向量")

2.3 Vector Stores(向量存储)

向量数据库是 RAG(检索增强生成)应用的核心组件,LangChain 支持多种向量数据库:

from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 文本分割器,将长文档切分成小块
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_size_overlap=200,
    separators=["\n\n", "\n", " ", ""]
)

# 切分文档
with open("document.txt", "r") as f:
    text = f.read()
chunks = text_splitter.split_text(text)

# 创建向量数据库
vectorstore = Chroma.from_texts(
    texts=chunks,
    embedding=OpenAIEmbeddings(),
    persist_directory="./chroma_db"
)

# 相似度搜索
query = "什么是LangChain?"
results = vectorstore.similarity_search(query, k=3)
for i, doc in enumerate(results):
    print(f"结果{i+1}: {doc.page_content[:100]}...")

2.4 Retrievers(检索器)

检索器是向量数据库的封装,提供了更灵活的检索策略:

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor

# 创建基础检索器
base_retriever = vectorstore.as_retriever(
    search_type="mmr",  # 最大边际相关性,平衡相关性和多样性
    search_kwargs={"k": 5}
)

# 使用LLM压缩检索结果,提取最相关部分
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=base_retriever
)

# 检索相关文档
docs = compression_retriever.get_relevant_documents("如何使用LangChain构建RAG应用?")

三、实战案例:构建智能文档问答系统

下面我们通过一个完整案例,演示如何使用 LangChain 构建一个基于 RAG 的智能问答系统。

3.1 系统架构

整个系统的处理流程如下:

  1. 用户提问 → 2. 检索相关文档片段 → 3. 构建提示词 → 4. 调用LLM生成答案 → 5. 返回结果

3.2 完整代码实现

import os
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain.document_loaders import TextLoader, PDFLoader
from langchain.prompts import PromptTemplate

# 设置API密钥
os.environ["OPENAI_API_KEY"] = "your-api-key"

class DocumentQA:
    """智能文档问答系统"""
    
    def __init__(self, docs_path: str, persist_directory: str = "./chroma_db"):
        """
        初始化问答系统
        
        Args:
            docs_path: 文档路径(支持txt、pdf)
            persist_directory: 向量数据库持久化目录
        """
        self.llm = ChatOpenAI(
            temperature=0.7,
            model_name="gpt-4",
            max_tokens=2000
        )
        self.embeddings = OpenAIEmbeddings()
        self.persist_directory = persist_directory
        
        # 加载和处理文档
        self.documents = self._load_documents(docs_path)
        self.vectorstore = self._create_vectorstore()
        
        # 创建问答链
        self.qa_chain = self._create_qa_chain()
    
    def _load_documents(self, docs_path: str):
        """加载文档并进行分割"""
        # 根据文件类型选择加载器
        if docs_path.endswith(".pdf"):
            loader = PDFLoader(docs_path)
        else:
            loader = TextLoader(docs_path, encoding="utf-8")
        
        documents = loader.load()
        
        # 文本分割
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=1000,
            chunk_overlap=200,
            separators=["\n\n", "\n", "。", " ", ""]
        )
        
        chunks = text_splitter.split_documents(documents)
        print(f"文档已分割为{len(chunks)}个片段")
        
        return chunks
    
    def _create_vectorstore(self):
        """创建向量数据库"""
        vectorstore = Chroma.from_documents(
            documents=self.documents,
            embedding=self.embeddings,
            persist_directory=self.persist_directory
        )
        vectorstore.persist()
        print(f"向量数据库已创建并保存到{self.persist_directory}")
        return vectorstore
    
    def _create_qa_chain(self):
        """创建问答链"""
        # 自定义提示词模板
        prompt_template = """你是一个专业的文档助手,请基于以下上下文回答用户问题。
如果上下文中没有相关信息,请明确说明"根据提供的文档,我无法回答这个问题"。

上下文:
{context}

用户问题: {question}

请提供详细、准确的回答:"""

        PROMPT = PromptTemplate(
            template=prompt_template,
            input_variables=["context", "question"]
        )
        
        # 创建检索型问答链
        qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            chain_type="stuff",
            retriever=self.vectorstore.as_retriever(
                search_type="similarity",
                search_kwargs={"k": 3}
            ),
            return_source_documents=True,
            chain_type_kwargs={"prompt": PROMPT}
        )
        
        return qa_chain
    
    def ask(self, question: str):
        """提问并获取答案"""
        result = self.qa_chain({"query": question})
        
        answer = result["result"]
        sources = result["source_documents"]
        
        # 格式化输出
        print(f"\n问题: {question}")
        print(f"\n答案: {answer}")
        print(f"\n参考来源({len(sources)}个片段):")
        
        for i, doc in enumerate(sources):
            print(f"  [{i+1}] {doc.page_content[:100]}...")
        
        return answer, sources

# 使用示例
if __name__ == "__main__":
    # 初始化问答系统
    qa_system = DocumentQA("./knowledge_base.pdf")
    
    # 进行问答
    qa_system.ask("文档的主要内容是什么?")
    qa_system.ask("有哪些关键技术点?")
    qa_system.ask("如何应用到实际项目中?")

3.3 高级优化技巧

优化1: 使用混合检索

结合关键词检索和语义检索,提高召回率:

from langchain.retrievers import EnsembleRetriever
from langchain.retrievers import BM25Retriever

# 创建BM25关键词检索器
bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 3

# 创建向量检索器
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# 混合检索器
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, vector_retriever],
    weights=[0.4, 0.6]  # 权重分配
)

# 使用混合检索
docs = ensemble_retriever.get_relevant_documents("用户问题")

优化2: 添加对话记忆

让问答系统支持多轮对话:

from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain

# 创建带记忆的对话式检索链
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

qa_chain = ConversationalRetrievalChain.from_llm(
    llm=llm,
    retriever=vectorstore.as_retriever(),
    memory=memory,
    verbose=True
)

# 多轮对话
response1 = qa_chain({"question": "这篇文档讲了什么?"})
response2 = qa_chain({"question": "能详细解释一下第二点吗?"})  # 会记住上下文

优化3: 流式输出

实现实时响应,提升用户体验:

from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

# 创建支持流式的LLM
llm = ChatOpenAI(
    temperature=0.7,
    streaming=True,
    callbacks=[StreamingStdOutCallbackHandler()]
)

# 流式生成答案
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=vectorstore.as_retriever()
)

# 答案会实时打印
qa_chain({"query": "请详细介绍一下LangChain"})

四、最佳实践与性能优化

4.1 Prompt Engineering 最佳实践

  • 明确角色定位:告诉AI它是谁,比如"你是一个专业的技术顾问"
  • 提供示例:Few-shot提示比Zero-shot效果更好
  • 结构化输出:要求AI以特定格式输出,便于解析
  • 链式思考:让AI"一步步思考",提高复杂问题的准确性

4.2 向量数据库选择建议

数据库优势适用场景
Chroma轻量级、易上手原型开发、小规模应用
Pinecone托管服务、高性能生产环境、大规模数据
Weaviate功能丰富、支持混合检索企业级应用
FAISS本地部署、速度快对数据隐私要求高的场景

4.3 成本控制策略

  • 缓存机制:对相同问题缓存答案,减少API调用
  • 模型选择:简单任务用GPT-3.5,复杂任务用GPT-4
  • Token优化:精简提示词,控制文档片段数量
  • 异步处理:批量处理文档嵌入,提高效率

五、常见问题与解决方案

Q1: 如何处理超长文档?

使用 Map-Reduce 策略:先对每个片段生成摘要,再汇总最终答案:

from langchain.chains import MapReduceDocumentsChain

map_reduce_chain = MapReduceDocumentsChain.from_params(
    llm=llm,
    verbose=True
)

# 自动处理超长文档
result = map_reduce_chain.run(documents)

Q2: 如何提高检索准确性?

多管齐下:

  • 使用更好的文本分割策略(按语义边界分割)
  • 增加文档元数据,辅助检索
  • 使用重排序模型对检索结果二次排序
  • 调大top-k值,增加召回数量

Q3: 如何部署到生产环境?

推荐方案:

  • 使用 FastAPI 封装为REST API
  • 向量数据库独立部署,保证稳定性
  • 添加Redis缓存层,降低延迟
  • 使用Celery处理异步任务
  • 监控API调用次数和成本

总结

LangChain 作为当前最成熟的 LLM 应用开发框架,极大地降低了AI应用的开发门槛。通过本文的讲解,你应该已经掌握了:

  • LangChain的核心概念:Chains、Prompts、Memory、Agents
  • 关键组件的使用:Models、Embeddings、Vector Stores、Retrievers
  • 如何构建一个完整的RAG问答系统
  • 性能优化和最佳实践

LangChain 的优势在于其模块化设计和丰富的生态,无论你是想快速验证想法,还是构建生产级应用,都能找到合适的工具。建议从简单的链开始,逐步尝试更复杂的智能体应用,在实践中深入理解每个组件的精髓。

下一步,可以探索 LangChain 的更多高级特性,如:

  • LangSmith: 应用调试和监控平台
  • LangServe: 将链部署为API服务
  • LangGraph: 构建有状态的复杂应用

AI应用开发的大门已经打开,LangChain是你最好的入场券。祝你开发愉快!

本文链接:https://www.kkkliao.cn/?id=895 转载需授权!

分享到:

版权声明:本文由廖万里的博客发布,如需转载请注明出处。


发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。