设计一个代码库问答系统(对着私有仓库问)
谁在问:开发者工具团队、平台组;面试官往往自己在做内部工具,会拿真实痛点压
口语化问法
- 我们几十个仓库,新人上手很慢,想做个能对着代码问的助手,怎么设计?
- 代码怎么切块?和文档切块有什么不一样?
- 仓库天天在变,索引怎么跟得上?
考察意图
四层:
- 你知不知道代码问答的查询是分类型的。「这个函数在哪定义」「处理超时的逻辑在哪」「这段为什么这么写」是三种完全不同的检索问题,用一套方案全覆盖必然三头都不好。
- 你怎么看索引式与代理式检索之争。这是 2026 年这道题必然被追到的点,站队的人会被追穿。
- 你有没有考虑新鲜度。代码库和文档库最大的区别是它每天都在变。
- 你有没有考虑「哪个在跑」。仓库里同时存在废弃实现、灰度分支、历史遗留,代码本身不会告诉你哪个是活的。
参考答案
60 分答案(及格线)
把仓库里的代码文件切块,用 embedding 建索引,问的时候检索相关代码片段拼进 prompt 回答,并给出文件路径和行号。切块的时候尽量按函数或类的边界切,别拦腰切断。仓库更新用 webhook 触发增量索引。权限按仓库维度过滤。
框架是对的,但它把代码当成了另一种文档。代码的三个特殊性——标识符查询、引用关系、变更历史——一个都没体现,追问一压就空。
90 分答案(有生产经验的回答)
① 先问四个问题:仓库数量和总行数?主要用户是新人上手、还是老手排查?问题类型的分布长什么样——这是最关键的一问,因为不同类型要不同的检索方式。代码能不能出网、权限按仓库还是按目录?
② 基线:先不建索引。 对绝大多数团队,最土也最有效的基线是代理式检索——让模型自己
grep、读文件、顺着引用爬,加上强制引用到文件与行号。理由很实在:零索引维护、永远是最新的代码、代码不出网。这套基线能到什么水平:定位类和局部逻辑类问题基本够用;不够用的是「跨模块的语义搜索」和「为什么这么写」。③ 加挂,每样说代价:
按查询类型分三条通道(这是本题的主结构):
定位类 「XX 函数在哪定义 / 谁调用了它」 → 符号索引(定义-引用图),精确、便宜、可完全信任 语义类 「处理超时的逻辑在哪」 → 语义检索,因为没有共同关键词 解释类 「这段为什么这么写」 → 需要 git blame + PR/commit 描述,代码里根本没有答案第三条通道最容易被忽略,也最能体现是否做过:「为什么」的答案不在代码里,在变更历史和讨论里。把 commit 信息、PR 描述、关联的 issue 一起索引,才答得了这类问题。
语义索引(如果决定上)。解决什么:跨模块的模糊语义查询,公开数据里语义检索相对纯 grep 的问答准确率大约高一成多,且仓库越大优势越明显。代价是什么:新鲜度——索引流水线跟不上活跃团队的提交速度,开发者查询时拿到的可能是几小时甚至几周前的快照;再加上代码要做向量化(合规)和大仓的建索引成本。什么时候不该用:仓库高频变动、代码不能出网、或者问题以定位类为主时。
代码专用的切块:按语法结构切(函数、类、方法),并且每个块要带上「上下文头」——文件路径、所属类、导入语句、以及函数签名。因为一段函数体脱离了这些信息几乎无法理解。代价:块之间有冗余,索引体积变大。
「哪个在跑」的元信息(这是代码问答最隐蔽的失败模式)。仓库里同时存在废弃实现、灰度分支、历史遗留,代码本身不会说哪个是活的。做法是把可获得的运行时信号接进来:部署配置、特性开关状态、最近有没有被调用的埋点数据、以及一份人工维护的「已废弃目录清单」。代价:这份元信息要维护;收益:它挡住的是「改错对象」这类最难发现的错误。
权限:按仓库和目录做前置过滤,且注意过滤后要放大召回再重排;引用要回查权限,不能给出用户看不到的文件链接。
④ 评测:评测集按查询类型分层(定位 / 语义 / 解释 / 跨模块),因为它们的失败原因完全不同,混在一起的总分没有指导意义。定位类有标准答案,可以自动判分;解释类只能人工或 judge,且 judge 要有校准集。零成本信号里最有用的是引用点击率(用户点开引用去核对,说明答案可核查)和重问率。线上还要盯一个专属信号:答案引用的文件是不是已被删除或已废弃——这是索引陈旧最直接的暴露方式。
⑤ 演进:出现「跨模块语义查询占比明显上升」→ 才上语义索引;出现「解释类问题占比高」→ 优先接变更历史而不是加强代码检索;出现「新人上手场景占主导」→ 该做的其实是生成并维护架构文档,而不是继续加强问答。退路:如果索引新鲜度长期跟不上(错误引用率超阈值),退回纯代理式检索。
追问链
代码怎么切块?和文档切块差别在哪?
期望① 按语法结构切,固定长度会把函数拦腰切断;② 每块带上下文头:文件路径、包名、所属类、关键导入、函数签名 —— 孤立函数体检出来没法用;③ 跨文件语义单元(接口与实现分处两文件、配置与使用点分离)靠符号索引连,不指望切块解决;④ 测试文件是高价值上下文,同时给出用法与预期行为信号说出「块要带上下文头」→ 真做过代码检索;只说「按函数切」→ 做过一半索引还是 grep?给我一个判断。
期望① 索引式买跨语义召回,代价是新鲜度、合规、大仓成本,不能出网别用;② 代理式买新鲜度与免维护,代价是读取吃掉 agent 的 token 大头;③ 别站队:两边争的不是同一个指标;④ 混合:符号索引定位、语义索引模糊查、代理式验现状信号说出「两边评价的指标不同」→ 读过双方材料;直接说「向量索引更先进」或「grep 才是正道」→ 在站队怎么回答「这段代码为什么这么写」?
期望答案不在代码里:① 载体是git blame、commit、PR 描述、关联 issue;② 提交信息全是「fix bug」这通道就空了,该立提交纪律不是加强模型;③ 给证据链,「为什么」最容易被编造;④ 找不到就说「未找到相关变更记录」,别拿代码风格推测信号指出「提交信息质量决定这条通道成不成立」→ 真做过代码考古;答「让模型分析代码推测意图」→ 正是幻觉高发的做法仓库天天在变,索引怎么办?
期望① 增量按提交触发,要处理重命名与移动,否则留下指向已删路径的僵尸块;② 删除必须真删,软删会检出已不存在的代码;③ 索引必然滞后,返回前校验文件与行号仍在,不在就回退代理式重查;④ 监控错误引用率;⑤ 只索引主干信号提出「返回前校验引用仍存在」→ 处理过陈旧索引;只说「用 webhook 增量更新」→ 没遇到过重命名与删除的坑接了十个仓库之后成本失控了,先砍什么?
期望账压倒性在输入侧:查缓存命中率(仓库名、时间戳别进静态前缀)、读取是否失控(一个问题读几百个文件)、索引重复注入。按顺序砍:探索加范围与轮次上限(只砍发散不伤正确率)→ 大段读取改先摘要后细读 → 低频仓库砍语义索引退回代理式 → 最后才降推理档位。特有一招:定位类查询不走模型,符号索引直接返回。换便宜模型当心 tokenizer:单价降了 token 涨了信号提出「定位类查询不走模型」→ 真优化过这类系统;只会说「换便宜模型 / 限制调用次数」→ 没做过成本归因
评分要点
- 按查询类型分通道(定位 / 语义 / 解释),不用一套方案全覆盖
- 基线选代理式检索并说得出理由(零维护、最新、不出网)
- 切块按语法结构且带上下文头
- 索引 vs grep 给三段式,指出两边评价指标不同且无公开对照
- 「为什么这么写」接变更历史,并承认提交信息质量决定成败
- 处理新鲜度:增量更新、真删除、引用存在性校验、错误引用率监控
- 考虑**「哪个在跑」**的元信息,防止改错对象
- 成本追问走成本三刀,且能提出「定位类查询不走模型」