本文迁移自 mindcarver/91ai · 原始位置
docs/evaluation/maxkb-architecture.md· 由 @阿新聊ai 整理。
MaxKB 技术架构与实现原理
MaxKB 是本次四平台横评里部署最轻、API 自动化最顺的一个。它定位是"开箱即用的企业内部知识库",单容器跑满所有依赖。本文拆解它的技术栈、架构、数据流和模型集成方式——所有结论均来自对其运行实例(容器进程、PostgreSQL 表结构、Django 序列化器源码、API 行为)的一手观察。
一、技术栈
| 层 | 选型 | 说明 |
|---|---|---|
| 后端框架 | Python / Django(DRF 风格的 REST) | API 在 /admin/api/...,序列化器在 apps/dataset/serializers/ |
| 部分高性能组件 | Go | 向量库适配、部分 IO 密集任务用 Go 实现 |
| 关系库 + 向量库 | PostgreSQL + pgvector(同一实例) | document / paragraph / embedding 三张核心表,embedding 列是 vector 类型 |
| 缓存 / 队列 | Redis + Celery | 异步 embedding、文档同步走 celery |
| 前端 | Vue | 单页应用,随容器发布 |
| 部署 | 单容器(4.6GB 镜像) | postgres + redis 全部内嵌,一条 docker run 起服务 |
"单容器"是 MaxKB 最重要的架构决策——把 postgres、redis、后端、前端全打到一个镜像里。代价是镜像偏大、不适合超大规模横向扩展;收益是部署心智负担最低(一条命令、3 分钟可用、不依赖外部编排)。对中小团队内部知识库这个目标场景,这个取舍很合理。
二、核心数据模型(PostgreSQL)
MaxKB 的知识库数据落在三张表上(实测 psql -U root -d maxkb 查到):
document (id, name, knowledge_id, status, status_meta, ...)
paragraph (id, document_id, content, status, status_meta, ...)
embedding (paragraph_id, knowledge_id, embedding:vector, search_vector:tsvector)
document通过knowledge_id关联到知识库(不是dataset_id,命名容易踩坑)。paragraph是切片后的段落,一个 document 切出多个 paragraph。embedding是独立的表,存 pgvector 向量 和 一个search_vector(tsvector,用于全文检索)——MaxKB 走的是混合检索(向量 + 全文),不是纯向量。
document.status 是个有意思的字段:它用多位字符编码状态,实测看到 nnn0 / nnn1 / nnn2 等值,末位 2 表示 embedding 成功。paragraph.status 同理。
三、数据流:从 PDF 到可检索
MaxKB 的灌库是两步式(split → batch_create),关键点是它不自动切片——你要么用它 split 的结果,要么自己切好。
[PDF/txt 文件]
│ POST /knowledge/<kid>/document/split (multipart file)
▼
[split 端点: 调 PdfSplitHandle / 文本解析, 返回 [{name, content:[{title,content}], source_file_id}]]
│ 注意: PDF 走 split_model.parse() 兜底分支, 输出 {title,content} dict 列表
▼
[PUT /knowledge/<kid>/document/batch_create body=[{name, paragraphs, source_file_id}]]
│ DocumentInstanceSerializer 校验, paragraphs 必须是 [{content:str}] 格式
▼
[batch_save: @post(post_embedding) 装饰器 → 事务提交后触发 celery 异步 embedding]
│ 对每个 document 调 Operate.refresh() → 走 celery → 向量化
▼
[paragraph 表写入 + embedding 表生成 pgvector 向量 + search_vector 生成 tsvector]
实测中踩过一个坑:batch_create 的 paragraphs 字段,传字符串列表会 500、传错结构会"创建文档但 chunk_num=None(0 chunk)"。正确姿势是用 split 端点返回的 content 列表原样传回。这个"split 出来什么、batch_create 吃什么"的对偶设计,需要读源码(apps/knowledge/serializers/document.py 的 ParagraphInstanceSerializer)才能确认。
四、模型集成:OpenAI 兼容接 Ollama
MaxKB 的模型管理是最直白的——走 OpenAI 兼容协议,几条 POST 就能把 Ollama 接上:
POST /admin/api/workspace/default/model
{
"name": "qwen2.5-14b",
"provider": "model_openai_provider",
"model_type": "LLM",
"api_base": "http://<ollama>:11434/v1",
"api_key": "ollama",
...
}
embedding 模型同理(model_type 换成 embedding)。配置完直接可用于灌库(向量化)和对话。
这也是 MaxKB "API 自动化最顺"的根源——没有 aiproxy 这样的中间层、没有插件市场、没有 mongo 模型注册表,模型就是一张 model 表里的记录,CRUD 即可。对比 FastGPT 的三层模型系统、Dify 的插件市场,MaxKB 的简单在这一项上是真的省心。
五、检索:hit_test 与混合检索
检索端点是 POST /workspace/default/knowledge/<kid>/hit_test,参数有个必须注意的坑:
{
"query_text": "...", # 查询文本
"top_number": 5, # top-K
"similarity": 0.6, # 相似度阈值 —— 必填, 不能为 null, 不能用 0.0(会返回空)
"search_mode": "embedding" # 检索模式 —— 必填, 漏了会 500
}
实测漏 search_mode 或把 similarity 设 0.0,都会得到 0 结果或 500——这是最容易让初学者以为"MaxKB 检索坏了"的坑,其实只是参数。
search_mode 支持 embedding(向量)/ blend(混合)等。混合检索同时用 embedding 表的 pgvector 向量和 search_vector 的 tsvector,做向量召回 + BM25 全文召回的融合。对中文/英文混合、有专有名词的语料,blend 通常比纯 embedding 稳。
六、为什么 MaxKB 在横评里表现"均衡"
回到横评结论,MaxKB 在四个数据集上:TechQA 90%、CUAD 84%、CMRC 92.5%、DocVQA 0%。它的特点是没有明显短板也没有极端长板(除了易用性和资源占用):
- 检索走 pgvector 混合检索,稳但不是最快(0.59s)。
- 建库 528s(646 篇)——中规中矩,比 Dify(1307s)快、比 RAGFlow(70s)慢。
- 不做 OCR——DocVQA 上 0 chunks,和 FastGPT 一样。这是它"均衡但不全场景"的边界。
- 单容器架构让它的资源占用最低(RAM ~最少、镜像最小),适合资源受限的私有化场景。
七、API 自动化友好度总结
MaxKB 是四家里**唯一能"半天纯 API 跑通全流程"**的:
- 登录:
POST /user/login,token 放Authorization: Bearer。 - 配模型:几条 POST(OpenAI 兼容)。
- 灌库:
split→batch_create。 - 检索:
hit_test(注意search_mode+similarity参数)。 - 对话:
/chat/api/<app>/chat/completions(注意用 app 的 secret_key 而非 admin token)。
没有反爬码、没有 CSRF 双提交、没有插件市场、没有 aiproxy 中间层。对要做自动化集成、CI/CD 灌库、多知识库批量管理的场景,MaxKB 的摩擦度最低。
八、扩展性与边界
MaxKB 的单容器架构在横向扩展上是弱项——pgvector 单实例的向量规模上限、单容器的并发能力,都比不上 RAGFlow(ES/Infinity 分布式)或 Dify(多服务可独立扩)。但对"几千到几万篇文档、内部几十到几百人用"的目标场景,MaxKB 的单容器足够,且运维成本最低。
如果你想深入它的某个组件(比如混合检索的融合策略、celery embedding 的并发控制、或 Go 写的向量适配层),可以继续拆。本文聚焦在"理解 MaxKB 为什么在横评里表现如此"所需的架构层面的最小完整图景。
评论
还没有评论
欢迎留下第一条评论,帮助这篇内容更快形成讨论。