1. 面试官到底想听什么:DocResearch 项目的定位与考察逻辑
DocResearch 这个名字听起来像个学术工具,实际上它是一个典型的Agent + RAG复合型项目。面试官拿到这份简历,脑子里第一反应不是“你用了什么框架”,而是“你到底有没有把一条完整的链路跑通过”。这两者之间的差距,比很多人想象的大得多。
我带过几个做 DocResearch 方向的同学准备面试,最常见的翻车方式就是:一上来就说“我用了 LangChain 搭了一个 RAG 知识库,接了 pgvector 做向量检索,然后用 Agent 做编排”。听起来很完整,但面试官只要追问一句“你的 chunk 是怎么切的,为什么这么切”,很多人就开始含糊了。问题不在于技术难,而在于没有把每个模块的决策逻辑内化成自己的东西。
DocResearch 这类项目的核心链路其实就四段:文档摄入与切分、向量化与存储、检索与重排、Agent 编排与生成。面试考察的不是你知不知道这四个词,而是你能不能讲清楚每一段里你做了什么选择、为什么这么选、遇到了什么问题、怎么解决的。热词里出现的 pgvector、Agent、RAG、Python 这些,本质上都是这条链路上的工具节点,面试官关心的是你对节点的理解深度,而不是节点本身的名字。
这篇文章面向的是正在准备 DocResearch 相关岗位面试的人,不管你用的是 Python 还是别的语言,不管你接的是 pgvector 还是别的向量库,底层逻辑是通的。我会按模块拆开讲,每个模块都告诉你面试官可能怎么问、你应该怎么答、哪些细节是加分项、哪些坑千万别踩。
2. 文档摄入与切分模块:最容易被低估的环节
2.1 为什么切分策略是面试的第一个分水岭
很多人觉得文档切分就是个预处理步骤,随便按 500 字切一刀就完事了。但面试官恰恰喜欢从这里切入,因为切分策略直接决定了后续检索质量的上限。你切得不好,后面 embedding 再强、重排再精细,也救不回来。
我在实际项目里踩过的坑是这样的:早期做 DocResearch 的时候,我按固定 512 token 切分,overlap 设了 50。结果检索的时候发现,很多问题的答案被切成了两半,一半在 chunk A 的末尾,一半在 chunk B 的开头。检索只召回了其中一个,生成出来的答案就是残缺的。后来我改成了基于语义边界的递归切分,优先按段落切,段落太长再按句子切,句子还长才按固定长度切。这个策略在 LangChain 里对应的是RecursiveCharacterTextSplitter,但关键不是用哪个类,而是你要能说清楚分隔符的优先级怎么定的。
面试官可能会这样问:“你的 chunk size 为什么选 512 而不是 256 或者 1024?”这时候你不能只说“因为大家都用 512”。合理的回答应该包含几个维度:你的 embedding 模型的最大输入长度是多少、你的文档类型是什么(技术文档段落短、法律合同段落长)、你的检索粒度需求是什么(是要精确到一句话还是一个完整论点)。比如你用的是 BGE 系列的模型,最大输入 512 token,那你切分的时候就要留余量,实际切到 400 左右比较稳妥,因为还要算上特殊 token。
2.2 元数据设计:面试官判断你有没有工程思维的关键
切分完之后,每个 chunk 不能光有文本内容,还得带上元数据。这一步很多人做得很粗糙,只存一个 source 字段就完事了。但元数据设计得好不好,直接决定了你后面能不能做过滤检索和溯源引用。
我在项目里给每个 chunk 至少挂了这几个字段:source_file(来源文件名)、chunk_index(在原文中的位置序号)、page_number(如果是 PDF)、section_title(所属章节标题)、doc_type(文档类型标签)。这几个字段看起来简单,但在面试里能展开讲的东西很多。比如section_title这个字段,我是怎么拿到的?如果是 Markdown 文档,可以直接从标题层级解析;如果是 PDF,就得靠版面分析或者启发式规则去猜。这个过程中你会遇到各种脏数据,比如 PDF 解析出来标题和正文混在一起、扫描件根本没有文字层,这些问题的处理方式就是面试官想听的实战细节。
注意:元数据字段不是越多越好。每多一个字段,存储和检索时就要多维护一份索引。我建议只保留那些你确实会在检索过滤或结果展示时用到的字段,其他的等有需求再加。
还有一个容易被忽略的点:文档去重。同一个文档可能被用户重复上传,如果你不做去重,检索结果里会出现大量重复内容,既浪费 token 又影响体验。我的做法是对文件内容做一次 hash,存一个content_hash字段,上传时先查这个 hash 是否已存在。这个细节在面试里提一嘴,面试官会觉得你确实在真实场景里跑过。
2.3 不同格式文档的处理差异
DocResearch 项目通常不会只处理纯文本。PDF、Word、Markdown、甚至网页剪藏都会遇到。每种格式的解析难点不一样,面试官如果问你“你处理过哪些格式的文档”,你最好能说出每种格式的具体坑。
PDF 最大的问题是版面复杂。双栏排版的论文,如果你直接用普通的文本提取库,会把左右两栏的内容交错在一起,读起来完全乱套。我试过用pdfplumber做基础提取,对于简单单栏文档够用,但遇到复杂版面就得上版面分析模型。另一个坑是 PDF 里的表格,普通提取会把表格内容变成一堆没有结构的文字,检索的时候根本没法用。我的处理方式是把表格单独提取出来,转成 Markdown 表格格式再存入,这样至少保留了行列结构。
Word 文档相对好处理,但要注意批注和修订记录。如果用户上传的是带修订的合同,你提取出来的文本可能包含已删除的内容,这个要在解析时过滤掉。Markdown 最省心,但要注意代码块的处理——代码块里的内容切分逻辑和普通文本不一样,不能按句子切,否则一个函数会被切得七零八落。
3. 向量化与 pgvector 存储:选型理由比参数更重要
3.1 Embedding 模型选型:别只说“我用了 OpenAI”
热词里出现了 pgvector,说明很多人会选择 PostgreSQL 加 pgvector 扩展来做向量存储。这个选择本身没问题,但面试官会问你为什么选 pgvector 而不是 Milvus、Qdrant 或者 FAISS。如果你的回答是“因为项目里已经用了 PostgreSQL”,那只能算半个答案。完整的回答应该包含几个层面:数据量级、运维成本、事务一致性需求、以及和现有技术栈的契合度。
pgvector 的优势在于它和关系型数据在同一个数据库里,你可以用 SQL 同时做向量检索和元数据过滤,不用维护两套存储系统。对于中小规模的 DocResearch 项目(比如几万到几十万个 chunk),pgvector 的性能完全够用。但它的劣势也要能说出来:当向量数量上到千万级,IVFFlat 或 HNSW 索引的构建和查询效率会明显下降,这时候可能就需要考虑专用向量库了。能主动说出自己方案的边界,比一味吹嘘方案好得多。
Embedding 模型的选择也是同理。热词里有 ollama,说明很多人会用本地模型做 embedding。本地模型的好处是数据不出域、没有 API 调用成本,但缺点是效果可能不如云端大模型。我在项目里的做法是:检索阶段用本地 embedding 模型保证速度和成本,重排阶段用更强的模型保证精度。这个组合策略在面试里讲出来,面试官会觉得你是在权衡之后做的决策,而不是随便选了一个。
3.2 pgvector 索引构建:参数背后的计算过程
pgvector 支持两种索引:IVFFlat 和 HNSW。IVFFlat 构建快、占用空间小,但查询时需要扫描多个列表,召回率和速度之间需要调参。HNSW 查询快、召回率高,但构建慢、内存占用大。面试官如果问你选了哪种索引,你一定要能说出选择理由和参数设置依据。
我选的是 HNSW,因为 DocResearch 的查询频率高、对延迟敏感,而且我的数据量级在百万以内,内存扛得住。HNSW 的关键参数是m和ef_construction。m是每个节点的最大连接数,默认 16,增大它能提高召回率但会增加内存和构建时间。ef_construction是构建时的候选列表大小,默认 64,增大它也能提高索引质量但构建更慢。我的设置是m=16, ef_construction=128,这是在几次压测之后定下来的——m再往上加,召回率提升不明显但内存涨得很快;ef_construction从 64 提到 128 召回率有可见提升,再往上就收益递减了。
查询时还有一个ef_search参数,控制搜索时的候选列表大小。这个参数可以在查询时动态调整,我一般设成 64 到 128 之间,根据对延迟的容忍度来调。这些参数的具体数值不是重点,重点是你要能说出你是怎么测的、测了什么指标、为什么定在这个值。
3.3 向量维度与存储成本的计算
Embedding 模型的输出维度直接影响存储成本。比如 1536 维的向量,每个 float32 占 4 字节,一条向量就是 6KB 左右。十万个 chunk 就是 600MB,加上索引开销可能到 1GB 以上。这个计算面试官可能会让你现场估,你要能快速算出来。
如果觉得存储成本太高,可以考虑几个方向:一是换用维度更低的 embedding 模型(比如 768 维甚至 384 维),二是用半精度浮点数存储(pgvector 支持halfvec类型),三是做向量量化。每种方案都有精度损失,你要能说清楚在什么场景下可以接受。比如 DocResearch 如果只是做内部知识库,精度损失一点问题不大;但如果是做法律或医疗领域的检索,那就得慎重。
4. 检索与重排模块:RAG 效果的分水岭
4.1 混合检索:为什么纯向量检索不够用
很多人做 RAG 只做向量检索,觉得 embedding 能搞定一切。但实际用下来会发现,纯向量检索在关键词匹配场景下表现很差。比如用户搜一个特定的错误码 “ERR_4032”,向量检索可能召回一堆语义相似但错误码不同的内容。这时候就需要混合检索:向量检索负责语义匹配,全文检索负责关键词精确匹配,两路结果融合后重排。
PostgreSQL 本身就有全文检索能力,配合 pgvector 可以在一套系统里同时做两种检索。我的做法是用tsvector做全文索引,查询时分别执行向量检索和全文检索,然后用RRF(Reciprocal Rank Fusion)做结果融合。RRF 的公式很简单:每个文档的得分是1/(k + rank)的累加,k 一般取 60。这个方法的优势是不需要调权重,两路检索的分数尺度不一样也能融合。
面试官可能会问:“你怎么评估检索效果?”这时候你要能说出具体的指标:Hit Rate、MRR、NDCG。Hit Rate 是最直观的,就是前 K 个结果里有没有包含正确答案。MRR 看的是正确答案排在第几位。NDCG 考虑了多个相关文档的排序质量。我一般会人工标注一批测试 query 和对应的正确 chunk,然后跑这些指标来对比不同策略的效果。这个评估流程在面试里讲出来,比空谈“效果不错”有说服力得多。
4.2 重排模型:什么时候需要,怎么选
检索召回之后,通常还需要一个重排步骤。因为向量检索的相似度分数并不总是和相关性一致,尤其是召回数量多的时候,排在前面的不一定是最相关的。重排模型(Reranker)通常是 cross-encoder 结构,把 query 和 document 拼在一起输入模型,输出一个相关性分数。它的精度比双塔式的 embedding 模型高,但速度慢,所以只适合对少量候选做精排。
我的流程是:向量检索 + 全文检索各召回 20 条,RRF 融合后取前 20 条,再用重排模型精排出前 5 条送给 LLM 生成。这个流程里每一步的数量都是可以调的,面试官如果问你为什么这么设,你要能说出权衡:召回太少可能漏掉正确答案,召回太多重排太慢且可能引入噪声。20 这个数字是我在延迟和召回率之间折中出来的,实际项目里可以根据硬件情况调整。
重排模型的选择上,如果追求效果可以用较大的 cross-encoder 模型,如果追求速度可以用蒸馏后的小模型。热词里提到的 ollama 也可以用来跑本地重排模型,但要注意 ollama 主要面向生成任务,做重排需要确认模型是否支持。我一般会用专门的 reranker 模型,比如 BGE-reranker 系列,效果和速度比较平衡。
4.3 RAG 的瓶颈到底在哪里
热词里有个词叫“rag瓶颈”,这个词很值得聊。很多人以为 RAG 的瓶颈在生成阶段,其实不是。RAG 的瓶颈通常在检索阶段。如果检索没召回正确的内容,LLM 再强也生成不出正确答案。而检索的瓶颈又往往在切分和 embedding 上——切分把答案切碎了,embedding 没把语义表达好,后面怎么调都白搭。
我在项目里做过一个统计:在所有回答错误的 case 里,超过七成是因为检索没召回正确 chunk,只有不到三成是 LLM 生成的问题。这个数据说明,优化 RAG 应该把精力花在检索链路上,而不是反复调 prompt。面试官如果问你“你怎么优化 RAG 效果”,你从这个角度回答,会比说“我调了 prompt”高一个层次。
5. Agent 编排模块:从 RAG 到 Agentic RAG 的跨越
5.1 Agent 在 DocResearch 里到底做什么
热词里 agent、agent开发、agentic rag 出现频率很高,说明这是当前的热点。但在 DocResearch 项目里,Agent 不是用来替代 RAG 的,而是用来增强 RAG 的。普通 RAG 的流程是固定的:检索、重排、生成。Agentic RAG 的流程是动态的:Agent 可以根据问题类型决定要不要检索、检索几次、用哪个检索工具、检索结果不够时要不要换个 query 再试。
举个例子,用户问“对比 A 文档和 B 文档里关于 X 的描述”。普通 RAG 可能只检索一次,召回的内容可能只覆盖了 A 或只覆盖了 B。Agentic RAG 的做法是:Agent 先判断这个问题需要分别检索两个文档,然后发起两次检索,最后把结果合并生成。这个决策过程就是 Agent 的价值所在。
面试官如果问你“你的 Agent 是怎么编排的”,你要能说清楚 Agent 有哪些工具可用、决策逻辑是什么、怎么防止 Agent 陷入死循环。我的做法是给 Agent 定义几个明确的工具:search_docs(检索文档)、get_doc_metadata(获取文档元信息)、compare_docs(对比多个文档的内容)。Agent 的 system prompt 里写清楚每个工具的使用场景和限制,同时设置最大迭代次数防止无限循环。
5.2 Agent 框架选型:LangChain 不是唯一答案
热词里有 langchain4j easy rag,说明 Java 生态里也有对应的方案。Python 生态里 LangChain 是最常见的,但它不是唯一选择。我在项目里对比过几个方案:LangChain 生态最全但抽象层太厚,调试的时候经常不知道底层发生了什么;LlamaIndex 在 RAG 场景下更专注,索引和检索的抽象更清晰;直接手写编排逻辑最灵活,但开发效率低。
最后我选的是LangGraph,因为它的状态机模型更适合 Agent 场景。每个节点是一个处理步骤,边是状态转移条件,整个流程可视化程度高,调试也方便。而且 LangGraph 对循环和条件分支的支持比 LangChain 的 Chain 更自然。这个选型理由在面试里讲出来,面试官会觉得你确实对比过、思考过,而不是随大流。
如果面试官问你“为什么不自己手写”,你可以说:手写当然可以,但 Agent 的编排涉及状态管理、错误重试、工具调用解析这些通用逻辑,用框架能省掉大量样板代码。关键是你要理解框架帮你做了什么,而不是把它当黑盒。
5.3 Agent 的安全与并发问题
热词里有“agent安全”和“ai agent 怎么扛并发”,这两个都是实际落地时绕不开的问题。安全方面,Agent 调用工具时要有权限控制,不能让 Agent 随意访问不该访问的文档。我的做法是在工具层面做过滤,每个工具调用时都带上用户身份,检索时只返回该用户有权限的文档。另外,Agent 的输入要做注入检测,防止用户通过精心构造的 prompt 让 Agent 执行非预期操作。
并发方面,DocResearch 的瓶颈通常在 embedding 和 LLM 调用上。embedding 可以批量处理,LLM 调用可以用异步加连接池。pgvector 的查询本身是并发的,但要注意连接池大小和数据库的最大连接数匹配。我一般会用asyncpg做异步数据库访问,配合asyncio做并发控制。如果并发量再大,可以考虑加一层缓存,把常见 query 的检索结果缓存起来。
提示:Agent 的并发控制不只是技术问题,也是成本问题。每次 Agent 迭代都可能调用 LLM,如果不加限制,一个复杂问题可能触发十几次 LLM 调用,成本会失控。设置最大迭代次数和超时时间是必须的。
6. 面试实战:怎么把模块讲成故事
6.1 用 STAR 法则组织每个模块的回答
面试官问项目细节的时候,最怕听到流水账。我建议用 STAR 法则来组织:Situation(背景)、Task(任务)、Action(行动)、Result(结果)。比如讲切分模块,你可以这样说:背景是文档格式多样、检索效果差;任务是设计一套通用的切分策略;行动是对不同格式用不同的解析器、按语义边界递归切分、加元数据;结果是检索 Hit Rate 从 60% 提升到 85%。这样讲既有细节又有数据,面试官很容易跟上。
每个模块都准备一个这样的故事,但不要背稿子。面试官可能会打断你追问细节,你要能自然地展开。比如他问“你说的语义边界具体怎么判断”,你就要能说出分隔符优先级、段落识别规则这些具体的东西。
6.2 常见追问与应对策略
面试官追问通常集中在几个方向:为什么这么选、遇到什么问题、怎么衡量效果、如果重做会怎么改。这四个问题几乎可以覆盖所有模块。你准备的时候,每个模块都把这四个问题的答案想清楚。
“如果重做会怎么改”这个问题特别能体现反思能力。比如我会说:如果重做切分模块,我会更早引入评估集,用数据驱动切分参数的选择,而不是凭感觉调。如果重做 Agent 模块,我会把工具调用的日志做得更细,方便分析 Agent 的决策路径。这种回答展示的是成长型思维,面试官很吃这一套。
6.3 别踩的坑:这些回答会让面试官皱眉
第一个坑是只讲框架不讲原理。说“我用了 LangChain 的 RetrievalQA”没问题,但面试官追问“RetrievalQA 内部做了什么”你答不上来,就露馅了。第二个坑是没有数据支撑。说“效果不错”不如说“Hit Rate 85%,MRR 0.72”。第三个坑是回避问题。遇到不会的就说不会,然后说说你会怎么去查、怎么去学,比硬编好。
还有一个坑是过度包装。明明只跑了几百个文档,非要说“大规模知识库”;明明只做了单机部署,非要说“分布式架构”。面试官都是过来人,一问细节就穿帮。老老实实说清楚数据量级和部署方式,反而显得可信。
7. 几个容易被忽略但很加分的细节
7.1 日志与可观测性
DocResearch 这种多模块系统,出问题的时候如果没有日志,排查起来非常痛苦。我在项目里给每个模块都加了结构化日志:检索时记录 query、召回数量、耗时;重排时记录输入输出和分数;生成时记录 prompt、token 消耗、耗时。这些日志不仅用于排查,还能用于分析用户行为、优化检索策略。
面试官如果问你“你怎么知道系统哪里出了问题”,你把这个日志体系讲出来,比说“我看报错”专业得多。更进一步,可以提一下用 LangSmith 或者自己搭一套 trace 系统,把一次完整请求的链路串起来看。
7.2 缓存策略
DocResearch 里有很多可以缓存的地方:embedding 结果可以缓存(同样的文本不用重复算)、检索结果可以缓存(同样的 query 不用重复查)、LLM 生成结果可以缓存(同样的问题不用重复生成)。缓存能显著降低延迟和成本,但要注意失效策略。文档更新了,相关的缓存要清掉。
我一般用 Redis 做缓存,key 的设计要包含文档版本号,这样文档更新后 key 自然变化,旧缓存自动失效。这个细节在面试里提出来,说明你考虑过生产环境的问题。
7.3 评估集的构建
没有评估集,优化就是盲人摸象。我在项目早期就建了一个小规模的评估集:50 个典型问题,每个问题标注了正确的 chunk。每次改动检索策略,都跑一遍评估集看指标变化。这个评估集不需要很大,但要有代表性,覆盖不同类型的问题(事实型、对比型、总结型)。
面试官如果问你“你怎么证明你的优化有效”,评估集就是最好的答案。你可以说:我建了一个 50 题的评估集,优化前 Hit Rate 是 62%,优化后是 85%,MRR 从 0.48 提升到 0.71。这种数据化的回答,比任何形容词都有力。
8. 从面试延伸到实际工作的思考
准备 DocResearch 面试的过程,其实也是梳理自己项目经验的过程。很多人做项目的时候是“能跑就行”,面试的时候才发现很多细节没想清楚。我的建议是:在做项目的过程中就养成记录决策的习惯。每次做一个技术选择,都写下来为什么这么选、考虑过哪些替代方案、预期效果是什么、实际效果如何。这些记录在面试时就是最好的素材。
另外,面试官问的很多问题,在实际工作中也会遇到。比如“怎么扛并发”不只是面试题,也是你上线后必须解决的问题。“怎么评估效果”不只是面试题,也是你持续优化的基础。把面试准备当成一次系统性的项目复盘,收获会比单纯背题大得多。
最后说一个我自己的体会:面试的时候,面试官其实不指望你什么都懂。他们更看重的是你思考问题的方式和解决问题的路径。遇到不会的问题,坦诚说不会,然后说说你会怎么去查资料、怎么去验证,这种态度比硬答更让人放心。DocResearch 这个项目涉及的面很广,没有人能每个细节都精通,关键是你能把自己做过的那部分讲透,把没做过的部分讲清楚思路。