
项目一:内部文档知识库问答(最推荐入手)
这是覆盖 RAG 完整链路、业务价值最直接的项目。两周内可以出一个 Demo。
【完整架构】
上传阶段(离线):
PDF/Word 上传 → Apache POI/PDFBox 提取文本
→ 按段落切片(每片 500 字,50 字重叠)
→ 调 Embedding API 转向量
→ 存入 PostgreSQL + pgvector
查询阶段(在线):
用户提问 → 转向量 → pgvector 检索 Top5 相似片段
→ 拼装 Prompt(问题 + 5段原文)→ Claude 生成答案
→ 附带引用来源(哪个文档第几页)
// Spring AI 实现,核心代码极简
@Service
public class KnowledgeService {
@Autowired
private VectorStore vectorStore; // pgvector
@Autowired
private ChatClient chatClient; // Claude
@Autowired
private EmbeddingModel embeddingModel; // Embedding
// 上传文档
public void ingest(MultipartFile file) {
// Spring AI 自动完成:提取文本 → 切片 → 向量化 → 存储
List<Document> docs = new TikaDocumentReader(file.getResource()).get();
var splitter = new TokenTextSplitter(500, 50, 5, 10000, true);
vectorStore.add(splitter.apply(docs));
}
// 问答
public String ask(String question) {
// 1. 检索相关文档
List<Document> relevant = vectorStore.similaritySearch(
SearchRequest.query(question).withTopK(5)
);
// 2. 拼 Prompt
String context = relevant.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n---\n\n"));
String prompt = """
根据以下文档内容回答问题,如果文档中没有相关信息,
请说"文档中未找到相关内容",不要凭空回答。
文档内容:
%s
问题:%s
""".formatted(context, question);
// 3. 调 Claude
return chatClient.prompt(prompt).call().content();
}
}
关键难点只有两个:切片策略(切太短语义不完整,切太长 Token 费用高,500 字 + 50 字重叠是经验值);以及引用溯源(要在 Document 的 metadata 里存文件名和页码,回答时带上来源)。
项目二:AI 自动生成单元测试
程序员自己用,开发体验提升立竿见影,也是向团队展示 AI 价值最容易的方式。
@Service
public class TestGenerator {
private static final String SYSTEM = """
你是 Java 单元测试专家。
给定一个 Java 方法,生成完整的 JUnit 5 测试类,要求:
1. 覆盖正常流程、边界值、异常情况
2. 使用 Mockito mock 依赖
3. 测试方法命名:should_预期结果_when_场景
只输出代码,不要任何解释。
""";
public String generate(String javaSourceCode, String methodName) {
// 用 JavaParser 精确提取目标方法,避免浪费 Token
CompilationUnit cu = StaticJavaParser.parse(javaSourceCode);
MethodDeclaration method = cu.findAll(MethodDeclaration.class)
.stream()
.filter(m -> m.getNameAsString().equals(methodName))
.findFirst()
.orElseThrow();
// 同时提取类结构(依赖注入字段等),给 AI 更多上下文
String classContext = extractClassContext(cu);
String prompt = """
类结构:
```java
%s
```
需要测试的方法:
```java
%s
```
请生成完整的测试类。
""".formatted(classContext, method.toString());
return claude.chat("claude-sonnet-4-6", 2048, 0.2, SYSTEM, prompt);
}
}
进阶做法:把这个功能做成 IntelliJ IDEA 插件,右键点击方法名就能生成测试,用起来和原生功能一样顺滑。
项目三:AI 代码 Review 工具(团队基础设施)
接入 GitLab Webhook,每次提 MR 自动触发 AI Review,评论直接显示在代码行上。
@RestController
public class GitLabWebhookController {
@PostMapping("/webhook/gitlab")
public void handleMR(@RequestBody GitLabEvent event) {
if (!"merge_request".equals(event.getObjectKind())) return;
if (!"open".equals(event.getObjectAttributes().getState())) return;
// 异步处理,不阻塞 GitLab
executor.submit(() -> reviewMR(event));
}
private void reviewMR(GitLabEvent event) {
int projectId = event.getProject().getId();
int mrIid = event.getObjectAttributes().getIid();
// 1. 获取 Diff
List<FileDiff> diffs = gitlabClient.getMRDiffs(projectId, mrIid);
// 2. 对每个改动文件做 Review(只看改动的行,控制 Token)
for (FileDiff diff : diffs) {
if (!diff.getNewPath().endsWith(".java")) continue;
if (diff.getDiff().length() > 8000) continue; // 太大的跳过
String review = aiReview(diff);
// 3. 把评论发回 GitLab(指定到具体行)
gitlabClient.addMRComment(projectId, mrIid, review,
diff.getNewPath(), diff.getNewLine());
}
}
private String aiReview(FileDiff diff) {
String prompt = """
Review 以下 Java 代码变更,关注:
- 潜在的 NPE 和线程安全问题 [HIGH]
- 性能问题(N+1查询、不必要的循环)[MEDIUM]
- 代码可读性和命名 [LOW]
只指出真实问题,不要夸奖。每条评论格式:
[HIGH/MEDIUM/LOW] 问题描述 → 建议改法
代码变更:
```diff
%s
```
""".formatted(diff.getDiff());
return claude.chat("claude-sonnet-4-6", 1024, 0.0, null, prompt);
}
}
项目四:AI 数据分析助手(Text-to-SQL)
业务方输入"上个月每个地区的销售额排名",AI 自动生成 SQL 执行,返回图表。
// 核心链路:自然语言 → SQL → 执行 → 结构化结果
public AnalysisResult analyze(String question, String dbSchema) {
// Step 1:生成 SQL(temperature=0,必须确定性)
String sql = generateSQL(question, dbSchema);
// Step 2:安全校验(只允许 SELECT,防止注入)
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
throw new SecurityException("只允许查询操作");
}
// 进一步:用 JSqlParser 做 AST 级别的安全校验
validateSQL(sql);
// Step 3:执行查询
List<Map<String, Object>> data = jdbcTemplate.queryForList(sql);
// Step 4:让 AI 生成图表配置(ECharts JSON)和文字解读
String insight = generateInsight(question, data);
return new AnalysisResult(sql, data, insight);
}
private String generateSQL(String question, String schema) {
String prompt = """
数据库表结构:
%s
将以下问题转为 SQL(只输出 SQL,不要任何解释):
%s
""".formatted(schema, question);
return claude.chat("claude-sonnet-4-6", 512, 0.0,
"你是 SQL 专家,只输出合法的 MySQL SELECT 语句", prompt);
}
这个项目的核心难点是安全------绝对不能让 AI 生成 DELETE/UPDATE 语句,要做多层防护:Prompt 约束 + SQL 解析校验 + 数据库只读账号三道防线。
