1. 核心组件 - 少样本提示few-shotting1.1. 概念少样本提⽰是⼀种通过向 LLM 提供少量具体⽰例或样本来教会它如何执⾏某项特定任务的技术。提⾼模型性能的最有效⽅法之⼀是给出⼀个【模型⽰例】指导⼤模型你想做什么、怎么做。下⾯⽤⼀个例⼦解释少样本提⽰的作⽤。参考之前学习的内容我们可以将其想象为“零样本提⽰”直接给模型⼀道考题不给任何例⼦。模型会凭借已有的知识直接回答。例如我们输⼊ 计算2;3?答案可能是from langchain_deepseek import ChatDeepSeek model ChatDeepSeek(modeldeepseek-chat) model.invoke(计算2;3).pretty_print() ######### 运行结果 ######### 好的我们来一步一步分析这个问题。 题目只是写了“计算2;3”但没有指定运算方式。常见的可能性有以下几种 1. **当作数字的拼接** 有时“2;3”可能会被理解为把 2 和 3 放在一起变成 23。不过更常见的写法是用逗号或直接连写。 2. **当做两个独立的数要求求和** 即计算 2 3 5。 3. **当做某种序列或并列输出** 题目可能只是要求写出这两个数本身比如结果是 “2 和 3”。 4. **涉及程序或数学中的分号用法** 在一些编程或数学表达里分号表示语句结束或分隔如果只是求值结果可能就是 2 和 3 两个值。少样本提⽰则是在给出考题前先给它看⼏道类似的、附有正确答案的例题。添加⽰例输⼊和预期输出的技术给到模型提⽰让模型通过例题来理解任务应该怎么做。例如以下样例模型通过样例后可以发现 与 含义相似便可以按照此规则得出?的结果是 5。from langchain_core.messages import HumanMessage, AIMessage from langchain_deepseek import ChatDeepSeek model ChatDeepSeek(modeldeepseek-chat) examples [ HumanMessage(计算1;1), AIMessage(2), HumanMessage(计算5;6), AIMessage(11), HumanMessage(计算2;3), ] model.invoke(examples).pretty_print()运行结果这能解决什么问题呢LLM 虽然知识渊博但有时我们需要它以⾮常特定的格式、⻛格或逻辑来回答问题。提供正确的⽰例可以减少模型“胡说⼋道”或犯低级错误的概率将其输出约束在你提供的范例范围内。举个例⼦更能理解强制要求模型以特定的格式如JSON、XML、特定的列表样式输出结果。样例可以当作格式样板。对于需要多步推理的复杂任务⽰例可以展⽰出思考链引导模型遵循类似的推理路径。有些任务很难⽤⽂字指令清晰描述例如“请⽤莎⼠⽐亚的⻛格写作”。提供⼏个例⼦⽐写⻓篇⼤论的指令更有效。1.2. 实现少样本提示实现少样本提⽰的第⼀步也是最重要的⼀步是提出⼀个好的⽰例数据集。好的⽰例应该在运⾏时相关、清晰、信息丰富并提供模型尚不知道的信息。如我们给出的例⼦就能很好的提⽰⼤模型 ;与 含义相似examples [ {input: 2;2, output: 4}, {input: 2;3, output: 5}, ]如何让⼤模型看懂这份⽰例呢之前我们说过聊天模型读的是聊天消息。因此接下来我们需要将⽰例集实例化成聊天模型可以读懂的聊天消息。对于 LangChain 就需要创建⼀个FewShotChatMessagePromptTemplate对象来实例化示例集。FewShotChatMessagePromptTemplate是一个提示词模板专门用来将示例集实例化为聊天消息用法如下from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate examples [ {input: 2;2, output: 4}, {input: 2;3, output: 5}, ] example_prompt ChatPromptTemplate( [ (human, {input}), (ai, {output}), ] ) few_shot_prompt FewShotChatMessagePromptTemplate( example_prompt example_prompt, # ChatPromptTemplate, 用于格式化单个示例 examples examples, # 样本提示 ) print(few_shot_prompt.invoke({}).to_messages())运行结果先来看看class langchain_core.prompts.few_shot.FewShotChatMessagePromptTemplate,他也实现了标准的Runnable接口。类的初始化参数说明examples样本示例example_promptChatPromptTemplate用于格式化单个示例类的方法说明.invoke()方法此方法与其它Runnable实例的.invoke()方法类似。输入一个字典给它返回完整的PromptValue:PromptValue的to_string()⽅法可以将提⽰值作为【字符串】返回。PromptValue的to_messages()⽅法可以将提⽰作为【消息列表】返回。最后得到示例集消息列表后就可以带上一起发送请求from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate from langchain_deepseek import ChatDeepSeek model ChatDeepSeek(modeldeepseek-chat) examples [ {input: 2;2, output: 4}, {input: 2;3, output: 5}, ] example_prompt ChatPromptTemplate( [ (human, {input}), (ai, {output}), ] ) few_shot_prompt FewShotChatMessagePromptTemplate( example_prompt example_prompt, # ChatPromptTemplate, 用于格式化单个示例 examples examples, # 样本提示 ) final_prompt ChatPromptTemplate( [ (system, 你是一个数学奇才), few_shot_prompt, (human, {input}), ] ) chain final_prompt | model chain.invoke({计算4;19}).pretty_print()运行结果1.3. 使用案例1.3.1. 案例一推理引导我们希望输⼊《教⽗》和《星球⼤战》的导演来⾃同⼀个国家吗让聊天模型可以先分析再得出结论⽽不是直接得出结论。分析过程需要展⽰出来⽰例如下from langchain_core.prompts import FewShotPromptTemplate,ChatPromptTemplate, FewShotChatMessagePromptTemplate, PromptTemplate from langchain_deepseek import ChatDeepSeek #1.定义模型 modelChatDeepSeek(modeldeepseek-chat) #2.创建字符串模板 example_prompt PromptTemplate.from_template(Question: {question}\n{answer}) #3.创建示例集 examples [ { question: 李⽩和杜甫谁更⻓寿, answer: 是否需要后续问题是的。 后续问题李⽩享年多少岁 中间答案李⽩享年61岁。 后续问题杜甫享年多少岁 中间答案杜甫享年58岁。 所以最终答案是李⽩ }, { question: 腾讯的创始⼈什么时候出⽣, answer: 是否需要后续问题是的。 后续问题腾讯的创始⼈是谁 中间答案腾讯由⻢化腾创⽴。 后续问题⻢化腾什么时候出⽣ 中间答案⻢化腾出⽣于1971年10⽉29⽇。 所以最终答案是1971年10⽉29⽇ }, { question: 孙中⼭的外祖⽗是谁, answer: 是否需要后续问题是的。 后续问题孙中⼭的⺟亲是谁 中间答案孙中⼭的⺟亲是杨太夫⼈。 后续问题杨太夫⼈的⽗亲是谁 中间答案杨太夫⼈的⽗亲是杨胜辉。 所以最终答案是杨胜辉 , }, { question: 电影《红⾼粱》和《霸王别姬》的导演来⾃同⼀个国家吗, answer: 是否需要后续问题是的。 后续问题《红⾼粱》的导演是谁 中间答案《红⾼粱》的导演是张艺谋。 后续问题张艺来⾃哪⾥ 中间答案中国。 后续问题《霸王别姬》的导演是谁 中间答案《霸王别姬》的导演是陈凯歌。 后续问题陈凯歌来⾃哪⾥ 中间答案中国。 所以最终答案是是 , }, ] #3.接下来我们需要格式化完整的样本提⽰。 prompt FewShotPromptTemplate( examplesexamples, example_promptexample_prompt, # PromptTemplate,用于格式化单个示例 suffixQuestion: {input}, # 放在示例之后的提示模板字符串 input_variables[input], # 变量的名称列表这些变量的值需要作为提示词的输入 ) #4.生成[HumanMessage]消息列表 prompt_messages prompt.invoke({input: 《教⽗》和《星球⼤战》的导演来⾃同⼀个国家吗}).to_messages() model.invoke(prompt_messages).pretty_print()运行结果1.3.2. 案例二使用示例数据增强LangChain信息提取能力这里我们来实现⼀个基于 LangChain 的结构化信息提取系统专⻔从⽂本中提取⼈物相关信息。例如我们希望对于输⼊以下⽂本篮球场上⾝⾼两⽶的中锋王伟默契地将球传给⼀⽶七的后卫挚友李明完成⼀记绝杀。这对⽼友⽤⼗年配合弥补了⾝⾼的差距。代码会提取出结构化数据如下所示people[ Person(name王伟, height_in_meters2, skin_colorNone, hair_colorNone), Person(name李明, height_in_meters1.7, skin_colorNone,hair_colorNone) ]from typing import List, Optional from pydantic import BaseModel, Field from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.messages import HumanMessage, SystemMessage from langchain_core.utils.function_calling import tool_example_to_messages from langchain_deepseek import ChatDeepSeek # 定义结构化数据 class Person(BaseModel): 个人信息 name: Optional[str] Field(defaultNone, description个人姓名) hair_color: Optional[str] Field(defaultNone, description如果知道这个人的头发颜色) skin_color: Optional[str] Field(defaultNone, description如果知道这个人的肤色) height_in_meters: Optional[str] Field(defaultNone, description以米为单位的身高) class Data(BaseModel): 提取关于人的数据 people: List[Person] # 定义两个简单的示例 examples [ (海洋是⼴阔⽽蓝⾊的。它有两万多英尺深。, Data(people[])), # 没有人物信息的情况 (小明是个湖南人, Data(people[Person(name小明, hair_colorNone, skin_colorNone, height_in_metersNone)])), # 人物信息不完全的情况 ] # 定义提示词模板 prompt_template ChatPromptTemplate( [ SystemMessage(content你是⼀个提取信息的专家只从⽂本中提取相关信息。如果您不知道要提取的属性的值属性值返回null), MessagesPlaceholder(example_messages), (user, {new_message}), ] ) # 构造请求的消息列表 # 样例消息列表 example_messages [] #遍历示例对将每个示例构造消息 for txt, tool_call in examples: # 根据提取结果生成AI响应文本 if tool_call.people: ai_response 检测到人 else: ai_response 未检测到人 # 将示例转化为模型可以认识的消息格式 example_messages.extend(tool_example_to_messages(txt,[tool_call], ai_responseai_response)) # 实例化提示词 formatted_prompt_template prompt_template.invoke({ example_messages: example_messages, new_message: 篮球场上⾝⾼两⽶的中锋王伟默契地将球传给⼀⽶七的后卫挚友李明完成⼀记绝杀。这对⽼友⽤⼗年配合弥补了⾝⾼的差距。 }) # 定义结构化输出模型自动解析为Pydantic对象 model ChatDeepSeek(modeldeepseek-chat) structured_model model.with_structured_output(schemaData) chain prompt_template | structured_model result chain.invoke({ example_messages: example_messages, new_message: 篮球场上⾝⾼两⽶的中锋王伟默契地将球传给⼀⽶七的后卫挚友李明完成⼀记绝杀。这对⽼友⽤⼗年配合弥补了⾝⾼的差距。 }) print(result)运行结果红色部分不用管1.4. 示例选择器Example Selector1.4.1. 概念⼀旦我们有了⽰例数据集就需要考虑提⽰中应该有多少个⽰例。关键的权衡是更多的⽰例通常会 提⾼性能但更⼤的提⽰会增加成本和延迟。超过某个阈值太多⽰例可能会开始混淆模型。找到正确数量的⽰例在很⼤程度上取决于模型、任务、⽰例的质量以及成本和延迟限制。有趣的是 模型越好它需要精准的⽰例就越少。但其实最佳的⽅法是使⽤不同数量的⽰例进⾏⼀些实验。 若此时我们有【⼤量】的⽰例数据集。对于⼤模型来说就没必要全部使⽤与参考。我们需要有⼀种⽅法可以根据给定的输⼊从数据集中选择⽰例。 在 LangChain 中⽰例选择器就可以帮我们从⼀组【⽰例的集合】中根据具体策略选择正确的【⽰例⼦集】构建少样本提⽰。选择的策略有Length根据长度选择Similarity根据予以相似性来选择MMR使用输入和示例之间的最大边际相关性来选择Ngram使用输入和示例之间ngram重叠来选择1.4.2. 按长度选择示例当我们担⼼构造提⽰时将超过上下⽂窗⼝⻓度根据特定⻓度内可以容纳的数量选择⽰例。对于较 ⻓的输⼊它将选择更少的⽰例来包含⽽对于较短的输⼊它将选择更多⽰例。实现按⻓度选择⽰例的⽰例选择器是class langchain_core.example_selectors.length_based.LengthBasedExampleSelector类参数如下example_promptPromptTemplate⽤于格式化⽰例的提⽰模板。examples模板所需的⽰例列表。max_length提示的最大长度get_text_length测量提示长度的方法。默认为字数统计内置方法add_example(dict[str, str])将新示例添加到列表中。select_examples(dict[str, str])-list[dict]根据输⼊⻓度选择要使⽤的⽰例。接下来演⽰⼀下如何使⽤⻓度⽰例选择器让我们给⼀个⽰例集输⼊和输出互为反义词定义PromptTemplate字符串模板包含输⼊和输出两个占位符.定义LengthBasedExampleSelector⻓度⽰例选择器设置初始⽰例集与最⼤⻓度定义⼀个FewShotPromptTemplate模板对象⽤于实例化⽰例将⽰例转化为聊天消息代码如下from html5lib.treeadapters.sax import prefix from langchain.agents.middleware import dynamic_prompt from langchain_core.example_selectors import LengthBasedExampleSelector from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate from LangChain.FewShotting.FewShotChatMessagePromptTemplate1 import example_prompt examples [ {input: happy, output: sad}, {input: tall, output: short}, {input: energetic, output: lethargic}, {input: sunny, output: gloomy}, {input: windy, output: calm}, ] example_prompt PromptTemplate( input_variables[input, output], templateInput: {input}\nOutput: {output}, ) example_selector LengthBasedExampleSelector( examplesexamples, example_promptexample_prompt, max_length25, ) dynamic_prompt FewShotPromptTemplate( example_selectorexample_selector, example_promptexample_prompt, prefix给出每个输入的反义词, suffixInput: {adjective}\nOutput:, input_variables[adjective], ) print( dynamic_prompt.invoke({adjective: big}).to_messages()[0].content )结果如下可以看到⽰例选择器将所有的⽰例都选择了因为还没有超过设置的⽰例最⼤⻓度⻓度计算是根 据字符串中由换⾏符或空格分隔的“单词”数量得到的默认。接下来让我们加⼊⻓输⼊验证⻓度选择器筛选⽰例。代码如下from html5lib.treeadapters.sax import prefix from langchain.agents.middleware import dynamic_prompt from langchain_core.example_selectors import LengthBasedExampleSelector from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate from LangChain.FewShotting.FewShotChatMessagePromptTemplate1 import example_prompt examples [ {input: happy, output: sad}, {input: tall, output: short}, {input: energetic, output: lethargic}, {input: sunny, output: gloomy}, {input: windy, output: calm}, ] example_prompt PromptTemplate( input_variables[input, output], templateInput: {input}\nOutput: {output}, ) example_selector LengthBasedExampleSelector( examplesexamples, example_promptexample_prompt, max_length25, ) dynamic_prompt FewShotPromptTemplate( example_selectorexample_selector, example_promptexample_prompt, prefix给出每个输入的反义词, suffixInput: {adjective}\nOutput:, input_variables[adjective], ) long_string ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常⼤⽐其他任何东西都要⼤得多1111111111111111111111111111111111111111111111 print( dynamic_prompt.invoke({adjective: long_string}).to_messages()[0].content )结果如下1.4.3. 按语义相似性选择示例1.4.3.1. 概念什么是语义相似性它是衡量文本在含义上的接近程度。例如下面两段文本“我喜欢狗”“我讨厌猫”这两段文本相似度低但是语义上都是对动物的态度。LangChain 能根据输⼊和⽰例之间的语义相似性来决定选择哪些⽰例它通过查找与输⼊具有最⼤余弦相似性的嵌⼊⽰例来实现这⼀点。1.4.3.2. 实现实现按语义相似性选择示例的示例选择器是class langchain_core.examples_selectors.semantic_similarity.SemticSimlarityExampleSelector类内置方法from_examples()根据⽰例集⽣成语义相似⽰例选择器输入examples示例列表 embeddins初始化的嵌入API接口如OpenAIEmbeddings() vectorstore_cls向量数据库接口类k最终要选择的示例数量默认为4.输出语义相似性的示例选择器。add_example(example: dict[str, str])将新⽰例添加到列表中。输入一个字典其中键作为输入变量值作为其值。select_examples(input_variables: dict[str, str])→list[dict]根据输⼊选择要使⽤的⽰例。输入一个字典其中键作为输入变量值作为其值。输出要包含在提示中的示例列表。接下来演⽰⼀下如何使⽤语义相似⽰例选择器让我们1.给⼀个⽰例集输⼊和输出互为反义词2.定义PromptTemplate字符串模板包含输⼊和输出两个占位符3.定义SemanticSimilarityExampleSelector语义相似⽰例选择器设置初始⽰例集与嵌⼊ API 接⼝4.定义⼀个FewShotPromptTemplate模板对象⽤于实例化⽰例将⽰例转化为聊天消息5.打印消息结果from langchain_chroma import Chroma from langchain_core.example_selectors import SemanticSimilarityExampleSelector from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate from langchain_openai import OpenAIEmbeddings from LangChain.FewShotting.FewShotChatMessagePromptTemplate1 import example_prompt # 反义词示例集合 examples [ {input: happy, output: sad}, {input: tall, output: short}, {input: energetic, output: lethargic}, {input: sunny, output: gloomy}, {input: windy, output: calm}, ] # 字符串模板 example_prompt PromptTemplate( input_variables[input, output], templateInput: {input}\nOutput: {output}, ) # 语义相似性示例选择器 example_selsector SemanticSimilarityExampleSelector.from_examples( examples, OpenAIEmbeddings(), Chroma, k1, ) similar_prompt FewShotPromptTemplate( example_selectorexample_selsector, # 注意参数名 example_promptexample_prompt, prefix给出每个输入的反义词, suffixInput: {adjective}\nOutput:, input_variables[adjective], ) print( similar_prompt.invoke({adjective: worried}).to_messages()[0].content )运行结果1.4.4.按照最大边际相关性选择示例(MMR)1.4.4.1. 概念什么是最⼤边际相关性它是⼀种重新排序算法它使⽤语义相似性作为基础⼯具从⼀个候选集中挑选出⼀组既能代表查询主题⼜彼此多样化的结果。听起来好像和语义相似性类似⽤⼀个例⼦看下两者的区别【语义相似性】就像⾯试官衡量每个应聘者与职位要求的匹配度。他会给每个应聘者打⼀个分数【最⼤边际相关性】就像团队经理MMR算法要组建⼀个团队。⽬标是选出⼀组“精华”结果⽽不是⼀个单⼀结果每个成员都要满⾜基本职位要求满⾜相关性。但经理不希望团队⾥全是只会⼀种技能的程序员。他需要前端、后端、算法、测试等不同专长的⼈以确保团队能⼒全⾯、减少冗余新颖性/多样性。经理的策略是先招⼀个最匹配的技术⼤⽜第⼀步然后接下来招的⼈既要技术达标 ⼜要和已招的⼈技能互补迭代过程。了解下使⽤最⼤边际相关性的场景更能让我们理解其概念语义相似性使⽤场景搜索引擎的基础排序、重复检测、聚类、语义搜索。MMR 使⽤场景推荐系统推荐与⽤⼾兴趣相关但⼜不同类型的物品避免“信息茧房”。⽂档摘要从⻓⽂档中选择能代表主旨⼜包含不同信息的句⼦避免摘要内容重复。RAG (检索增强⽣成)在从知识库检索完⼀堆相关⽂档后使⽤ MMR 进⾏去重和多样化筛选再交给LLM⽣成答案能有效提升答案质量和减少幻觉。1.4.4.2. 实现了解了相关概念后LangChain 提供了按最⼤边际相关性选择⽰例的能⼒该⽰例选择器是class langchain_core.example_selectors.semantic_similarity.MaxMarginalRelevan ceExampleSelector类内置⽅法from_examples()根据⽰例集⽣成 MMR ⽰例选择器输⼊examples ⽰例列表embeddings 初始化的嵌⼊ API 接⼝如OpenAIEmbeddings()vectorstore_cls向量存储数据库接⼝类。k 最终要选择的⽰例的数量。默认值为 4。输出MMR ⽰例选择器add_example(example: dict[str, str])将新⽰例添加到列表中。输⼊⼀个字典其中键作为输⼊变量值作为其值。select_examples(input_variables: dict[str, str])→list[dict]根据输⼊选择要使⽤的⽰例。◦输⼊⼀个字典其中键作为输⼊变量值作为其值。输出要包含在提⽰中的⽰例列表。接下来演⽰⼀下如何使⽤ MMR ⽰例选择器让我们给⼀个⽰例集输⼊和输出互为反义词定义PromptTemplate字符串模板包含输⼊和输出两个占位符定义MaxMarginalRelevanceExampleSelectorMMR ⽰例选择器设置初始⽰例集与嵌⼊API 接⼝定义⼀个FewShotPromptTemplate模板对象⽤于实例化⽰例将⽰例转化为聊天消息打印消息结果from langchain_chroma import Chroma from langchain_core.example_selectors import MaxMarginalRelevanceExampleSelector from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate from langchain_openai import OpenAIEmbeddings # 反义词⽰例集合 examples [ {input: happy, output: sad}, {input: tall, output: short}, {input: energetic, output: lethargic}, {input: sunny, output: gloomy}, {input: windy, output: calm}, ] # 字符串模板 example_prompt PromptTemplate( input_variables[input, output], templateInput: {input}\nOutput: {output}, ) # MMR ⽰例选择器 example_selector MaxMarginalRelevanceExampleSelector.from_examples( examples, OpenAIEmbeddings(), Chroma, k2, ) # ⽤于实例化⽰例的模板 similar_prompt FewShotPromptTemplate( # 提供了⼀个ExampleSelector⽽不是examples。 example_selectorexample_selector, example_promptexample_prompt, prefix给出每个输⼊的反义词, suffixInput: {adjective}\nOutput:, input_variables[adjective], ) print( similar_prompt.invoke({adjective: worried}).to_messages()[0].content )运行结果给出每个输⼊的反义词 Input: happy Output: sad Input: windy Output: calm Input: worried Output: