乐于分享
好东西不私藏

知识库系列(补充):AI 智能助手——给阅读端补一条「问答」的路

知识库系列(补充):AI 智能助手——给阅读端补一条「问答」的路

前四篇把系统的四个端讲完了:写入端(Obsidian + Git)、Web 阅读端、小程序阅读端,还有它们之间的同步机制。这一篇是个补充,单独讲一个新加进来的模块——AI 智能助手。它是在主线跑通之后,给阅读侧再增加的一个能力。

一、为什么搜索还不够

读者用知识库的链路在前几篇里讲清楚了:按目录找、按关键词搜、点击 WikiLink 进行跳转。这三条覆盖了绝大多数场景。

但除了这些之外,用户有时想要直接提问,比如:

"OTP 怎么用?"
"小程序的临时账号怎么变成正式账号?"
"我们这个仓库的写入流程是什么?"

这些不是关键词,是问题。搜索能搜到"OTP"出现过的几篇文档,但读者还得自己挨个翻、自己拼答案。如果回答跨了三篇文档,搜索只是完成了三分之一,剩下三分之二仍然是脑力活。

所以阅读端需要一条新路径:用户直接问、系统直接答,而且答案必须建立在知识库内容之上,不能让 AI 凭空编。

AI 智能助手就是来补这条路的。

二、不是简单接一个大模型——核心安全设计

很多人对"加一个 AI 助手"的第一反应是:找个大模型 API 接进来不就完了?

如果只考虑功能,这样确实最快。但放到一个有权限分级的团队知识库里,这样接是不安全的,主要有两层风险:

风险
后果
内容外发
把 kbase-internal 这种内部仓库内容喂给远程大模型 API,等于把内部资料发给了第三方
权限绕过
前端直连 AI,权限判断被绕过——AI 不知道当前用户能看哪些内容,会基于"它能看到的全部"回答

所以这套助手在架构上做了三层隔离:

前端 → 知识库后端(鉴权 + 权限过滤)→ 本地 AnythingLLM 服务

具体落地的四条硬约束:

