第 20 期 | Agent 评测与反馈:LLM-as-a-judge
(申请发送: agentupdate)
好的,各位未来的 AI 全栈大师们!我是你们的导师,一个在 AI 领域摸爬滚打十年的老兵,也是一个对教育充满热情的布道者。很高兴能在《LangChain 全栈大师课:从零基础到生产级 AI 应用》的课堂上再次与大家相见。
经过前两期的预热和基础铺垫,我们已经对 LangChain 有了初步的认识,知道它是构建复杂 LLM 应用的“瑞士军刀”。但光有工具还不够,我们还得学会怎么“用”这个工具,怎么和底层的 AI 模型打交道。
今天,我们要揭开 AI 应用中最核心、最艺术,也最容易被忽视的一环——Prompt Engineering 的神秘面纱。在我们的“智能客服知识库”项目中,Prompt Engineering 简直就是客服小助手的“灵魂画师”,它决定了我们的 AI 是一个只会鹦鹉学舌的复读机,还是一个真正能理解用户意图、提供精准且有温度服务的智能伙伴。
🎯 本期学习目标
在本期课程中,你将不仅仅是学习 Prompt Engineering 的理论,更要将它实实在在地运用到我们的智能客服项目中,让你的 AI 应用能力实现质的飞跃:
- 理解 Prompt Engineering 的核心原理及其在 AI 应用中的重要性:掌握如何通过精心设计的 Prompt,与大模型进行高效沟通,从而实现特定任务。
- 掌握 LangChain 中
PromptTemplate的基本使用:学会如何利用 LangChain 提供的强大工具,为我们的智能客服构建可复用、可定制化的指令。 - 学习如何设计结构化、清晰的 Prompt:提升客服小助手理解用户意图、提取关键信息和生成精准回复的能力,告别“牛头不对马嘴”的尴尬。
- 初步探索 Few-shot Prompting:了解如何通过提供少量示例,让模型更好地理解任务模式和期望的输出格式,进一步提升客服回复的质量和一致性。
📖 原理解析
各位同学,听好了!如果你觉得 Prompt Engineering 就是简单地给 AI 提个问题,那你就大错特错了。它可不是简单的问答游戏,它更像是一门和 AI 沟通的艺术,你得学会怎么和这个超级聪明的“小朋友”把话说清楚,把你的需求表达得明明白白。
在我们的“智能客服知识库”项目中,用户可能会问各种各样的问题:“我的订单在哪里?”,“如何申请退货?”,“你们的会员积分怎么用?”,甚至是一些模糊的抱怨。我们的智能客服需要做的,不仅仅是找到答案,更要理解用户真正的意图,提取关键信息,然后以一种专业、友好、符合品牌调性的方式给出回复。这一切的背后,都离不开 Prompt Engineering 的功劳。
Prompt Engineering 是什么? 简单来说,Prompt Engineering 就是设计和优化输入给大型语言模型 (LLM) 的文本指令(即 Prompt),以引导模型产生期望输出的过程。它包含了一系列技巧和策略,旨在最大化 LLM 的性能,使其在特定任务上表现得更准确、更相关、更高效。
为什么 Prompt Engineering 对智能客服如此关键?
- 意图理解(Intent Understanding):用户输入的自然语言往往含糊不清,Prompt 可以指导 LLM 识别出用户提问的核心意图,例如是查询订单、退换货还是技术支持。
- 信息提取(Information Extraction):从用户输入中提取关键实体,如订单号、商品名称、问题描述等,为后续的知识库检索或 API 调用提供参数。
- 回复生成(Response Generation):在获取到相关信息后,Prompt 可以指导 LLM 以特定的语气、格式和内容生成最终回复,确保回复的专业性、准确性和用户友好性。
- 角色扮演与约束(Role-playing & Constraints):通过 Prompt,我们可以让 LLM 扮演一个“资深客服专家”的角色,并限制其回答范围,避免生成无关或不当的内容。
一个好的 Prompt,通常包含以下几个核心要素:
- 指令 (Instruction):清晰明确地告诉模型要做什么。
- 上下文 (Context):提供必要的背景信息,帮助模型更好地理解任务。
- 输入数据 (Input Data):用户实际的查询或需要处理的数据。
- 输出格式 (Output Format):明确期望的输出结构(如 JSON、列表、段落)。
- 示例 (Examples - Few-shot):通过提供少量输入-输出对的示例,让模型学习任务模式。
- 约束 (Constraints):限制模型的行为,如语气、长度、不允许提及的内容等。
Mermaid 图解:Prompt Engineering 在智能客服中的工作流
理解了这些,我们再来看一下 Prompt Engineering 在我们的智能客服项目中的核心地位。它就像一个翻译官,把我们人类的需求翻译成 AI 能懂的语言,再把 AI 的“思考”翻译成人类能懂的答案。
graph TD
A[用户输入原始问题] --> B{Prompt Engineering - 意图理解}
B --> C[Prompt Template for Intent Classification]
C --> D[LLM (e.g., GPT-4)]
D --> E{分类意图: ORDER_STATUS, REFUND, PRODUCT_INFO...}
E -- 匹配意图 --> F[从知识库/DB中检索相关信息]
F --> G{Prompt Engineering - 回复生成}
G --> H[Prompt Template for Response Generation]
H --> I[LLM (e.g., GPT-4)]
I --> J[生成最终客服回复]
J --> K[返回给用户]
subgraph Prompt Engineering 的核心作用
C
H
end
style B fill:#f9f,stroke:#333,stroke-width:2px
style G fill:#f9f,stroke:#333,stroke-width:2px从图中可以看到,无论是理解用户意图,还是基于检索到的信息生成最终回复,Prompt Engineering 都扮演着核心的“灵魂画师”角色。它确保了 AI 能够“开窍”,而不是“开溜”。
LangChain 中的 PromptTemplate
LangChain 对 Prompt Engineering 提供了非常友好的抽象——PromptTemplate。它允许我们定义一个带有占位符的字符串模板,然后动态地填充这些占位符,生成最终的 Prompt。这大大提升了 Prompt 的可维护性和复用性。
想象一下,如果每次用户提问,你都要手动构造一个包含所有指令、上下文和用户问题的长字符串,那简直是地狱!PromptTemplate 就是来拯救你的!
💻 实战代码演练 (客服项目中的具体应用)
好了,理论铺垫到此为止,是时候撸起袖子干活了!我们将用 LangChain 的 PromptTemplate 来武装我们的智能客服小助手。
场景一:意图分类 - 让客服小助手准确理解用户想干嘛
在智能客服中,第一步往往是理解用户的意图。例如,用户问“我的快递到哪了?”,我们应该识别出这是“订单状态查询”;如果问“买错了,能退吗?”,则是“退货政策咨询”。
我们将构建一个 Prompt Template,让 LLM 帮助我们进行意图分类。
# Python 代码示例
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
import os
# 设置你的 OpenAI API Key
# 实际项目中,请使用环境变量或更安全的配置方式
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY" # 替换为你的实际 API Key
print("--- 场景一:意图分类 - 让客服小助手准确理解用户想干嘛 ---")
# 1. 定义意图分类的 PromptTemplate
# 我们会给模型一些明确的指令,告诉它有哪些可能的意图,并要求它只返回意图名称
intent_classification_template = """
你是一名专业的电商客服意图分类机器人。你的任务是根据用户的提问,将其归类到最符合的意图类别中。
请从以下可用类别中选择一个,并只返回该类别的名称。如果没有任何类别匹配,请返回 "UNKNOWN"。
可用类别:
- ORDER_STATUS: 查询订单状态、物流信息
- REFUND_RETURN: 申请退货、退款、查询退换货政策
- PRODUCT_INFO: 查询商品详情、库存、功能、推荐
- ACCOUNT_MANAGEMENT: 账户问题、密码重置、个人信息修改
- TECHNICAL_SUPPORT: 技术故障、网站使用问题
- COMPLAINT_SUGGESTION: 投诉、建议、反馈
- OTHER: 其他不属于上述类别的通用问题
用户提问: "{user_query}"
请选择最匹配的意图类别:
"""
# 使用 PromptTemplate 创建一个可格式化的 Prompt 对象
intent_prompt = PromptTemplate.from_template(intent_classification_template)
# 2. 初始化 LLM
# 这里我们使用 OpenAI 的 GPT-3.5-turbo 模型
# temperature 参数设置为 0,表示我们希望模型给出确定性、一致性的回答,这对于分类任务很重要
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 3. 构建 LangChain 链
# PromptTemplate | LLM | OutputParser
intent_chain = intent_prompt | llm | StrOutputParser()
# 4. 模拟用户提问并获取意图分类
user_queries = [
"我的包裹到哪了?",
"我买的衣服尺寸不合适,想退货怎么办?",
"这款手机的电池续航怎么样?",
"我的账户好像被盗了,登录不进去。",
"你们网站太卡了,能不能优化一下?",
"你们今天晚餐吃什么?", # 一个不相关的提问
"我有一个关于商品材质的问题" # 另一个商品信息相关提问
]
print("\n--- 开始意图分类测试 ---")
for query in user_queries:
# 使用 chain.invoke() 来执行链,传入用户查询
classified_intent = intent_chain.invoke({"user_query": query})
print(f"用户提问: '{query}' -> 分类意图: '{classified_intent}'")
# TypeScript 代码示例 (概念演示,不直接运行,需配置环境)
"""
// import { PromptTemplate } from "@langchain/core/prompts";
// import { ChatOpenAI } from "@langchain/openai";
// import { StringOutputParser } from "@langchain/core/output_parsers";
// // 设置你的 OpenAI API Key
// // process.env.OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
// console.log("--- 场景一:意图分类 - 让客服小助手准确理解用户想干嘛 ---");
// const intentClassificationTemplate = `
// 你是一名专业的电商客服意图分类机器人。你的任务是根据用户的提问,将其归类到最符合的意图类别中。
// 请从以下可用类别中选择一个,并只返回该类别的名称。如果没有任何类别匹配,请返回 "UNKNOWN"。
// 可用类别:
// - ORDER_STATUS: 查询订单状态、物流信息
// - REFUND_RETURN: 申请退货、退款、查询退换货政策
// - PRODUCT_INFO: 查询商品详情、库存、功能、推荐
// - ACCOUNT_MANAGEMENT: 账户问题、密码重置、个人信息修改
// - TECHNICAL_SUPPORT: 技术故障、网站使用问题
// - COMPLAINT_SUGGESTION: 投诉、建议、反馈
// - OTHER: 其他不属于上述类别的通用问题
// 用户提问: "{user_query}"
// 请选择最匹配的意图类别:
// `;
// const intentPrompt = PromptTemplate.fromTemplate(intentClassificationTemplate);
// const llm = new ChatOpenAI({ modelName: "gpt-3.5-turbo", temperature: 0 });
// const intentChain = intentPrompt.pipe(llm).pipe(new StringOutputParser());
// const userQueries = [
// "我的包裹到哪了?",
// "我买的衣服尺寸不合适,想退货怎么办?",
// "这款手机的电池续航怎么样?",
// "我的账户好像被盗了,登录不进去。",
// "你们网站太卡了,能不能优化一下?",
// "你们今天晚餐吃什么?",
// "我有一个关于商品材质的问题"
// ];
// async function runIntentClassification() {
// console.log("\n--- 开始意图分类测试 ---");
// for (const query of userQueries) {
// const classifiedIntent = await intentChain.invoke({ user_query: query });
// console.log(`用户提问: '${query}' -> 分类意图: '${classifiedIntent}'`);
// }
// }
// runIntentClassification();
"""
运行结果示例:
--- 场景一:意图分类 - 让客服小助手准确理解用户想干嘛 ---
--- 开始意图分类测试 ---
用户提问: '我的包裹到哪了?' -> 分类意图: 'ORDER_STATUS'
用户提问: '我买的衣服尺寸不合适,想退货怎么办?' -> 分类意图: 'REFUND_RETURN'
用户提问: '这款手机的电池续航怎么样?' -> 分类意图: 'PRODUCT_INFO'
用户提问: '我的账户好像被盗了,登录不进去。' -> 分类意图: 'ACCOUNT_MANAGEMENT'
用户提问: '你们网站太卡了,能不能优化一下?' -> 分类意图: 'COMPLAINT_SUGGESTION'
用户提问: '你们今天晚餐吃什么?' -> 分类意图: 'UNKNOWN'
用户提问: '我有一个关于商品材质的问题' -> 分类意图: 'PRODUCT_INFO'
看到了吗?通过一个简单的 PromptTemplate,我们就能让 LLM 完美地完成了意图分类任务!这就是 Prompt Engineering 的魅力。
场景二:回复生成 - 让客服小助手给出专业、友好的回复
在智能客服中,仅仅分类意图是不够的,我们还需要根据用户意图和从知识库中检索到的信息,生成一个有帮助、有礼貌的回复。
假设我们已经识别出用户意图是 ORDER_STATUS,并且已经从数据库中查询到了订单信息(例如:订单号 123456,状态 已发货,预计送达 2023-10-26)。现在,我们来构建一个 Prompt Template,让 LLM 生成最终的客服回复。
# Python 代码示例
print("\n--- 场景二:回复生成 - 让客服小助手给出专业、友好的回复 ---")
# 1. 定义回复生成的 PromptTemplate
# 我们会给模型一个客服的角色,以及一些具体的回复要求和格式
response_generation_template = """
你是一名友善且专业的电商客服,你的任务是根据提供的订单信息,为用户生成一条礼貌、清晰的订单状态查询回复。
请确保回复包含以下信息:
1. 首先向用户问好。
2. 明确告知订单号。
3. 清晰说明当前订单状态。
4. 如果提供了预计送达日期,请一并告知。
5. 提供下一步的建议或安抚,例如“请耐心等待”或“如有其他问题随时联系”。
6. 回复的语气要积极、友好。
订单信息:
订单号: {order_id}
订单状态: {order_status}
预计送达日期: {delivery_date}
用户提问: "{user_query}"
请生成客服回复:
"""
response_prompt = PromptTemplate.from_template(response_generation_template)
# 2. LLM 保持不变,或者根据需要调整 temperature (可以稍微调高一点点,让回复更自然)
llm_for_response = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
# 3. 构建 LangChain 链
response_chain = response_prompt | llm_for_response | StrOutputParser()
# 4. 模拟获取到的订单信息并生成回复
order_data_1 = {
"order_id": "EC20231025001",
"order_status": "已发货",
"delivery_date": "2023年10月28日",
"user_query": "我的订单EC20231025001到哪了?"
}
order_data_2 = {
"order_id": "EC20231024005",
"order_status": "正在打包",
"delivery_date": "暂无明确日期,预计3-5个工作日内发出",
"user_query": "我的订单EC20231024005什么时候能发货?"
}
print("\n--- 开始回复生成测试 ---")
print(f"\n用户提问: '{order_data_1['user_query']}'")
generated_response_1 = response_chain.invoke(order_data_1)
print("客服回复:\n", generated_response_1)
print(f"\n用户提问: '{order_data_2['user_query']}'")
generated_response_2 = response_chain.invoke(order_data_2)
print("客服回复:\n", generated_response_2)
# TypeScript 代码示例 (概念演示,不直接运行,需配置环境)
"""
// import { PromptTemplate } from "@langchain/core/prompts";
// import { ChatOpenAI } from "@langchain/openai";
// import { StringOutputParser } from "@langchain/core/output_parsers";
// // process.env.OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
// console.log("\n--- 场景二:回复生成 - 让客服小助手给出专业、友好的回复 ---");
// const responseGenerationTemplate = `
// 你是一名友善且专业的电商客服,你的任务是根据提供的订单信息,为用户生成一条礼貌、清晰的订单状态查询回复。
// 请确保回复包含以下信息:
// 1. 首先向用户问好。
// 2. 明确告知订单号。
// 3. 清晰说明当前订单状态。
// 4. 如果提供了预计送达日期,请一并告知。
// 5. 提供下一步的建议或安抚,例如“请耐心等待”或“如有其他问题随时联系”。
// 6. 回复的语气要积极、友好。
// 订单信息:
// 订单号: {order_id}
// 订单状态: {order_status}
// 预计送达日期: {delivery_date}
// 用户提问: "{user_query}"
// 请生成客服回复:
// `;
// const responsePrompt = PromptTemplate.fromTemplate(responseGenerationTemplate);
// const llmForResponse = new ChatOpenAI({ modelName: "gpt-3.5-turbo", temperature: 0.7 });
// const responseChain = responsePrompt.pipe(llmForResponse).pipe(new StringOutputParser());
// const orderData1 = {
// order_id: "EC20231025001",
// order_status: "已发货",
// delivery_date: "2023年10月28日",
// user_query: "我的订单EC20231025001到哪了?"
// };
// const orderData2 = {
// order_id: "EC20231024005",
// order_status: "正在打包",
// delivery_date: "暂无明确日期,预计3-5个工作日内发出",
// user_query: "我的订单EC20231024005什么时候能发货?"
// };
// async function runResponseGeneration() {
// console.log("\n--- 开始回复生成测试 ---");
// console.log(`\n用户提问: '${orderData1.user_query}'`);
// const generatedResponse1 = await responseChain.invoke(orderData1);
// console.log("客服回复:\n", generatedResponse1);
// console.log(`\n用户提问: '${orderData2.user_query}'`);
// const generatedResponse2 = await responseChain.invoke(orderData2);
// console.log("客服回复:\n", generatedResponse2);
// }
// runResponseGeneration();
"""
运行结果示例:
--- 场景二:回复生成 - 让客服小助手给出专业、友好的回复 ---
--- 开始回复生成测试 ---
用户提问: '我的订单EC20231025001到哪了?'
客服回复:
您好!您的订单号EC20231025001目前状态为【已发货】,预计将于2023年10月28日送达。请您耐心等待,物流信息更新后会及时通知您。如有其他问题,欢迎随时联系我们!
用户提问: '我的订单EC20231024005什么时候能发货?'
客服回复:
您好!您的订单号EC20231024005目前状态为【正在打包】。抱歉,目前暂无明确的送达日期,但预计将在3-5个工作日内发出。请您放心,我们会尽快处理并安排发货。感谢您的耐心等待!
是不是很酷?我们的客服小助手不仅能识别意图,还能根据具体信息生成个性化、专业且友好的回复了!这就是 Prompt Engineering 带来的力量。通过精心设计的 Prompt,我们就像给 AI 注入了灵魂,让它变得“善解人意”。
场景三:Few-shot Prompting - 举例说明,让模型学得更快更准
有时候,仅仅通过指令描述任务可能不够,模型可能无法完全捕捉到我们期望的细微差别或输出格式。这时,Few-shot Prompting 就派上用场了。通过提供少量高质量的输入-输出示例,我们可以直接向模型展示“你想让它怎么做”。
对于智能客服,Few-shot 可以用来:
- 确保特定类型问题的回复格式一致。
- 指导模型在特定情境下使用特定词汇或语气。
- 帮助模型处理一些边界情况或复杂的用户表达。
# Python 代码示例
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
print("\n--- 场景三:Few-shot Prompting - 举例说明,让模型学得更快更准 ---")
# 1. 定义 Few-shot 示例
# 假设我们希望在用户表达感谢时,模型能给出更温馨、更人性化的回复,而不是简单的“不客气”
examples = [
{
"input": "非常感谢您的帮助,问题解决了!",
"output": "非常高兴能帮到您!如果您未来还有任何疑问,请随时联系我们,我们乐意效劳!"
},
{
"input": "谢谢,你们的服务真棒!",
"output": "感谢您的认可,这是我们最大的动力!期待为您提供更好的服务!"
},
{
"input": "太给力了,谢谢!",
"output": "不客气,很高兴能为您提供帮助!祝您生活愉快!"
},
]
# 2. 定义单个示例的 Prompt Template
# 这个模板用于格式化每个示例的输入和输出
example_formatter_template = """
用户: {input}
客服: {output}
"""
example_prompt = PromptTemplate.from_template(example_formatter_template)
# 3. 定义 FewShotPromptTemplate
# 它会把所有示例和用户实际的查询组合起来
few_shot_prompt = FewShotPromptTemplate(
examples=examples, # 示例列表
example_prompt=example_prompt, # 格式化示例的 Prompt Template
prefix="你是一名友善且乐于助人的客服机器人。请参考以下对话示例,以相似的风格和语气回复用户:\n", # 前缀指令
suffix="\n用户: {user_input}\n客服:", # 后缀,包含用户实际输入和模型要完成的部分
input_variables=["user_input"], # 用户输入变量
example_separator="\n\n" # 示例之间的分隔符
)
# 4. 初始化 LLM (可以稍微调高 temperature,让回复更具创造性)
llm_for_fewshot = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
# 5. 构建 LangChain 链
fewshot_chain = few_shot_prompt | llm_for_fewshot | StrOutputParser()
# 6. 模拟用户输入并获取回复
user_input_1 = "谢谢你们的耐心解答!"
user_input_2 = "你们客服效率真高,谢谢啦!"
print("\n--- 开始 Few-shot 回复生成测试 ---")
print(f"\n用户输入: '{user_input_1}'")
generated_fewshot_response_1 = fewshot_chain.invoke({"user_input": user_input_1})
print("客服回复:\n", generated_fewshot_response_1)
print(f"\n用户输入: '{user_input_2}'")
generated_fewshot_response_2 = fewshot_chain.invoke({"user_input": user_input_2})
print("客服回复:\n", generated_fewshot_response_2)
# TypeScript 代码示例 (概念演示,不直接运行,需配置环境)
"""
// import { FewShotPromptTemplate, PromptTemplate } from "@langchain/core/prompts";
// import { ChatOpenAI } from "@langchain/openai";
// import { StringOutputParser } from "@langchain/core/output_parsers";
// // process.env.OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
// console.log("\n--- 场景三:Few-shot Prompting - 举例说明,让模型学得更快更准 ---");
// const examples = [
// {
// input: "非常感谢您的帮助,问题解决了!",
// output: "非常高兴能帮到您!如果您未来还有任何疑问,请随时联系我们,我们乐意效劳!"
// },
// {
// input: "谢谢,你们的服务真棒!",
// output: "感谢您的认可,这是我们最大的动力!期待为您提供更好的服务!"
// },
// {
// input: "太给力了,谢谢!",
// output: "不客气,很高兴能为您提供帮助!祝您生活愉快!"
// },
// ];
// const examplePrompt = PromptTemplate.fromTemplate(`
//