LangChain 多智能体私有化文档问答系统(自研100% 独立全链路落地)面经
一、介绍一下你的RAG 项目
我独立做过一个私有化 RAG 多智能体问答系统,核心解决企业内部文档不能上公网、隐私安全的问题。流程分五步:
1)文档接入:支持 PDF/Word 等格式上传;
2)语义切块:1000 字符、200 重叠,平衡语义完整和噪声;
3)向量化入库:用 Embedding 把文本转向量,本地向量库存,Redis 缓存对话历史;
4)检索排序:先向量粗排 Top8,再重排选 Top3,提升相关性;
5)多智能体:简单问题走 RAG 链,复杂问题(总结 / 对比)用协调者 + 研究员分工,本地 Ollama 生成答案。
工程上用 Docker 打包,日志自动上报,支持一键部署,全程本地闭环,保证隐私。
二、向量化原理理解浅
文本字面相似度精度很低,无法准确判断语义。向量化是用Embedding 模型把文本映射到高维向量空间,是把文字转换成数学向量,通过计算余弦相似度 / 向量距离来判断语义相关性,从而实现精准检索。比如用nomic-embed-text模型,输入"模拟退火算法",输出768个浮点数组成的向量。为什么是768维?这是模型设计时确定的,维度越高表达能力越强,但计算量也越大。768是效果和效率的平衡点。
我的系统使用余弦相似度,因为 nomic-embed-text 的输出已归一化(|v|=1),此时余弦相似度 = 内积,计算最快。
核心原理是语义相似=向量相近。训练时让相似文本的向量距离近,不相似文本的向量距离远。这样"算法"和"方法"的向量就会很接近,"算法"和"冰箱"就会很远。
Matryoshka 特性:模型可以输出 64/128/256/512/768 维向量,前 k 维已包含主要语义信息,支持动态降维。
三、向量库用什么?Redis 和向量库区别
向量库:FAISS/Milvus,专门做高维向量检索,速度快、精度高。
Redis:做对话历史缓存、临时存储,不适合存海量向量。
我项目:本地向量库存向量,Redis 缓存对话,保证隐私。
四、文本切块讲得太浅
按模型最大输入、语义完整性、召回率综合设定;1000 字符平衡信息密度与噪声,200 重叠避免语义断裂;用召回率 / 精确率做对比实验确定最优参数。 chunk_size=1000、overlap=200是多组对比实验的最优解。测试了500/1000/1500,500信息碎片化,1500冗余多token浪费,overlap = 200防止边界语义截断,保证关键信息不被切分。
递归分割按段落→句子→词优先级,确保不断语义。
为什么要做文本切块?
第一,LLM上下文窗口限制。我用的qwen2.5:7b只有4096 token,约3000中文字符。50页论文10万字,直接塞不进去。必须切成小块,每次只取相关的几块。
第二,注意力计算复杂度。Self-Attention是O(L²)复杂度,L增加1倍,计算量增加4倍。处理5000字推理5秒,10万字根本跑不动。
第三,检索精度。不切块的话,整篇论文只有一个向量,检索时无法定位到具体段落。切块后,每个块对应一个语义单元,检索精度从58%提升到92%。
五、Rerank的原理是什么?
向量检索(Bi-Encoder)把 Query 和 Doc 独立编码,只能做词汇匹配。比如问"时间复杂度",它会错误地把"空间复杂度"排在前面,因为"复杂度"这个词匹配了。
Rerank 用 Cross-Encoder 解决这个问题。Cross-Encoder 把 Query 和 Doc 拼接后同时输入 BERT,通过 Self-Attention 机制让两者深度融合,Self-Attention 让 Query 的每个 token 可以 attend 到 Doc 的所有 token。当 Query 的"时间" token 对 Doc 的所有 token 注意力分数都很低时,模型判断不相关。这种 token-level 的细粒度对齐是 Bi-Encoder 做不到的。
工程优化:用线程池执行同步推理,不阻塞 asyncio 事件循环;做动态开关,高并发时自动关闭 Rerank 保障响应速度。
六、项目工程化描述弱
用Docker 容器化,支持一键部署;日志分info/error/warn三级,自动上报异常;支持PDF/Word/Excel多格式解析。
Docker/K8s
把代码 + 依赖 + 环境打包成容器,一次构建、到处运行,解决环境不一致。
核心用途
环境统一:本地、服务器运行一致
快速部署:一键启动服务
日志持久化:挂载宿主机目录存日志
你项目里怎么用
RAG 项目用 Docker 打包,日志挂载本地自动收集,方便测试和问题定位。
一句话:Docker = 打包环境、一致、好部署
K8s:容器编排、扩缩容、自愈,用于大规模服务部署。
是什么
管理大量 Docker 容器的集群平台,负责调度、运维。
核心能力
自动扩缩容:流量大自动加容器
故障自愈:容器挂了自动重启
负载均衡:分发流量、保证稳定
一句话:Docker = 集装箱,K8s = 港口管理
七、工程工具Git
git status → git add . → git commit -m"xxx" → git pull → git push。
gatestates → git status
get bronch → git branch
get bronch 杠 A → git branch -a
getad → git add
get commit → git commit
pol 上去 → git pull
posh → git push
如果要进行一个完整的控制,就是你在Python 脚本通过控制台是可以控制的,提交前肯定要 git status 先检查一下你当前的状态,然后再检查一下你当前的分支, git branch -a 看一下远程所有的分支,本地的改动和修正 git add 一下,加上之后然后再 git commit 一下,给你这次的提交和修改做一个记录。
最后再 git pull 一下,把你的代码 git push 到远程仓库里面,就不用手动点了。
三、面试必背|Git 标准提交流程(直接背)
git status 查看当前文件修改状态
git branch 查看当前所在分支
git branch -a 查看所有本地和远程分支
git add . 将修改添加到暂存区
git commit -m "提交信息" 提交到本地仓库
git pull 拉取远程最新代码,避免冲突
git push 推送到远程仓库
状态 → 分支 → 添加 → 提交 → 拉取 → 推送
status → branch → add → commit → pull → push
八、日志监控讲得模糊
没讲日志分级、异常捕获、排查思路。
用Python logging模块按级别输出;Docker 挂载目录持久化;关键节点埋点,便于定位文件解析 / 向量入库 / 模型调用异常。
三级日志分类
|
级别 |
记录内容 |
代码位置 |
示例 |
|
INFO |
正常业务流程 |
文档上传、检索成功、问答完成 |
logger.info(f"文件上传成功: {filename}") |
|
WARNING |
轻微异常 |
检索结果少、会话即将过期、重试 |
logger.warning(f"检索结果不足: {len(docs)}条") |
|
ERROR |
致命异常 |
模型宕机、向量库失败、接口报错 |
logger.error(f"Ollama连接失败: {e}") |
日志关键词索引
|
问题类型 |
日志关键词 |
定位方法 |
|
文档解析问题 |
document, loader, PDF |
grep "ERROR.*document" app.log |
|
向量库问题 |
vector, chroma, embedding |
grep "ERROR.*vector" app.log |
|
模型推理问题 |
ollama, llm, generate |
grep "ERROR.*ollama" app.log |
|
接口问题 |
api, upload, chat |
grep "ERROR.*api" app.log |
|
会话问题 |
session, memory, redis |
grep "WARNING.*session" app.log |
生产落地改进
|
改进项 |
优先级 |
说明 |
|
接入Prometheus |
高 |
指标采集、存储、查询 |
|
接入Grafana |
高 |
可视化仪表盘 |
|
接入Alertmanager |
中 |
告警推送(钉钉/企微/邮件) |
|
链路追踪(Jaeger) |
中 |
分布式调用链,定位慢请求 |
|
日志聚合(ELK/Loki) |
中 |
多节点日志统一查询 |
|
SLA/SLO监控 |
低 |
可用性99.9%监控 |
日志监控是怎么做的?
我做了日志和监控两个维度。
日志方面:用 Loguru 做分级日志。INFO 记录正常业务流程(上传、检索成功),WARNING 记录轻微异常(检索结果少、重试),ERROR 记录致命异常(模型宕机、向量库失败)。日志按天轮转,保留30天,错误日志保留90天。
问题定位:通过日志关键词快速定位——document 开头是文档问题,ollama 是模型问题,vector 是向量库问题,api 是接口问题。结合 request_id 可以追踪完整请求链路。
监控方面:接入 Prometheus + Grafana。暴露了 QPS、P99延迟、错误率、向量库规模、活跃会话数等指标。配置了告警规则:P95延迟>3秒告警、错误率>5%告警、Ollama 宕机告警、CRAG拒绝率过高告警。
当前短板:缺少链路追踪(Jaeger)和日志聚合(ELK)。生产落地第一件事就是补上这两块,实现从用户请求到 LLM 调用的完整链路可观测。
用 Python logging 分级输出(info/warn/error)
Docker 挂载目录,日志持久化到宿主机
异常(上传失败、检索错误)自动记录,方便定位
一句话:logging + 挂载 + 分级,自动收集、便于排查
不足:“我不太懂”“只是调用”,拉低技术可信度。
换成:这块我侧重工程落地,原理正在系统补全,既诚实又显积极。
九、召回的时候用到了向量化吗?
用到了,而且向量化是召回的核心。
召回的完整流程是:用户问题 → 查询改写 → 向量化 → 向量检索 → 返回 Top8。
向量化把文本转换成768维数学向量。文档在入库时已经向量化并存到 Chroma 了。用户提问时,我把查询也向量化,然后用余弦相似度在向量库里找最相似的8个文档向量。这个过程本质是最近邻搜索,用 HNSW 索引加速,从10万文档中找最相似的8个只需要几十毫秒。
向量化之所以重要,是因为它能做语义匹配。即使用户问"算法效率",文档里写的是"时间复杂度",向量化也能召回,因为两者的向量在语义空间中是相近的。传统的关键词检索做不到这一点。
我用的是nomic-embed-text 模型,输出768维向量,已归一化,适合余弦相似度计算。实测召回率92%,满足业务需求。如果召回结果不足3条,还会降级到关键词检索(BM25)兜底。
更多推荐



所有评论(0)