第九章:RAG知识库开发之【LangChain 基础入门:从零构建大模型应用】

目录

前言

[一、LangChain 概述与核心架构](#一、LangChain 概述与核心架构)

[1.1 什么是 LangChain?](#1.1 什么是 LangChain?)

[1.2 六大核心模块](#1.2 六大核心模块)

[1.3 为什么需要 LangChain?](#1.3 为什么需要 LangChain?)

二、环境搭建与基础使用

[2.1 安装与环境配置](#2.1 安装与环境配置)

[2.2 第一个 LangChain 程序:基础问答](#2.2 第一个 LangChain 程序:基础问答)

[2.3 理解 LCEL:LangChain 的新一代语法](#2.3 理解 LCEL:LangChain 的新一代语法)

[2.4 流式输出实现](#2.4 流式输出实现)

[2.5 结构化输出解析](#2.5 结构化输出解析)

三、核心组件深入

[3.1 提示词模板(Prompts)](#3.1 提示词模板(Prompts))

[3.2 记忆组件(Memory)](#3.2 记忆组件(Memory))

[3.3 文档加载与处理(RAG 基础)](#3.3 文档加载与处理(RAG 基础))

[3.4 链(Chains)的进阶用法](#3.4 链(Chains)的进阶用法)

[3.5 智能体(Agents)](#3.5 智能体(Agents))

[3.6 回调系统(Callbacks)](#3.6 回调系统(Callbacks))

四、实战项目:智能客服机器人

本章练习题及答案

一、选择题(每题4分,共20分)

二、填空题(每空3分,共15分)

三、简答题(每题10分,共30分)

四、实操题(35分)

总结


前言

你是否曾想过,将大语言模型(LLM)的强大能力集成到自己的应用中,却不知从何下手?你是否在尝试调用各种 AI 接口时,被繁琐的提示词管理、上下文记忆和工具调用问题困扰?LangChain 正是为解决这些痛点而生的框架。

作为 2024-2025 年最热门的大模型应用开发框架,LangChain 已成为 AI 工程师的必备技能。它不是一个独立的模型服务,而是一个编排框架------就像乐高积木的说明书,帮你把各种"AI 积木"(模型、数据、工具)巧妙地组合起来,构建复杂的智能应用。

本文将带你从零开始,系统学习 LangChain 的核心概念与实战技巧。无论你是后端、前端还是运维工程师,只要对 AI 应用开发感兴趣,这篇文章都适合你。我们会通过大量可运行的代码示例,让你在实践中掌握这一强大工具。


一、LangChain 概述与核心架构

1.1 什么是 LangChain?

LangChain 是一个用于开发由大语言模型驱动的应用程序的开源框架。它由 Harrison Chase 于 2022 年创建,旨在简化 LLM 应用的开发流程。简单来说,LangChain 解决了三个核心问题:

  1. 标准化接口:统一调用不同提供商(OpenAI、阿里云、本地模型等)的 LLM

  2. 上下文增强:让 LLM 能够访问外部数据源(文档、数据库、API)

  3. 任务编排:将多个 LLM 调用和工具使用组合成复杂的工作流

1.2 六大核心模块

LangChain 采用模块化设计,主要包括以下六个组件:

模块 核心作用 典型应用
模型 I/O 标准化 LLM 交互接口 统一调用多模型、提示词模板化
数据增强 提升输入数据质量 文档加载、文本分割、RAG
链(Chains) 构建可复用任务流程 多步骤任务组合
记忆(Memory) 维护上下文状态 多轮对话、历史记录
智能体(Agents) 动态决策与工具调度 自动选择工具、复杂推理
回调(Callbacks) 全链路监控与可观测性 日志记录、性能分析

1.3 为什么需要 LangChain?

你可能会问:"直接调用 OpenAI API 不就行了吗?"确实,对于简单的问答,几行代码就能搞定。但当你的需求变得复杂时,LangChain 的价值就体现出来了:

  • 多轮对话:需要手动管理消息历史,LangChain 提供了开箱即用的记忆组件

  • 文档问答:需要分割文档、生成嵌入向量、检索相关片段,LangChain 封装了整个流程

  • 工具调用:需要让 LLM 决定调用哪个 API、如何传参,LangChain 的 Agent 能自动处理


二、环境搭建与基础使用

2.1 安装与环境配置

首先,创建一个干净的 Python 环境并安装必要的包:

bash 复制代码
# 创建虚拟环境(推荐)
python -m venv langchain-env
source langchain-env/bin/activate  # Linux/Mac
# langchain-env\Scripts\activate   # Windows

# 安装核心依赖
pip install langchain langchain-openai python-dotenv

注意 :截至 2025 年底,LangChain 已更新至 0.3+ 版本,旧版的 langchain.llms 等模块已移至子包。本文使用最新的 API 写法。

创建 .env 文件存放 API 密钥:

复制代码
OPENAI_API_KEY=your-api-key-here
# 如果使用国内服务,例如阿里云百炼
DASHSCOPE_API_KEY=your-dashscope-key

2.2 第一个 LangChain 程序:基础问答

让我们从一个简单的例子开始,感受 LangChain 的工作方式:

python 复制代码
# demo_01_basic_chain.py
"""
基础问答链示例
演示如何使用 LangChain 创建最简单的 LLM 调用链
"""

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.schema.runnable import RunnableSequence

# 加载环境变量
load_dotenv()

# 1. 初始化模型
# temperature=0.7 控制输出的随机性,0 最确定,1 最随机
llm = ChatOpenAI(
    model="gpt-3.5-turbo",  # 使用的模型名称
    temperature=0.7,         # 创造力程度
    api_key=os.getenv("OPENAI_API_KEY")
)

# 2. 定义提示词模板
# 使用 {topic} 作为变量占位符
prompt = PromptTemplate.from_template(
    "用一句话解释什么是{topic},要求通俗易懂。"
)

# 3. 构建链(现代写法:使用 | 管道操作符)
# 这是 LCEL (LangChain Expression Language) 的核心语法
chain = prompt | llm

# 4. 执行链
result = chain.invoke({"topic": "机器学习"})

# 输出结果
print("回答:", result.content)
print("---")
print("响应元数据:", result.response_metadata)

运行结果示例

回答: 机器学习就是让电脑通过分析大量数据自动学习规律,不需要人类一步步教它怎么做。


响应元数据: {'token_usage': {...}, 'model_name': 'gpt-3.5-turbo', ...}

2.3 理解 LCEL:LangChain 的新一代语法

2024 年后,LangChain 主推 LCEL(LangChain Expression Language) ,它使用 | 管道操作符组合组件,代码更简洁、更易读:

python 复制代码
# 旧版写法(已弃用,仅供参考)
# from langchain.chains import LLMChain
# chain = LLMChain(llm=llm, prompt=prompt)

# 新版 LCEL 写法
chain = prompt | llm | output_parser  # 可串联多个组件

LCEL 的核心优势

  • 函数式组合 :用 | 直观表达数据流

  • 自动并行:多个独立分支可自动并行执行

  • 原生流式支持.stream() 方法直接可用

  • 完美集成 LangSmith:自动记录每一步的执行细节

2.4 流式输出实现

流式输出能显著提升用户体验,让用户感觉模型在"实时思考":

python 复制代码
# demo_02_streaming.py
"""
流式输出示例
让模型一个字一个字地返回结果,提升交互体验
"""

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate

load_dotenv()

llm = ChatOpenAI(
    model="gpt-3.5-turbo",
    temperature=0.8,
    streaming=True  # 开启流式输出
)

prompt = PromptTemplate.from_template("请写一首关于编程的短诗,主题是{topic}")

chain = prompt | llm

print("=== 流式输出效果 ===\n")
# 使用 stream 方法逐块获取输出
for chunk in chain.stream({"topic": "Python"}):
    # chunk.content 是每次返回的文本片段
    print(chunk.content, end="", flush=True)
print("\n\n=== 输出完成 ===")

2.5 结构化输出解析

很多时候,我们希望 LLM 返回格式化的数据(如 JSON),便于程序处理:

python 复制代码
# demo_03_structured_output.py
"""
结构化输出示例
让 LLM 返回 JSON 格式的数据,便于程序解析
"""

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.output_parsers import StructuredOutputParser, ResponseSchema

load_dotenv()

# 1. 定义期望的输出格式
response_schemas = [
    ResponseSchema(name="name", description="菜品的名称"),
    ResponseSchema(name="ingredients", description="主要食材列表,用数组表示"),
    ResponseSchema(name="cooking_time", description="烹饪时间(分钟)"),
    ResponseSchema(name="difficulty", description="难度等级:简单/中等/困难")
]

# 创建输出解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

# 2. 获取格式化指令(会自动生成要求 LLM 输出 JSON 的提示)
format_instructions = output_parser.get_format_instructions()

# 3. 构建包含格式要求的提示词
prompt = PromptTemplate(
    template="""请推荐一道{cuisine}菜系的家常菜。

{format_instructions}

请直接返回 JSON,不要有其他内容。""",
    input_variables=["cuisine"],
    partial_variables={"format_instructions": format_instructions}
)

# 4. 执行并解析
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.5)
chain = prompt | llm | output_parser

result = chain.invoke({"cuisine": "川菜"})

print("解析后的结构化数据:")
print(f"菜名: {result['name']}")
print(f"食材: {', '.join(result['ingredients'])}")
print(f"烹饪时间: {result['cooking_time']}分钟")
print(f"难度: {result['difficulty']}")

三、核心组件深入

3.1 提示词模板(Prompts)

提示词模板是 LangChain 的基础组件,它帮助我们将用户输入和固定指令组合成完整的提示词。

python 复制代码
# demo_04_prompt_templates.py
"""
提示词模板的高级用法
演示 Few-shot 学习和动态模板
"""

from langchain.prompts import (
    PromptTemplate,
    FewShotPromptTemplate,
    ChatPromptTemplate,
    MessagesPlaceholder
)
from langchain_openai import ChatOpenAI

# 示例 1:Few-shot 学习模板
examples = [
    {"question": "什么是 Python?", "answer": "Python 是一种解释型、面向对象的高级编程语言。"},
    {"question": "什么是 Django?", "answer": "Django 是 Python 的一个 Web 框架,遵循 MVC 设计模式。"}
]

example_template = """
问题: {question}
回答: {answer}
"""

example_prompt = PromptTemplate(
    input_variables=["question", "answer"],
    template=example_template
)

few_shot_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix="你是一个技术专家,请用专业但易懂的方式回答问题。\n\n示例:",
    suffix="\n问题: {input}\n回答:",
    input_variables=["input"]
)

# 测试 Few-shot 模板
print("=== Few-shot 模板效果 ===")
print(few_shot_prompt.format(input="什么是 FastAPI?"))

# 示例 2:对话式提示词模板(包含历史消息占位符)
chat_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的助手,名字叫{bot_name}。"),
    MessagesPlaceholder(variable_name="history"),  # 动态插入历史消息
    ("human", "{input}"),
])

print("\n=== 对话式模板 ===")
print(chat_prompt.format(bot_name="小助手", input="你好"))

3.2 记忆组件(Memory)

记忆是构建对话式 AI 的关键。LangChain 提供了多种记忆策略:

python 复制代码
# demo_05_memory.py
"""
记忆组件完整示例
演示多种记忆策略的使用
"""

from langchain.memory import (
    ConversationBufferMemory,
    ConversationBufferWindowMemory,
    ConversationTokenBufferMemory,
    ConversationSummaryMemory
)
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.schema.runnable import RunnableLambda
import os
from dotenv import load_dotenv

load_dotenv()

llm = ChatOpenAI(model="gpt-3.5-turbo")

# ========== 1. 缓冲区记忆(保留全部历史)==========
buffer_memory = ConversationBufferMemory(
    return_messages=True,
    memory_key="history"
)

# 添加对话示例
buffer_memory.save_context(
    {"input": "我叫张三"},
    {"output": "你好张三!很高兴认识你。"}
)
buffer_memory.save_context(
    {"input": "我喜欢编程"},
    {"output": "太棒了!编程是一项很有价值的技能。"}
)

print("=== 缓冲区记忆内容 ===")
print(buffer_memory.load_memory_variables({}))

# ========== 2. 窗口记忆(只保留最近 K 轮)==========
window_memory = ConversationBufferWindowMemory(
    k=2,  # 只保留最近 2 轮对话
    return_messages=True
)

# ========== 3. Token 限制记忆(基于 token 数量截断)==========
# 适用于控制 API 调用成本
token_memory = ConversationTokenBufferMemory(
    llm=llm,
    max_token_limit=100,  # 最多保留 100 个 token
    return_messages=True
)

# ========== 4. 摘要记忆(自动生成对话摘要)==========
summary_memory = ConversationSummaryMemory(
    llm=llm,
    return_messages=True
)

summary_memory.save_context(
    {"input": "我想预订一张去北京的机票"},
    {"output": "好的,请问您什么时候出发?"}
)
summary_memory.save_context(
    {"input": "下周一早上"},
    {"output": "已为您查询下周一早上飞往北京的航班。"}
)

print("\n=== 摘要记忆自动生成的摘要 ===")
print(summary_memory.load_memory_variables({}))

# ========== 5. 在链中使用记忆 ==========
# 创建包含记忆的对话链
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的助手。"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

# 使用 RunnableLambda 包装记忆加载
def load_memory(input_dict):
    history = buffer_memory.load_memory_variables({})
    return {
        "history": history.get("history", []),
        "input": input_dict["input"]
    }

chain = RunnableLambda(load_memory) | prompt | llm

print("\n=== 使用记忆的对话 ===")
result = chain.invoke({"input": "我叫什么名字?"})
print(result.content)
# 输出会正确回答:张三

3.3 文档加载与处理(RAG 基础)

RAG(检索增强生成)是 LangChain 最强大的应用场景之一。它让 LLM 能够基于你自己的文档回答问题。

python 复制代码
# demo_06_rag_basic.py
"""
RAG 基础示例
从本地文档构建知识库,实现文档问答
需要先安装: pip install langchain-community chromadb pypdf
"""

import os
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.document_loaders import TextLoader, PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA

load_dotenv()

# 1. 加载文档(支持多种格式)
# 示例:加载文本文件
loader = TextLoader("knowledge.txt", encoding="utf-8")
# 如果是 PDF: loader = PyPDFLoader("document.pdf")
documents = loader.load()

print(f"加载了 {len(documents)} 个文档片段")

# 2. 文本分割
# 由于 LLM 有上下文长度限制,需要将长文档切分成小块
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # 每个块的最大字符数
    chunk_overlap=50,    # 块之间的重叠字符数(保持上下文连贯)
    separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""]
)

chunks = text_splitter.split_documents(documents)
print(f"文档被分割成 {len(chunks)} 个块")

# 3. 创建向量嵌入
# 将文本转换为向量,便于语义检索
embeddings = OpenAIEmbeddings()

# 4. 创建向量数据库(使用 Chroma)
# 向量数据库会存储文本块及其对应的向量表示
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db"  # 持久化存储路径
)

print("向量数据库创建完成")

# 5. 创建检索器
retriever = vectorstore.as_retriever(
    search_kwargs={"k": 3}  # 每次检索返回最相关的 3 个块
)

# 6. 构建检索问答链
qa_chain = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(model="gpt-3.5-turbo", temperature=0),
    chain_type="stuff",  # stuff: 将所有检索结果放入上下文
    retriever=retriever,
    return_source_documents=True,  # 返回引用的文档片段
    verbose=True
)

# 7. 提问
question = "文档中提到了哪些关键技术?"
result = qa_chain.invoke({"query": question})

print(f"\n问题: {question}")
print(f"回答: {result['result']}")
print(f"\n引用来源: {len(result['source_documents'])} 个片段")

知识库文件示例(knowledge.txt)

LangChain 是一个用于开发大语言模型应用的框架,于 2022 年发布。

它的核心组件包括模型 I/O、检索、链、记忆和智能体。

RAG(检索增强生成)是 LangChain 的重要功能,它允许 LLM 访问外部知识库。

RAG 的流程包括:文档加载、文本分割、向量化、检索和生成。

LCEL(LangChain 表达式语言)是 2024 年推出的新语法,使用管道操作符组合组件。

3.4 链(Chains)的进阶用法

链可以将多个步骤组合成一个可复用的工作流:

python 复制代码
# demo_07_chains_advanced.py
"""
链的高级用法
演示顺序链、条件链和自定义链
"""

from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.schema.runnable import RunnableLambda, RunnableBranch
from langchain_core.output_parsers import StrOutputParser
import os
from dotenv import load_dotenv

load_dotenv()

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.5)
parser = StrOutputParser()

# ========== 1. 顺序链:多步骤处理 ==========
# 场景:用户评价 → 情感分析 → 生成回复

# 步骤1:情感分析
sentiment_prompt = PromptTemplate.from_template(
    "分析以下用户评价的情感是正面、负面还是中性:\n'{review}'\n只输出:正面/负面/中性"
)
sentiment_chain = sentiment_prompt | llm | parser

# 步骤2:根据情感生成回复
reply_prompt = PromptTemplate.from_template(
    """用户评价的情感是:{sentiment}
请根据情感生成一个合适的客服回复。
原评价:{review}
回复:"""
)

def prepare_reply_input(review):
    sentiment = sentiment_chain.invoke({"review": review})
    return {"sentiment": sentiment, "review": review}

reply_chain = (
    RunnableLambda(prepare_reply_input) 
    | reply_prompt 
    | llm 
    | parser
)

# 测试
review_text = "产品质量很好,但发货速度太慢了!"
print("=== 顺序链处理结果 ===")
print(f"原始评价: {review_text}")
print(f"客服回复: {reply_chain.invoke(review_text)}")

# ========== 2. 条件分支链 ==========
# 根据问题的类型路由到不同的处理流程

# 定义不同场景的处理函数
def handle_math(question):
    prompt = PromptTemplate.from_template("解决这个数学问题:{question}")
    chain = prompt | llm | parser
    return chain.invoke({"question": question})

def handle_general(question):
    prompt = PromptTemplate.from_template("回答这个问题:{question}")
    chain = prompt | llm | parser
    return chain.invoke({"question": question})

# 分类器:判断问题类型
classifier_prompt = PromptTemplate.from_template(
    "判断以下问题属于 'math' 还是 'general':\n'{question}'\n只输出一个词:"
)
classifier_chain = classifier_prompt | llm | parser

# 构建条件路由
def route_question(question):
    category = classifier_chain.invoke({"question": question}).strip().lower()
    if "math" in category:
        return handle_math(question)
    else:
        return handle_general(question)

print("\n=== 条件分支链示例 ===")
print("数学问题:", route_question("一个苹果5元,买3个需要多少钱?"))
print("通用问题:", route_question("什么是人工智能?"))

# ========== 3. 并行执行 ==========
# 同时执行多个独立任务,提升效率
from langchain.schema.runnable import RunnableParallel

# 定义多个并行的处理分支
parallel_chain = RunnableParallel(
    summary=(
        PromptTemplate.from_template("用一句话总结:{text}") 
        | llm | parser
    ),
    keywords=(
        PromptTemplate.from_template("提取3个关键词:{text},用逗号分隔") 
        | llm | parser
    ),
    sentiment=(
        PromptTemplate.from_template("判断情感极性(正面/负面/中性):{text}") 
        | llm | parser
    )
)

text = "LangChain框架非常好用,它大大简化了LLM应用的开发流程,强烈推荐!"
print("\n=== 并行处理结果 ===")
results = parallel_chain.invoke({"text": text})
for key, value in results.items():
    print(f"{key}: {value}")

3.5 智能体(Agents)

智能体是 LangChain 最强大的特性------它让 LLM 能够自主决定调用哪些工具、以什么顺序执行:

python 复制代码
# demo_08_agent_basic.py
"""
智能体基础示例
让 LLM 自主选择和使用工具
需要安装: pip install langchain-community wikipedia
"""

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain.tools import Tool
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from langchain.prompts import PromptTemplate
import math

load_dotenv()

# 1. 定义工具

# 工具1:计算器
def calculator(expression: str) -> str:
    """执行数学计算,输入应为数学表达式"""
    try:
        # 安全地评估数学表达式
        result = eval(expression, {"__builtins__": {}}, {"math": math})
        return f"计算结果: {result}"
    except Exception as e:
        return f"计算错误: {e}"

# 工具2:维基百科查询
wikipedia = WikipediaQueryRun(
    api_wrapper=WikipediaAPIWrapper(top_k_results=1, lang="zh")
)

# 工具3:自定义 API 模拟
def get_current_time(query: str) -> str:
    """获取当前时间"""
    from datetime import datetime
    return f"当前时间是: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"

# 将函数包装成 Tool 对象
tools = [
    Tool(
        name="Calculator",
        func=calculator,
        description="执行数学计算。输入应为数学表达式,如 '2+3*4'"
    ),
    Tool(
        name="Wikipedia",
        func=wikipedia.run,
        description="查询维基百科获取知识"
    ),
    Tool(
        name="GetTime",
        func=get_current_time,
        description="获取当前日期和时间"
    )
]

# 2. 创建智能体
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# ReAct 提示词模板
agent_prompt = PromptTemplate.from_template("""
你是一个智能助手,可以调用工具来回答问题。

你有以下工具可用:
{tools}

工具名称:{tool_names}

回复格式(必须严格遵守):
Question: 用户的问题
Thought: 思考是否需要使用工具
Action: 要使用的工具名称(必须是 [{tool_names}] 之一)
Action Input: 工具的输入参数
Observation: 工具返回的结果
...(可以重复 Thought/Action/Action Input/Observation 多次)
Thought: 我现在知道答案了
Final Answer: 最终回答

开始!

Question: {input}
Thought: {agent_scratchpad}
""")

agent = create_react_agent(
    llm=llm,
    tools=tools,
    prompt=agent_prompt
)

# 创建执行器
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,  # 打印执行过程
    max_iterations=5,  # 最多执行5步
    handle_parsing_errors=True
)

# 3. 测试智能体
print("=== 智能体测试 ===\n")

queries = [
    "请帮我计算 123 * 456 等于多少?",
    "现在几点了?",
    "请介绍一下 Python 编程语言,然后计算 100 除以 3 等于多少",
]

for query in queries:
    print(f"\n用户: {query}")
    print("智能体执行过程:")
    result = agent_executor.invoke({"input": query})
    print(f"\n最终回答: {result['output']}")
    print("-" * 50)

3.6 回调系统(Callbacks)

回调系统让你能够监控 LLM 应用的运行状态:

python 复制代码
# demo_09_callbacks.py
"""
回调系统示例
用于监控、日志记录和调试
"""

from langchain.callbacks.base import BaseCallbackHandler
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.schema.runnable import RunnableSequence
from typing import Dict, Any, List
import time

class CustomCallbackHandler(BaseCallbackHandler):
    """自定义回调处理器"""
    
    def __init__(self):
        self.step_times = []
        self.current_step_start = None
    
    def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs):
        """LLM 开始调用时触发"""
        self.current_step_start = time.time()
        print(f"\n[LLM 开始] 提示词长度: {len(prompts[0])} 字符")
    
    def on_llm_end(self, response, **kwargs):
        """LLM 调用结束时触发"""
        elapsed = time.time() - self.current_step_start
        self.step_times.append(elapsed)
        print(f"[LLM 结束] 耗时: {elapsed:.2f} 秒")
        
        # 输出 token 使用情况
        if hasattr(response, 'llm_output') and response.llm_output:
            token_usage = response.llm_output.get('token_usage', {})
            print(f"[Token 统计] 输入: {token_usage.get('prompt_tokens', 0)}, "
                  f"输出: {token_usage.get('completion_tokens', 0)}")
    
    def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs):
        """链开始执行时触发"""
        print(f"\n[链开始] 输入: {inputs}")
    
    def on_chain_end(self, outputs: Dict[str, Any], **kwargs):
        """链执行结束时触发"""
        print(f"[链结束] 输出: {outputs}")
    
    def on_llm_error(self, error: Exception, **kwargs):
        """LLM 调用出错时触发"""
        print(f"[错误] LLM 调用失败: {error}")

# 使用回调
callback = CustomCallbackHandler()

llm = ChatOpenAI(
    model="gpt-3.5-turbo",
    temperature=0,
    callbacks=[callback]  # 注册回调
)

prompt = PromptTemplate.from_template("用一句话解释什么是{concept}")
chain = prompt | llm

print("=== 带监控的链执行 ===")
result = chain.invoke({"concept": "区块链"})
print(f"\n回答: {result.content}")

print(f"\n=== 执行统计 ===")
print(f"总 LLM 调用次数: {len(callback.step_times)}")
print(f"平均耗时: {sum(callback.step_times)/len(callback.step_times):.2f} 秒")

四、实战项目:智能客服机器人

结合所学知识,让我们构建一个完整的智能客服机器人:

python 复制代码
# demo_10_customer_service_bot.py
"""
完整实战项目:智能客服机器人
整合了记忆、RAG、工具调用等核心功能
"""

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.memory import ConversationBufferMemory
from langchain.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import TextLoader
from langchain.tools import Tool
from langchain.agents import create_react_agent, AgentExecutor
from langchain.schema.runnable import RunnableWithMessageHistory
import json
from datetime import datetime

load_dotenv()

class CustomerServiceBot:
    """智能客服机器人"""
    
    def __init__(self, knowledge_base_path: str = None):
        self.llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
        self.memory = ConversationBufferMemory(
            return_messages=True,
            memory_key="chat_history"
        )
        
        # 初始化知识库(如果提供)
        self.retriever = None
        if knowledge_base_path and os.path.exists(knowledge_base_path):
            self._init_knowledge_base(knowledge_base_path)
        
        # 定义工具
        self.tools = self._create_tools()
        
        # 创建智能体
        self.agent = self._create_agent()
    
    def _init_knowledge_base(self, path: str):
        """初始化知识库"""
        # 加载文档
        loader = TextLoader(path, encoding="utf-8")
        documents = loader.load()
        
        # 文本分割
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=500,
            chunk_overlap=50
        )
        chunks = text_splitter.split_documents(documents)
        
        # 创建向量数据库
        embeddings = OpenAIEmbeddings()
        vectorstore = Chroma.from_documents(
            documents=chunks,
            embedding=embeddings,
            persist_directory="./customer_service_db"
        )
        
        self.retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
        print(f"[初始化] 知识库已加载,包含 {len(chunks)} 个文档块")
    
    def _create_tools(self):
        """创建客服工具"""
        
        def check_order_status(order_id: str) -> str:
            """查询订单状态"""
            # 模拟订单查询
            orders = {
                "12345": "已发货,预计3天内送达",
                "67890": "已签收",
                "11111": "处理中"
            }
            status = orders.get(order_id.strip(), "未找到该订单,请确认订单号")
            return f"订单 {order_id} 状态: {status}"
        
        def calculate_refund(amount: str) -> str:
            """计算退款金额"""
            try:
                refund = float(amount) * 0.9  # 假设90%退款
                return f"预计退款金额: {refund:.2f} 元"
            except:
                return "金额格式错误,请输入数字"
        
        def get_support_hours(query: str) -> str:
            """获取客服工作时间"""
            return "客服工作时间:周一至周日 9:00-21:00(节假日照常服务)"
        
        tools = [
            Tool(
                name="CheckOrder",
                func=check_order_status,
                description="查询订单状态。输入订单号,如 '12345'"
            ),
            Tool(
                name="CalculateRefund",
                func=calculate_refund,
                description="计算退款金额。输入原支付金额"
            ),
            Tool(
                name="GetSupportHours",
                func=get_support_hours,
                description="获取客服工作时间"
            )
        ]
        
        # 如果知识库可用,添加知识库检索工具
        if self.retriever:
            def search_knowledge(query: str) -> str:
                docs = self.retriever.get_relevant_documents(query)
                if docs:
                    return "\n".join([doc.page_content for doc in docs[:2]])
                return "未找到相关信息"
            
            tools.append(Tool(
                name="SearchKnowledgeBase",
                func=search_knowledge,
                description="搜索产品知识库,回答产品相关问题"
            ))
        
        return tools
    
    def _create_agent(self):
        """创建智能体"""
        agent_prompt = PromptTemplate.from_template("""
        你是一个专业的客服机器人,名字叫"小智"。
        
        你的职责:
        1. 友好、耐心地回答用户问题
        2. 使用工具查询订单、计算退款、获取工作时间
        3. 对于不确定的问题,诚实地说"我需要转接人工客服"
        
        可用工具:
        {tools}
        
        工具名称:{tool_names}
        
        回复格式:
        Question: 用户问题
        Thought: 分析需要什么信息
        Action: 工具名称(如果需要)
        Action Input: 工具参数
        Observation: 工具返回
        (可以重复上述步骤)
        Thought: 我已经有足够信息了
        Final Answer: 最终回复
        
        开始对话!
        
        Previous conversation:
        {chat_history}
        
        Question: {input}
        Thought: {agent_scratchpad}
        """)
        
        agent = create_react_agent(
            llm=self.llm,
            tools=self.tools,
            prompt=agent_prompt
        )
        
        return AgentExecutor(
            agent=agent,
            tools=self.tools,
            verbose=False,
            max_iterations=3,
            handle_parsing_errors=True
        )
    
    def chat(self, user_input: str) -> str:
        """处理用户消息"""
        # 保存用户输入到记忆
        self.memory.save_context({"input": user_input}, {"output": ""})
        
        # 获取历史记录
        history = self.memory.load_memory_variables({})
        
        # 调用智能体
        result = self.agent.invoke({
            "input": user_input,
            "chat_history": history.get("chat_history", "")
        })
        
        response = result["output"]
        
        # 保存回复到记忆
        self.memory.save_context({"input": user_input}, {"output": response})
        
        return response
    
    def clear_memory(self):
        """清空对话历史"""
        self.memory.clear()
        print("[系统] 对话历史已清空")

# 测试客服机器人
def main():
    # 创建知识库文件(示例)
    knowledge_content = """
    产品特性:
    1. 7天无理由退货
    2. 1年质保服务
    3. 全国联保,支持上门维修
    
    常见问题:
    Q: 如何申请退货?
    A: 在订单页面点击申请售后,填写退货原因即可。
    
    Q: 质保期内维修需要费用吗?
    A: 非人为损坏的故障,质保期内免费维修。
    """
    
    with open("knowledge_base.txt", "w", encoding="utf-8") as f:
        f.write(knowledge_content)
    
    # 初始化机器人
    bot = CustomerServiceBot(knowledge_base_path="knowledge_base.txt")
    
    print("=" * 50)
    print("智能客服机器人已启动")
    print("输入 'quit' 退出,输入 'clear' 清空对话")
    print("=" * 50)
    
    while True:
        user_input = input("\n用户: ").strip()
        
        if user_input.lower() == 'quit':
            print("客服机器人: 感谢您的咨询,再见!")
            break
        elif user_input.lower() == 'clear':
            bot.clear_memory()
            print("客服机器人: 对话历史已清空")
            continue
        elif not user_input:
            continue
        
        response = bot.chat(user_input)
        print(f"客服机器人: {response}")

if __name__ == "__main__":
    main()

本章练习题及答案

一、选择题(每题4分,共20分)

1. LangChain 中 LCEL 的核心语法是什么?

  • A. chain = chain1 + chain2

  • B. chain = chain1 >> chain2

  • C. chain = prompt | model | parser

  • D. chain = chain1.and_then(chain2)

答案:C

解析:LCEL(LangChain Expression Language)使用管道操作符 | 来组合组件,这是现代 LangChain 的标准写法。

2. 以下哪个记忆组件会自动生成对话摘要来减少 token 消耗?

  • A. ConversationBufferMemory

  • B. ConversationBufferWindowMemory

  • C. ConversationTokenBufferMemory

  • D. ConversationSummaryMemory

答案:D

解析:ConversationSummaryMemory 会使用 LLM 自动生成对话摘要,用摘要替代完整历史,适合长对话场景。

3. 在 RAG 流程中,文本分割的 chunk_overlap 参数作用是什么?

  • A. 控制分割块的数量

  • B. 控制块之间的重叠字符数,保持上下文连贯

  • C. 控制块的最大长度

  • D. 控制向量维度

答案:B

解析:chunk_overlap 让相邻文本块之间有重叠内容,避免在分割边界处丢失关键上下文。

4. 以下哪种 Chain 类型最适合让 LLM 自主决定调用哪个工具?

  • A. SequentialChain

  • B. Agent

  • C. LLMChain

  • D. SimpleSequentialChain

答案:B

解析:Agent(智能体)可以动态决策,根据用户输入选择使用哪些工具、按什么顺序执行。

5. 关于 LangChain 回调系统,下列说法正确的是?

  • A. 只能用于错误处理

  • B. 可以监控 LLM 调用、链执行的各个阶段

  • C. 只能在同步模式下使用

  • D. 需要付费才能使用

答案:B

解析:回调系统提供 on_llm_start、on_llm_end、on_chain_start 等多个钩子,可以全方位监控执行过程。

二、填空题(每空3分,共15分)

  1. LangChain 的六大核心模块包括:模型 I/O、数据增强、++记忆++、智能体和回调处理器。

  2. 在 LCEL 中,使用 | 操作符连接组件,最后调用 ++invoke++ 方法执行链。

  3. 对话式提示词模板中,使用 ++MessagesPlaceholder++ 作为历史消息的动态占位符。

  4. 向量数据库中,用于衡量文本语义相似度的技术称为 ++embedding++(嵌入/向量化)。

  5. 智能体的 ReAct 模式中,"Re" 代表推理(Reasoning),"Act" 代表 ++行动(Acting)++。

三、简答题(每题10分,共30分)

1. 请简述 RAG(检索增强生成)的工作流程及其解决了 LLM 的什么问题?

参考答案:

RAG 的工作流程包括 5 个步骤:

  1. 文档加载:从 PDF、TXT 等源加载文档

  2. 文本分割:将长文档切分成适当大小的块

  3. 向量化:使用嵌入模型将文本块转换为向量

  4. 检索:根据用户问题,从向量库中检索最相关的文本块

  5. 生成:将检索结果作为上下文,连同用户问题一起提交给 LLM 生成回答

RAG 解决了 LLM 的三个核心问题:

  • 知识时效性:LLM 训练数据有截止日期,RAG 可接入实时数据

  • 幻觉问题:通过提供真实上下文,减少模型捏造信息

  • 私有数据访问:让 LLM 能够基于企业内部文档回答问题,而不需要重新训练

2. 请对比 ConversationBufferMemory 和 ConversationBufferWindowMemory 的区别及适用场景。

参考答案:

特性 BufferMemory WindowMemory
存储策略 保存全部对话历史 只保留最近 K 轮
Token 消耗 随时间线性增长 恒定(受 K 控制)
长期记忆 保留所有信息 会遗忘早期对话
适用场景 短对话、需要完整上下文 长对话、控制成本

适用场景

  • BufferMemory:适合客服诊断、技术支持等需要完整上下文的场景

  • WindowMemory:适合日常闲聊、信息查询等早期对话相关性低的场景

3. 解释 Agent 中的 ReAct 模式及其工作流程。

参考答案:

ReAct 模式结合了推理 (Reasoning)和行动(Acting),让 LLM 能够交替进行思考和执行操作。

工作流程:

python 复制代码
Question → Thought → Action → Observation → Thought → ... → Final Answer
  1. Thought:LLM 分析当前状态,决定下一步做什么

  2. Action:选择一个工具并给出输入参数

  3. Observation:执行工具并返回结果

  4. 重复:根据观察结果继续思考,直到获得足够信息

  5. Final Answer:给出最终回答

例如用户问"今天北京天气如何",Agent 会:思考需要查询天气 → 执行搜索工具 → 观察结果 → 整理回答。

四、实操题(35分)

题目:构建一个多轮对话的 RAG 问答系统

要求

  1. 使用 LangChain 构建一个支持多轮对话的问答系统

  2. 系统需包含:文档加载、向量存储、对话记忆

  3. 实现一个自定义的对话检索链(ConversationalRetrievalChain)

  4. 提供完整的代码和注释

参考答案

python 复制代码
"""
实操题参考答案:多轮对话 RAG 系统
"""
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
from langchain.prompts import PromptTemplate

load_dotenv()

# 1. 准备知识库数据
knowledge = """
LangChain 是一个用于开发大语言模型应用的框架,于 2022 年发布。
它的主要组件包括:模型 I/O、检索、链、记忆和智能体。

RAG 是检索增强生成的缩写,它通过检索外部知识来增强 LLM 的回答质量。
RAG 特别适合构建基于私有文档的问答系统。

LCEL 是 LangChain Expression Language 的缩写,它使用 | 管道操作符来组合组件。
LCEL 支持并行执行、流式输出和自动追踪等特性。

Memory 模块用于在对话之间维护状态。
常用的记忆类型包括:BufferMemory、WindowMemory、SummaryMemory 和 VectorStoreMemory。
"""

with open("qa_knowledge.txt", "w", encoding="utf-8") as f:
    f.write(knowledge)

# 2. 构建 RAG 组件
def build_rag_system():
    # 加载文档
    loader = TextLoader("qa_knowledge.txt", encoding="utf-8")
    documents = loader.load()
    
    # 文本分割
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=300,
        chunk_overlap=50
    )
    chunks = text_splitter.split_documents(documents)
    print(f"文档已分割为 {len(chunks)} 个块")
    
    # 创建向量存储
    embeddings = OpenAIEmbeddings()
    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=embeddings,
        persist_directory="./chat_qa_db"
    )
    
    # 创建记忆(返回消息格式)
    memory = ConversationBufferMemory(
        memory_key="chat_history",
        return_messages=True,
        output_key="answer"  # 指定输出键
    )
    
    # 自定义提示词模板
    custom_prompt = PromptTemplate.from_template("""
    你是一个知识库助手。请基于以下上下文回答用户问题。
    
    对话历史:
    {chat_history}
    
    上下文:
    {context}
    
    用户问题:{question}
    
    回答要求:
    1. 如果上下文中没有相关信息,请诚实地说"我不知道"
    2. 回答要简洁、准确
    3. 可以引用对话历史中的信息
    
    回答:
    """)
    
    # 创建对话检索链
    qa_chain = ConversationalRetrievalChain.from_llm(
        llm=ChatOpenAI(model="gpt-3.5-turbo", temperature=0),
        retriever=vectorstore.as_retriever(search_kwargs={"k": 2}),
        memory=memory,
        combine_docs_chain_kwargs={"prompt": custom_prompt},
        verbose=True
    )
    
    return qa_chain

