32-企业知识库助手完整设计
设计一个企业知识库助手:把架构、权限与交付贯通
一个有说服力的求职项目,应能回答用户如何完成任务、数据怎样隔离、故障以后怎样恢复。本文把前面的后端与 AI 能力收束到企业知识库助手,给出可逐步实现的参考设计,不把方案写成已经上线的经历。
Spring AI 接入片段使用 Java 17、Spring Boot 3.5.3 与 Spring AI 1.1.8;协议与第三方平台示例另行标明适用范围。
1. 需求和架构从一条完整链路开始
先做制度问答与本人订单查询,不默认自动写入。
需求从用户行为开始
普通用户上传自己有权限维护的资料,查看导入进度,询问制度并打开来源;用户也可以查询自己订单的状态。管理员维护知识库范围与文档发布状态,但不意味着所有对话都需要以管理员权限执行。
第一阶段只做只读助手,不自动支付、取消订单或审批。这个范围使模型决策与写入风险分开,能够优先完成可检查的知识和业务链路。
验收条件包括:引用指向正确版本,无依据时说明无法确认,其他租户资料不可进入回答,本人订单可查而他人订单不可查,工具超时时不编造状态。
用模块边界代替过早拆服务
先采用模块化单体:身份与权限、文档管理、导入任务、检索问答、业务工具、会话审计。模块通过明确接口协作,不直接任意访问其他模块的内部表。
Vue 3
↓ 同源 API
Java 应用
├─ 认证与数据权限
├─ 文档 / 会话 / 导入任务
├─ Spring AI:检索、模型、受控工具
└─ 可选 MCP 接口:受授权的业务查询
↓
MySQL:业务事实、任务、权限、执行记录
Redis:短期缓存、限流、可恢复的会话上下文
文件存储:原始文档与派生内容
向量存储:片段向量与过滤元信息
Worker 可以先是同一项目中的独立进程或受控后台组件,使用任务表领取工作。只有隔离、扩展或团队边界真的需要时,再拆成独立服务。
数据模型怎样支撑整条链路?
核心对象包含租户、用户、知识库、文档、文档版本、片段、导入任务、会话、消息和工具执行。对象都需要稳定标识,跨存储通过这些标识关联。
| 对象 | 关键字段 | 负责的事实 |
|---|---|---|
| document_version | 文档、版本、内容摘要、状态 | 哪份内容有效 |
| import_task | 版本、状态、租约、重试次数 | 导入进度与恢复 |
| chunk | 版本、序号、来源定位 | 片段来源与引用 |
| conversation | 租户、用户、状态 | 会话归属 |
| tool_execution | 操作标识、操作者、结果 | 调用审计与重复识别 |
MySQL 中保存的发布状态是检索可见性的依据。向量库中存在一个片段,不代表它已经允许使用。入库、更新和删除要维护索引与业务元数据的一致性。
不把 Redis 当成唯一业务记录。缓存失效后,系统应该能够从持久数据恢复必要状态;对模型上下文和完整聊天历史分别管理。
两条主要链路怎样执行?
文档链路是:上传验证 → 保存原文与任务 → 解析 → 切分 → 向量化 → 建立索引 → 校验 → 发布。失败时保留状态和错误,不能把半完成版本展示为正式知识。
问答链路是:验证会话归属 → 判断问题范围 → 授权检索或业务查询 → 构造证据上下文 → 生成 → 保存消息与来源 → 返回。权限过滤发生在取得内容和进入模型之前。
订单工具始终调用业务服务,租户与用户来自服务器上下文。知识规则说明应该怎样处理,订单工具说明现在是什么状态,回答中保留这种证据区别。
Spring AI 的 ChatClient、工具与 RAG 能力用于组织应用流程,业务状态和权限仍由项目实现。ChatClient、工具调用。
2. 权限和任务恢复贯穿数据生命周期
上传、Worker、检索、工具、历史与下载都限制范围。
重试、取消与删除怎样配合?
网络超时、临时限流适合受控重试,采用退避、随机抖动和整体尝试上限。格式不支持、文件损坏或权限已撤销应停止,并给出可修改原因。
用户删除文档时先撤销可见状态,再取消正在运行的任务。Worker 在发布之前重新检查文档是否仍可发布,防止一个迟到任务把已删除内容重新恢复可见。
取消可以停止后续批次,但已经产生的费用和远端写入不一定撤销。记录残留资源并安排清理,不能只将状态改为 CANCELLED 就认为全部完成。
查询接口和监控怎样设计?
任务查询返回状态、阶段、已处理数量、重试次数和安全的错误说明。必须核对任务所属租户和用户范围,不能凭任务编号公开导入内容。
监控等待时长、失败分类、重试积压、租约接管和孤立文件。恢复检查包括 Worker 崩溃、重复领取、向量库失败、权限撤销和版本更新竞争。
预期结果是同一任务可以恢复、片段不会无限重复、未完成版本不进入检索。实际是否满足,需要在项目实现后验证,不能由设计直接宣布成功。
检索阶段怎样阻止内容进入模型?
在检索查询中传入可信租户、授权知识库与有效版本条件。向量库过滤能力必须实际支持这些约束;不支持时,采用分库分索引或其他可以保证范围的方案。
检索后的再次授权检查用于防御陈旧元信息与撤权竞争,不能替代检索前过滤。否则未授权内容可能已经进入重排器、外部服务、日志或共享缓存。
登录身份
↓
服务器计算允许知识库与当前版本
↓
带权限约束的检索
↓
校验候选仍可访问 → 必要时重排 → 组织上下文
↓
模型生成 → 来源下载再次授权
缓存键包含租户、权限范围或相应修订以及检索配置。相同问题不代表可以跨用户共享答案,尤其回答中包含个人业务结果时。
用矩阵检查每个入口
准备租户 A、B,各有普通用户和管理员,使用合成文档和订单。以下是预期用例,不是已完成测试报告:
| 场景 | 预期行为 | 检查渠道 |
|---|---|---|
| A 用户请求 B 文档 | 拒绝且不泄漏存在性 | API、日志、下载 |
| A 问题与 B 文档高度相似 | B 内容不进入候选或模型 | 检索、重排、上下文 |
| 猜测他人会话编号 | 拒绝读取与停止 | 会话、消息、取消 |
| 同问题命中缓存 | 按权限范围使用缓存 | Key、答案和来源 |
| 导入期间撤销文档 | 不发布已撤销版本 | Worker 与索引 |
| 工具参数伪造身份 | 不影响可信身份 | 工具、SQL、审计 |
授权测试不能只检查页面显示,还要尝试直接接口、旧引用链接、历史会话和后台恢复。越权阻断应有内部记录,但避免日志保存受限内容全文。
3. 前端状态也属于正确性
列表、聊天和移动端按实际契约处理失败与兼容。
先统一列表接口约定
列表请求明确页码从一还是零开始、每页上限、排序字段和过滤条件。后端设置稳定排序,例如 created_at DESC, id DESC,避免同一时间的数据顺序不确定。
以下是本文使用的响应结构:
{
"rows": [{"id": "1001", "orderNo": "ORDER001", "status": "CREATED"}],
"total": 1,
"pageNum": 1,
"pageSize": 10
}
较大的数据库 ID 可作为字符串返回,避免 JavaScript 数值精度问题。时间格式、时区和金额单位同样要在契约中明确,而不是由前端猜测。
表单校验和提交怎样分工?
前端校验长度、必填和基本格式,帮助用户即时修改;后端执行同样或更严格的规则,并检查权限与业务状态。绕过网页直接调用接口,规则仍须成立。
打开新建窗口时创建新的表单对象,编辑窗口使用选中记录的副本。不要直接绑定表格对象,否则用户取消编辑后,表格已经被改动。
Element UI 的表单项 prop 对应表单模型字段,校验规则与字段名必须一致。resetFields 与初始值有关,不能假设它永远清空成空字符串。Form 文档。
提交期间禁用按钮可以减少重复点击,但不能替代后端幂等。乐观锁冲突时提示用户刷新,不应静默覆盖其他人的修改。
删除与分页有哪些边界?
删除当前页最后一条记录后,可能需要回到上一页重新查询。根据删除后的 total 和页大小计算合法页码,再加载,避免永久停留在空页。
筛选条件变化时回到第一页。页大小变化也要重新计算或重置页码。输入框正在编辑的关键词与已经应用的筛选值分开保存,可以避免翻页时意外应用尚未提交的输入。
后端权限变化时,页面隐藏按钮只能改善交互。真正的接口授权和数据范围始终由服务器执行。
同一份代码有哪些不同运行条件?
H5 使用浏览器环境,受到同源策略和 CORS 约束。小程序受到平台的合法域名、HTTPS 等规则约束。App 还受设备网络、证书与系统权限影响。
开发工具中的“忽略域名校验”不能代表发布后的行为。真机上的 localhost 指的是设备自己,不是开发电脑。应提供设备可达且符合平台要求的测试地址。
明确构建目标、平台版本、基础库与 HBuilderX 版本,把问题记录为可复现条件,而不是只说“手机访问不了”。uni.request 文档。
网络问题按层次排查
先确认设备是否能访问服务域名,再检查 HTTPS 证书、平台域名设置和请求状态。服务端日志没有收到请求时,问题通常发生在到达接口之前。
H5 的浏览器控制台可检查预检请求和 CORS;小程序看平台网络日志与合法域名;App 看真机网络和证书。反向代理路径也要核对,避免测试环境多了一层前缀。
请求失败和业务失败分开展示。例如断网提示检查网络,403 提示当前操作无权限,参数错误则指向需要修改的字段。
流式响应和组件兼容不能想当然
浏览器 fetch 的 ReadableStream 示例不能直接保证在所有 uni-app 目标端工作。平台是否支持分块、相关回调与取消,需要查目标端能力表并真机验证。
对于 AI 聊天,可以选择目标端支持的分块请求,或通过后端 WebSocket 协议转发,但仍需认证、事件顺序、终态和断线恢复。不要为了统一接口名称而省略这些约束。
维护一张目标端验证表:登录、请求、上传、下载、取消、长连接和组件展示。明确“已支持”“需兼容实现”“尚未验证”,有助于避免上线前才发现差异。
4. 部署后怎样证明可用和可恢复
产物、配置、检查与故障记录对应,不把模板填成实测结果。
部署前固定产物与数据边界
保存应用提交与镜像 digest、数据库迁移版本、配置修订、模型配置、知识库版本及文件存储位置。秘密通过受控配置提供,不写入镜像或公开文档。
部署网络中只暴露必要入口,数据库与 Redis 不作为公共服务。原始文件下载经过权限检查,向量库访问限于受控应用和任务组件。
先使用 Compose 表达最小服务关系,扩展到多实例时明确任务领取、会话一致性和共享存储。一个单机 Compose 文件不能直接证明高可用。
健康检查与业务检查分开
应用存活检查进程是否能工作,就绪检查是否能承接关键请求。把所有外部依赖绑定到存活探针,可能因为模型短暂不可用反复重启应用。
业务检查使用合成账号、合成文档与只读订单:登录、读取授权文档、回答并引用来源、查询本人订单、查看任务和历史。检查不产生真实交易或敏感数据。
发布记录可以采用如下模板:
{
"releaseId": "release-001",
"imageDigest": "部署时填写实际值",
"schemaRevision": "migration-007",
"configRevision": "config-004",
"knowledgeRevision": "knowledge-012",
"checks": {
"login": "待验证",
"authorizedRetrieval": "待验证",
"ownedOrderTool": "待验证",
"history": "待验证"
},
"rollbackTarget": "release-000"
}
模板没有填写通过状态,避免把准备好的检查清单当成执行记录。
按依赖制定故障场景
| 场景 | 预期服务行为 | 主要恢复检查 |
|---|---|---|
| Redis 不可用 | 限定降级,不绕过权限 | 会话上下文、限流策略 |
| MySQL 不可用 | 关键写入和授权查询失败关闭 | 连接恢复、迁移与事务 |
| 向量库不可用 | 明确知识查询不可用 | 候选、版本与过滤 |
| 模型超时 | 中止或标记未完成 | 预算、取消和历史状态 |
| Worker 崩溃 | 租约后可接管 | 重复片段、任务状态 |
| 文件存储失败 | 上传失败或保持待处理 | 孤立文件和引用完整性 |
| 磁盘压力 | 告警与限制任务 | 日志、队列与可写空间 |
这些是待验证假设,项目实现后可能需要修订。降级不能取消数据权限,例如 Redis 中权限缓存失败时,应该查询可信持久来源或拒绝,而不是默认全部允许。
一次演练怎样执行?
演练前确认隔离环境、可恢复备份和停止条件,记录初始任务、文档与会话状态。选择一种故障,不同时中断全部依赖。
注入后观察用户行为、错误分类、预算和后台任务。恢复依赖,再检查服务是否需要人工动作、任务是否重复、历史是否准确以及权限是否仍生效。
例如 Worker 崩溃场景:让任务进入向量写入阶段,停止测试 Worker,等待租约失效,启动接管者。核对片段 ID、有效版本指针和任务终态。不要只看新 Worker 已启动就宣布恢复。
为每次演练记录实际时间线、证据与结果。没有测量之前不承诺“几秒恢复”或“零丢失”。
回滚与数据恢复怎样验证?
应用回滚使用已知镜像,保留兼容结构。数据库恢复在隔离环境完成,检查原文、片段元信息和索引版本是否匹配。
向量索引允许重建时,保留原始内容、切分规则、模型配置和稳定 ID。恢复可能产生新的模型费用与耗时,需要进入容量与恢复计划。
外部工具有副作用时,恢复后通过执行记录对账;不能因为本地恢复到旧状态,就再次执行已经在远端完成的操作。
恢复报告需要回答什么?
报告包含故障范围、用户影响、检测方式、恢复动作、未恢复部分和验证证据。记录目标与实际差距,避免只写“服务恢复正常”。
例如知识问答恢复,但文档导入积压仍需处理,就明确标注。系统部分可用不代表每条链路完全恢复。
最终形成运行手册:谁能操作、从哪里取得发布与任务记录、如何判断继续恢复或停止、怎样联系负责人员。真正的工程能力体现在有证据的恢复过程。
5. 哪些设计保留,哪些按证据增加
把取舍与重新评估条件写进决策记录。
保留的核心链路是什么?
保留文档版本、持久化导入任务、授权检索、受控业务工具、会话历史和调用审计。这些能力直接对应知识更新、任务恢复、数据隔离与问题追踪。
它们比复杂 Agent 更优先。文档版本混乱时,模型规划再聪明也可能引用旧制度;权限遗漏时,更强的检索会让泄漏更容易发生。
同时保留评价样本和发布记录。没有样本,优化依赖感觉;没有产物关联,故障恢复依赖记忆。
为什么简化为模块化单体?
参考方案的业务规模和团队协作边界尚未验证,先用明确模块接口控制复杂度。数据库事务、调用链和部署步骤更容易理解,也更便于形成第一条完整链路。
这不是宣布微服务没有价值。若导入任务资源占用严重影响在线问答,可以先隔离 Worker;如果团队和独立发布需求出现,再讨论服务拆分。
重新评估条件应具体:独立扩容需求、发布冲突、故障隔离要求、模块归属稳定。只因为文章涉及 Spring Cloud,不足以成为拆分理由。
为什么暂不开放自动写操作?
知识问答与本人订单查询已经能展示模型集成、权限和证据链路。自动退款、审批或取消订单则增加授权确认、幂等、事务与补偿要求。
第一阶段将工具设为只读,缩小失败影响。未来增加写工具时,先设计操作标识、执行记录、确认过程和未知结果查询,再接入模型。
读写分类必须由真实实现保证。工具描述写“只读”,方法里却修改记录,仍然违反边界。描述与代码契约要一致。
为什么没有默认加入全部优化?
混合检索、重排和查询改写是候选方案,先按失败分类决定是否引入。它们增加依赖、调用成本、延迟和新的失败路径。
SubAgent 与 A2A 也保留为扩展能力。固定流程能够完成的工作不必拆成多个模型;确需委派外部独立 Agent 时,再使用任务协作协议。
下面是简化的设计决策记录:
| 决策 | 当前理由 | 重新评估条件 |
|---|---|---|
| 模块化单体 | 降低初期运维与事务成本 | 独立扩展或团队边界明确 |
| 持久化任务表 | 导入需要状态与恢复 | 任务规模及调度需求变化 |
| 只读业务工具 | 先验证授权与事实查询 | 写操作确认与幂等方案完成 |
| 基线检索先行 | 需要可比较起点 | 失败证据支持加入组件 |
| 外部协议按需 | 避免重复包装 | 确有工具复用或 Agent 委派 |
决策不只是选项名称,还要有理由与退出条件。这样实现结果不符合预期时,可以合理修改方案。
哪些能力仍需实际验证?
需要验证数据库事务和竞争、文档解析与切分、向量过滤的实际能力、权限撤销窗口、模型事件与取消、任务租约恢复,以及备份可恢复性。
性能与费用同样待测量。设计上存在并行机会,不等于实际延迟已经下降;索引可重建,不等于能在目标时间内重建。
可以维护实现证据表,每一行明确状态:
功能:本人订单工具
设计:参数仅含订单编号,身份由后端提供
实现:待填入实际提交与文件
验证:待填入授权、超时和响应检查
限制:仅只读;远程 MCP 委托另行实现
这份记录让方案文章、项目代码和简历内容保持一致,避免把“了解”误写成“已完成生产实践”。
求职时怎样组织项目介绍?
先用一句话说明用户与问题,再说明自己完成的范围。随后选择两三个有证据的重点,例如导入恢复、跨租户隔离或工具未知结果处理。
介绍顺序可以是需求、约束、方案、关键实现、验证和限制。面试官追问时,用真实接口、数据结构、失败用例和发布记录展开,而不是重复架构图上的名词。
还要诚实说明样本规模和环境。合成文档实验可以展示方法,但不能被包装成真实企业大规模上线。能够解释局限和下一步验证,通常比虚构漂亮数字更可靠。
6. 设计判断与适用边界
实现顺序从最小纵向链路开始:一份合成文档、两名不同范围用户、一条有引用的回答和一个只读订单工具。每个阶段保存代码、数据迁移、权限样本、评价记录与发布材料,再扩展文档格式和复杂 Agent。
这份参考设计尚未给出上线指标。求职介绍区分已实现、已验证和待完成范围,真实实验完成后再补证据。架构图展示的是关系,真正的项目质量来自权限不泄漏、失败可恢复、版本可定位与结果可解释。
Martin Fowler 讨论先建立单体再逐步拆分的取舍。这是架构判断的参考观点,不能替代具体团队、业务边界与运维条件。 Martin Fowler:Monolith First。
Agent 评估文章提示我们保留执行轨迹与可检查结果。本文评价方案不以模型自我陈述代替真实业务状态。 Anthropic:Demystifying evals for AI agents。
7. 问答
问:为什么先做单体?
答:减少分布式运维和一致性成本,同时保留清楚模块边界。
问:为什么第一阶段不做自动退款?
答:先验证知识和只读工具链路,写操作需要额外授权、幂等与补偿。
问:探针成功能证明业务恢复吗?
答:还需要关键业务与数据一致性检查。
问:Redis 故障能默认跳过权限吗?
答:不能,降级仍需可信权限来源或失败关闭。
问:为什么复盘方案也有价值?
答:它明确需求与取舍,帮助实现时检查假设;实际工程结论仍需证据。
问:技术多是否让项目更有竞争力?
答:更有说服力的是完整链路、合理边界和验证质量。