第 23 期 | 长历史上下文优化:Context Window 挤压战

⏱ 预计阅读 18 分钟 更新于 2026/5/7
💡 进群学习加 wx: agentupdate
(申请发送: agentupdate)

🎯 本期学习目标

嘿,各位未来的 AI 大师们!欢迎来到《LangChain 全栈大师课》的第一站。我知道你们都迫不及待想用 AI 搞点大事情了,别急,罗马不是一天建成的,智能客服也不是一蹴而就的。本期,我们将打下最坚实的基础,让你:

  • 理解 LangChain 的核心价值和定位: 为什么我们需要这个框架,它解决了 LLM 应用开发的哪些痛点?
  • 掌握 LangChain 的基本构成模块: 深入剖析 LLMs、PromptTemplates 和 Output Parsers 这三大基石。
  • 学会构建一个最简的智能客服应答骨架: 让你的客服小助手从“零”开始,能够进行初步的对话。
  • 熟悉 LangChain 的基础开发流程: 为后续更复杂、更强大的功能迭代铺平道路。

准备好了吗?系好安全带,我们即将启程!

📖 原理解析

Alright,各位,先来点“道”上的东西。在深入代码之前,我们得先搞清楚我们到底在玩什么,以及为什么 LangChain 是这个游戏的“外挂”。

LangChain 是什么?为什么是它?

简单来说,LangChain 就是一个用于开发基于大语言模型(LLMs)的应用程序的框架。你可能会问,我直接调用 OpenAI 的 API 不香吗?香!但当你开始构建复杂的 LLM 应用时,你会发现仅仅调用 API 远远不够。

想象一下,你的智能客服小助手需要:

  1. 接收用户提问。
  2. 将提问包装成一个清晰的指令(Prompt),告诉 LLM 它应该扮演什么角色,回答什么问题。
  3. 等待 LLM 的回复。
  4. 将 LLM 原始、可能有点随意的回复,转换成我们系统能理解、能处理的结构化数据。
  5. 在必要时,还需要调用外部工具、访问知识库、记住之前的对话等等。

这些操作,如果你每次都手动拼接字符串、手动解析 JSON、手动管理状态,那简直就是一场噩梦!你的代码会变得像一团意大利面,难以维护,更别提扩展了。

LangChain 的核心价值就在于:

  • 抽象化: 它为不同的 LLM(OpenAI, Anthropic, 开源模型等)提供了统一的接口,让你轻松切换。
  • 模块化: 将 LLM 应用的各个组成部分(如模型、提示词、输出解析器、记忆、工具等)解耦成独立的模块。
  • 可组合性: 最关键的一点!你可以像搭乐高一样,把这些模块串联起来,形成复杂的逻辑链(Chain),构建出强大且可扩展的 AI 应用。

所以,LangChain 就像是 LLM 应用开发的“操作系统”,它提供了一整套工具和约定,让你能高效、优雅地构建起你的 AI 帝国。

LangChain 的三大基石:LLMs, Prompts, Output Parsers

在 LangChain 的世界里,有几个概念你必须刻在脑子里,它们是构建一切的起点:

  1. LLMs (Large Language Models) / ChatModels:

    • 这是你应用的“大脑”,负责理解、生成文本。LangChain 提供了 LLM 类(用于文本补全)和 ChatModel 类(用于多轮对话),它们是与底层大模型交互的抽象层。
    • 你不需要关心底层模型是 OpenAI 的 GPT-4 还是 Llama 2,LangChain 帮你抹平了差异。
  2. PromptTemplates (提示词模板):

    • 这是你与“大脑”沟通的“语言”。你不能直接对 LLM 说“帮我查订单”,你需要告诉它:“你是一个专业的客服,用户问订单,请给出查询链接和注意事项。”
    • PromptTemplate 允许你定义一个带有占位符的字符串模板,动态地填充用户输入、上下文信息等,确保每次与 LLM 交互时都能提供清晰、一致的指令。这是控制 LLM 行为的关键!
  3. Output Parsers (输出解析器):

    • LLM 的输出通常是自由格式的文本。但很多时候,我们希望得到结构化的数据,比如 JSON、列表,甚至是特定格式的日期。
    • OutputParser 的作用就是将 LLM 的原始文本输出,安全、可靠地转换成你的程序可以理解和使用的结构化格式。这能极大地提升 LLM 应用的健壮性和可用性。

现在,让我们用一个简单的流程图来可视化这三者如何在我们的智能客服小助手中协同工作。

graph TD
    A[用户提问: "我的订单物流信息在哪里?"] --1. 接收输入--> B{PromptTemplate: 构造指令}
    B --2. 填充模板(如: "用户提问是'...'")--> C[LLM: 大语言模型 (例如 OpenAI GPT-3.5)]
    C --3. 生成文本回复--> D[LLM原始输出: "好的,请您提供订单号。您可以在官网订单详情页查看物流信息。"]
    D --4. 结构化解析--> E{OutputParser: (可选) 提取关键信息或格式化}
    E --5. 返回结构化结果--> F[智能客服小助手: 返回给用户或进行后续操作]

