当C++遇上提示词工程:我用大模型重构了团队的代码审查
本文介绍了一个利用大模型重构代码审查流程的实践案例。作者针对团队C++代码审查效率低下的痛点,开发了一个500行的C++工具,通过Git Hook触发,提取代码diff并调用LLM API进行预审查。文章重点剖析了提示词工程的关键作用,展示了从简单提示词到融合角色设定、Few-shot示例和思维链的PromptBuilder三版演进过程,最终将问题识别率从30%提升至85%,使团队代码审查时间从2
当C++遇上提示词工程:我用大模型重构了团队的代码审查
三个月前,我们组每周的Code Review会议要开两个小时,现在只需要四十分钟。变化的起点不是换了流程,而是我花两个周末写了一个500行的C++小工具。
背景:代码审查的老痛点
我们组做的是嵌入式中间件,C++代码库大概二十万行。团队八个人,每周合入的MR少说也有三四十个。
代码审查这件事的问题,不在于大家不重视——恰恰相反,大家都知道重要,但实际执行起来总是打折扣。核心原因就两个字:没空。
每个人手上都有排期压力,让你花半小时仔细看别人的代码,嘴上说没问题,实际上经常是扫一眼就LGTM了。结果就是:看起来每个MR都有审查记录,但内存泄漏、线程安全这类深层问题,经常是上线之后才暴露。
去年底有一次线上事故,根因是一个std::shared_ptr的循环引用导致内存持续增长,Review的时候三个人都没看出来。事后复盘的时候,组长说了一句话:“咱们的Code Review到底是在审代码,还是在走形式?”
这句话刺激了我。我开始想,能不能用大模型来做一层自动化的预审查——不是替代人工审查,而是在人工Review之前,先用AI过一遍,把明显的问题标出来。
整体思路
想法其实很简单:Git Hook触发,提取代码diff,组装提示词,调LLM的API,拿到审查意见,生成报告。
但实际做的时候发现,这件事的难点完全不在"调API",而在"怎么写提示词"。同一段有问题的代码,提示词写得好,模型能精确定位到具体行号并给出修复建议;提示词写得差,模型只会给你"建议添加错误处理"这种废话。

整个工具我用C++写的,没用Python,原因很务实——我们的CI环境是纯C++工具链,加一个Python运行时代价太大。C++调HTTP API虽然啰嗦一点,但其实也就是个socket+JSON的事。
核心模块:PromptBuilder
整个工具里最核心的模块不是HTTP客户端,不是JSON解析器,而是PromptBuilder——负责把原始的代码diff变成一段高质量的提示词。
这个模块我前后重写了三版,每一版的审查效果都有质的提升。

