【学习笔记3】AI 工程实战

项目一:内部文档知识库问答(最推荐入手)

这是覆盖 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 解析校验 + 数据库只读账号三道防线。

相关推荐
火山引擎开发者社区2 小时前
玩转 ArkClaw:用自动修复打造稳定可靠的 AI 助理
人工智能
guslegend2 小时前
第10节:设计高效混合检索架构,提升召回精度
人工智能·架构·大模型·rag
Flying pigs~~2 小时前
检索增强生成RAG项目tools_01:Docker 极简实战
运维·人工智能·docker·容器·大模型·agent·rag
黎阳之光2 小时前
去标签化定位时代:黎阳之光自研技术,可见即可定位,无感亦能解算
大数据·人工智能·算法·安全·数字孪生
犽戾武2 小时前
YOLOv8 目标检测模型训练与 RK3588 NPU 部署全记录
人工智能·计算机视觉·目标跟踪
weixin_408099672 小时前
python请求文字识别ocr api
开发语言·人工智能·后端·python·ocr·api·ocr文字识别
一休哥助手3 小时前
2026年4月14日人工智能早间新闻
人工智能
跨境猫小妹3 小时前
平台评价体系调整跨境卖家如何提升转化率
大数据·人工智能
炽烈小老头3 小时前
【每天学习一点算法 2026/094/14】分数到小数
学习·算法