我把传统项目问答升级成了 Agent-RAG:Spring Boot + FastAPI + ChromaDB 工程落地实践

我把传统项目问答升级成了 Agent-RAG:Spring Boot + FastAPI + ChromaDB 工程落地实践

还在让 AI 靠"猜"理解你的项目吗?

我最近把 SwiftBoot 里的项目问答链路从"普通 RAG 检索"升级成了一套更像工程系统的 Agent-RAG:前面先做意图分流,再按需调用源码检索或文档检索,后面再接长期记忆、实时增量索引和流式输出。做完之后,一个很直观的变化是:AI 不再只会给通用答案,而是真的开始"按项目说话"了。


先说结论:为什么我没有停在"普通 RAG"

很多人给项目接 AI 的第一步,通常是这样的:

  1. 把代码切片
  2. 存进向量库
  3. 用户一提问就统一检索
  4. 把结果拼进 Prompt 里让大模型回答

这条路当然能跑通,但只要项目稍微复杂一点,很快就会遇到几个问题:

  • 用户明明在问"具体哪段代码怎么实现",系统却把一堆说明文档也塞进来
  • 用户其实在问"这个功能怎么用",系统却优先命中了底层源码片段
  • 代码刚改完,知识库还是旧的
  • 前一轮已经聊过的上下文,下一轮又得重新解释一遍
  • AI 虽然答对了,但你不知道它到底引用了哪些内容

我后来意识到,问题不在于"有没有 RAG",而在于检索链路太粗了

如果把所有问题都丢给同一种检索策略,最后得到的往往不是"更聪明的 AI",而是"更昂贵的模糊匹配"。

所以这次我做的不是再加一个向量库,而是把整条问答链路拆开,做成了一套更像真实系统的 Agent-RAG。


这套 Agent-RAG 到底长什么样

我先给一张流程图,基本就是 SwiftBoot 里现在的实际链路。
CODE
DOC
MEMORY
CHAT
用户提问
Spring Boot / SysAiController
意图预检 /intent/detect
意图类型
Agent 工具 fetch_source_impl
Agent 工具 fetch_business_doc
长期记忆召回 /memory/query
直接回答或快路径规则
FastAPI /retrieve
ChromaDB 检索 code/doc 集合
按 intent + tool_name 二次重排
返回相关代码/文档片段
历史记忆集合 memory
LLM 生成最终回答
SSE 流式输出到前端
Watchdog 文件监听
增量切片解析
project-skills 技能库

如果只看这张图,最核心的区别就 3 个:

  • 不是"所有问题统一检索",而是先做意图路由
  • 不是"一个 search 工具包打天下",而是拆成源码检索和文档检索两个工具
  • 不是"只管查代码",而是把长期记忆、增量同步、技能知识库、流式输出一起纳入链路

1. Java 侧不直接硬搜,而是先把问题变成"任务类型"

SwiftBoot 这边的后端入口在 SysAiController。我这里没有让大模型一上来就盲搜,而是先定义了两个明确的 Agent 工具:

  • fetch_source_impl:专门查源码
  • fetch_business_doc:专门查业务文档

对应代码大致就是这样:

java 复制代码
private JSONArray buildAgentTools() {
    JSONArray tools = new JSONArray();

    JSONObject fetchSourceTool = new JSONObject();
    fetchSourceTool.set("type", "function");
    JSONObject f1 = new JSONObject();
    f1.set("name", "fetch_source_impl");
    f1.set("description", "当用户询问具体代码实现、报错排查、数据库表结构、接口定义或类方法时调用。");

    JSONObject fetchDocTool = new JSONObject();
    fetchDocTool.set("type", "function");
    JSONObject f2 = new JSONObject();
    f2.set("name", "fetch_business_doc");
    f2.set("description", "当用户询问系统整体架构、业务流程、功能使用说明时调用。");

    tools.add(fetchSourceTool);
    tools.add(fetchDocTool);
    return tools;
}

这个设计看起来很简单,但收益特别大。

