AI 机器人项目设计详解

0 阅读22分钟

AI 机器人项目设计详解

对应项目:

这篇文档专门解决一个高频面试问题:

面试官问你“这个 AI 项目是怎么设计的”,你怎么讲,才能显得你不是只会调模型接口,而是真的有系统设计能力?

这篇稿子会重点展开:

  • 项目定位和业务目标
  • 为什么不能只做一个简单聊天接口
  • 整体架构怎么拆
  • 普通聊天、联网搜索、RAG 客服三条链路怎么跑
  • Advisor 为什么是项目核心设计
  • 数据层、文件层、流式层为什么这么设计
  • 面试时怎么讲得更像自己真做过

一、这个项目本质上是什么

这个项目本质上不是一个“调用大模型 API 的 Demo”,而是:

一个基于 Spring Boot 3 + Spring AI 构建的 AI 对话平台,支持普通聊天、联网搜索增强问答,以及基于私有知识库的智能客服。

也就是说,这个项目不是只做一个 /chat 接口,而是围绕真实产品需求,把下面这些能力都做进去了:

  • 会话管理
  • 历史消息持久化
  • SSE 流式输出
  • 长上下文记忆
  • 联网搜索
  • 网页正文抓取与清洗
  • Markdown 知识库管理
  • 分片上传与合并
  • 异步向量化
  • RAG 检索问答

所以这个项目真正考验的是:

  • AI 应用工程化能力
  • 模块拆分能力
  • 对话链路设计能力
  • 数据和异步流程设计能力

二、面试时开场第一句话怎么说

建议你这样讲:

这个项目是一个基于 Spring Boot 3 + Spring AI + PostgreSQL/pgvector 搭建的 AI 对话平台,后端主要支持三类能力:普通多轮聊天、联网搜索增强问答,以及基于私有 Markdown 知识库的智能客服。设计上我把记忆注入、联网搜索、RAG 检索、流式消息落库这些能力拆成了独立的 Advisor 和业务模块,这样系统既能满足聊天体验,也有比较完整的工程化能力。

这句话的好处是:

  • 一上来就把项目定位拉高
  • 直接点出“不是简单调用模型”
  • 把核心设计点 Advisor 提前抛出来

三、为什么这个项目不能写成一个简单 Controller

很多 AI 项目一开始都会写成这样:

  • Controller 收到用户输入
  • 直接拼 prompt
  • 调模型
  • 把结果返回

这种写法短期能跑,但很快就会遇到问题:

  • 多轮对话记忆怎么做
  • 会话和消息怎么存
  • 联网搜索逻辑放哪
  • 知识库检索怎么接进去
  • 流式输出和落库怎么兼顾
  • 文件上传与向量化怎么管理
  • 以后想新增能力时怎么扩展

所以这个项目没有走“单 Controller 直调模型”的路线,而是采用了:

  • Controller 负责入口
  • Service 负责业务
  • Advisor 负责模型调用链增强
  • PostgreSQL 负责业务数据和向量数据
  • Event/Listener 负责异步向量化

这才是一个真正能扩展的 AI 应用后端架构。


四、整体架构怎么拆

这个项目后端可以拆成 6 层:

  1. Controller 接口层
  2. Service 业务层
  3. Advisor 增强链
  4. 数据访问层
  5. 文件与事件处理层
  6. 外部能力集成层

五、各层分别负责什么

1. Controller 接口层

核心文件:

这一层主要负责:

  • 接收前端请求
  • 参数校验
  • 区分不同对话场景
  • 组装 ChatModel/ChatClient
  • 选择要挂哪些 Advisor
  • 返回 REST 或 SSE 响应

也就是说,Controller 层负责“搭路由和组装调用链”,而不是承载复杂业务。

2. Service 业务层

主要负责:

  • 对话新建、删除、重命名、分页查询
  • 消息分页查询
  • 文件检查、上传、合并、删除、分页查询
  • 搜索服务
  • 网页内容抓取服务

这层让业务逻辑从 Controller 中剥离出来。

3. Advisor 增强链

