导读 :在与大模型交互的过程中,简单的"一问一答"往往难以胜任复杂的推理任务。本文将深入介绍三种提升大模型推理能力的核心技术------思维链(Chain of Thought, COT) 、自我一致性(Self-Consistency) 和 ReAct 模式。从"让模型先想再答"的基本思路,到"多次采样投票取最优"的暴力美学,再到"推理与行动交织"的 Agent 底层逻辑,逐步带你掌握 Prompt 高级推理的完整技能树。
一、思维链 COT:让模型先想再答
1.1 什么是思维链?
思维链(Chain of Thought,简称 COT)的核心思想非常直观:强制让模型在输出最终答案之前,先把推理过程写出来。
举个简单的例子:
小明有 5 个苹果,给了小红 2 个,又从超市买了 3 个,后来发现有 1 个烂了。请问小明现在有几个苹果?
人类的大脑会自动进行分步计算:5 - 2 + 3 - 1 = 5。但没有思维链的大模型可能会"凭感觉"直接给出一个数字,准确率难以保证。
1.2 为什么 COT 有效?
大模型生成 Token 是一个自回归过程------后面的 Token 依赖前面已生成的内容。当模型先写出:
- 步骤 1:5 - 2 = 3
- 步骤 2:3 + 3 = 6
- 步骤 3:6 - 1 = 5
再生成最终答案时,答案的生成依赖于前面正确的推理链路,整体准确率自然就提升了。这就好比考试时要求你"写出解题过程"------过程对了,答案大概率不会错。
1.3 Zero-Shot COT:一句话激活思维链
最简单的使用方式,不需要给任何示例,只需在 Prompt 末尾加上一句"魔法咒语":
请一步一步思考。
示例对比:
不加 COT 的 Prompt:
一个项目有三名开发,每人每天完成两个功能点,项目共60个功能点,
其中20%需要双人协作完成,完成项目需要多少天?
加上 COT 的 Prompt:
一个项目有三名开发,每人每天完成两个功能点,项目共60个功能点,
其中20%需要双人协作完成,完成项目需要多少天?
让我们一步一步思考。
加上 COT 后,模型会自动拆解:先计算单人功能点数,再计算协作功能点的耗时,最后汇总得出正确的总天数。
常用的 COT 触发语句包括:
让我们一步一步思考请先分析再给出结论请展示你的推理过程Think step by step
1.4 Few-Shot COT:用示例教会模型推理
当零样本效果不够理想时,可以通过提供带推理过程的示例来引导模型学习思考模式。
以"判断代码是否存在并发安全问题"为例,我们项目中的 code-review.st 模板(位于 prompt/src/main/resources/prompts/code-review.st)就使用了类似的思路:
请 review 以下 {language} 代码:
{code}
检查重点:
1. 空指针和异常处理
2. 性能问题(循环、IO、数据库查询)
3. 并发安全
4. 资源释放(IO流、连接等)
5. 代码可读性
对每个问题:标注【严重程度】,说明原因,给出修复示例。
在 CodeReviewService(com.jichi.prompt.service.CodeReviewService)中,我们通过 PromptTemplate 加载这个模板,并结合 System Message 引导模型按 COT 风格逐步分析:
@Service
public class CodeReviewService {
private final DashScopeChatModel chatModel;
@Value("classpath:prompts/code-review.st")
private Resource codeReviewPromptResource;
public CodeReviewService(DashScopeChatModel chatModel) {
this.chatModel = chatModel;
}
public String review(String code, String language) {
PromptTemplate pt = new PromptTemplate(codeReviewPromptResource);
String userPrompt = pt.render(Map.of(
"language", language,
"code", code
));
return chatModel.call(new Prompt(
List.of(
new SystemMessage("""
你是一个资深工程师,专注代码质量。
找出 Bug、性能问题和最佳实践违反,每个问题标注严重程度。
"""),
new UserMessage(userPrompt)
)
)).getResult().getOutput().getText();
}
}
通过 System Message 中"找出 Bug、性能问题和最佳实践违反,每个问题标注严重程度"的指令,模型会按照"操作拆解 -> 场景分析 -> 风险判断"的链路来逐一分析。
1.5 COT + 结构化输出:推理完毕打包带走
在实际工程中,我们往往需要模型先推理,再输出结构化的结果 ,以便程序直接消费。在项目中,BugAnalysisController(com.jichi.prompt.controller.BugAnalysisController)就是一个典型案例:
@RestController
@RequestMapping("/api/bug")
public class BugAnalysisController {
private final ChatClient chatClient;
public BugAnalysisController(DashScopeChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel)
.defaultSystem("""
你是一个资深 Java 工程师,擅长 bug 分析。
分析代码时,先推理出 bug 类型和根因,再填写结构化结论。
不确定的字段填 null。
""")
.build();
}
record BugAnalysis(
String bugType,
String rootCause,
List<String> affectedScenarios,
String severity,
String fix
) {}
@PostMapping("/analyze")
public BugAnalysis analyzeBug(@RequestBody String code) {
return chatClient.prompt()
.user("分析这段代码的 bug:\n\n" + code)
.call()
.entity(BugAnalysis.class);
}
}
注意 System Prompt 中的关键语句:"先推理出 bug 类型和根因,再填写结构化结论"------这就是 COT 与结构化输出的结合。模型会先进行推理分析,然后输出类似这样的结构化结果:
{
"bugType": "并发安全问题",
"rootCause": "count++ 不是原子操作,多线程下会产生数据竞争",
"affectedScenarios": ["高并发计数", "多线程累加"],
"severity": "HIGH",
"fix": "使用 synchronized 关键字或 AtomicInteger 替代 int 类型"
}
Spring AI 的 .entity(BugAnalysis.class) 会自动将模型的 JSON 输出反序列化为 Java Record,非常适合在企业内部构建代码审核助手。
1.6 COT 的适用场景与局限
适合使用 COT 的场景:
| 场景 | 原因 |
|---|---|
| 数学逻辑计算 | 需要中间步骤保证准确性 |
| 代码 Bug 分析 | 需要追踪执行路径 |
| 法律/合同分析 | 需要逐条审查条款 |
| 需求分析与拆解 | 需要先拆分再逐一评估 |
| 复杂决策 | 需要衡量多个因素 |
不适合使用 COT 的场景:
- 简单翻译、简单分类
- 纯创意写作
- 追求极致响应速度的场景
需要注意的是,COT 会增加 Token 消耗。只有在对准确率有较高要求的场景下,才值得开启思维链。
1.7 内置思考模式:COT 的进化
如今主流模型已经将 COT 能力内置到模型内部 ,无需在 Prompt 中显式要求。在项目的 ThinkingController(com.jichi.prompt.controller.ThinkingController)中,我们通过开启 enableThinking 参数来使用内置思考模式:
@RestController
@RequestMapping("/api/thinking")
public class ThinkingController {
private final DashScopeChatModel chatModel;
public ThinkingController(DashScopeChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/qwen3")
public String deepAnalysis(@RequestParam String question) {
return chatModel.call(new Prompt(
new UserMessage(question),
DashScopeChatOptions.builder()
.withModel("qwen3-235b-a22b") // Qwen3 支持思考模式的模型
.withEnableThinking(true) // 开启内置思考模式
.build()
)).getResult().getOutput().getText();
}
}
内置思考模式与手动 COT 的区别在于:手动 COT 的推理过程会出现在普通输出中,而内置思考模式(withEnableThinking(true))的推理过程对用户不可见,但效果通常更好。
二、Self-Consistency:多次推理取最优答案
2.1 核心思想:一次算不对,多算几次
Self-Consistency(自我一致性)是一种"稍显暴力但效果极佳"的技术。其核心逻辑非常朴素:
同一个问题让模型推理多次,然后取出现次数最多的答案------多数投票机制。
这就好比十个人做同一道数学题,八个人得出的答案都是 5,另外两个人分别算出 6 和 7,那我们有充分理由相信 5 是正确答案。
2.2 原理解析
大模型每次生成时本身带有一定的随机性(由 temperature 参数控制)。对同一个问题多次采样时:
- 正确答案倾向于在多次采样中反复出现
- 错误答案通常具有随机性,难以重复
因此,通过统计出现频率最高的答案,就能有效排除偶然的推理错误。
关键参数 :temperature 必须大于 0,否则每次输出完全相同,多次采样就失去了意义。
2.3 基础实现:并行采样 + 投票
项目中的 SelfConsistencyService(com.jichi.prompt.service.SelfConsistencyService)完整实现了这个模式:
@Service
public class SelfConsistencyService {
private final DashScopeChatModel chatModel;
public SelfConsistencyService(DashScopeChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* Self-Consistency:采样 N 次,多数投票
*
* @param question 问题
* @param sampleCount 采样次数(建议 3-7 次)
* @return 出现最多的答案
*/
public String query(String question, int sampleCount) throws Exception {
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// Self-Consistency 需要 temperature > 0,否则每次输出相同,没有意义
DashScopeChatOptions options = DashScopeChatOptions.builder()
.withTemperature(0.7)
.build();
List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < sampleCount; i++) {
futures.add(CompletableFuture.supplyAsync(
() -> chatModel.call(new Prompt(
new UserMessage(question + "\n\n让我们一步一步思考。"),
options
)).getResult().getOutput().getText(),
executor));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.get(60, TimeUnit.SECONDS);
List<String> answers = futures.stream()
.map(f -> {
try { return f.get(); }
catch (Exception e) { return null; }
})
.filter(Objects::nonNull)
.toList();
return majorityVote(answers);
}
private String majorityVote(List<String> answers) {
return answers.stream()
.collect(Collectors.groupingBy(a -> a, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(answers.get(0));
}
}
实现要点:
- 使用
Executors.newVirtualThreadPerTaskExecutor()创建虚拟线程池并行发起请求,避免串行等待 - 每次请求的 Prompt 末尾追加
"\n\n让我们一步一步思考。",结合 COT 提升单次推理质量 temperature设为 0.7 保证每次采样的多样性majorityVote方法对所有结果进行groupingBy统计,取频次最高的作为最终答案- 设置 60 秒超时,防止个别请求拖住整体
对应的 Controller 暴露了 REST 接口,默认采样 5 次:
@RestController
@RequestMapping("/self-consistency")
public class SelfConsistencyController {
private final SelfConsistencyService selfConsistencyService;
public SelfConsistencyController(SelfConsistencyService selfConsistencyService) {
this.selfConsistencyService = selfConsistencyService;
}
@GetMapping("/query")
public String query(@RequestParam String question,
@RequestParam(defaultValue = "5") int sampleCount) throws Exception {
return selfConsistencyService.query(question, sampleCount);
}
}
2.4 结构化输出 + 自我一致性
在实际业务中,往往需要对结构化结果进行多次采样后聚合。项目中的 ContractAnalysisService(com.jichi.prompt.service.ContractAnalysisService)完整展示了这个模式------合同风险分析场景。
首先定义结构化输出模型,使用 Java Record + 枚举:
// com.jichi.prompt.enums.Verdict
public enum Verdict { YES, NO, UNCERTAIN }
// com.jichi.prompt.entity.ContractRisk
public record ContractRisk(
Verdict hasRisk,
String riskType,
int severity // 1-10
) {}
然后在 ContractAnalysisService 中实现多次采样 + 聚合:
@Service
public class ContractAnalysisService {
private final DashScopeChatModel chatModel;
private final BeanOutputConverter<ContractRisk> converter;
public ContractAnalysisService(DashScopeChatModel chatModel) {
this.chatModel = chatModel;
this.converter = new BeanOutputConverter<>(ContractRisk.class);
}
public ContractRisk analyzeWithConsistency(String clause) throws Exception {
int sampleCount = 5;
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
DashScopeChatOptions options = DashScopeChatOptions.builder()
.withTemperature(0.5)
.build();
String userContent = "分析这个合同条款:\n" + clause
+ "\n\n请先逐步分析,再给出结论。\n\n" + converter.getFormat();
List<CompletableFuture<ContractRisk>> futures = new ArrayList<>();
for (int i = 0; i < sampleCount; i++) {
futures.add(CompletableFuture.supplyAsync(() -> {
String raw = chatModel.call(new Prompt(
List.of(
new SystemMessage("你是合同法律顾问,分析合同条款是否存在法律风险。先思考,再给出结论。"),
new UserMessage(userContent)
),
options
)).getResult().getOutput().getText();
return converter.convert(raw);
}, executor));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.get(60, TimeUnit.SECONDS);
List<ContractRisk> results = futures.stream()
.map(f -> { try { return f.get(); } catch (Exception e) { return null; } })
.filter(Objects::nonNull)
.toList();
return aggregateResults(results);
}
聚合逻辑是 Self-Consistency 与结构化输出结合的关键------对不同字段分别采用投票和取均值策略:
private ContractRisk aggregateResults(List<ContractRisk> results) {
// 1. 风险判定投票:统计 YES 和 NO 的次数,多数决定最终结论
long yesCount = results.stream().filter(r -> r.hasRisk() == Verdict.YES).count();
long noCount = results.stream().filter(r -> r.hasRisk() == Verdict.NO).count();
Verdict majorityVerdict = yesCount >= noCount ? Verdict.YES : Verdict.NO;
// 2. 风险等级取均值:对多数派结果的严重程度取平均
double avgSeverity = results.stream()
.filter(r -> r.hasRisk() == majorityVerdict)
.mapToInt(ContractRisk::severity)
.average()
.orElse(5.0);
// 3. 风险类型聚合:统计出现频率最高的风险类型
String topRiskType = results.stream()
.filter(r -> r.hasRisk() == majorityVerdict)
.map(ContractRisk::riskType)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(t -> t, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse("未知风险");
return new ContractRisk(majorityVerdict, topRiskType, (int) Math.round(avgSeverity));
}
}
注意这里使用了 BeanOutputConverter<ContractRisk> 来自动处理模型输出到 Java Record 的转换,converter.getFormat() 会在 Prompt 中追加 JSON Schema 格式说明,引导模型输出符合规范的 JSON。
2.5 置信度评估
多次采样还带来一个额外好处------可以计算置信度:
置信度 = 最高票答案出现次数 / 总采样次数
例如 5 次采样中有 4 次得出 Verdict.YES(有风险),则置信度为 80%。
在生产环境中,可以设置置信度阈值:
- 置信度 >= 80%:直接采信结果
- 置信度 60%-80%:标记为"建议人工复核"
- 置信度 < 60%:触发人工审核流程
这种机制避免了将不确定的结果直接推送给用户。
2.6 成本权衡
Self-Consistency 本质是用N 倍的 API 费用换取更高的准确率,因此需要根据场景权衡:
| 场景类型 | 是否值得使用 | 说明 |
|---|---|---|
| 合同审查、安全检测 | 值得 | 高准确率要求,错误代价高 |
| 关键决策辅助 | 值得 | 关键问题值得多花成本 |
| 情感分析、内容分类 | 不值得 | 低风险场景,成本过高 |
| 日常内容生成 | 不值得 | 无需高精度 |
| 新一代推理模型 | 不值得 | 模型本身准确率已足够高,边际收益低 |
采样次数建议 :3-5 次通常足够,超过 7 次边际收益递减明显。建议选择奇数次,方便投票决胜。项目中 SelfConsistencyController 默认使用 5 次采样。
2.7 与推理模型的关系
Self-Consistency 是 Prompt 层面的技术,不依赖特定模型。但对于已经内置深度推理能力的模型(如 Qwen3-235B 开启 enableThinking),这些模型在内部已经做了类似的"多路径探索",此时再叠加 Self-Consistency 的意义不大。
因此:
- 普通/快速模型 + Self-Consistency = 性价比之选
- 推理增强模型 (如
qwen3-235b-a22b+withEnableThinking(true))= 不需要额外加 Self-Consistency
三、ReAct 模式:推理与行动交织
3.1 什么是 ReAct?
ReAct 来自 2022 年的同名论文,全称是 Re asoning + Act ing,即推理与行动的交替执行。它是 Agent 系统中最核心的 Prompt 模式,也是后续 Agent 开发的底层逻辑基础。
与纯 COT 不同,ReAct 的循环模式是:
思考(Thought) -> 行动(Action) -> 观察(Observation) -> 思考 -> 行动 -> ... -> 最终答案
COT vs ReAct 的本质区别:
- COT:思考 -> 答案(信息全部来自模型内部知识)
- ReAct:思考 -> 行动 -> 观察 -> 思考 -> ... -> 答案(可以获取外部实时数据)
3.2 一个直观的例子
用户问:"北京天气怎么样,适合穿什么?"
ReAct 的执行过程:
[Thought] 用户问的是北京的实时天气,我需要查询天气数据
[Action] 调用天气工具 getWeather("北京")
[Observation] 返回结果:城市:北京,温度:18°C,天气:晴,风力:3级
[Thought] 18度的晴天,早晚可能偏凉
[Answer] 北京今天晴天,气温 18°C,风力 3 级,建议穿轻薄外套,方便早晚增减。
模型在每一步推理后,判断是否需要调用外部工具获取信息,然后根据工具返回的结果继续推理,直到信息充足后给出最终答案。
3.3 传统 ReAct Prompt 的写法
在框架封装之前,需要手动编写 ReAct 格式的 System Prompt:
你是一个智能助手,可以使用以下工具完成任务:
【工具列表】
1. getWeather(city): 查询指定城市的实时天气
2. getStockPrice(symbol): 查询股票实时价格
3. calculate(expression): 计算数学表达式
【工作流程】
1. 先思考(Thought):分析用户问题,判断需要什么信息
2. 再行动(Action):调用合适的工具获取数据
3. 观察结果(Observation):根据工具返回的结果继续思考
4. 重复步骤 1-3,直到收集到足够信息
5. 给出最终答案(Final Answer)
【格式要求】
Thought: <你的思考过程>
Action: <工具名称>(参数)
Observation: <工具返回结果>
... (重复直到完成)
Final Answer: <最终回答>
【注意事项】
- 每次只调用一个工具
- 如果不需要工具就能回答,直接给出 Final Answer
3.4 框架封装:Spring AI 的 ReAct 托管
手动编写 ReAct Prompt 比较繁琐,Spring AI 框架已经将 ReAct 模式进行了封装,自动管理思考-行动-观察的循环。
第一步:定义工具
项目中使用 @Tool 和 @ToolParam 注解定义工具。以下是实际代码(位于 com.jichi.prompt.tools 包下):
@Component
public class WeatherTools {
@Tool(description = "查询指定城市的实时天气,返回温度、天气状况和风力")
public String getWeather(
@ToolParam(description = "城市名称,例如:北京、上海") String city) {
return String.format("城市:%s,温度:18°C,天气:晴,风力:3级", city);
}
}
@Component
public class StockTools {
@Tool(description = "查询股票实时价格")
public String getStockPrice(
@ToolParam(description = "股票代码,例如:AAPL、600036") String symbol) {
return String.format("股票代码:%s,当前价格:168.42 USD", symbol);
}
}
@Component
public class CalculatorTools {
@Tool(description = "计算数学表达式")
public String calculate(
@ToolParam(description = "要计算的数学表达式,例如:(10 + 5) * 3 / 2") String expression) {
return expression + " = 计算结果(实际应接入表达式求值库)";
}
}
第二步:注册工具到 ChatClient 并设置 System Prompt
在 AgentController(com.jichi.prompt.controller.AgentController)中,通过 defaultTools() 注册工具:
@RestController
@RequestMapping("/agent")
public class AgentController {
private final ChatClient agentClient;
public AgentController(
DashScopeChatModel chatModel,
WeatherTools weatherTools,
StockTools stockTools,
CalculatorTools calculatorTools) {
this.agentClient = ChatClient.builder(chatModel)
.defaultSystem("""
你是一个智能助手,可以查天气、查股价、做计算。
根据用户问题决定是否需要使用工具,使用工具后结合结果给出准确答案。
""")
.defaultTools(weatherTools, stockTools, calculatorTools)
.build();
}
@GetMapping("/ask")
public String ask(@RequestParam String question) {
return agentClient.prompt()
.user(question)
.call()
.content();
}
}
第三步:直接提问,框架自动处理 ReAct 循环
// GET /agent/ask?question=苹果公司(AAPL)的股价折合人民币是多少?
String answer = agentClient.prompt()
.user("苹果公司(AAPL)的股价折合人民币是多少?")
.call()
.content();
Spring AI 在内部自动完成了以下过程:
- 发送请求给模型
- 模型决定调用
getStockPrice("AAPL")-> 获取美元股价 - 模型决定调用
getExchangeRate("USD", "CNY")-> 获取汇率 - 模型决定调用
calculate("168.42 * 7.24")-> 计算人民币价格 - 模型组织自然语言回复给用户
整个多步推理过程由框架自动托管,开发者只需定义好工具即可。
3.5 金融场景:多工具组合实战
项目中还有一个更专业的金融 Agent 示例------FinanceAgentController(com.jichi.prompt.controller.FinanceAgentController),使用 FinanceTools 将股票、汇率、计算三个工具合并到一个类中:
@Component
public class FinanceTools {
@Tool(description = "查询股票实时价格")
public String getStockPrice(@ToolParam(description = "股票代码") String symbol) {
return symbol + " 当前价格:168.42 USD";
}
@Tool(description = "查询货币汇率")
public String getExchangeRate(
@ToolParam(description = "源货币代码") String from,
@ToolParam(description = "目标货币代码") String to) {
return from + "/" + to + " = 7.24";
}
@Tool(description = "计算数学表达式")
public String calculate(@ToolParam(description = "数学表达式") String expression) {
return expression + " ≈ 1219.36";
}
}
@RestController
@RequestMapping("/finance-agent")
public class FinanceAgentController {
private final ChatClient agentClient;
public FinanceAgentController(DashScopeChatModel chatModel, FinanceTools financeTools) {
this.agentClient = ChatClient.builder(chatModel)
.defaultSystem("""
你是一个金融助手,可以查询实时股价、汇率,并做数学计算。
遇到需要数据的问题,先调工具获取数据,再给出完整答案。
""")
.defaultTools(financeTools)
.build();
}
@GetMapping("/ask")
public String ask(@RequestParam String question) {
return agentClient.prompt()
.user(question)
.call()
.content();
}
}
ReAct 真正强大之处在于自动串联多个工具调用。查询"苹果公司股价折合人民币是多少"时,模型会自动拆解为三步:
Step 1: 调用 getStockPrice("AAPL") -> 获取股价 168.42 USD
Step 2: 调用 getExchangeRate("USD", "CNY") -> 获取汇率 7.24
Step 3: 调用 calculate("168.42 * 7.24") -> 计算得 1219.36
Final Answer: 苹果公司当前股价为 168.42 美元,按当前汇率 1 美元约等于 7.24 人民币,折合约 1219.36 元人民币。
3.6 循环控制
ReAct 理论上可以进行无限次的思考-行动循环,因此在生产环境中务必设置最大循环次数 ,防止死循环导致资源耗尽。在 Spring AI 中可以通过 toolCallingChatOptions 或 Advisor 来限制:
ChatClient client = ChatClient.builder(chatModel)
.defaultTools(tools)
.build();
// 调用时可通过 ChatOptions 控制最大工具调用轮次
client.prompt()
.user(question)
.options(DashScopeChatOptions.builder()
.withMaxTokens(2048) // 限制输出长度,间接控制循环
.build())
.call()
.content();
3.7 COT vs ReAct 对比总结
| 对比维度 | COT | ReAct |
|---|---|---|
| 信息来源 | 模型内部知识 | 模型知识 + 外部工具/API |
| 适用场景 | 逻辑推演、数学计算 | 实时查询、执行动作、多步任务 |
| 调用成本 | 较低(单次调用) | 较高(多次 API 交互) |
| 是否需要工具 | 不需要 | 需要 @Tool 定义并注册到 ChatClient |
| 典型应用 | Bug 分析、合同审查 | 智能助手、金融查询、自动化操作 |
四、总结
本文介绍了三种提升大模型推理能力的关键技术,它们形成了一个递进的体系:
-
COT(思维链) :最基础的推理增强手段。通过强制模型"先推理再回答",利用自回归生成的特性,让后续 Token 依赖前面正确的推理步骤,从而提升复杂问题的准确率。使用门槛极低,一句"让我们一步一步思考"即可激活。在项目中,
BugAnalysisController的 System Prompt 中 "先推理出 bug 类型和根因,再填写结构化结论" 就是 COT 的典型应用。 -
Self-Consistency(自我一致性) :在 COT 基础上的进一步增强。通过多次并行采样、投票取最优,利用概率统计的力量过滤掉偶然的推理错误。项目中
SelfConsistencyService使用虚拟线程并行采样 +majorityVote投票,ContractAnalysisService则展示了结构化输出场景下的多维度聚合策略。适用于对准确率要求极高的场景(如合同审查、安全检测),但需要权衡 N 倍的 API 成本。 -
ReAct(推理+行动) :从"纯思考"升级为"思考+行动"。模型不再局限于内部知识,而是可以调用外部工具获取实时信息,并在"思考-行动-观察"的循环中逐步解决问题。项目中
AgentController和FinanceAgentController通过 Spring AI 的ChatClient.defaultTools()注册@Tool注解的工具类,框架自动托管 ReAct 循环,开发者只需关注工具实现。
在实际项目中,这三种技术可以灵活组合:ReAct 的每一步推理都可以使用 COT 来保证质量,而关键决策节点还可以叠加 Self-Consistency 来提升置信度。理解它们的原理和适用边界,才能在不同场景下做出最优选择。