V1:裸提示(命中率约30%)
最早我就是把diff原文拼上一句"请审查以下代码变更"直接发给API。结果可想而知,回来的东西要么太泛泛,要么完全跑偏。比如你改了一个网络模块的超时逻辑,它给你说"建议添加单元测试"——没错,但没用。
V2:规则注入(命中率约65%)
第二版我开始认真设计提示词结构。核心改动是加了三样东西:角色设定、审查清单、输出格式约束。
V3:Few-shot + 思维链(命中率约85%)
第三版是质的飞跃。我加入了真实的审查案例作为Few-shot示例,并且要求模型先分析代码意图,再逐条检查,最后给出结论——这就是所谓的"思维链"。
下面是最终版的PromptBuilder实现:
promptbuilder.h
#ifndef PROMPT_BUILDER_H
#define PROMPT_BUILDER_H
/// @file promptbuilder.h
/// @brief 提示词构建器 v3.0
/// @details 支持角色设定、规则注入、Few-shot示例、思维链引导
/// @usage
/// PromptBuilder builder;
/// builder.setSystemRole("Senior C++ code reviewer");
/// builder.addRule("检查RAII合规性");
/// builder.addFewShotExample(badCode, reviewResult);
/// auto prompt = builder.build(diffContent);
#include <string>
#include <vector>
/// @brief 审查规则条目
struct ReviewRule
{
std::string category; // 规则分类(如"内存安全")
std::string description; // 具体检查内容
std::string severity; // 严重等级: error / warning / info
};
/// @brief Few-shot示例条目
struct FewShotExample
{
std::string codeSnippet; // 有问题的代码片段
std::string reviewOutput; // 期望的审查输出
};
class PromptBuilder
{
public:
/// @brief 设置系统角色描述
void setSystemRole(const std::string& role); // 定义模型扮演的角色
/// @brief 添加审查规则
void addRule(const ReviewRule& rule); // 注入一条检查规则
/// @brief 批量加载规则文件
bool loadRulesFromFile(const std::string& path); // 从JSON文件加载规则集
/// @brief 添加Few-shot示例
void addFewShotExample(const FewShotExample& example); // 注入一个审查范例
/// @brief 设置项目编码规范摘要
void setCodingStandard(const std::string& standard); // 注入团队编码规范
/// @brief 构建最终提示词
std::string build(const std::string& diffContent) const; // 组装完整prompt
/// @brief 构建系统消息(用于Chat API的system字段)
std::string buildSystemMessage() const; // 生成system角色消息
private:
std::string systemRole_; // 角色定义
std::string codingStandard_; // 编码规范摘要
std::vector<ReviewRule> rules_; // 审查规则列表
std::vector<FewShotExample> examples_; // Few-shot示例列表
/// @brief 格式化规则为文本
std::string formatRules() const; // 将规则列表拼接为文本
/// @brief 格式化Few-shot示例
std::string formatExamples() const; // 将示例拼接为文本
/// @brief 构建思维链引导语
std::string buildChainOfThought() const; // 生成CoT推理引导
};
#endif // PROMPT_BUILDER_H
promptbuilder.cpp
#include "promptbuilder.h"
#include <fstream>
#include <sstream>
void PromptBuilder::setSystemRole(const std::string& role)
{
systemRole_ = role; // 存储角色描述
}
void PromptBuilder::addRule(const ReviewRule& rule)
{
rules_.push_back(rule); // 追加一条规则
}
bool PromptBuilder::loadRulesFromFile(const std::string& path)
{
std::ifstream file(path); // 打开规则文件
if (!file.is_open()) // 文件不存在则返回失败
return false;
std::string line; // 逐行读取缓冲
ReviewRule currentRule; // 当前解析的规则
while (std::getline(file, line)) // 逐行遍历
{
if (line.empty()) // 空行表示一条规则结束
{
if (!currentRule.category.empty()) // 确保规则有效
{
rules_.push_back(currentRule); // 保存已解析的规则
currentRule = {}; // 重置为空规则
}
continue;
}
size_t sep = line.find(':'); // 查找键值分隔符
if (sep == std::string::npos) // 格式不合法则跳过
continue;
std::string key = line.substr(0, sep); // 提取键名
std::string val = line.substr(sep + 1); // 提取值
if (key == "category") // 解析分类字段
currentRule.category = val;
else if (key == "description") // 解析描述字段
currentRule.description = val;
else if (key == "severity") // 解析严重度字段
currentRule.severity = val;
}
if (!currentRule.category.empty()) // 处理文件末尾的最后一条
rules_.push_back(currentRule);
return true; // 加载完成
}
void PromptBuilder::addFewShotExample(const FewShotExample& example)
{
examples_.push_back(example); // 追加一个范例
}
void PromptBuilder::setCodingStandard(const std::string& standard)
{
codingStandard_ = standard; // 存储编码规范文本
}
std::string PromptBuilder::formatRules() const
{
std::ostringstream oss; // 拼接缓冲区
oss << "## Review Checklist\n"; // 清单标题
for (size_t i = 0; i < rules_.size(); ++i) // 遍历所有规则
{
const auto& r = rules_[i]; // 当前规则引用
oss << i + 1 << ". " // 序号
<< "[" << r.severity << "] " // 严重等级标签
<< r.category << ": " // 分类
<< r.description << "\n"; // 具体描述
}
return oss.str(); // 返回格式化文本
}
std::string PromptBuilder::formatExamples() const
{
if (examples_.empty()) // 无示例则返回空
return "";
std::ostringstream oss; // 拼接缓冲区
oss << "## Examples of Good Reviews\n\n"; // 示例区标题
for (size_t i = 0; i < examples_.size(); ++i) // 遍历所有示例
{
const auto& ex = examples_[i]; // 当前示例引用
oss << "### Example " << i + 1 << "\n" // 示例编号
<< "Code:\n```cpp\n" // 代码块开始
<< ex.codeSnippet // 问题代码
<< "\n```\n" // 代码块结束
<< "Review:\n" // 审查结果
<< ex.reviewOutput << "\n\n"; // 期望输出
}
return oss.str(); // 返回格式化文本
}
std::string PromptBuilder::buildChainOfThought() const
{
return // 思维链引导模板
"## Analysis Steps\n"
"Please follow these steps:\n"
"1. Understand the INTENT of this change\n" // 步骤1: 理解意图
"2. Check each rule in the checklist\n" // 步骤2: 逐条检查
"3. For each issue found, provide:\n" // 步骤3: 问题输出格式
" - Line number\n" // 行号
" - Severity (error/warning/info)\n" // 严重度
" - Problem description\n" // 问题描述
" - Suggested fix with code\n" // 修复建议
"4. If no issues found, explicitly state LGTM\n"; // 无问题则LGTM
}
std::string PromptBuilder::buildSystemMessage() const
{
std::ostringstream oss; // 拼接系统消息
oss << "You are " << systemRole_ << ".\n\n"; // 角色设定
if (!codingStandard_.empty()) // 如果有编码规范
oss << "## Project Coding Standard\n"
<< codingStandard_ << "\n\n"; // 注入规范
oss << formatRules() << "\n"; // 注入审查规则
oss << formatExamples(); // 注入Few-shot示例
oss << buildChainOfThought(); // 注入思维链引导
return oss.str(); // 返回完整系统消息
}
std::string PromptBuilder::build(const std::string& diffContent) const
{
std::ostringstream oss; // 拼接用户消息
oss << "Please review the following C++ code change:\n\n" // 审查指令
<< "```diff\n" // diff代码块开始
<< diffContent // 实际的代码变更
<< "\n```\n\n" // diff代码块结束
<< "Provide your review following " // 引导按格式输出
<< "the analysis steps above.\n";
return oss.str(); // 返回完整用户消息
}
使用示例:
#include "promptbuilder.h"
#include <iostream>
int main()
{
PromptBuilder builder; // 创建构建器实例
// 设置审查角色
builder.setSystemRole( // 定义角色身份
"a senior C++ developer with 10+ years experience "
"specializing in memory safety and concurrency"
);
// 注入编码规范
builder.setCodingStandard( // 注入团队规范摘要
"- Use RAII for all resource management\n"
"- Prefer const reference over pointer\n"
"- All public methods must be thread-safe"
);
// 添加审查规则
builder.addRule({ // 规则1: 内存安全
"Memory Safety",
"Check for raw new/delete, prefer smart pointers",
"error"
});
builder.addRule({ // 规则2: 线程安全
"Thread Safety",
"Check shared state access without mutex",
"error"
});
builder.addRule({ // 规则3: 异常安全
"Exception Safety",
"Check for resource leaks in exception paths",
"warning"
});
// 添加Few-shot示例
builder.addFewShotExample({ // 注入一个审查范例
"void process(Data* d) {\n" // 问题代码
" auto* buf = new char[1024];\n"
" d->parse(buf);\n"
" delete[] buf;\n"
"}",
"[error] Line 2: Raw new/delete detected.\n" // 期望的审查输出
"Suggested fix: use std::vector<char> or "
"std::unique_ptr<char[]> for automatic cleanup."
});
// 构建提示词
std::string systemMsg = builder.buildSystemMessage(); // 生成系统消息
std::string userMsg = builder.build(diffContent); // 生成用户消息
std::cout << "=== System Message ===\n" // 输出系统消息
<< systemMsg << "\n\n"
<< "=== User Message ===\n" // 输出用户消息
<< userMsg << std::endl;
return 0;
}
提示词的三个关键技巧
做了三版迭代之后,我总结出三条对C++代码审查最有效的提示词技巧:
第一,给模型一个具体的专家人设。 不是笼统的"你是一个代码审查员",而是"你是一个有10年经验的C++开发者,专长内存安全和并发编程"。人设越具体,模型给出的建议就越贴近实际。
第二,审查规则要分严重等级。 如果你把所有问题都标成同一级别,模型会倾向于平均用力,把命名规范和内存泄漏放在同一个权重。分了error/warning/info之后,模型会优先花精力分析高严重度的问题。
第三,Few-shot示例比任何描述都管用。 与其花200字解释"什么是好的审查意见",不如直接给一个例子。我最后放了三个示例:一个内存安全的、一个线程安全的、一个性能相关的。模型会自动学习这些示例的分析深度和输出格式。
实际效果
工具上线三个月,跑了大概四百多次自动审查。数据是真的,但统计方式比较粗糙——我就是手动抽了每周的审查报告做的对比,不算严谨的A/B测试。