这是整个项目最核心的设计层。

核心类有:

它们负责在模型调用前后做增强:

  • 注入历史消息
  • 注入联网搜索上下文
  • 注入 RAG 检索上下文
  • 流式收集模型输出并落库

4. 数据访问层

主要依赖:

  • MyBatis Plus
  • PostgreSQL
  • pgvector

负责:

  • 存储会话
  • 存储消息
  • 存储文件元数据
  • 存储分片信息
  • 存储知识库向量

5. 文件与事件处理层

负责:

  • 文件分片上传
  • 文件合并
  • 状态管理
  • 上传成功后发布事件
  • 异步向量化

6. 外部能力集成层

主要是:

  • OpenAI 兼容模型接口
  • SearXNG 搜索引擎
  • OkHttp 抓取网页
  • Jsoup 清洗 HTML

六、为什么 Advisor 是整个项目最核心的设计

这是面试最值得展开的地方。

如果不用 Advisor,会发生什么?

  • 记忆注入写在 Controller
  • 联网搜索写在 Controller
  • RAG 检索写在 Controller
  • 流式落库也写在 Controller

最后就会变成:

  • 一个很长的方法
  • 一堆 if-else
  • 不同场景互相耦合

所以这个项目把“模型调用增强”统一抽成了 Advisor。

本质上,Advisor 的作用就是:

在模型调用链的前后插入可复用的增强逻辑。

这样每个 Advisor 只负责一件事:

  • 记忆 Advisor 只管“补历史消息”
  • 联网 Advisor 只管“做搜索并补上下文”
  • RAG Advisor 只管“检索知识库并补上下文”
  • 流式落库 Advisor 只管“收集输出并落库”

这种设计的好处非常明显:

  • 逻辑职责清晰
  • 不同场景下可自由组合
  • 新增能力时不容易破坏老链路
  • 代码更符合扩展开放原则

这能非常强地体现你的架构思维。


七、普通聊天链路是怎么设计的

普通聊天对应的核心入口是:

整体链路

  1. 前端调用 /chat/completion

  2. 后端解析:

    • 用户消息
    • chatId
    • 模型名称
    • 温度
    • 是否开启联网搜索
  3. 构建 OpenAiChatModel

  4. 构建 ChatClient

  5. 根据是否联网决定挂哪个 Advisor

  6. 一律挂上流式落库 Advisor

  7. 模型开始流式输出

  8. 结果通过 SSE 推送给前端

  9. 流结束后完整问答入库

为什么这样设计

这样设计的目的,是把“普通聊天”和“增强聊天”统一到一套主流程里。

不管是:

  • 纯聊天
  • 联网搜索聊天

本质上都是:

  • 创建模型调用
  • 挂不同增强器
  • 流式输出

这让主链路保持一致,扩展性很好。


八、为什么普通聊天要做“记忆注入”

如果每次请求只把当前一句话发给模型,那就不是真正的多轮聊天。

所以项目里做了:

  • CustomChatMemoryAdvisor

它负责:

  1. 根据 chatId 查询历史消息
  2. 按时间顺序组装消息列表
  3. 转成模型能理解的 message 结构
  4. 把历史消息和当前问题一起送给模型

为什么不直接让前端传全部上下文

直接让前端每次传全量上下文,问题很多:

  • 前端负担大
  • 历史太长时请求体变大
  • 容易出现前后端上下文不一致
  • 后端不好统一控制上下文长度

而把消息存在数据库,再由后端统一拉最近 N 条消息,有几个明显优点:

  • 后端可统一裁剪上下文
  • 前端接口更简洁
  • 历史消息可审计
  • 对话可以分页查看

这也是一个很典型的“产品级设计”。


九、为什么流式输出还要做“完整落库”

项目里做了一个非常重要的设计:

  • CustomStreamLoggerAndMessage2DBAdvisor

它不是只负责把模型 token 流式推给前端,而是做了两件事:

  1. 实时向前端推送
  2. 流结束后,把完整问答落库

为什么不边流边写库

