乐于分享
好东西不私藏

模块1(下):用Spring AI从0到1搭一个代码审查助手

模块1(下):用Spring AI从0到1搭一个代码审查助手

模块1(下):用Spring AI从0到1搭一个代码审查助手

上篇讲了4个核心技巧——角色约束、格式控制、Few-shot、思维链。今天用完整实战把它们串起来,再聊聊温度参数怎么选、以及我踩过的4个坑。


温度参数怎么选

这是另一个让我踩坑的点。

一开始我以为temperature=0最稳定,结果发现模型回答越来越"保守",稍微有点开放性的问题就直接说"这个问题我无法回答"。

后来才明白:temperature控制的是"随机性",不是"准确性"。

场景
Temperature
效果
代码审查、JSON输出
0.1 - 0.3
稳定、可复现
问答、总结、翻译
0.3 - 0.5
平衡稳定和自然
创意写作、头脑风暴
0.7 - 0.9
有创意、不重复
精确计算
0.0
最小随机性

Java代码里的设置:

// 精确场景:代码审查var strictRequest = OpenAiChatOptions.builder()        .withTemperature(0.1)        .build();// 平衡场景:日常问答var balancedRequest = OpenAiChatOptions.builder()        .withTemperature(0.5)        .build();// 创意场景:起名字、想创意var creativeRequest = OpenAiChatOptions.builder()        .withTemperature(0.8)        .build();

所以:温度不是越高越好,也不是越低越稳定。选对场景就行。


完整实战:Spring AI代码审查助手

来一个能直接跑的完整例子。

Maven依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency>

application.yml配置

spring:ai:openai:api-key:${OPENAI_API_KEY}model:gpt-4o

完整Controller

@RestController@RequestMapping("/api/review")publicclassCodeReviewController{privatefinal ChatModel chatModel;publicCodeReviewController(ChatModel chatModel){this.chatModel = chatModel;    }/**     * 代码审查主接口     * 结合了:角色约束 + Few-shot + CoT + 结构化输出     */@PostMapping("/java")public Map<String, Object> reviewJavaCode(@RequestBody Map<String, String> request){        String code = request.get("code");        String prompt = buildReviewPrompt(code);var options = OpenAiChatOptions.builder()                .withModel("gpt-4o")                .withTemperature(0.2)  // 低温保证格式稳定                .withResponseFormat(new ResponseFormat("json_object"))                .withMaxTokens(2000)                .build();try {            String result = chatModel.call(new Prompt(prompt, options))                    .getResult().getOutput().getText();return Map.of("success"true,"result", parseJson(result),"raw", result            );        } catch (Exception e) {return Map.of("success"false,"error", e.getMessage()            );        }    }private String buildReviewPrompt(String code){return"""            你是一个拥有15年经验的Java架构师,精通Spring全家桶、并发编程、JVM调优。            请用思维链方式审查代码,先分析逻辑,再指出问题,最后给出评分和建议。            评分标准:            - 90-100:生产级代码,可直接上线            - 70-89:有优化空间,需要review            - 50-69:存在明显风险,需要重构            - 50以下:建议重写            审查代码:            %s            请返回JSON格式(不要添加任何解释性文字):            {                "score": 分数,                "grade": "等级描述",                "thinking": ["思考步骤1", "思考步骤2", "思考步骤3"],                "issues": [                    {                        "severity": "high|medium|low",                        "type": "NPE|并发|性能|安全|规范",                        "location": "代码位置",                        "description": "问题描述",                        "suggestion": "修改建议"                    }                ],                "summary": "整体评价"            }            """.formatted(code);    }private Map<String, Object> parseJson(String json){// 实际项目中建议使用Jackson解析// 这里简化处理return Map.of("raw", json);    }}

测试请求

curl -X POST http://localhost:8080/api/review/java \  -H "Content-Type: application/json" \  -d '{    "code": "public String getUserName(Integer userId) { return userDao.findById(userId).getName(); }"  }'

返回示例:

{"success"true,"result": {"score"45,"grade""建议重写","thinking": ["userId可能是null,直接传入会导致NPE","findById可能返回null,调用getName()会NPE","整体缺乏参数校验和异常处理"    ],"issues": [      {"severity""high","type""NPE","location""第1行","description""Integer参数可能为null","suggestion""添加 Objects.requireNonNull(userId) 或判空处理"      },      {"severity""high","type""NPE","location""第1行","description""findById可能返回Optional.empty()","suggestion""使用 Optional.ofNullable() 或 ifPresent()"      }    ],"summary""存在严重的空指针风险,建议重构后上线"  }}

常见坑,我帮你踩过了

坑1:Prompt越长越好?

不是。Prompt太长会导致:

  • 模型"注意力分散",关键指令被稀释
  • token消耗增加,响应变慢
  • 成本上涨

解法:删除所有"礼貌性废话",只留指令。模型不需要"请您帮我...",直接"帮我..."就行。

坑2:temperature=0就稳定?

不一定。温度控制的是token选择的随机性,但模型本身有"默认行为"。

想让输出稳定,温度+结构化输出要组合使用

var options = OpenAiChatOptions.builder()        .withTemperature(0.1)  // 低温        .withResponseFormat(new ResponseFormat("json_object"))  // 强制JSON        .build();

坑3:示例越多越好?

不是。3个示例足够,再多反而会让模型"过度拟合"到示例的风格。

Few-shot的黄金规则:3个示例,覆盖正向、负向、边界情况。

坑4:直接问"你理解了吗?"

没用。模型会说"我理解了",但不代表它真的理解了。

解法:让模型复述任务或给出执行计划,通过输出来验证理解。


今天就能动手的任务

学再多不如动手练。今天给你三个任务,从简单到复杂:

任务1(5分钟):改造你现有的一个Prompt,加上角色约束。

把"帮我写代码"改成:

"你是一个Spring Boot专家,请帮我写一个用户注册接口,包含参数校验和异常处理"

看看回答质量有没有变化。

任务2(15分钟):让ChatGPT返回一个JSON结构化的自我介绍。

用这个Prompt:

"请以JSON格式返回你自己的介绍,包含name、experience(年数)、skills(数组)"

然后在代码里解析这个JSON,感受结构化输出的价值。

任务3(30分钟):用Spring AI写一个带Few-shot的翻译接口。

  • System Prompt定义角色(中英翻译专家)
  • Few-shot给出3个翻译示例
  • 让用户输入中文,返回英文JSON

完整代码可以参考上面的Controller,改改Prompt就行。


总结

这一篇我们用Spring AI把四个技巧——角色约束、格式控制、Few-shot、CoT——串起来,实现了完整的代码审查助手。

关键点回顾:

  • 温度参数要匹配场景,不是越低越好
  • 结构化输出+低温是稳定JSON的黄金组合
  • Few-shot用3个示例覆盖正负向和边界情况
  • 思维链能显著提升复杂任务的准确率

下一篇文章,我们聊模块2:RAG深度调优——怎么让你的AI真正"懂"你的业务数据。

(完整代码已上传到GitHub,文末有链接)


往期文章

  • Java转AI,我整理了8个模块的学习路线图

如果这篇文章对你有帮助,欢迎关注我

我会持续分享Java AI落地的实战经验,涵盖Spring AI、RAG、智能体开发等核心模块。