第 25 期 | LangSmith 进阶:Prompt 管理与 A/B 测试

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

🎯 本期学习目标

好了,各位未来的 AI 大师们,欢迎来到《LangChain 全栈大师课》的第一期!从今天起,我们将一起踏上构建生产级 AI 应用的征途。本期作为开篇,我们不玩虚的,直捣黄龙,让你彻底掌握 LangChain 应用的基石。

学完本期,你将:

  1. 彻底理解 LangChain Core 的三大核心组件:LLM 模型、Prompt 模板和 Output Parser 的作用与协作机制。
  2. 掌握 LangChain Expression Language (LCEL):学会如何用优雅的管道符 | 将这些核心组件串联起来,构建一个基础的 LLM 工作流。
  3. 为智能客服项目打下坚实基础:亲手搭建我们智能客服小助手的第一个“大脑”,让它能根据你的指令进行初步的问答。
  4. 避开初学者常犯的“大坑”:了解 API Key 安全、Prompt 工程思维以及 LangChain 的性能考量,让你少走弯路。

📖 原理解析

为什么是 LangChain?难道我们不能直接调用 OpenAI 或者 Anthropic 的 API 吗?当然可以!但那就像你只想造个轮子,却要每次都从炼钢开始。LangChain 就像是你手里的乐高积木,它把那些重复的、繁琐的、但又必不可少的部分都给你模块化了。它不仅仅是一个调用 LLM 的库,更是一个编排 LLM 应用的工作流框架

想象一下我们的“智能客服知识库”项目:它不仅仅需要回答问题,还需要理解用户意图、从海量知识库中检索信息、总结回答、甚至与外部工具交互。如果每次都从头写逻辑,你很快就会陷入意大利面条式的代码泥潭。LangChain 的出现,就是为了解决这个痛点,它提供了一套标准化的接口和组件,让你能像搭积木一样,快速、灵活地构建复杂的 LLM 应用。

LangChain Core 是 LangChain 的心脏,它定义了所有 LLM 应用最基础、最通用的抽象和功能。本期,我们聚焦于三大核心组件:

  1. LLM (Large Language Model) 模型

    • 道的层面:这是我们智能客服的“大脑”,负责理解、推理和生成文本。LangChain 对各种 LLM 提供了统一的接口封装,无论是 OpenAI 的 GPT 系列,还是 Anthropic 的 Claude,抑或是 Hugging Face 上的开源模型,你都可以用相似的方式来调用它们。这大大降低了模型切换的成本,让你能专注于业务逻辑,而不是适配不同模型的 API。
    • 术的层面:在 LangChain 中,LLM 通常分为两种类型:BaseLLM (用于文本补全,如 text-davinci-003) 和 BaseChatModel (用于对话,如 gpt-3.5-turbo)。对于智能客服这种对话场景,我们几乎总是使用 BaseChatModel,因为它更擅长处理对话历史,理解角色和多轮交互。
  2. Prompt (提示) 模板

    • 道的层面:Prompt,这玩意儿是你的 LLM 应用的灵魂,是它让你的 AI 从一个无所不知但又啥都不懂的“傻白甜”变成一个目标明确、专业对口的“小能手”。一个好的 Prompt 能够引导 LLM 按照你期望的方式进行输出,无论是扮演客服角色、总结文本还是生成特定格式的数据。对于智能客服来说,Prompt 是我们定义“客服小助手”人设、回答风格和专业知识的关键。
    • 术的层面:LangChain 提供了强大的 PromptTemplateChatPromptTemplateChatPromptTemplate 更适用于对话模型,它允许你定义不同角色的消息(SystemMessageHumanMessageAIMessage),让 LLM 更好地理解上下文和角色扮演。比如,我们可以通过 SystemMessage 来告诉 LLM:“你是一个专业的智能客服,请用友好、简洁的语气回答问题。”
  3. Output Parser (输出解析器)

    • 道的层面:LLM 吐出来的都是自由文本,但很多时候我们希望得到结构化的数据,比如一个 JSON 对象、一个列表、或者一个布尔值。Output Parser 的作用就像一个“翻译官”,它能把 LLM 生成的非结构化文本,按照我们预设的格式进行解析和转换。这对于后续的程序逻辑处理至关重要。
    • 术的层面:LangChain 提供了多种 Output Parser,从最简单的 StrOutputParser (直接返回字符串) 到复杂的 PydanticOutputParser (解析成 Pydantic 模型对象),应有尽有。对于智能客服,如果我们希望它不仅给出答案,还能标记问题类别(如“这是关于计费的问题”),Output Parser 就变得不可或缺。