因为很多项目问答失败,不是向量库不行,而是问题类型没有分清楚

举个最典型的例子:

  • "SysUserController 里分页查询怎么做的?"这显然应该走源码检索
  • "这个系统的权限中心怎么使用?"这其实更适合走文档和技能知识

你要是让这两类问题用同一套召回逻辑,最终结果大概率就是一锅粥。


2. FastAPI 这层不是摆设,它承担了真正的检索编排

我把检索引擎单独放在了 Python 侧,用 FastAPI 暴露接口,核心入口包括:

  • /retrieve
  • /memory/add
  • /memory/query
  • /intent/detect
  • /nlp/similarity

主入口大致像这样:

python 复制代码
app = FastAPI(title="SwiftBoot AI Knowledge Engine")

@app.post("/retrieve")
async def retrieve_knowledge(request: QueryRequest):
    results = db.query(
        request.question,
        n_results=request.n_results,
        intent=request.intent,
        tool_name=request.tool_name
    )
    return {"results": response}

@app.post("/memory/add")
async def add_memory(request: MemoryAddRequest):
    memory_db.add_messages(request.user_id, payload, request.session_id)
    return {"status": "ok"}

@app.post("/memory/query")
async def query_memory(request: MemoryQueryRequest):
    results = memory_db.query(
        request.question,
        user_id=request.user_id,
        n_results=request.n_results,
        session_id=request.session_id
    )
    return {"results": response}

这样拆分有两个实际好处:

第一,Java 侧职责更清晰

Java 后端负责:

  • 接收用户请求
  • 组织 Agent 调用
  • 管控安全与会话
  • 做 SSE 流式输出

Python 引擎负责:

  • 检索
  • 向量重排
  • 记忆召回
  • 意图识别
  • 增量知识同步

这比把所有东西都堆进一个 Controller 里舒服太多。

第二,检索策略可以单独演进

后面你要升级 embedding、换重排逻辑、补分集合召回,甚至接更复杂的检索实验,都可以主要在 Python 层做,不会把整个业务后端搅乱。


3. 真正让我觉得"有点意思"的,是这层意图路由

很多所谓项目 RAG,用户不管问什么,系统都只会做一件事:检索。

但 SwiftBoot 这里我单独做了 intent_routing.json,用"语义 + 规则"混合方式先判定问题类型,当前主要有 4 类:

  • CODE
  • DOC
  • MEMORY
  • CHAT

配置里能看到很明确的分流规则:

json 复制代码
{
  "settings": {
    "similarity_threshold": 0.70,
    "fallback_to_rules": true,
    "default_intent": "CHAT"
  },
  "intents": [
    { "id": "CODE" },
    { "id": "DOC" },
    { "id": "MEMORY" },
    { "id": "CHAT" }
  ]
}

它背后的思路其实很朴素:

  • 能判断成闲聊,就别浪费检索资源
  • 能判断成历史追问,就优先走长期记忆
  • 能判断成文档型问题,就别用代码片段污染上下文
  • 真正需要查实现时,再去调源码工具

这一步做完之后,整个问答体验的"稳定感"会明显提升。

因为系统终于不再是"见问题就搜",而是开始像个值班工程师一样先判断:你现在到底是在问原理、问实现、问历史,还是随口聊一句。


4. 向量库不是只存一份文本,而是至少分成 code / doc / memory 三层

这次我在 ChromaDB 里没有偷懒地把所有东西都塞到一个集合里,而是拆成了:

  • code:源码相关切片
  • doc:文档相关切片
  • memory:历史对话长期记忆

对应初始化逻辑在 vector_store.py 里能看到:

python 复制代码
self.collection = self.client.get_or_create_collection(name=COLLECTION_NAME)
self.doc_collection = self.client.get_or_create_collection(name=DOC_COLLECTION_NAME)

后面在检索阶段,又会根据 intenttool_name 做一次偏置重排:

python 复制代码
prefer_code = tool_name == "fetch_source_impl" or intent in ["CODE", "DEBUG"]
prefer_doc = tool_name == "fetch_business_doc" or intent == "DOC"

if prefer_code and item["origin"] == "code":
    origin_bonus += 0.18

if prefer_doc and item["origin"] == "doc":
    origin_bonus += 0.18

这块别小看。

很多项目问答系统做不准,问题不在召回,而在召回后没有做偏置重排

也就是说,系统虽然查到了相关内容,但不知道当前上下文里谁该排前面。

而我这里的做法就是:

  • 你走源码工具,我就给代码片段更高权重
  • 你走文档工具,我就给 markdown 和 schema 更高权重
  • 最后再统一返回 Top-N

这种"轻量但明确"的工程策略,往往比单纯堆模型更有性价比。


5. 代码一改,知识库就得跟着动,不然 AI 很快就开始胡说

我觉得项目 AI 最容易被忽略的一点,是知识库新鲜度

你今天刚改完一个接口,结果 AI 还在按昨天的代码回答,这种体验其实比"完全不会"还糟糕,因为它会给你一种"它好像懂"的错觉。

所以我在 ai-engine/file_watcher.py 里接了 Watchdog,直接监听项目目录变化。

可监听的目录包括:

  • swiftboot-backend
  • swiftboot-ui
  • devDoc
  • project-skills
  • ai-engine
  • release_notes

文件变动后的处理链路是:

  1. 识别文件类型
  2. 先删旧切片
  3. 重新解析
  4. 再写入向量库

关键逻辑大致是这样:

python 复制代码
def _process_event(self, event):
    source_id = filename.replace("\\", "/")
    self.db.delete_by_source(source_id)

    if filename.endswith(".java"):
        chunks = self.java_parser.parse_file(filename)
    elif filename.endswith(".py"):
        chunks = self.py_parser.parse_file(filename)
    elif filename.endswith(".vue"):
        chunks = self.vue_parser.parse_file(filename)

    if chunks:
        self.db.add_documents(chunks)

这意味着在 SwiftBoot 里,知识库不是"定期离线构建"的,而是跟着开发动作一起增量更新的。

这一步对体验的改善很直接:

  • 刚改完 Controller,就能马上追问实现
  • 刚补完文档,文档型问答就能命中新内容
  • 刚新增技能知识,后端下次加载就能把它纳入项目背景

6. 这套链路不只是"能查到",还尽量做到"能解释为什么这么答"

我自己其实不太喜欢那种完全黑盒式的 AI。

它给你一个答案,看起来很像那么回事,但你不知道它到底是:

  • 真查到了代码
  • 碰巧猜对了
  • 还是在一本正经地胡说

所以我在工具执行这层,特意把"引用文件名"和"检索耗时"这些信息保留下来。

executeAgentTool 里,除了调 /retrieve,还会:

  • 按工具类型二次过滤结果
  • 记录工具调用耗时
  • 提取引用文件名
  • 把查阅过的文件反馈给前端

对应代码片段:

java 复制代码
ragRequest.set("question", query);
ragRequest.set("n_results", 10);
ragRequest.set("intent", detectedIntent);
ragRequest.set("tool_name", functionName);

String ragResponse = HttpRequest.post(RAG_API_URL)
        .timeout(60000)
        .body(ragRequest.toString())
        .execute()
        .body();

AiTraceContext.addToolCall(functionName, args, "RAG_SEARCH_RESULT", ragDuration);

最近版本里,我还把这一层继续往前推了一步,把工具调用和检索上下文纳入了 AI Trace 追踪链路。

这件事的意义不是"让界面更酷",而是让系统更可调试。

因为你终于可以看清楚:

  • 这次为什么走了源码检索
  • 命中了哪些文件
  • 工具调用花了多长时间
  • 最终回答是不是建立在正确上下文上

从工程角度看,这比"答对一次"更重要。


7. 别忽略长期记忆,它决定了系统像不像"在持续协作"

除了知识检索,我这里还补了一层对话记忆。