约束点
具体做法
AnythingLLM 不暴露
本地大模型容器只在内部网络可达,不对外网开放
后端中转
前端调 /api/v1/qa/*,后端校验用户权限后再调本地服务
引用二次过滤
AI 回答带的"引用来源"由后端再过一遍权限——AnythingLLM 不知道用户身份,这是最后一道闸,命中越权整体回绝
一键可关
后端配置项一关,前端连入口都不渲染,整套系统按现状跑,对其他能力零影响

三、范围的克制:从三种到一种

这是我自己觉得整个 AI 助手里最值得讲的工程化决策。

最初设计这个模块时,我们规划了三种问答范围:

当前文档     —— 只基于现在打开的这一篇答
当前仓库     —— 基于当前文档所在的整个仓库答
全部仓库     —— 基于用户有权限的所有仓库答

听起来都很有道理。功能丰富,用户灵活。

但真正动手实现到一半,我们把后两种砍掉了,只留"当前仓库"一种。三种范围对照下来,每种的真实评估是这样的:

范围
评估结论
砍 / 留
当前文档
一篇能答的问题用户自己往下翻就能看到,AI 没什么增量价值
砍
当前仓库
权限边界天然清晰、召回范围足够大、刚好命中真实需求
留
全部仓库
多 workspace 并行检索 + 合并,每次合并都是一次额外权限校验,能力的边界越宽,出错的代价越大
砍

砍完之后产品形态变得很干净:

形态
说明
入口前置条件
必须先打开一个仓库下的文档,否则入口禁用,提示"请先打开一个知识库文档后再使用 AI 问答"
范围选择器
只有一个选项「当前仓库」,前端不展示其他选项
后端硬防护
即使有人构造请求传入 doc 或 all,后端也会强制改成 repo,这一层是死的、不是配置

这样设计的本质是:把"能做的"和"应该做的"区分开。技术上能跨仓库问答,但产品上不该这么做。多一个选项就多一份权限风险,而多出来那份功能价值非常有限。这种时候,砍掉就是最好的设计。

四、不许凭空答——证据强制可点击

第二个值得讲的设计点:每条 AI 回答下面都必须带可点击的引用来源。

这一点看起来简单,但它背后是一个明确的取舍——我们用的是 query 模式而不是 chat 模式:

模式
行为
致命弱点 / 优势
chat
LLM 通用知识 + 知识库内容混合作答
答得更"流畅",但它会编——知识库没答案时会用通用知识补一段看起来很合理的话,读者分不清是知识库还是幻觉
query
严格基于知识库内容,没召回就拒答
答得更"克制"——没召回时直接说"在知识库里没找到相关内容",对读者诚实

所以这套助手统一用 query 模式,再配上一整套围绕"引用"的机制:

机制
解决什么
引用必带
每条回答附带它依赖的文档片段,明确标注来自哪个仓库的哪篇文档
默认折叠
折叠成"▸ 引用文档 N 个",避免一打开就视觉过载
点击跳转
点击引用直接跳到原文档
双层权限过滤
AnythingLLM 召回后,后端再过一遍权限——AI 给的每一条引用,都是用户原本就该能看到的
失效降级
引用文档已被删除(同步还没追上)时,跳转给"该文档已被移除或您无权访问"提示,不暴露内部错误

这一整套机制的目的只有一个:让 AI 的每一句话都能被读者验证。读者不必"信任 AI",他可以点击引用看原文,自己判断 AI 答得对不对。

正常回答:
无引用&无权限:

五、三个看不见但很关键的细节

除了上面两个核心决策,还有几件事是用户感知不到、但实际跑起来非常关键。

(一)问题改写——把"OTP 怎么用"变成完整问题

读者输入的问题往往很短:"OTP 怎么用"。直接拿去 RAG 检索召回率很差,因为知识库里没有任何文档的标题或正文是这五个字。

所以正式问答前,系统先用 LLM 把问题补全成:

"如何在知识库 Web 端启用并使用 OTP 二次验证?包括绑定流程、登录流程"

补全后展示成一张"改写建议"卡片,让用户三选一:

用户选择
系统行为
用这版
直接拿改写版进入 RAG 检索
改一改再发
用户在卡片上编辑后再发
用原版
跳过改写,原问题进入 RAG

代价是多一次 LLM 调用 + 几百毫秒延迟,但召回质量能提一个档次。改写超时(3 秒)自动 fallback 到原问题,不阻塞主流程;同一原问题 5 分钟内有缓存,不重复调用。

(二)水位提示——给用户一个诚实的状态

Git 仓库 push 之后,到内容能被 AI 检索到,中间有个端到端的同步链路:webhook → pull → 重建索引 → 重建向量库。整个过程 SLA 是 30 秒。

但这 30 秒里如果用户刚好在问,问出来的答案可能是旧版本的。

两个极端方案的取舍是这样的:

方案
问题
硬阻塞用户提问
体验太差
完全不提示
用户拿了旧版本答案以为系统在骗他
中间方案(实际采用)
抽屉顶部一条温和的黄色提示——「当前仓库刚刚有更新,问答可能未覆盖最新文档(约 N 秒后生效)」,不阻塞,但让用户知道这个状态

体现的是一个判断:不要假装系统什么都跟得上,老老实实告诉用户系统的真实状态。用户能接受偶尔慢一点,但不能接受偏离预期的结果。

(三)三层限流——保护后端,也保护其他用户

AI 问答会消耗 LLM 算力,这个算力是有限的。如果一个用户疯狂提问,整个系统的响应都会被拖慢,影响其他读者。

所以问答接口有三层限流:

层级
限制对象
用户频率
每个用户每分钟最多 N 次问答
用户并发
同一用户同时最多 N 个进行中的会话
全局并发
整个系统同时最多 N 个 LLM 调用

被限流时不是粗暴 503,而是返回一个"限流通知",告诉用户当前的限流原因和大概多久后能再问。这跟水位提示是同一个哲学:告诉用户系统的真实状态,而不是让用户瞎猜。

六、本篇与前四篇的关系

最后总结一句。AI 智能助手是给阅读端补的一条新路径,但它没有改变前四篇确立的核心架构:

写入端:Obsidian + Git(不变)
Web 阅读端:目录 + 搜索 + WikiLink + 评论 + 现在多了 AI 问答
小程序端:沉浸阅读 + 一键登录 + 听文章(本期不涉及 AI 问答)
同步链路:仓库变更 → 重建索引 + 现在多了一步同步到 AI 向量库

它对系统其他部分的影响是插件式追加的,而不是侵入性的。可以一键关掉,关掉后系统按现状跑。

前四篇让前四件事成立,第五篇让"问得出来"也成立。到这里,这套知识库的故事就讲完了。

欢迎有兴趣的读者留言讨论,以上。


附:AI 助手功能速查

能力
解决的场景
当前仓库范围问答
跨多篇文档的问题,让 AI 帮读者拼答案
问题改写建议
把短问题补全为完整问题,提高召回质量
引用可点击跳转
答案必须有出处,读者能验证
双层权限过滤
用户在原系统看不到的内容,问答里也绝不暴露
同步水位提示
内容刚改完时诚实告知用户状态,不假装跟得上
三层限流
保护后端算力,避免单用户拖慢全局
一键开关
整套能力可选启用,不影响主架构

相关学习资料