而将这些组件连接起来的“胶水”,就是 LangChain Expression Language (LCEL)

LCEL

  • 道的层面:LCEL,这名字听起来有点酷,有点高级,但本质上,它就是 LangChain 提供给你的一套超能力,让你能像搭积木一样,把 LLM 应用的各个模块,用管道符 | 优雅地连接起来。它不仅让代码变得简洁易读,更重要的是,它提供了异步执行、流式传输、并行处理、回退机制等高级功能,为构建高性能、鲁棒的生产级应用打下了基础。
  • 术的层面:在 LCEL 中,你可以像 Unix 管道一样,将一个组件的输出作为下一个组件的输入。例如 prompt | llm | output_parser 就构成了一个完整的链条。

现在,让我们用一张 Mermaid 图来直观地感受一下 LangChain Core 的这些组件是如何协同工作的:

graph TD
    subgraph LangChain Core - LLM 应用的骨架
        A[Prompt Template
定义意图与格式] --> B[LLM Model
智能核心,生成响应]; B --> C[Output Parser
结构化输出,确保格式]; end U[用户输入
原始问题] --> A; C --> D[结构化/格式化输出
客服回答]; style A fill:#e0f7fa,stroke:#00796b,stroke-width:2px,color:#000,font-weight:bold style B fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#000,font-weight:bold style C fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,color:#000,font-weight:bold style U fill:#fce4ec,stroke:#ad1457,stroke-width:2px,color:#000,font-weight:bold style D fill:#f0f4c3,stroke:#9e9d24,stroke-width:2px,color:#000,font-weight:bold linkStyle 0 stroke-width:2px,stroke:red; linkStyle 1 stroke-width:2px,stroke:blue; linkStyle 2 stroke-width:2px,stroke:green; linkStyle 3 stroke-width:2px,stroke:purple;

这张图清晰地展示了从用户输入到最终输出的整个流程。用户的问题首先经过 Prompt Template 进行格式化和包装,然后发送给 LLM Model 进行处理,LLM 生成的原始响应再由 Output Parser 进行结构化,最终得到我们期望的客服回答。这个流程就是我们智能客服小助手的第一个“大脑”工作流!

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

好了,原理讲明白了,是时候撸起袖子,把这些概念落地到我们的“智能客服知识库”项目上了。本期,我们将构建一个最基础的智能客服问答机器人。它能接收用户问题,然后以一个专业的客服助理的身份给出回答。

准备工作

首先,确保你的开发环境已准备就绪。我们需要安装 LangChain 库以及你选择的 LLM 提供商的 SDK。这里我们以 OpenAI 为例。

# Python 环境
pip install langchain langchain-openai python-dotenv

# TypeScript/JavaScript 环境
npm install langchain @langchain/openai dotenv

别告诉我你还在代码里硬编码 API Key!这是大忌!我们使用 python-dotenvdotenv 来管理环境变量。在项目根目录下创建一个 .env 文件,并填入你的 OpenAI API Key:

# .env 文件
OPENAI_API_KEY="sk-your-openai-api-key-here"

Python 代码实现

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.messages import HumanMessage, SystemMessage

# 1. 加载环境变量
load_dotenv()

# 2. 初始化 LLM 模型
# 我们选择 ChatOpenAI,因为它是对话模型,更适合客服场景
# model_name 可以根据你的需求选择,例如 "gpt-4" 获取更高质量回答,但成本更高
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7)

# 3. 定义 Prompt 模板
# 这是我们智能客服的“人设”和“指令”
# SystemMessagePromptTemplate 定义了客服的背景和行为准则
# HumanMessagePromptTemplate 接收用户的问题
prompt_template = ChatPromptTemplate.from_messages(
    [
        SystemMessagePromptTemplate.from_template(
            "你是一个专业的智能客服助理,你的任务是简洁、准确地回答用户关于我们产品(智能客服知识库)的问题。"
            "请保持友好和乐于助人的语气。如果问题超出你的知识范围,请礼貌地告知用户你无法回答,并建议他们联系人工客服。"
        ),
        HumanMessagePromptTemplate.from_template("{user_question}"), # {user_question} 是一个占位符
    ]
)

# 4. 初始化 Output Parser
# 最简单的输出解析器,直接将 LLM 的响应作为字符串返回
output_parser = StrOutputParser()

# 5. 使用 LCEL 串联组件,构建智能客服链
# 管道符 `|` 将 Prompt 的输出作为 LLM 的输入,LLM 的输出作为 Output Parser 的输入
support_copilot_chain = prompt_template | llm | output_parser

# 6. 模拟用户提问并获取回答
def get_copilot_response(question: str) -> str:
    """
    模拟智能客服接收用户问题并返回回答。
    """
    print(f"\n--- 用户提问 ---")
    print(f"用户: {question}")
    
    # 调用链来获取回答
    # input 字典的键需要与 prompt_template 中定义的占位符名称一致
    response = support_copilot_chain.invoke({"user_question": question})
    
    print(f"\n--- 智能客服回答 ---")
    print(f"客服: {response}")
    return response

if __name__ == "__main__":
    print("欢迎使用智能客服小助手!输入 '退出' 结束对话。")
    while True:
        user_input = input("你: ")
        if user_input.lower() == "退出":
            print("感谢您的使用,再见!")
            break
        
        get_copilot_response(user_input)
        
        # 我们可以尝试一些边界情况
        if user_input.lower() == "测试边界":
            get_copilot_response("你们产品的退款政策是什么?")
            get_copilot_response("给我讲个笑话吧。") # 应该会被System Prompt限制
            get_copilot_response("如何配置知识库的检索策略?")
            get_copilot_response("请问今天天气怎么样?") # 应该会被System Prompt限制

TypeScript 代码实现

import 'dotenv/config'; // 确保在文件顶部加载环境变量
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { RunnableSequence } from '@langchain/core/runnables';

// 1. 环境变量已通过 'dotenv/config' 加载

// 2. 初始化 LLM 模型
// 使用 process.env.OPENAI_API_KEY 确保 API Key 被正确加载
const llm = new ChatOpenAI({
  modelName: "gpt-3.5-turbo",
  temperature: 0.7,
  openAIApiKey: process.env.OPENAI_API_KEY, // 明确传入 API Key
});

// 3. 定义 Prompt 模板
const promptTemplate = ChatPromptTemplate.fromMessages([
  SystemMessagePromptTemplate.fromTemplate(
    "你是一个专业的智能客服助理,你的任务是简洁、准确地回答用户关于我们产品(智能客服知识库)的问题。" +
    "请保持友好和乐于助人的语气。如果问题超出你的知识范围,请礼貌地告知用户你无法回答,并建议他们联系人工客服。"
  ),
  HumanMessagePromptTemplate.fromTemplate("{user_question}"), // {user_question} 是一个占位符
]);

// 4. 初始化 Output Parser
const outputParser = new StringOutputParser();

// 5. 使用 LCEL 串联组件,构建智能客服链
// 在 TypeScript 中,RunnableSequence 是构建链的常用方式
const supportCopilotChain = RunnableSequence.from([
  promptTemplate,
  llm,
  outputParser,
]);

