使用 Instructor 实现 Uncertainty-Routed Chain of Thought:以不确定性路由提升 LLM 结构化推理的置信度
【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor
导读
Uncertainty-Routed Chain of Thought(不确定性路由思维链)是一种源自 Gemini 论文的自一致性质询增强技术:通过并行生成多条思维链、对其多数答案进行投票,并仅在一致性达到阈值时才采纳该结果,从而显著提升复杂问答场景下的输出可靠性。本文将以 Instructor 项目中的官方实现为骨架,逐步拆解其算法原理、完整可运行代码,并深入from_provider与create的源码实现,帮助你掌握在结构化输出场景下落地这一技术的完整方法。
什么是 Uncertainty-Routed Chain of Thought
传统的 Chain of Thought(思维链)prompting 通过引导模型"逐步思考"来提升推理质量,但它只产生一条推理路径,模型的单次采样既可能受随机性影响,也可能在推理中途走偏。Uncertainty-Routed Chain of Thought 的核心思想是:让模型并行生成多条思维链,只有当这些链对最终答案达成足够高的一致性时,才把多数答案作为最终解输出;若一致性不足,则说明模型对该问题"不确定",此时再单独生成一条答案作为兜底。
从算法角度看,该方法包含三个关键组成部分:
- 多链采样:对同一个问题生成多条 chain of thought 推理链(在 Gemini 论文中通常为 8 或 32 条);
- 多数投票:统计各条链给出的最终答案,取出现频率最高的答案作为候选解;
- 阈值路由:只有当同意该多数答案的链所占比例高于预设阈值时,才采纳多数答案;否则触发一次额外的生成作为最终响应。
这一思路与 Self-Consistency(自一致性) 一脉相承,区别在于:Self-Consistency 无条件地把多数答案当作最终解,而 Uncertainty-Routed CoT 额外引入了一个"置信度门槛",把不确定的场景显式地路由到重新生成的分支上。正如 提示技术总览 中所归纳的,两者都属于"处理不确定性"类别的技术,适合决策依赖高置信度输出的任务。
算法流程总览
Uncertainty-Routed CoT 的完整流程可以概括为以下步骤:
生成 k 条思维链(并行) ↓ 统计各条链的 correct_answer(多数投票) ↓ majority_count / k ≥ threshold ? ├─ 是 → 输出多数答案(高置信度路径) └─ 否 → 单独再生成一次,输出新答案(不确定兜底路径)在后续的代码实现中,我们将k设为 8、threshold设为 0.6。从源码结构看,这套流程完全可以用 Instructor 的异步客户端 + Pydantic 响应模型轻松落地,无需任何自定义解析逻辑。
完整实现:基于 Instructor 的 Uncertainty-Routed CoT
以下代码来自仓库文档 uncertainty_routed_cot.md,它演示了如何在一次生物学选择题场景中应用该技术:
from pydantic import BaseModel import instructor from textwrap import dedent from typing import Literal import asyncio from collections import Counter client = instructor.from_provider("openai/gpt-5-nano", async_client=True) class ChainOfThoughtResponse(BaseModel): chain_of_thought: str correct_answer: Literal["A", "B", "C", "D"] async def generate_response(query: str, options: dict[str, str]): formatted_options = "\n".join( [f"{key}:{answer}" for key, answer in options.items()] ) return await client.create( model="gpt-4o", response_model=ChainOfThoughtResponse, messages=[ { "role": "system", "content": dedent( f""" You are a world class AI who excels at answering complex questions. Choose one of the options below that best answers the question you are about to be asked <question> {query} </question> <options> {formatted_options} </options> """ ), } ], ) async def generate_batch_responses( query: str, options: dict[str, str], num_chains: int ) -> list[ChainOfThoughtResponse]: coros = [generate_response(query, options) for _ in range(num_chains)] return await asyncio.gather(*coros) if __name__ == "__main__": question = """In a population of giraffes, an environmental change occurs that favors individuals that are tallest. As a result, more of the taller individuals are able to obtain nutrients and survive to pass along their genetic information. This is an example of""" options = { "A": "directional selection", "B": "stabilizing selection", "C": "sexual selection", "D": "disruptive selection", } correct_answer = "A" k = 8 threshold = 0.6 responses = asyncio.run(generate_batch_responses(question, options, k)) votes = Counter([response.correct_answer for response in responses]) print(votes) #> Counter({'A': 8}) majority_vote_element, majority_vote_count = votes.most_common(1)[0] print(majority_vote_element, majority_vote_count) #> A 8 majority_threshold = majority_vote_count / k if majority_threshold < threshold: response = asyncio.run(generate_response(question, options)) response = response.correct_answer else: response = majority_vote_element print(response) #> A逐段拆解:每一行代码背后的设计意图
1. 用 Pydantic 模型固化推理结构
class ChainOfThoughtResponse(BaseModel): chain_of_thought: str correct_answer: Literal["A", "B", "C", "D"]这是整个方案的结构化基石。chain_of_thought字段强制模型先输出完整推理过程,correct_answer则用Literal["A", "B", "C", "D"]把答案约束在四个选项中。Instructor 会把该 Pydantic 模型编译成工具调用 schema 或 JSON Schema,从而保证每条思维链都返回合法且可统计的答案——这正是投票环节能够直接使用Counter的前提。如果答案字段不加以类型约束,LLM 可能返回格式各异的自由文本,多数投票就会失效。
2. 客户端初始化:from_provider的异步模式
client = instructor.from_provider("openai/gpt-5-nano", async_client=True)from_provider是 Instructor 的统一客户端工厂。根据源码 instructor/v2/auto_client.py 的实现,其第一个参数要求"provider/model-name"格式的模型字符串(如"openai/gpt-4"、"anthropic/claude-3-sonnet"),函数内部通过model.split("/", 1)解析出提供商与模型名,然后路由到对应的提供商实现;async_client=True则返回AsyncInstructor实例,使我们可以用await client.create(...)进行异步调用。
其完整签名还支持以下参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
model | 必填 | 形如"provider/model-name"的模型标识符 |
async_client | False | 置为True时返回异步客户端(本技术必需,用于并行采样) |
cache | None | 可传入缓存适配器(如AutoCache、RedisCache)实现透明响应缓存,会透传到各 provider 实现 |
mode | None | 覆盖该 provider 的默认调用模式(tool calling / JSON mode 等),不传则使用推荐默认值 |
3. 单次思维链生成:generate_response
return await client.create( model="gpt-4o", response_model=ChainOfThoughtResponse, messages=[...], )这里通过create完成一次带结构化输出的调用。查看核心客户端实现 instructor/v2/core/client.py 可以看到,create统一接收response_model、context、max_retries(默认 3 次重试)、strict(默认True,强制严格 schema 校验)、token_budget以及透传给底层 provider 的**kwargs,最终调用底层 SDK 的create并返回已通过 Pydantic 验证的实例。
messages中使用dedent组织系统提示词:要求模型"作为世界级 AI,从选项中选出最佳答案",并把题目与选项用<question>、<options>标签包裹。这一提示词结构本身不依赖特定格式约定,是易读且便于维护的实践。
4. 并行采样:generate_batch_responses
coros = [generate_response(query, options) for _ in range(num_chains)] return await asyncio.gather(*coros)这是实现"一次生成 k 条链"的关键。列表推导式创建num_chains个协程,asyncio.gather并发执行它们,使 8 条(或 32 条)思维链的生成几乎同时完成,避免串行调用带来的线性延迟。异步客户端在这里是不可或缺的:同步客户端无法并发地发出请求。
5. 投票与阈值路由
votes = Counter([response.correct_answer for response in responses]) majority_vote_element, majority_vote_count = votes.most_common(1)[0] majority_threshold = majority_vote_count / k if majority_threshold < threshold: response = asyncio.run(generate_response(question, options)) response = response.correct_answer else: response = majority_vote_elementCounter对 8 条链的答案计数,most_common(1)取出票数最多的答案及其票数,majority_vote_count / k计算多数答案的得票比例。当该比例低于threshold(本例为 0.6)时,说明模型内部意见分歧较大、整体置信度不足,此时走"兜底分支"——单独再生成一次并取其答案;否则直接采用多数答案。示例中 8 条链全部输出"A",得票比例 1.0 远超阈值,因此直接输出A。
6. 关键参数的可调空间
| 参数 | 本例取值 | 作用与调参方向 |
|---|---|---|
k(采样链数) | 8 | 越大越接近真实投票分布,但成本线性上升;Gemini 论文中取 8 或 32 |
threshold | 0.6 | 采纳多数答案所需的最小一致性比例;越高越保守、触发兜底分支越频繁 |
temperature | 未显式设置 | 若希望链间存在足够多样性,可参考 self_consistency.md 中的做法显式传入(如temperature=0.5)作为**kwargs透传 |
从源码结构看,temperature等采样参数通过create的**kwargs直接透传给底层 provider SDK,因此无需额外封装即可在generate_response中按需添加。
何时使用 Uncertainty-Routed CoT
结合仓库 提示技术总览 中的技术地图,这一技术最适合以下场景:
- 高错误成本决策:答案错误代价高昂(如医疗、金融、审核类任务),需要显式置信度门槛;
- 多选题/分类任务:答案域可枚举(如
Literal约束的选项),投票统计天然可行; - 已有异步基础设施:
asyncio.gather的并发收益需要异步客户端支撑,适合已经采用异步 I/O 的服务。
若你的场景允许无条件信任多数意见,可退化为更简单的 Self-Consistency;若你想根据"各链一致性"自适应地决定是否再采样,本技术正是从不确定性角度给出的答案。此外,你还可以将它与 Memory of Thought 等强调高置信度示例的技术组合使用,进一步增强关键场景下的稳定性。
参考来源
本文基于仓库文档 uncertainty_routed_cot.md 撰写,技术背景源自论文Gemini: A Family of Highly Capable Multimodal Models,源码依据见 instructor/v2/auto_client.py 与 instructor/v2/core/client.py。相关技术对照可参考 self_consistency.md 与 提示技术总览。
【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考