SysAiController 会在合适的时候尝试调用 /memory/query,把和当前问题相近的历史内容召回回来。对用户来说,最直观的感受是:

  • 你不用每次都重新解释同一个模块背景
  • 上一轮刚聊过的设计结论,下一轮还能接着说
  • 系统会更像"持续协作",而不是"每问一次都重新开机"

这部分不是为了炫技,而是因为真实开发就是连续的。

只做代码检索,不做上下文连续性,AI 很容易变成一个"每次都像第一次见你"的同事。


8. 这套架构最适合什么场景

如果你的项目符合下面这些特征,这种 Agent-RAG 方案会特别合适:

  • 后端、前端、AI 引擎混合技术栈
  • 代码和文档都很多
  • 业务问题和源码问题混在一起
  • 项目迭代快,知识库必须跟着更新
  • 你希望 AI 不只是生成代码,还能解释项目

反过来说,如果只是一个十几个文件的小 Demo,其实没必要把链路做这么完整。

但只要进入"真实项目协作"阶段,你就会发现:越是复杂的项目,越不能用单一检索思路硬顶。


最后说下我这次踩出来的一个判断

做项目级 AI,真正难的不是"把大模型接进来",而是把下面这几件事同时做好:

  • 让系统知道什么时候该查源码,什么时候该查文档
  • 让知识库能跟着代码实时更新
  • 让历史对话能被合理召回
  • 让结果尽可能可追踪、可解释
  • 让整条链路不打断用户的交互体验

说白了,Agent-RAG 的重点不是 Agent,也不是 RAG,而是工程编排。

我这次在 SwiftBoot 里做的,就是把这套编排真正落到了项目里,而不是停在一个概念 Demo。


效果图

首页展示

智能会话窗口

AI看板

本地知识库chunk来源

算力消耗排行榜

模型能力指标

算力消耗分析

源码与项目地址

这套 Agent-RAG 的完整思路和工程实现,我已经放进 SwiftBoot 里了。它不是单独的实验仓库,而是直接融进了一个可以持续演进的 Spring Boot 3 + Vue 3 全栈项目。

如果你也在做这些方向,可以直接拉代码看实现,或者拿里面的链路去改成你自己的版本:

如果你后面想把这篇文章再升级成"更适合发布"的版本,我建议再补 2~3 张图:

  • AI 助手对话界面
  • 索引构建流或 AI Trace 界面
  • 配置页/规则治理页

但如果你只是先发 CSDN,这一版带 Mermaid 流程图已经够用了。


如果你也在做项目级 AI,欢迎来交流。

我自己越来越确定一件事:相比"再换一个更强的模型",很多时候先把检索链路和工程结构搭对,收益反而更大。

#Java #SpringBoot #FastAPI #ChromaDB #RAG #Agent #AI #开源项目

相关推荐
serve the people2 小时前
BERT模型
人工智能·深度学习·bert
木斯佳2 小时前
前端八股文面经大全:得物AI应用开发一面(2026-03-23)·面经深度解析【加精】
前端·人工智能·ai·markdown·chat·rag
绒绒毛毛雨2 小时前
On the Plasticity and Stability for Post-Training Large Language Models
人工智能·机器学习·语言模型
自由生长20243 小时前
技术科普-存算分离
架构
H5css�海秀10 小时前
今天是自学大模型的第一天(sanjose)
后端·python·node.js·php
SuniaWang10 小时前
《Spring AI + 大模型全栈实战》学习手册系列 · 专题六:《Vue3 前端开发实战:打造企业级 RAG 问答界面》
java·前端·人工智能·spring boot·后端·spring·架构
韩立学长10 小时前
Springboot校园跑腿业务系统0b7amk02(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
sheji341610 小时前
【开题答辩全过程】以 基于springboot的扶贫系统为例,包含答辩的问题和答案
java·spring boot·后端
IDZSY043011 小时前
AI社交平台进阶指南:如何用AI社交提升工作学习效率
人工智能·学习