// 6. 模拟用户提问并获取回答
async function getCopilotResponse(question: string): Promise<string> {
  console.log(`\n--- 用户提问 ---`);
  console.log(`用户: ${question}`);

  // 调用链来获取回答
  const response = await supportCopilotChain.invoke({ user_question: question });

  console.log(`\n--- 智能客服回答 ---`);
  console.log(`客服: ${response}`);
  return response;
}

// 模拟交互
async function main() {
  console.log("欢迎使用智能客服小助手!输入 '退出' 结束对话。");
  const readline = require('readline').createInterface({
    input: process.stdin,
    output: process.stdout
  });

  for await (const line of readline) {
    const userInput = line.trim();
    if (userInput.toLowerCase() === "退出") {
      console.log("感谢您的使用,再见!");
      readline.close();
      break;
    }

    await getCopilotResponse(userInput);

    // 我们可以尝试一些边界情况
    if (userInput.toLowerCase() === "测试边界") {
      await getCopilotResponse("你们产品的退款政策是什么?");
      await getCopilotResponse("给我讲个笑话吧。"); // 应该会被System Prompt限制
      await getCopilotResponse("如何配置知识库的检索策略?");
      await getCopilotResponse("请问今天天气怎么样?"); // 应该会被System Prompt限制
    }
  }
}

if (require.main === module) {
  main();
}

代码解析:

  1. 加载环境变量:这是安全实践!永远不要把敏感信息直接写在代码里。
  2. 初始化 LLM:我们实例化了 ChatOpenAImodel_name (或 modelName) 决定了使用哪个具体的模型,temperature 控制生成文本的随机性(0.0 最确定,1.0 最随机,客服场景通常设为 0.5-0.7 比较合适)。
  3. 定义 Prompt 模板
    • ChatPromptTemplate.from_messages 是构建对话型 Prompt 的推荐方式。
    • SystemMessagePromptTemplate 用来设置 AI 的“角色”和“行为准则”。这是我们定义智能客服的关键!它让 AI 知道自己是一个专业的客服,应该怎么说话,什么能说,什么不能说。
    • HumanMessagePromptTemplate 则用于接收用户的实际输入。{user_question} 是一个占位符,LangChain 会在 invoke 时用实际的用户问题替换它。
  4. 初始化 Output Parser:这里我们使用了最基础的 StrOutputParser (Python) 或 StringOutputParser (TypeScript),它只是简单地把 LLM 的输出直接作为字符串返回。在后续的课程中,我们将引入更复杂的解析器来获取结构化数据。
  5. 构建链:这是 LCEL 的魅力所在!
    • Python 中,我们直接使用管道符 |prompt_templatellmoutput_parser 串联起来。
    • TypeScript 中,我们使用 RunnableSequence.from 来构建同样的链。 无论哪种语言,其核心思想都是:上一个组件的输出作为下一个组件的输入。
  6. 调用链:通过 support_copilot_chain.invoke({"user_question": question}) (Python) 或 await supportCopilotChain.invoke({ user_question: question }) (TypeScript),我们将用户问题传递给链,并获得最终的客服回答。invoke 方法接收一个字典(或 JavaScript 对象),其键名必须与 Prompt 模板中的占位符名称一致。

运行这些代码,你就能看到你的第一个智能客服小助手在命令行中与你对话了!尝试问一些关于“智能客服知识库”的问题,也试试问一些超出它知识范围的问题,看看它是否能按照 System Prompt 的指示,礼貌地拒绝并引导用户联系人工客服。这就是 Prompt Engineering 的力量!

坑与避坑指南