# 3. 交互式对话
def main():
    print("=" * 50)
    print("多轮对话 RAG 问答系统")
    print("知识库内容:LangChain、RAG、LCEL、Memory")
    print("输入 'quit' 退出,输入 'clear' 清空记忆")
    print("=" * 50)
    
    chain = build_rag_system()
    
    while True:
        question = input("\n用户: ").strip()
        
        if question.lower() == 'quit':
            print("再见!")
            break
        elif question.lower() == 'clear':
            chain.memory.clear()
            print("对话记忆已清空")
            continue
        elif not question:
            continue
        
        # 调用链
        result = chain.invoke({"question": question})
        print(f"助手: {result['answer']}")
        
        # 可选:显示检索到的来源
        if 'source_documents' in result:
            print(f"\n(参考了 {len(result['source_documents'])} 个文档片段)")

if __name__ == "__main__":
    main()

预期交互示例

用户: LangChain 是什么?

助手: LangChain 是一个用于开发大语言模型应用的框架,于 2022 年发布。

用户: 它的主要组件有哪些?

助手: LangChain 的主要组件包括:模型 I/O、检索、链、记忆和智能体。

用户: 那 RAG 呢?

助手: RAG 是检索增强生成的缩写,它通过检索外部知识来增强 LLM 的回答质量,特别适合构建基于私有文档的问答系统。


