面试面经 · 地平线
【地平线】【大模型开发】【暑期实习一面】面经(八股+大模型项目+手撕)
地平线 大模型开发 暑期实习一面面经
背景: 海外一年制硕士,投递岗位为大模型开发暑期实习,面试时长约 55 分钟,全程以项目问答和八股穿插进行,整体节奏偏务实。
题目摘要
- Go 语言基础:goroutine 与 channel 的底层实现,GMP 模型中 P 的本地队列如何避免锁竞争。
- MySQL/Redis 八股:MySQL 的 MVCC 机制在 RR 隔离级别下如何解决幻读;Redis 的持久化机制 RDB/AOF 混合使用场景,以及缓存穿透的布隆过滤器实现原理。
- 大模型项目深挖:围绕一个 RAG 项目展开,追问了 chunk 切分策略、向量检索召回率优化、以及 Function Calling 的容错处理。
- 上下文管理与成本优化:如何控制多轮对话的 token 消耗,以及针对长文档场景的推理成本优化方案。
- 手撕算法题:题目为「找到字符串中所有字母异位词」(LeetCode 438 变体)。给定字符串 s 和 p,返回 s 中所有 p 的异位词子串的起始索引。要求写出 O(n) 时间复杂度的滑动窗口解法,并写出关键去重逻辑。
项目深挖
面试官针对简历中的 RAG 项目(基于大模型的企业知识库问答)进行了连续追问,以下是三个核心问题及参考回答方向:
追问 1:你的 chunk 切分策略是怎么设计的?为什么不用固定长度?
参考方向:固定长度切分会导致语义断裂,比如把一个完整的 SQL 语句或代码块截断。我的做法是采用「结构感知切分」,先按 Markdown 标题层级或代码块边界进行粗切分,再对超过阈值(如 512 token)的块做递归切分。同时,对切分后的块保留元信息(如来源页码、章节路径),方便后续引用溯源。
追问 2:向量检索的召回率不理想,你怎么排查和优化?
参考方向:先分维度定位问题。第一步检查 embedding 模型是否匹配领域,比如通用 embedding 对代码或专业术语效果差,考虑微调或替换为领域模型。第二步做查询改写,对用户问题先做意图分类,如果是问句则提取核心实体后做多路召回。第三步调参,包括 top-k 的选取、相似度阈值、以及是否引入重排序模型(如 cross-encoder)。实际项目中,我通过加入关键词 BM25 与向量检索的混合召回,将召回率提升了约 15%。
追问 3:Function Calling 在项目里怎么用的?如果模型调错参数或返回非法 JSON 怎么办?
参考方向:Function Calling 主要用来对接内部工单系统和数据库查询接口。容错方面做了三层:第一层是 JSON 解析失败时,会用正则提取函数名和参数片段做修复;第二层是参数校验,根据 function schema 做类型检查,非法参数直接丢弃并调用默认函数;第三层是超时与重试,模型调用超过 3 秒就降级为纯文本回复。另外,所有 function 的调用日志都会回流,定期筛选 badcase 来微调 few-shot 示例。
手撕算法题详解
题目: 给定字符串 s 和 p,返回 s 中所有 p 的异位词子串的起始索引。异位词指字母相同但排列不同的字符串。
关键思路:
- 使用固定大小的滑动窗口(窗口长度等于
p的长度),配合两个数组分别记录p和当前窗口内各字符的出现次数。 - 初始化时先统计
p的字符频率,然后滑动窗口遍历s,每次移动时更新窗口内字符计数,并比较两个计数数组是否相等。 - 时间复杂度 O(n),空间复杂度 O(1)(因为字符集大小固定为 26)。
代码要点:
func findAnagrams(s string, p string) []int {
if len(s) < len(p) {
return []int{}
}
var pCount, sCount [26]int
for i := range p {
pCount[p[i]-'a']++
sCount[s[i]-'a']++
}
res := []int{}
if pCount == sCount {
res = append(res, 0)
}
for i := len(p); i < len(s); i++ {
sCount[s[i]-'a']++
sCount[s[i-len(p)]-'a']--
if pCount == sCount {
res = append(res, i-len(p)+1)
}
}
return res
}面试时先讲思路,再写代码,最后主动提了边界条件(如 s 长度小于 p、空字符串输入),面试官比较认可。
准备建议
- 项目准备要能扛住「为什么」连环问。 不要只讲你做了什么,而是把每个技术选型背后的 trade-off 想清楚。比如「为什么用 Redis 缓存向量结果而不是直接查库」「为什么选这个 embedding 模型而不是别的」。建议画一张项目架构图,在面试前对照每个模块自问至少三个「为什么这么做、有没有更好的方案」。
- Go 语言八股要结合源码理解,不要死记概念。 地平线的技术栈偏底层,面试官会追问到具体实现细节。比如 GMP 模型,除了知道 P 有本地队列外,还要能说清楚「work stealing 机制什么时候触发」「全局队列的锁粒度如何优化」。建议直接读
runtime/proc.go中关键函数,配合《Go 语言设计与实现》相关章节。
- 大模型方向要准备一个「成本优化」的故事。 这是区别于普通后端候选人的关键点。提前算好一笔账:你的项目每天调用多少次 LLM API、平均 token 数、单次成本是多少,然后讲清楚你是怎么降本的(比如 prompt 压缩、缓存相似 query、用小模型做分类路由)。面试官听到具体数字会明显更感兴趣。
结果反馈
一面通过,3 天后约了二面,二面主要考察系统设计。
想系统备战大厂大模型/Agent 开发?NiceOffer 提供 SDE+LLM 双轨 1v1 陪跑,合同保底 40w 年薪,文末扫码咨询。