边流边写库的缺点很明显:

  • 一条完整回答会被拆成很多碎片
  • 中途断流时数据很脏
  • 前端历史展示不方便

所以项目里选择的是:

  • 先用 StringBuilder 或类似方式累积完整内容
  • 等模型输出结束后,再把用户消息和完整助手消息一起写库

这样设计的好处

  • 数据库存的是完整消息,而不是碎片
  • 审计和回放更方便
  • 历史对话展示更自然
  • 流式体验和持久化一致性兼顾

这个点非常适合在面试里展示你对“流式接口”和“落库一致性”的理解。


十、联网搜索链路为什么不是“搜一下就喂给模型”

联网搜索核心不在于“能搜索”,而在于“搜索结果怎么转成高质量上下文”。

项目里对应的核心模块是:

  • SearXNGServiceImpl
  • SearchResultContentFetcherServiceImpl
  • NetworkSearchAdvisor

完整链路

  1. 用户开启联网搜索
  2. NetworkSearchAdvisor 拦截用户问题
  3. 调 SearXNG 搜索关键词
  4. 拿到一批搜索结果 URL
  5. 用 OkHttp + CompletableFuture 并发抓取网页
  6. 用 Jsoup 从 HTML 中提取正文
  7. 过滤无效内容,控制长度
  8. 把清洗后的内容拼成 context
  9. question + context 塞进增强 Prompt
  10. 交给模型回答

为什么不能直接把搜索结果标题简介塞给模型

因为:

  • 搜索摘要往往不够详细
  • 很多关键信息在正文里
  • 标题和摘要容易误导模型

为什么不能直接把整页 HTML 塞给模型

因为:

  • 噪音太多
  • HTML 标签浪费 token
  • 广告、导航、脚本内容会污染上下文

所以正确的工程做法就是:

  • 先搜索
  • 再抓正文
  • 再清洗
  • 最后增强 Prompt

这就是这个项目里联网搜索的设计亮点。


十一、为什么用 SearXNG

很多面试官会问:

为什么不直接用某个搜索引擎 API?

你可以这样回答。

1. 它是元搜索引擎

可以聚合多个搜索来源,对外统一成一个接口。

2. 自部署,控制力更强

比直接依赖第三方商业搜索 API 更灵活。

3. 后端对接成本低

业务层只需对接一个搜索服务。

4. 方便后续切换和扩展

以后想换搜索源,对业务代码影响小。

这体现的是“中间层抽象”的意识。


十二、为什么抓网页正文要用并发

如果串行抓 10 个网页,每个网页几百毫秒到几秒,整体响应就会很慢。

所以项目里用了:

  • OkHttp
  • CompletableFuture
  • 双线程池

来并发抓取内容。

为什么这很重要?

  • 联网搜索场景下,用户对延迟非常敏感
  • 搜索结果条数越多,串行方式越不可接受
  • 并发抓取可以显著降低整体等待时间

这说明你不是只关心“功能能不能跑”,而是考虑到了性能体验。


十三、RAG 客服链路是怎么设计的

RAG 客服对应核心模块:

核心链路

  1. 用户发送客服问题
  2. Controller 创建模型调用
  3. 挂载 CustomerServiceAdvisor
  4. Advisor 根据问题去 pgvector 做相似度检索
  5. 拿到 topK 相关文档
  6. 把文档内容拼接成上下文
  7. 用模板把上下文和问题组合成增强 Prompt
  8. 交给模型生成答案
  9. 流式返回给前端

为什么要把客服和普通聊天分开

因为客服场景强调的是:

  • 基于知识库回答
  • 减少幻觉
  • 输出要稳定
  • 超出范围时要收口

而普通聊天更强调:

  • 通用性
  • 灵活回答
  • 多轮交互体验

这两种场景的目标不同,Prompt 和上下文来源也不同,所以设计上要分开。


十四、为什么要把 RAG 做成单独的 Advisor

这也是一个很重要的设计点。

如果把 RAG 写死在 Controller 里,后面会遇到问题:

  • Controller 逻辑越来越重
  • 提示词和检索逻辑耦合
  • 想切换检索策略不方便

