在企业场景里,「问答」通常不是一个单点功能,而是知识管理、合规协作、交付效率的底层能力。
我在这个项目里尝试把一个看似教学化的 RAG 示例,按企业可落地链路重构为一套可运行、可扩展、可部署的知识问答系统:
GitHub: github.com/suguangsong…
本文完整记录了这个从 0 到 1 的实现过程,并给出可直接复用的工程化设计思路。
一、为什么做这个项目
很多团队会用“内部文档 QA”做 PoC,但常见问题有四类:
- 只做了单文件 demo,无法支持多文档和稳定更新。
- 检索效果不稳定,语义 TopK 返回结果“看起来相关”,却经常漂移。
- 前后端耦合强,接口化程度低。
- 部署方式依赖本机环境,生产迁移成本高。
ChainKnowledge 的目标是把这 4 个问题在一个仓库里一并解决:
- 覆盖 LangChain 核心能力(加载、切片、向量化、检索增强生成、记忆)
- 支持 PDF/DOCX/CSV/HTML 等多格式文档上传与增量入库
- 增加检索重排(Reranker)提升答案相关性
- 增加 FastAPI API 层,支持前后端分离
- 提供 Docker + docker-compose + Nginx 反向代理部署方案
二、项目架构(分层设计)
项目代码采用分层目录组织,核心目标是“每层只做一类事”:
src/chainknowledge/
core/ # 配置、LLM、加载、切片、向量库、记忆、检索重排
services/ # 文档入库、QA 组装
ui/ # Streamlit 演示端
api/ # FastAPI 接口层
配套还有:
frontend/index.html:纯前端静态页,可直接联调 APIDockerfile、docker-compose.yml、docker/nginx.conf:部署层requirements.txt、.env.example、README.md:运行与交付层
三、端到端链路设计
1. 文档入库链路
文档上传路径分为三段:
loader:按后缀选择 Loader(PDF、DOCX、TXT、CSV、HTML)splitter:RecursiveCharacterTextSplitter分片(可配置 chunk 大小与重叠)vector_store:Chroma持久化向量库,支持复用与可视化计数
核心收益是:
- 对不同格式采用统一文档对象抽象
- 语义切片粒度可控
- 可持续增量入库
2. 问答链路(RAG)
在 qa_service 里,问题经过以下流程:
- 使用
vector_store.similarity_search_with_score拉取候选文档 - 可选进入重排器
SimpleHybridReranker - 按 top-k 组装上下文
- 与历史对话(
ConversationBufferWindowMemory)拼接 prompt - 调用 LLM 生成可追溯答案
回答返回时附带来源片段(source/page/snippet),满足“可解释问答”的企业要求。
3. 会话记忆
采用 ConversationBufferWindowMemory 维持窗口对话历史,避免一次性注入全部历史导致上下文污染。
memory_window 支持在前端动态控制,适合不同业务场景中平衡“上下文长度”和“成本开销”。
四、检索重排:从“语义 TopK”走向“可控排序”
单纯依赖向量相似度有时会“抓大不抓准”。
本项目通过 SimpleHybridReranker 引入关键字重排,显式加入 lexical signal,改进企业术语检索体验。
重排规则:
vector_score:将原始相似度分数转为越大越优的归一值lexical_score:查询词与文档片段的 Jaccard 关键词重叠final_score = alpha * vector_score + (1 - alpha) * lexical_score
配置参数:
RERANK_CANDIDATE_MULTIPLIER:扩大候选集(先放宽召回)RERANK_TOP_K:重排后保留多少片段RERANK_ALPHA:向量与关键词权重平衡(0~1)RERANK_ENABLED:开关,方便线上快速 A/B 测试
同时 /api/chat 在响应中返回 candidate_k、retrieved_count、returned_count,便于观测重排前后差异。
五、API 化设计(FastAPI)
相比传统 demo 的“页面触发式问答”,API 化后可以对接更广泛的客户端。
src/chainknowledge/api/main.py 提供以下核心能力:
GET /api/health:服务健康GET /api/status:向量库状态与就绪信息POST /api/documents:多文件上传入库POST /api/chat:多轮问答DELETE /api/sessions/{session_id}:清空会话记忆POST /api/knowledge/clear:清空知识库
API 请求模型支持可选覆盖参数(每次调用可动态调参),包括:
provider/model/api_key/api_basetop_k/reranker_candidate_multiplier/reranker_top_kreranker_alpha/reranker_enabledmemory_window
这样在 A/B、线上调优或排障时,不需要重启服务即可验证参数效果。
六、前后端分离实现
项目额外提供一个极简前端(frontend/index.html):
- 支持文档上传与清空知识库
- 支持会话提问与清会话
- 实时展示服务状态
- 展示来源片段和重排统计,便于人工确认检索质量
它演示了“API-first”之后最小可交付前端形态,工程上足够轻量,便于后续替换成企业内部界面。
七、部署方案:Docker + Nginx
docker-compose.yml 里定义了两类服务:
chainknowledge-api:构建并运行 FastAPI 后端(uvicorn)chainknowledge-nginx:托管静态前端并反代/api
并通过命名卷持久化关键目录:
chroma_data:向量库upload_data:原始上传文件
启动后可直接访问:
- 前端:
http://127.0.0.1:8080 - 健康接口:
http://127.0.0.1:8080/api/health
这意味着你的知识库在容器重启后仍可保留状态,降低运维风险。
八、为什么能值得使用
这个项目不是“堆模型组件”,而是在工程中明确落地了以下点:
- 配置驱动:通过
.env控制 provider/model/参数,便于环境隔离 - 链路解耦:Loader、Splitter、Vector Store、QA、API 分离,便于单元替换
- 可复用接口:同一套核心服务可被 Streamlit 与 API 同时复用
- 可观测性:返回检索统计字段,便于排障与调优
- 部署友好:Docker + compose + 反代,能直接接入 CI/CD 与容器编排体系
企业在此基础上很容易继续演进:
- 改为多租户
collection切分 - 接入鉴权与操作审计
- 替换重排器为 CrossEncoder / rerank 模型
- 接入企业内部向量库(Milvus、pgvector、Pinecone 等)
九、实用的调用示例
健康与状态检查:
curl http://127.0.0.1:8000/api/health
curl http://127.0.0.1:8000/api/status
查询示例(可直接运行):
curl -X POST http://127.0.0.1:8000/api/chat \
-H "Content-Type: application/json" \
-d '{
"session_id":"demo",
"question":"请说明公司请假流程",
"provider":"openai",
"top_k":4,
"reranker_candidate_multiplier":3,
"reranker_top_k":4,
"reranker_alpha":0.7,
"reranker_enabled":true,
"memory_window":6
}'
重点查看返回里的 candidate_k / retrieved_count / returned_count,能快速评估检索链路的召回质量。
十、总结
ChainKnowledge 的价值不在“我写了一个 ChatGPT 界面”,
而在于把一个教学型 RAG 系统,转化为一套能在企业中长期运行的服务化组件:
- 功能闭环完整
- 配置与参数可控
- 接口清晰可复用
- 部署路径可直接执行
- 结果可追溯可复盘
如果你也在做企业知识库问答,这个仓库可作为你从 PoC 到生产的起点模板。