使用 Instructor 与 KNN 为 Few-Shot 提示词选择高效 In-Context 示例

使用 Instructor 与 KNN 为 Few-Shot 提示词选择高效 In-Context 示例 使用 Instructor 与 KNN 为 Few-Shot 提示词选择高效 In-Context 示例【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor导读本文讲解如何基于语义相似度K 近邻KNN为 Few-Shot 提示词自动挑选最相关的 In-Context 示例先用嵌入模型将候选示例与用户查询映射到向量空间再找出与查询距离最近的 k 个示例作为上下文注入提示词。通过结合instructor的结构化输出能力Pydantic 响应模型 重试机制你将掌握一套示例检索 → 模板组装 → 结构化回答的完整链路可在问答、分类、信息抽取等场景中直接落地。为什么示例选择决定 Few-Shot 效果Few-Shot 提示词的效果强依赖示例的质量与相关性。固定的示例集合在面对不同查询时往往只有一部分与当前问题语义接近其余示例不仅无益还可能把模型带偏。在 示例排序 文档中也可以看到LLM 输出对示例的选取与顺序都非常敏感。KNNK-Nearest Neighbors提供了一种简单而有效的自动筛选思路不把所有示例都塞进提示词而是为每一次查询动态挑选语义上最接近的 k 个示例。在 提示词技术索引 中该方法被归类为 Few-Shot 技术下的 KNN Example Selection定位是选择与查询相似的示例以获得领域相关的准确性Domain-specific accuracy。方法总览四个步骤以下实现以instructor为核心整体流程如下嵌入示例将候选示例的问题文本通过嵌入模型转换为向量嵌入查询将当前用户查询转换为同一向量空间中的向量KNN 检索计算查询向量与所有示例向量的距离取出距离最小的 k 个示例组装上下文将选出的示例连同其答案格式化后注入提示词交给 LLM 生成结构化回答。每一步都可以独立复用前三步相当于一个检索器第四步则是标准的instructor结构化调用。完整实现KNN 示例检索 Instructor 结构化输出下面代码来自仓库文档 docs/prompting/few_shot/exemplar_selection/knn.md我们结合仓库源码逐步拆解。import instructor from pydantic import BaseModel from openai import OpenAI import math from textwrap import dedent class Example(BaseModel): question: str answer: str class Response(BaseModel): answer: str oai OpenAI() client instructor.from_provider(openai/gpt-4o) def distance(a: list[float], b: list[float]): return 1 - sum(ai * bi for ai, bi in zip(a, b)) / ( math.sqrt(sum(ai**2 for ai in a)) * math.sqrt(sum(bi**2 for bi in b)) ) def embed_queries(queries: list[str]) - list[tuple[list[float], str]]: return [ (embedding_item.embedding, query) for embedding_item, query in zip( oai.embeddings.create(inputqueries, modeltext-embedding-3-large).data, queries, ) ] def knn( embedded_examples: list[tuple[list[float], str]], query_embedding: list[float], k: int, ): distances [ (distance(embedding, query_embedding), example) for embedding, example in embedded_examples ] distances.sort(keylambda x: x[0]) return distances[:k] def generate_response(examples: list[str], query: str): formatted_examples \n.join(examples) return client.create( modelgpt-4o, response_modelResponse, messages[ { role: user, content: dedent( f Respond to the following query with the most accurate and concise answer possible. examples {formatted_examples} /examples query {query} /query ), } ], ) def generate_question_and_answer_pair( questions: list[str], question_and_answers: list[dict[str, str]] ) - list[str]: question_to_answer {} for question in question_and_answers: question_to_answer[question[question]] question[answer] return [ dedent( f example question{question}/question answer{question_to_answer[question]}/answer /example ) for question in questions ] if __name__ __main__: examples [ {question: What is the capital of France?, answer: Paris}, {question: Who wrote Romeo and Juliet, answer: Shakespeare}, {question: What is the capital of Germany?, answer: Berlin}, ] query What is the capital of Italy? # Step 1 : Embed the Examples embeddings embed_queries([example[question] for example in examples] [query]) embedded_examples embeddings[:-1] embedded_query embeddings[-1] # Step 3: Find the k closest examples to the query k_closest_examples knn(embedded_examples, embedded_query[0], 2) for example in k_closest_examples: print(example) # (0.4013468481736857, What is the capital of France?) # (0.4471368596136872, What is the capital of Germany?) # Step 4: Use these examples as in-context examples formatted_examples generate_question_and_answer_pair( [example[1] for example in k_closest_examples], examples ) response generate_response(formatted_examples, query) print(response.answer) # Rome关键细节解析1. 结构化响应模型class Example(BaseModel): question: str answer: str class Response(BaseModel): answer: strResponse声明了 LLM 输出的目标结构instructor会据此约束模型只返回一个answer字段。这正是instructor与普通提示词调用的本质区别——响应模型response_model让结果天然可校验、可反序列化。2. 客户端初始化oai OpenAI() client instructor.from_provider(openai/gpt-4o)这里有两个客户端oai用于调用 OpenAI 嵌入接口/v1/embeddingsclient是经过instructor包装的结构化输出客户端。from_provider接受provider/model-name形式的字符串如openai/gpt-4o其实现位于 instructor/v2/auto_client.py除了model外还支持async_client返回异步客户端、cache透明响应缓存例如AutoCache/RedisCache与mode覆盖 provider 默认模式等参数。3. 余弦距离语义相似度的度量def distance(a: list[float], b: list[float]): return 1 - sum(ai * bi for ai, bi in zip(a, b)) / ( math.sqrt(sum(ai**2 for ai in a)) * math.sqrt(sum(bi**2 for bi in b)) )该函数计算的是1 - 余弦相似度即余弦距离。余弦相似度度量两个向量方向的接近程度与向量长度无关当两个向量方向完全一致时相似度为 1、距离为 0正交时为 1方向相反时为 2。距离越小代表示例与查询在语义上越接近。这里没有依赖 scikit-learn 等第三方库纯标准库实现便于直接复制使用。4. 批量嵌入与 KNN 检索def embed_queries(queries: list[str]) - list[tuple[list[float], str]]: return [ (embedding_item.embedding, query) for embedding_item, query in zip( oai.embeddings.create(inputqueries, modeltext-embedding-3-large).data, queries, ) ]一次 API 调用即可批量嵌入所有文本返回(向量, 原文)元组列表避免向量与文本顺序错位。主程序中把示例与查询合并成一次嵌入请求embeddings[:-1]是示例、embeddings[-1]是查询既省一次往返又保证它们处于同一向量空间。def knn(embedded_examples, query_embedding, k): distances [ (distance(embedding, query_embedding), example) for embedding, example in embedded_examples ] distances.sort(keylambda x: x[0]) return distances[:k]对每个示例计算与查询的距离按距离升序排序后取前 k 个。示例输出中可以看到与 What is the capital of Italy? 距离最近的两个示例分别是法国首都0.4013和德国首都0.4471而莎士比亚0.4471 之后被自动排除——KNN 的核心价值就在这里示例随查询动态变化。5. 模板组装将示例还原为 (问题, 答案) 对检索出的结果只是问题文本需要回到原始示例数据中取回对应答案再格式化为 XML 风格的example块formatted_examples generate_question_and_answer_pair( [example[1] for example in k_closest_examples], examples )最终提示词把选中的问答对放入examples标签、把查询放入query标签并明确要求给出最准确、最简洁的回答。dedent负责去掉多行字符串的缩进保证模板整洁。6. 结构化生成response client.create( modelgpt-4o, response_modelResponse, messages[...], ) print(response.answer) # Rome最终模型输出被反序列化为Response对象直接访问.answer即可拿到Rome。client.create的核心参数messages、response_model、max_retries、strict等可参见 instructor/v2/core/client.py 中Response.create的签名max_retries默认 3支持传入tenacity.Retrying实例自定义重试策略strict默认True启用严格模式。工程化要点与扩展方向1. 示例库规模化时的注意点当候选示例较多时每次查询都做一次全量两两距离计算代价会上升。可以预先对所有示例做一次嵌入并持久化向量数据库或本地缓存instructor的cache参数也支持响应级缓存查询时只对查询文本做一次嵌入再对预计算的向量做 KNN。小规模场景下当前实现已经足够。2. 与仓库内其他 Few-Shot 技术互补示例生成SG-ICL当领域内示例不足时可先用 LLM 生成示例再走 KNN 挑选参见 docs/prompting/few_shot/example_generation/sg_icl.md示例排序Example OrderingKNN 决定选哪些示例排序决定怎么排两者可组合使用参见 docs/prompting/few_shot/example_ordering.mdKATE 变体example_ordering.md中提到的 KATEk-Nearest Example Tuning是 KNN 思想的进阶——为测试集多个查询分别检索 k 个最近邻再统计跨查询出现频率最高的示例作为全局最佳示例集合Vote-K仓库 docs/prompting/few_shot/exemplar_selection/vote_k.md 记录了基于相似度的进阶选择思路当前为 WIP 状态。3. 结构化输出的可靠性收益本例中instructor的价值在于把回答必须是简洁事实这一软性要求固化为Response结构约束。由于max_retries默认开启即使模型偶尔输出非结构化文本instructor也会基于 Pydantic 校验自动重试这在批量离线生成、自动化评测等场景中能显著降低脏数据比例。适用场景与限制场景推荐度说明知识问答如本例首都查询高问题语义相近答案格式统一分类 / 情感分析高示例类别对应清晰KNN 检索稳定生成类任务写作、翻译中语义相似不一定代表风格相似可结合人工筛选示例库很小 10 条低KNN 收益有限全量注入更简单需要留意的是KNN 的效果依赖嵌入模型的质量与领域匹配度本例使用text-embedding-3-large且检索的是问题之间的相似度若示例与查询在措辞上差异巨大语义向量可能无法完全捕捉任务意图此时可考虑给示例补充标签或描述后再嵌入。参考资料What Makes Good In-Context Examples for GPT-3?arXiv:2101.06804论证了与测试输入语义相近的训练样本能显著提升 GPT-3 的 few-shot 性能是 KNN 示例选择方法的核心依据The Prompt Report: A Systematic Survey of Prompting TechniquesarXiv:2406.06608系统综述了包括示例选择在内的各类提示词技术仓库内完整技术图谱与其余 50 提示词技术的用法可查阅 docs/prompting/index.md。【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考