而做成单独 Advisor 后,好处很明显:

  • 检索逻辑独立
  • Prompt 模板独立
  • 可替换性更强
  • 更方便后续扩展 metadata 过滤、不同 topK 策略、不同检索方式

这本质上是在做:

检索层和生成层之间的桥接抽象。

这个表述很适合面试。


十五、为什么 PostgreSQL 同时承担业务存储和向量存储

这个项目里 PostgreSQL 承担了两类角色:

  1. 业务库
  2. 向量库底座

业务库存什么

  • 会话
  • 消息
  • 文件元数据
  • 文件分片信息

向量库存什么

  • Markdown 切分后的文档向量

这样设计的好处

1. 架构更简单

一套数据库就能支撑业务表和向量检索。

2. 运维成本低

不需要额外引入专门向量数据库。

3. 对中等规模项目足够实用

当前这种场景下,pgvector 已经可以满足需求。

4. 业务数据和知识库数据管理更统一

例如删除文件时,可以很方便地同时清理元数据和向量。

这体现的是“在当前规模下做合理技术选型”的能力。


十六、知识库文件为什么要做“分片上传 + 合并 + 异步向量化”

很多人做知识库问答时只做了一步:

  • 上传文件
  • 直接同步向量化

这在 Demo 里可以,但在真实系统里问题很多:

  • 大文件上传不稳定
  • 断点续传难做
  • 向量化耗时长,会阻塞请求
  • 失败后难恢复

所以这个项目做了更完整的设计。

完整链路

  1. 前端先调文件检查接口

  2. 服务端根据 MD5 判断:

    • 是否已存在
    • 是否支持秒传
    • 哪些分片已上传
  3. 前端分片上传文件

  4. 服务端记录每个分片信息

  5. 分片齐全后执行合并

  6. 合并完成后更新文件状态

  7. 发布“上传完成”事件

  8. 监听器异步解析 Markdown 并向量化

  9. 更新最终状态

这样设计的价值

  • 大文件上传更稳
  • 可支持断点续传
  • 合并和向量化解耦
  • 主请求响应更快
  • 状态更清晰,便于排查问题

这就是为什么这个项目看起来更像“系统设计”,而不是“AI 能力拼装”。


十七、为什么异步向量化很关键

向量化通常比较耗时,原因包括:

  • 文件解析
  • 文本切分
  • embedding 计算
  • 向量写入
  • 去重判断

如果把这些都放在上传请求里同步执行,会带来几个问题:

  • 接口耗时很长
  • 用户体验差
  • 容易超时
  • 出错时主流程体验不好

所以项目里用:

  • 事件
  • 监听器
  • 异步线程池

来做向量化。

这是一种非常典型的工程优化思路:

主流程负责接收和确认,重计算流程异步化处理。


十八、为什么要做文件状态机

一个文件从上传到最终可用,不是一步完成的。

它至少会经历:

  • 上传中
  • 待向量化
  • 向量化中
  • 完成
  • 失败

如果没有状态机,会发生什么?

  • 前端不知道当前文件能不能用
  • 出错了不好排查
  • 无法区分“还在处理中”还是“已经失败”

所以文件状态管理实际上是知识库工程化里很重要的一部分。

这也很适合面试里讲:

我们没有把文件上传看成一个简单表单动作,而是把它设计成带状态流转的生命周期管理,这样前端展示、异常恢复和问题排查都会更清晰。


十九、Prompt 为什么也算架构的一部分

在传统后端里,大家讲架构时更关注代码和数据库。

但在 AI 项目里,Prompt 其实也是系统设计的一部分。

因为它决定了:

  • 模型的角色
  • 回答边界
  • 输出格式
  • 是否允许幻觉
  • 超出知识库时怎么处理

这个项目里至少有两类 Prompt 设计:

1. 联网搜索场景 Prompt

强调:

  • 基于上下文回答
  • 综合多个来源
  • 尽量带来源信息
  • 不要胡乱扩展

2. 智能客服场景 Prompt

强调:

  • 角色一致
  • 严格基于知识库
  • 超出范围要明确收口
  • 输出口径稳定

所以你在面试时可以说:

这个项目里我不是把 Prompt 当成随手写的一段文本,而是把它当成模型行为控制的一部分,不同业务场景用了不同模板,再结合 Advisor 注入上下文,来控制回答质量和稳定性。


二十、SSE 为什么适合这个项目

这个项目用了 SSE 做流式输出。

为什么选 SSE,而不是一上来就用 WebSocket?

因为这里的核心需求是:

  • 用户发一次请求
  • 后端单向持续返回模型生成结果

这非常适合 SSE。

SSE 的优点

  • 协议简单
  • 基于 HTTP,接入成本低
  • 前端容易消费
  • 对“模型逐 token 输出”场景很友好

为什么不是 WebSocket

WebSocket 更适合:

  • 高频双向交互
  • 实时协作
  • 长期连接状态管理

而这个项目当前主要是单向流式回答,所以 SSE 是更轻量的选择。

这体现的是技术选型的合理性。


二十一、AI 项目的完整三条链路怎么讲

面试时最好能一口气把三条链路讲清楚。

链路 1:普通聊天

  1. 前端发 /chat/completion
  2. 后端构建模型调用
  3. 根据 chatId 拉历史消息
  4. 记忆 Advisor 注入上下文
  5. 模型流式生成回答
  6. 前端通过 SSE 接收内容
  7. 流式落库 Advisor 收集完整消息并入库

链路 2:联网搜索聊天

  1. 前端开启联网搜索参数
  2. 后端挂载 NetworkSearchAdvisor
  3. 搜索问题关键词
  4. 并发抓取网页正文
  5. 清洗后构造上下文
  6. 把增强 Prompt 交给模型
  7. 流式返回并落库

链路 3:RAG 智能客服

  1. 用户进入客服场景提问
  2. 后端挂载 CustomerServiceAdvisor
  3. 通过 pgvector 检索相关文档
  4. 构造上下文和增强 Prompt
  5. 模型流式回答
  6. 前端显示结果

如果你能顺畅讲出这三条链路,面试官基本会认可你对这个项目的掌控程度。


二十二、这个项目的亮点怎么讲

建议你重点讲 6 个亮点。

亮点 1:不是单一聊天接口,而是多场景 AI 平台

包含普通聊天、联网问答、RAG 客服三类能力。

亮点 2:Advisor 链式增强设计

记忆、联网、RAG、落库模块化,扩展性很好。

亮点 3:PostgreSQL 同时支撑业务数据和向量检索

架构简单、成本可控、适合当前规模。

亮点 4:流式输出和完整落库兼顾

兼顾用户体验和数据一致性。

亮点 5:知识库管理做了完整生命周期设计

包括文件检查、分片上传、合并、异步向量化和状态机。

亮点 6:联网搜索不是简单摘要拼接,而是正文抓取和清洗

质量更高,也更像真实产品能力。


二十三、这个项目最容易被追问的难点

难点 1:如何让普通聊天真正具备“记忆”

难点在于:

  • 历史消息如何持久化
  • 怎么控制上下文长度
  • 怎么在每次请求时回放历史

你的解法:

  • 会话和消息落 PostgreSQL
  • 通过 Advisor 按 chatId 拉最近 N 条消息注入上下文

难点 2:流式输出和落库如何兼容

难点在于:

  • 流式输出是碎片化的
  • 持久化更需要完整消息

你的解法:

  • 先流式返回
  • 同时累积完整内容
  • 结束后统一落库

难点 3:RAG 不只是检索,还要处理工程链路

难点在于:

  • 文件上传
  • 文件切分
  • 向量化耗时
  • 去重
  • 状态维护

你的解法:

  • 文件服务 + 事件驱动 + 异步向量化 + 状态机

难点 4:联网搜索结果如何变成高质量上下文

难点在于:

  • 搜索结果摘要信息不足
  • 原始 HTML 噪音很大

你的解法:

  • 搜链接
  • 抓正文
  • Jsoup 清洗
  • 控制上下文长度