看到了吗?从用户的一句简单提问,到最终客服小助手给出响应,背后是 PromptTemplate 引导 LLM 生成内容,再由 OutputParser 精心打磨的过程。这就是 LangChain 核心模块的魅力!

💻 实战代码演练 (客服项目中的具体应用)

好了,理论听起来很酷,但我们是实战派!现在,就让我们把这些概念变成代码,构建我们智能客服小助手的第一个版本。

场景设定:新手客服小助手

在这一期,我们的智能客服小助手还很“年轻”,它没有外部知识库,也没有记忆。它就像一个刚入职的新手客服,只能根据你给的“培训手册”(Prompt)和它自己的“聪明才智”(LLM)来回答问题。

目标: 让我们的客服小助手能够根据用户提问,给出一个礼貌且初步的回答。

准备工作:安装依赖

首先,确保你的开发环境准备就绪。我们需要 LangChain 库和 OpenAI 库(或者你选择的其他 LLM 提供商)。

pip install langchain-community langchain-openai python-dotenv

langchain-community 包含了许多常用的集成,langchain-openai 专门用于 OpenAI 模型,python-dotenv 方便我们管理 API 密钥。

环境变量配置: 创建一个 .env 文件在你的项目根目录,并填入你的 OpenAI API 密钥:

OPENAI_API_KEY="sk-YOUR_OPENAI_API_KEY_HERE"

注意: 永远不要把 API 密钥硬编码到你的代码里!使用环境变量是最佳实践。

代码实现:构建你的第一个 LLM Chain

我们将使用 Python 来实现。

import os
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# 1. 加载环境变量
# Load environment variables from .env file
load_dotenv()

# 2. 初始化 LLM 模型
# Initialize the LLM model (e.g., OpenAI's GPT-3.5-turbo)
# 温度参数 (temperature) 控制模型的创造性。0 表示更确定和一致的输出。
# The temperature parameter controls the creativity of the model. 0 means more deterministic and consistent output.
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) # 也可以是 gpt-4, gpt-4o 等

# 3. 定义 PromptTemplate:这是我们客服小助手的“培训手册”
# Define the PromptTemplate: This is the "training manual" for our customer service copilot.
# 我们希望它扮演一个专业的角色,并明确回答用户的问题。
# We want it to act as a professional, clearly answering user questions.
prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个专业、友好、乐于助人的智能客服助手。请礼貌、准确、简洁地回答用户关于产品或服务的问题。如果无法回答,请告知用户将转接人工客服。"),
        ("user", "{user_question}")
    ]
)

# 4. (可选) 定义 Output Parser:将 LLM 的原始输出转换为我们想要的格式
# (Optional) Define Output Parser: Converts the raw LLM output into our desired format.
# 对于本期,我们只是简单地获取字符串输出,所以使用 StrOutputParser。
# For this episode, we simply want a string output, so we use StrOutputParser.
output_parser = StrOutputParser()

# 5. 构建 Chain:将 Prompt, LLM 和 Output Parser 串联起来
# Build the Chain: Connects the Prompt, LLM, and Output Parser.
# LangChain 的 pipe 语法 ( | ) 让 Chain 的构建变得非常直观和优雅。
# LangChain's pipe syntax ( | ) makes building Chains very intuitive and elegant.
customer_service_chain = prompt_template | llm | output_parser

# 6. 运行我们的智能客服小助手!
# Run our intelligent customer service copilot!
print("--- 智能客服小助手启动 ---")
print("请输入您的问题 (输入 '退出' 结束):")

while True:
    user_input = input("\n用户: ")
    if user_input.lower() == '退出':
        print("客服小助手: 感谢您的使用,再见!")
        break

    # 调用 Chain 处理用户输入
    # Invoke the Chain to process user input
    try:
        response = customer_service_chain.invoke({"user_question": user_input})
        print(f"客服小助手: {response}")
    except Exception as e:
        print(f"客服小助手: 抱歉,处理您的问题时遇到了一点小麻烦。请稍后再试或联系人工客服。错误信息: {e}")

代码解析:每一步的魔法

  1. load_dotenv(): 确保你的 API 密钥被安全加载。这是基础!
  2. ChatOpenAI(model="gpt-3.5-turbo", temperature=0):
    • 我们选择了 ChatOpenAI,因为它更适合像客服对话这样的多轮交互场景。
    • model="gpt-3.5-turbo":指定使用的模型。你可以根据需求和成本考虑选择 gpt-4gpt-4o 等更强大的模型。
    • temperature=0:这个参数非常重要!它控制了 LLM 输出的“随机性”或“创造性”。对于客服场景,我们通常希望回答是确定、一致且事实性强的,而不是天马行空。所以,设置为 0 是个不错的选择。
  3. ChatPromptTemplate.from_messages(...):
    • 这是我们客服小助手的“灵魂”!我们通过 from_messages 方法定义了一个基于消息的 Prompt 模板。
    • ("system", "...")系统消息,用于设定 LLM 的角色、行为和总体指导方针。这是告诉 LLM “你是谁,该怎么做”的关键。
    • ("user", "{user_question}")用户消息,这里我们用 {user_question} 作为占位符,LangChain 会在运行时自动用用户的真实输入填充它。
  4. StrOutputParser():
    • 这是最简单的输出解析器,它只是将 LLM 的原始字符串输出原封不动地返回。
    • 你可能会觉得这没啥用?别急,在后续的课程中,我们会看到更强大的解析器,它们能把 LLM 的输出变成 JSON、Python 对象,甚至是自定义的数据结构!
  5. prompt_template | llm | output_parser:
    • 这就是 LangChain 的“Pipe”魔法!它将 prompt_template 的输出作为 llm 的输入,再将 llm 的输出作为 output_parser 的输入,最终形成一个端到端的工作流
    • 这种链式调用(Chain)是 LangChain 强大可组合性的体现,代码简洁而富有表现力。
  6. customer_service_chain.invoke({"user_question": user_input}):
    • 调用 Chain 的核心方法。我们将用户输入包装成一个字典,键名 (user_question) 必须与 PromptTemplate 中定义的占位符一致。
    • Chain 会自动完成填充模板、调用 LLM、解析输出的全过程,并返回最终结果。

现在,运行这段代码,你的第一个智能客服小助手就上线了!你可以尝试问它一些问题,看看它如何回答。

坑与避坑指南

作为一名经验老道的导师,我必须得给你们敲敲警钟,避免踩到那些我当年踩过的坑。

  1. Prompt Engineering 是门艺术,更是科学:

    • 坑: 以为 Prompt 就是随便写几句话。结果 LLM 回答驴头不对马嘴,或者过于泛泛。
    • 避坑: Prompt 是你与 LLM 沟通的“圣经”。一个好的 Prompt 应该包含:
      • 角色设定 (Persona): “你是一个专业的客服。”
      • 任务目标 (Goal): “回答用户关于产品或服务的问题。”
      • 约束条件 (Constraints): “礼貌、准确、简洁,无法回答时转接人工。”
      • 输出格式 (Format, 选填): 比如“请以 JSON 格式返回答案。”(我们后续会讲)
    • 黄金法则: 多测试,多迭代。调整 Prompt 往往比调整模型参数更有效。
  2. API Key 管理:安全第一!

    • 坑: 直接把 OPENAI_API_KEY = "sk-..." 写在代码里,然后把代码推到 GitHub 上。
    • 避坑: 永远、永远、永远(重要的事情说三遍)不要把敏感信息硬编码到代码中。使用 .env 文件配合 python-dotenv 是最基本的安全实践。在生产环境中,更应该使用云服务商提供的密钥管理服务。
  3. 模型选择与成本考量:

    • 坑: 一上来就无脑用最强大的模型(比如 GPT-4o),结果账单飞起。
    • 避坑: 不同的模型有不同的能力和价格。
      • gpt-3.5-turbo:性价比之王,处理日常客服问题绰绰有余。
      • gpt-4 / gpt-4o:更强的推理能力、更长的上下文,适合复杂问题或需要高质量创意输出的场景。
      • 建议: 从最便宜且能满足需求的模型开始,只有当遇到瓶颈时再考虑升级。
  4. LLM 输出的非确定性:

    • 坑: 期望 LLM 每次都给出完全相同的回答,或者严格按照你想要的格式输出。
    • 避坑: LLM 本质上是概率模型,即使 temperature=0 也不能保证 100% 的确定性。它的输出可能会有细微差别,偶尔也会“幻觉”(Hallucination)。
      • 解决方案: 引入 OutputParser 来规范输出,增加重试机制,或者在 Prompt 中强调输出格式,并进行后处理验证。这是为什么 OutputParser 如此重要的原因!
  5. 冷启动问题:

    • 坑: 第一次调用 LLM 发现响应特别慢,以为是代码或网络问题。
    • 避坑: 大部分 LLM API 在首次调用时会有“冷启动”延迟,因为模型可能需要加载到 GPU 上。后续调用会快很多。在生产环境中,考虑预热机制或使用异步调用。

📝 本期小结

恭喜你!在本期课程中,你已经成功迈出了 LangChain 学习的第一步。我们:

  • 理解了 LangChain 作为 LLM 应用开发框架的核心价值:抽象、模块化、可组合性。
  • 掌握了三大基石:LLMs (ChatOpenAI)、PromptTemplates (ChatPromptTemplate) 和 Output Parsers (StrOutputParser)。
  • 亲手构建了一个最基本的智能客服小助手:它能根据你的“培训手册”和用户提问,给出初步的回答。
  • 探讨了开发中常见的“坑”,并学会了如何巧妙避开它们。

我们的客服小助手现在虽然能说会道,但它还是个“健忘症患者”,每次提问都像第一次见面。它也不知道我们的产品手册、FAQ 列表在哪里。别急,这正是我们下一期要解决的核心问题!

在下一期,我们将深入探索 LangChain 的另一个核心概念——记忆(Memory),让我们的客服小助手能够记住之前的对话,真正实现多轮交流。同时,我们还会引入文档加载器(Document Loaders),让它能从外部知识库中学习!

期待下一期的精彩内容,继续你的 AI 探索之旅!