作为一名资深 AI 架构师,我见过太多新手在这里栽跟头。这些“坑”你必须提前知道,才能走得更远:

  1. API Key 安全是头等大事

    • :把 OPENAI_API_KEY 硬编码在代码里,或者直接上传到 GitHub 公开仓库。这相当于把你的银行卡密码贴在额头上出门。
    • 避坑:永远使用环境变量!python-dotenvdotenv 是你的好朋友。在生产环境中,使用云服务商提供的密钥管理服务(如 AWS Secrets Manager, Azure Key Vault, Google Secret Manager)。本地开发时,确保 .env 文件被 .gitignore 忽略。
  2. Prompt Engineering 是艺术也是科学

    • :认为只要把问题扔给 LLM 就行了,不花时间精心设计 Prompt。结果 LLM 乱答一通,或者答非所问。
    • 避坑:Prompt 是你与 LLM 沟通的唯一桥梁。它需要清晰、明确、有结构。
      • 角色定义:清楚地告诉 LLM 它应该扮演什么角色(“你是一个专业的客服助理”)。
      • 任务说明:明确你的任务是什么(“回答用户关于产品的问题”)。
      • 约束条件:设定输出的格式、语气、长度限制,以及处理未知情况的策略(“如果超出知识范围,请礼貌告知”)。
      • 迭代优化:Prompt 不是一蹴而就的,需要反复测试、调整和优化。
  3. LLM 模型的选择与成本考量

    • :一开始就无脑使用最强大的模型(如 gpt-4),导致成本飙升,响应速度慢。或者为了省钱使用性能不足的模型,导致效果差。
    • 避坑:根据你的业务需求和预算选择合适的模型。
      • gpt-3.5-turbo:性价比高,速度快,适合大部分日常客服问答。
      • gpt-4:推理能力更强,适合复杂问题、多步骤推理,但成本更高,速度较慢。
      • 开源模型 (通过 Ollama/HuggingFace):成本最低(自托管),但需要管理基础设施,性能可能不如商业模型,适合对数据隐私有极高要求或预算有限的场景。
      • 温度 (temperature):设置为 0.0 会让模型输出更确定、重复性高;设置为 1.0 会更具创造性、随机性。客服场景通常在 0.5-0.7 之间,既能保持一定灵活性,又不至于胡编乱造。
  4. LLM 的“幻觉”与事实核查

    • :盲目信任 LLM 生成的所有内容,不进行任何事实核查,尤其是在关键业务场景。
    • 避坑:LLM 可能会“一本正经地胡说八道”,这被称为“幻觉”。对于智能客服,这意味着它可能会捏造产品功能、退款政策等信息。
      • Prompt 限制:在 Prompt 中明确要求“只根据已知信息回答,如果不知道,请告知”。
      • RAG (Retrieval Augmented Generation):这是我们后续课程会深入讲解的核心技术。通过从外部知识库中检索相关信息,并将其作为上下文提供给 LLM,可以大大减少幻觉,提高回答的准确性。这是生产级智能客服的标配!
  5. LCEL 的异步与流式处理

    • :在处理大量请求或需要实时反馈的场景中,同步调用 LLM 导致响应慢,用户体验差。
    • 避坑:LCEL 天生支持异步 ainvoke() 和流式 astream()。对于客服应用,用户希望快速得到回答,所以异步和流式输出是必须掌握的技能,我们会在后续课程中逐步引入。提前知道这一点,你就赢在了起跑线上。

📝 本期小结

恭喜你!在本期《LangChain 全栈大师课》中,我们不仅深入理解了 LangChain Core 的三大基石:LLM 模型、Prompt 模板和 Output Parser,更重要的是,你亲手使用 LangChain Expression Language (LCEL) 构建了你的第一个智能客服小助手。

我们为客服小助手赋予了初步的“大脑”和“人设”,让它能够根据你的指令,以专业的态度进行基础问答。这个看似简单的链条——Prompt | LLM | Output Parser——却是所有复杂 LLM 应用的起点。

你还学到了作为一名资深开发者,在构建 LLM 应用时必须注意的“坑”和“避坑指南”,包括 API Key 安全、Prompt 工程思维、模型选择以及对 LLM 局限性的认识。

这只是万里长征的第一步。在接下来的课程中,我们将在此基础上不断迭代,为我们的“智能客服知识库”添加记忆、集成外部知识库(RAG)、调用工具、实现多轮对话等更高级的功能,逐步把它打造成一个真正的生产级 AI 应用。

准备好了吗?下一