32-企业知识库助手完整设计

O泡李华 4

设计一个企业知识库助手:把架构、权限与交付贯通

一个有说服力的求职项目,应能回答用户如何完成任务、数据怎样隔离、故障以后怎样恢复。本文把前面的后端与 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 故障能默认跳过权限吗?

答:不能,降级仍需可信权限来源或失败关闭。

问:为什么复盘方案也有价值?

答:它明确需求与取舍,帮助实现时检查假设;实际工程结论仍需证据。

问:技术多是否让项目更有竞争力?

答:更有说服力的是完整链路、合理边界和验证质量。

参考资料