二十四、这个项目有哪些可以继续优化的地方

这个问题面试官也可能问。

你可以这样回答。

1. 记忆管理还能更精细

现在是按最近 N 条消息注入,后续可以改成:

  • 按 token 长度裁剪
  • 按摘要 + 最近消息混合注入

2. RAG 检索还能更强

可以继续优化:

  • chunk 粒度
  • topK 策略
  • 元数据过滤
  • rerank

3. 联网搜索可以加更强的结果排序

现在主要是搜索 + 正文抓取,后续可以做:

  • 可信站点优先
  • 多源交叉验证
  • 摘要去重

4. 对话和知识库的观测能力还能提升

比如增加:

  • 检索命中率统计
  • Prompt 版本追踪
  • token 消耗监控
  • 用户提问分类分析

这样会显得你不仅关注当前实现,也有后续产品化思路。


二十五、面试时的 1 分钟版本

这个项目是一个基于 Spring Boot 3 和 Spring AI 的 AI 对话平台,主要支持普通多轮聊天、联网搜索增强问答,以及基于私有 Markdown 知识库的智能客服。设计上我把记忆注入、联网搜索、RAG 检索、流式落库这些能力拆成了独立的 Advisor,这样不同场景可以按需组合,而不会把所有逻辑堆到 Controller 里。数据层上我用 PostgreSQL 同时存会话、消息和知识库向量;知识库部分还做了分片上传、文件合并和异步向量化,所以整个系统既满足了聊天体验,也具备比较完整的工程化能力。


二十六、面试时的 3 分钟展开版

这个项目本质上不是一个简单的 AI 聊天 Demo,而是一个比较完整的 AI 应用后端。它主要有三类能力:普通多轮聊天、联网搜索增强问答,以及基于私有知识库的智能客服。
设计上我重点考虑的是,模型调用前后会有很多增强逻辑,比如普通聊天要带历史记忆,联网场景要先检索并抓取网页正文,客服场景要先做向量检索并构造知识库上下文,如果这些都堆在 Controller 里,代码会非常难维护。所以我基于 Spring AI 的 Advisor 机制,把这些能力拆成了独立模块,比如记忆 Advisor、联网搜索 Advisor、RAG Advisor,还有一个专门负责流式输出收集和消息落库的 Advisor。
数据层方面,我用 PostgreSQL 存会话、消息、文件元数据,同时通过 pgvector 存知识库向量。普通聊天时,根据 chatId 从数据库拉历史消息做上下文注入;联网搜索时,先调用 SearXNG 获取结果,再用 OkHttp 和 CompletableFuture 并发抓取网页内容,用 Jsoup 提取正文后拼进 Prompt;智能客服则通过 pgvector 检索最相关知识片段,再用固定模板约束模型基于知识库回答。
另外知识库部分我没有做成简单的单文件上传,而是支持文件检查、分片上传、合并和异步向量化,这样更符合真实系统场景。所以这个项目最核心的设计点,是把 AI 能力做成了模块化、链路化和工程化,而不只是单点调用模型。


二十七、面试官继续深挖时怎么接

他问“你这个项目最核心的设计是什么”

你就答:

  • Advisor 链式增强机制

因为它把复杂的模型调用逻辑解耦了。

他问“你这个项目最难的地方是什么”

你就答:

  • 流式输出和落库兼容
  • RAG 工程链路设计
  • 联网搜索结果清洗和上下文控制

他问“你真正做了什么”

你就答:

  • 不是只写了 Controller
  • 而是参与了链路拆分、存储设计、Advisor 设计、RAG 设计、联网搜索设计、文件生命周期设计

他问“这个项目和普通 AI Demo 的区别是什么”

你就答:

  • 有状态
  • 有存储
  • 有链路增强
  • 有文件生命周期
  • 有流式与落库设计
  • 有真实工程能力

二十八、最后一句总结怎么说

这个项目最能体现我的,不只是我会接大模型,而是我能把 AI 能力做成一个有分层、有存储、有增强链、有异步流程和有产品化思路的工程系统。