总结

本文系统介绍了 LangChain 框架的核心概念与实践技巧。从环境搭建到 RAG 应用,从记忆管理到智能体构建,我们通过大量可运行的代码示例,展示了这一强大框架的使用方法。

关键收获

  1. 模块化思维:LangChain 将复杂的 LLM 应用拆解为可组合的组件,理解每个组件的职责是高效开发的基础

  2. LCEL 为核心 :掌握 | 管道语法是使用现代 LangChain 的必备技能

  3. RAG 是标配:让 LLM 访问外部知识是解决幻觉和时效性问题的关键

  4. 记忆需谨慎:不同场景选择合适的记忆策略,平衡上下文完整性和成本

  5. 智能体是未来:赋予 LLM 使用工具的能力,开启了自动化任务的新可能

进阶学习路径

  • 深入 LangGraph:学习基于图的工作流编排

  • 掌握 LangSmith:生产级应用的调试与监控

  • 探索多模态:结合图像、音频等多模态输入

  • 优化性能:缓存、流式输出、异步调用

随着大模型技术的持续演进,LangChain 也在快速迭代。建议读者多关注官方文档和社区动态,保持学习的热情。AI 应用开发的门槛从未如此之低,希望本文能帮助你迈出坚实的第一步!


🌟 感谢您耐心阅读到这里!