几个比较突出的变化:
内存相关问题的检出率提升最明显。 像shared_ptr循环引用、裸new/delete、异常路径的资源泄漏,这些是人眼最容易忽略但模型最擅长捕捉的。毕竟模型不会"扫一眼就过",它会逐行看。
审查耗时从平均45分钟降到12分钟。 这不是说人不看了——而是AI先出一份预审报告,把可疑的地方标出来,人只需要看标红的部分做二次确认。相当于从"大海捞针"变成了"验证答案"。
最意外的收获是新人上手速度变快了。 以前新人提MR,老员工要花很多时间在"教你什么是好代码"上面。现在AI的审查报告本身就是一份活的编码规范教材,新人看几次就知道团队在意什么。
踩过的坑
说几个实际开发中踩的坑,给想做类似事情的人省点时间。
Diff太长的处理。 大模型的上下文窗口是有限的,一个大MR改了几十个文件、上千行diff,直接丢进去会被截断。我的做法是按函数级别切分diff,每个函数单独发一次请求,最后合并结果。代价是API调用次数增加,成本上来了,但准确率也上来了。
JSON输出的稳定性问题。 即使你在提示词里明确要求"输出JSON格式",模型偶尔还是会在前后加一些解释性文字,导致JSON解析失败。最终我的方案是用正则先提取markdown代码块里的JSON,再做解析,同时加了重试逻辑。
误报的心理成本。 这是最容易被忽视的问题。如果AI报了10个问题里有5个是误报,时间久了人就会养成"直接忽略AI建议"的习惯,比没有AI还糟糕。所以我在V3版本里大幅提高了报告的门槛——宁可漏报,不要误报。对于"不确定"的问题,标成info级别单独放在报告末尾,不打断主审查流程。
C++的模板代码几乎没法审。 这一点和我上一篇文章的结论一致。涉及到SFINAE、折叠表达式、Concepts这类高级模板技巧,模型给出的建议经常是错的。我的策略是在规则里明确标注"跳过模板元编程相关变更",不审比瞎审好。
成本核算
很多人关心调API的成本。以我们的使用量为例:每周约40个MR,每个MR平均3次API调用(按函数切分),每次调用约4K tokens输入+1K tokens输出。按Claude Sonnet的价格算下来,每月大概不到200块人民币。
对比一下:一个资深开发者每周花在Code Review上的时间,按时薪折算至少是这个成本的十倍。
写在最后
做这个工具让我体会最深的一点是:提示词工程不是"调参",是"产品设计"。
你不是在调一个黑盒的超参数,你是在设计一套人和模型的协作协议。角色设定是在定义"谁来干活",审查规则是在定义"干什么活",Few-shot示例是在定义"干成什么样",思维链是在定义"按什么步骤干"。
这套思路不局限于代码审查。日志分析、故障诊断、技术文档生成——凡是有明确评判标准、有固定输出格式、有历史案例可参考的任务,都可以用类似的方法落地。
大模型的能力上限很高,但下限也可以很低。区别在于你喂给它什么样的指令。这件事,值得每个开发者花时间去琢磨。
本文为个人项目实践分享,代码经过简化,实际生产版本包含更多异常处理和边界检查。
更多推荐

所有评论(0)