【免费下载链接】AIBro
AI Bro — a local-first workspace for AI conversations, source-linked knowledge, and tasks.
AI Bro(AIBro)是一个本地优先(local-first)的 AI 学习与研究工作台,它的知识检索核心是BM25 倒排索引关键词检索 + 可选的混合语义搜索:你保存的笔记、论文和课件会被切成带来源的段落,建立本地倒排索引;提问时先用 BM25 找出命中原文,配置了 embedding 服务后还会叠加向量语义排序,最终让 AI 的回答能逐句标注来源、点开原文核对。
为什么“检索原文”比“凭记忆回答”更可靠
把几百页课件丢给大模型直接问,最常见的翻车方式是幻觉:模型一本正经地编造一个原文里不存在的页码或数据。AIBro 的思路是 RAG(检索增强生成):先检索、后回答。
- 资料保存在本机,检索也发生在本地内存和 IndexedDB 中,不依赖外部搜索服务;
- AI 每轮回答都能继续按需检索,而不是一次性截取固定长度的“开头片段”;
- 回答中每个关键论断可以标注
[[cite:...]]引用标记,点击即可跳转到原文对应段落。
第 1 步:把资料切成“可引用的段落”
检索的前提是索引。AIBro 在 app/context-retrieval.js 中把每份资料拆成小段落(chunk),而不是整篇塞给模型:
- 按Markdown 标题和代码围栏识别段落边界,标题作为“heading 上下文”保留,方便命中后定位章节;
- 每段正文控制在约1300 字符,优先在换行处断开,避免把一个完整句子拦腰切掉;
- 论文按
abstract、methods、experiments等结构化小节切分,并保留每小节自带的引用页码; - PDF 类导入资料按页拆分,每段都带着页码信息。
为什么是“小而带上下文”的段落?因为命中一段 1300 字的片段,比命中一整章更容易被模型准确理解,也更容易在界面上展示给你核对。
第 2 步:倒排索引——BM25 的“查字典”结构
正排索引是“段落 → 包含哪些词”,而倒排索引是“词 → 出现在哪些段落里”,就像给整本书做的反向字典。
在 app/context-retrieval.js#L301-L341 的indexFor中,每个段落会:
- 分词:英文按单词切分;中文用
Intl.Segmenter分词,并为较长的词补充二元组(bigram)——即使分词器不认识某个课程名或方法名,也能靠相邻字组合命中; - 统计每个词在段落中出现的次数(词频 tf),写入段落的词频表;
- 把“词 → 段落 ID 集合”登记到
postings(倒排链表)中。
索引是派生缓存:每次查询会用“签名”比对,内容没变的段落直接复用旧索引;你编辑、移动或删除资料后,对应片段自动失效重建。重启应用后索引从本地持久化的工作区重建,全程离线可用,无需任何 API Key。
第 3 步:BM25 打分——好段落凭什么排前面
BM25 是经典的关键词排序算法,核心直觉是:一个词越少段落用到它(区分度越高),又越密集出现在某个段落里(相关度越高),这个段落就越该排前面。
app/context-retrieval.js#L342-L355 中的实现是教科书式的:
score = idf * (tf * 2.2) / (tf + 1.2 * (0.25 + 0.75 * 段落长度 / 平均长度))- idf(逆文档频率):罕见词权重高,“量子纠错”比“方法”更有区分力;
- 长度归一化:防止长段落因“碰巧出现”占便宜;
- 词同时命中标题、heading 或标签时再乘 2 倍加权(app/context-retrieval.js#L351-L352)。
结果不是“一次性返回所有”,而是分页:默认每页 20 条命中段落,带nextOffset游标,AI 觉得证据不够可以继续翻页,而不是被“只给前两个片段”的老规则卡住。
| 检索通道 | 擅长 | 典型短板 |
|---|---|---|
| BM25 关键词检索 | 精确名词、编号、代码、中英混排 | 同义改写(“梯度下降” vs “参数优化”) |
| 向量语义搜索 | 语义相近的改写与模糊提问 | 需要额外 embedding 服务 |
第 4 步:混合语义搜索——RRF 把两路排名融合
关键词检索有个盲区:你问“参数是怎么更新的”,而原文写的是“梯度下降”——词都对不上。这时语义搜索补位。
配置方式:在 设置 → 知识库语义搜索 中填入任意OpenAI 兼容的 embedding 端点、模型名和 Key(也支持本机免 Key 服务)。向量保存在本机 IndexedDB,由 app/vector-index.js 管理:
- 每段落的
标题 + heading + 正文计算SHA-256 指纹,内容没变就不重新向量化,更新只处理变化的段落; - 查询时先给查询句向量化,与所有已建向量算余弦相似度,得到第二路排名;
- 两路排名用RRF(Reciprocal Rank Fusion,倒数排名融合)合并:每路排名第 i 名的段落贡献
1/(60+i+1)分(app/vector-index.js#L136-L139),分数相加持高者居上。
RRF 的好处是不挑分数量纲——BM25 分数和余弦相似度本就不能直接相加,只用“名次”融合最稳妥。
两个重要的健壮性设计:
- 语义查询8 秒超时(app/vector-index.js#L83-L96),超时或向量索引未建立时自动回退纯 BM25,并在结果面板明确标注
semanticStatus(如“向量索引尚未建立,本轮使用关键词检索”); - 融合完成后会重查一遍当前资料,确保你刚删除或编辑过的内容不会“诈尸”出现在结果里。
第 5 步:AI 的工具化检索与引用闭环
检索结果不会直接糊到模型眼前就完事。AIBro 在 app/knowledge-access.js 里把检索包装成一组 AI 可调用的只读工具(声明见 app/tool-specs.js#L34-L44):
| 工具 | 作用 |
|---|---|
search(query, offset) | 混合/关键词检索,返回带来源、页码、chunkId的段落 |
list(query, offset) | 分页列出当前可读范围的资料目录 |
read(recordType, id, offset) | 按偏移量读取某份资料的完整正文 |
read_page(id, page) | 按页读取 PDF 原件(文字或页面图像) |
neighbors(chunkId, version) | 读取命中片段前后各 2 段,补上下文 |
配套的防“空转”机制:连续多次搜索没有新证据时,系统会自动把目录页喂给模型引导它改用read读候选资料,而不是无休止换同义词。
回答生成后,引用协议(app/citation-evidence.js#L10)要求 AI 只在论断确实有依据时才写[[cite:ev...]]标记,检索命中不等于事实核验,无来源支撑就必须明说“这是推断”。界面中每条回答都会展示“索引范围 / 原文文件数 / 已返回段落数 / 来源条目”等覆盖度指标——检索了多少、没检索到什么,一目了然。
这套设计给普通用户带来什么
- 零配置可用:不配 embedding 也有一路完整的本地 BM25 检索,断网照样查资料;
- 中文友好:分词 + 二元组双保险,“Transformer”和“变换器”式的混合表达都能命中;
- 改了就失效:编辑、移动、删除资料后索引签名同步更新,AI 读到的永远是当前版本;
- 透明可核对:每条引用都能点回原文段落和页码,检索覆盖度公开在回答面板里。
原理细节可在官方文档 docs/KNOWLEDGE_RETRIEVAL.md 中继续查阅;检索引擎源码入口是 app/context-retrieval.js 与 app/vector-index.js。想体验完整工作流,可以从README.md了解安装与上手步骤,把第一份课件放进项目,问出你的第一个问题。
【免费下载链接】AIBro
AI Bro — a local-first workspace for AI conversations, source-linked knowledge, and tasks.
相关推荐
10分钟生成可写盘的黑苹果 OpenCore EFI:OpCore-Simplify 新手完整教程
10分钟生成可写盘的黑苹果 OpenCore EFI:OpCore Simplify 新手完整教程 你不必手写 config.plist,也不必查遍全网设备 I
开发工具CLIWHartTest知识库RAG原理解析:Qdrant向量检索+BM25混合搜索+Reranker重排序完整指南
WHartTest知识库RAG原理解析:Qdrant向量检索+BM25混合搜索+Reranker重排序完整指南 WHartTest 是一款 AI 驱动的测试自动
人工智能AI 应用AI AgentRAGMCP 服务AI 技能后端前端测试接口测试质量保障Agent 编排Laya如何从通知洪流中精准找答案:ChromaDB向量搜索×FTS5 BM25混合检索与RRF融合排序完整指南
Laya如何从通知洪流中精准找答案:ChromaDB向量搜索×FTS5 BM25混合检索与RRF融合排序完整指南 Laya 是一个开源、本地优先(local f
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考