🚀 技术成长没有捷径,但每一次的阅读、思考和实践,都在默默缩短您与成功的距离。

💡 如果本文对您有所启发,欢迎点赞👍、收藏📌、分享📤给更多需要的伙伴!

🗣️ 期待在评论区看到您的想法、疑问或建议,我会认真回复,让我们共同探讨、一起进步~

🔔 关注我,持续获取更多干货内容!

🤗 我们下篇文章见!

相关推荐
TechExplorer3652 小时前
Claude Code 最佳实践指南
ai·claude code
gao_tjie4 小时前
OpenAI Images Edits API 的应用与使用
ai
一只小阿乐4 小时前
vue前端处理流式数据
前端·javascript·ai·大模型·全栈开发·agentai
企业架构师老王4 小时前
企业级AI Agent工具功能差异深度对比:架构师视角的选型逻辑与提效实战
人工智能·ai
java资料站4 小时前
第06章:LangChain使用之Tools
microsoft·langchain
真心喜欢你吖4 小时前
OpenClaw安装部署Mac操作系统版 - 打造你的专属AI助理
java·人工智能·macos·ai·语言模型·智能体·openclaw
chools4 小时前
Java后端拥抱AI开发之个人学习路线 - - Spring AI【第二期】
java·人工智能·学习·spring·ai
程序员夏末5 小时前
【AI Agent基础 | 第五篇】简析MCP(模型上下文协议)
人工智能·ai·ai agent
Agent产品评测局5 小时前
保险行业自动化工具选型,核保理赔全流程优化:2026年大模型Agent重塑数智金融新基座
大数据·人工智能·ai·金融·自动化