字节一面面经

字节一面的面经,还有一些答疑

前言

最后编辑于2026年03月13日


今天字节一面了!!!最后感觉来说,还行吧,有几个问题答不上来,代码手撕也没写完。不过面试官人挺好了,长得宽厚亲近,没给什么压力。

首先是自我介绍,我就简单介绍了一下,主要讲了我的项目经历:

  1. 先讲我的身份和学历,顺带提一下我的在校课程:嵌入式、C语言、计网,以及我的一个奖学金
  2. 然后就开始讲项目:
    1. 第一个是我的毕设:基于体检报告的AI健康管理大模型,这个项目中我独立搭建了从OCR提取到信息结构化提取再到LoRA微调、多策略RAG的全链路AI+医疗健康系统
    2. 具体展开来说:首先是面对体检报告的长尾难题,毕竟实际体检报告的样式是多样的,不同的医院样式不同,同时还叠加用户随手拍的难题,所以结构化提取体检报告信息是一个难题。在这里我搭建了一条pipeline,先使用ocr模型得出初步结果,再使用脚本做简单的解析,然后再使用大模型做进一步的处理,比如关键实体名词识别、结果总结以方便后续rag检索。
    3. 然后是使用权威数据和合成数据来微调模型,增强模型在多轮对话的能力,这一部分我主要是参考了一篇论文,即模型在专业知识方面较强,但存在在多轮对话中不能发起更具引导性的提问、难以利用多轮对话的中散落的信息等问题,我的微调就主要为了解决这些问题。
    4. 然后是构建医疗知识库,里面集成了权威医疗数据和之前在体检报告中提取的用户个人数据,其中用户个人数据的rag结果是一个动态的历史轨迹,以此实现大模型更专业、更个性化的建议生成。
    5. 第二个是我的毕设综合课程设计,项目名是:结合rag的医疗健康领域ai问答,我在这个项目里面担任组长。
    6. 这个项目也是基于rag的医疗问答系统,我们采用了本地部署的方案,使用GGUF的模型,以实现在边缘资源受限设备的部署,最后是可以在没有显存,只有内存的机器上部署。
    7. 我们的权威资料是放在向量数据库中,并构建了多策略的rag检索,我们首先将用户的提问分类到具体某一个科室,因为我们的数据是按科室进行大分类的,然后在这个科室里面进行稠密向量和稀疏向量的检索,最后将结果做加权排序(也就是rff,但是我当时想不起来叫什么了),实现了召回数据的质量的提升。
    8. 第三个是我比较久之前的一个比赛:元美杯,我在比赛中担任队长,项目名是:基于LSTM的电商销量预测和AI服务系统搭建。
    9. 项目中我主导团队完成了从数据建模到服务部署的全链路ai系统开发,主要实现了销量预测和用户画像的生成。
    10. 我们最后是选择了LSTM作为我们销量预测模型,实现了未来7天的销量预测。
    11. 同时我设计分布式的架构,即用flask搭建ai服务器,django搭建用户前端页面
    12. 用户画像我们是调用当时的chatgpt-3.5的api来生成,增强个性推荐能力。比赛最后我们获得优秀奖。
  3. 讲完项目我就简单地讲一下毕业实习:
    1. 接下来是我的毕业实习,这是学校安排我们去富士康的一个实习,我在其中担任组长,主要做的是参观冲压产线和实践复现其中的机械臂部分,我们学习了机械臂的编程方式和与上位机的通信。最后在小组汇报上取得前5名的成绩。
  4. 最后讲了一下我的技能证书:
    1. 然后是我的技能证书,我目前已经通过了英语四级。
    2. 我熟悉python和c/c++,以及rnn/lstm和transformers架构、微调,还有rag系统。

然后就开始提问。

一开始是基于项目的提问:

  1. 问我第一个项目的目的和难点是什么。
    • 目的是做出一个完整的从ocr提取体检报告到结构化解析再到数据入库,最后结合rag进行健康管理的ai系统,难点还是比较多,就拿ocr举例,我刚刚提到了长尾难题,就是体检报告的样式不确定不统一,我导师是医科大学那边的,我可以拿到一些实际的体检报告,从中就可以看到实际的体检报告是很复杂,所以我需要搭建一条比较复杂的pipeline,特别是其中的表格部分,毕竟体检报告很多信息都在表格里面,对于表格部分,我将ocr得到的原始结果先进行简单的脚本解析,然后使用大模型进行进一步的处理。
  2. 问我知道为什么lora流行,简单介绍一下lora。
    • lora高效的地方就在于它冻结了大部分的参数,其实我觉得不能叫冻结,它就是不去更新原始的权重矩阵,而是用一个A乘B矩阵代替,而A、B矩阵的形状分别是n乘于r和r乘于m,所以参数量就从nm下降到(n+m)r,所以lora就十分高效。
  3. 为什么rag比关键词匹配好(这个我还是比较熟悉的,但是其它rag本身的内容挺多的,我当时就讲得有点乱了)
    • 因为rag使用了嵌入式模型生成的稠密向量,而这个向量本身是带有语义,其实也就是利用了嵌入式模型的高纬度语义空间;而关键词匹配就不太能做到,比如大学生和本科生,这两个词在语义上是相近的,但关键词匹配就不一定能匹配到,所以rag就是利用了在高纬度语义空间里面的向量的距离来表达相似性。
  4. 问我pdf的切片问题,有没有切片过pdf,怎么处理的(其实我没怎么处理过pdf……)
    • 肯定是处理过pdf的,pdf的处理确实毕竟麻烦,首先要做的就是选择质量高的pdf,比如我从国家卫健委下载的pdf,本身就比较结构化,带有标题等信息,就比较好切分。如果是那种图片塞pdf里面的,就要先ocr,然后再处理,如果有表格,就可以利用到我之前搭建的表格处理管线
  5. 那你是怎么切分pdf的(我直接开始瞎编了)
    • 我目前用的方法是滑动窗口+一定重叠来切分。(其实可以再编多几个,比如按语义,文本段落等级等等,但是我本身不懂,要是他追问我就露馅了)

然后就是一般性的提问了,问了不少多线程和多进程的问题,但是我几乎没看这部分,有点积极:

  1. c++到现在多了哪些特性
    • 从c99到现在c23,c26多了很多很多东西,这我一时说不出来。呃……比如很常用的auto,还有虚函数继承
  2. 刚刚提到了auto,你知道什么迭代器吗
    • 我不知道
  3. 你用过stl容器吗,你知道哪些stl容器
    • stl容器平时还是很常用,首先是顺序容器:array,这个需要在初始化时申明长度,和c样式的数组比较像,在比较新的c++23可以用auto自动推导长度和类型;vector,这个就非常常用,它是可变长度的数组,给我的感觉比较像python的列表,可以通过push_back来添加新内容,顺带一提,vector有容积和长度两个概念,长度是现有元素的个数,容积是最长长度,这是个小坑,我之前被坑过,当vector容器满了还添加新内容时,它就会触发移动,内部元素一般是浅拷贝,如果类的移动构造函数有noexpect后缀,那就是浅拷贝,毕竟浅拷贝比较高效;还有list,这个就是双向链表;还有一个u开头的,我不会念(其实就是deque,当时想不起了)。接着是键值容器:set和multiset,map和multimap,以及他们的unordered版本,unordered版本就是基于哈希的,我实际中unordered set用得很少。
  4. 你知道c++近些版本新出了左值和右值吗,他们的区别在哪
    • 知道,从最简单的角度来看,左值和右值最大的区别就是:左值是可以取地址的,它是有分配到一块内存,而右值是不能取地址的,它只是一个临时变量,像是函数的返回值。
  5. 左值引用和右值引用都有什么用
    • 左值引用从表面上看就是给变量取了个别名,但其实从底层汇编的角度取看,左值引用本质上也是指针,只是编译器帮我们展开了,左值引用可以用来传递函数,方便修改原值;右值引用就是去用一个临时变量,一般用在类的移动构造函数,用来转移所有权,就像unique智能指针,它可以用move或者release转移所有权(其实我这里讲得有点乱,应该说move可以将一个左值变成右值,然后用来初始化unique指针;release是unique指针的内部方法,可以将所有权转移给另一个unique指针)
  6. 现在请你讲一下tcp的流程
    • 讲tcp的话,肯定要讲带tls的,毕竟现在已经没有不用tls的了。首先服务器处于监听状态,用户端发送一个syn过去,这是唯一一个没有ack的包,服务器收到之后,就发一个有ack和syn的,ack number 等于刚刚客户端发来的包的seq number + 1,接下来都是这样交替+1,客户端收到之后,就会也发一个有syn和ack的包过去(好像是只有ack,但是我当时记错了),这样双方就完成了三次握手。其实三次握手的目的是检验双方是否都可以进行收发操作,毕竟tcp需要保证联通的可靠性。接下来就是tls的内容,首先客户端发一个它生成的随机数,还有它的tls版本,希望的加密算法等给服务器,服务器收到之后就先检验是否合适,一般都没问题的,然后它就把它生成的一个随机数,还有它的数字证书发给客户端;客户端收到之后,先用公钥解密数字证书,看是否匹配(这里我没说清楚,用公钥解密数字签名能得到一个hash,这个hash和证书的hash对比就能知道是否被篡改了),没问题之后,就得到了服务器的公钥,然后就把一个随机生成数字发给服务器(这里我漏了这个随机数要用公钥加密了),服务器收到之后,那到了这一步,双方都有三个随机数了,客户端生成的,服务器生成的,客户端刚刚生成的,用这三个随机数组合成一个会话密钥,之后的通信都用这个密钥来加密报文。等到要结束会话的适合,一端就会发一个fin的包,这里以客户端主动发为例,客户端发送了这个fin的包之后,就会进入fin wait 1,服务器收到之后,就会发一个ack包过去,然后进入closed wait环节,在这个环节里面,服务器就是把没干完的活先做完,没发完的包先发完,之后就会发一个fin的包(之后就进入last ack环节,但是当时我忘记了,所以就打个哈哈,糊弄过去了),刚刚客户端收到服务器的ack包之后,就会先进入fin wait 2,来等待服务器把没发的包发完,等到服务器也发fin包过来之后,就最后发一个ack包过去,服务器收到之后,双方就都关闭了。(其实这里还有,就是客户端发最后一个ack之后,是进入time wait环节,等待2msl即两倍报文最大生存时间后再关闭,服务器就是收到就关闭)
  7. 数字证书的加密是对称的吗
    • 我一开始还没听懂,毕竟数字证书相关的几个环节都有加密,我不知道他在问哪个,然后我就随便说了:数字证书的流程是,服务器把证书给证书签发机构,机构用他们的私钥加密这个证书,这就是数字签名,然后和证书打包在一起(这里我讲错了,机构是先把证书转hash,再加密hash,这才是数字签名),客户端用存在本地的机构的公钥解密,然后匹配。就是私钥加密,公钥解密,这算对称还是非对称?我觉得应该是非对称的?
  8. 为什么tcp要从一个随机的seq number 开始
    • 如果每次都从0开始,那么就很容易被攻击,因为别人可以完全重复你的序列。(其实这里我只粗看了一下,所以就不多说了,防止露馅)
  9. tcp除了seq number之外,还有什么方法保证连接可靠性
    • 当时脑袋卡壳了,想不出来,呃了一会,说:拥塞控制?(其实还有流量控制、重传等等)
  10. 大模型应用中,为什么gpu比cpu高效
    • 因为gpu的计算核心非常多,而大模型的推理和反向传播中,基本都是线性计算,而gpu计算核心多就很适合做这个。
  11. gpu为什么适合矩阵乘法
    • 因为它的计算核心多,就可以开很多并行去计算
  12. 矩阵乘法如何并行(这里我一开始误解,还以为他要我讲矩阵乘法的优化,这我是真忘了)
    • (先瞎扯了一下自己不记得矩阵乘法的优化方法)因为矩阵乘法是行乘于列,而每一个这个都是相互独立的,没有状态相关的,所以能开很多并行,而行和列相乘的内部也有很多计算是相互独立的,也可以开很多并行。
  13. 线程和进程的区别,他们的优缺点
    • 线程的优点是可以不管实际的核心是多少,可以开很多,然后还有一个优点是,线程之间的通信比较方便,可以直接共享。缺点是线程能得到的计算能力比较差,毕竟线程都是在一个cpu核心上。进程的优点是它真正在不同的核心上,可以利用到cpu更多的计算能力,我在很久之前学习c++时,就用c++和opencv做过一个简单的识别程序,多线程就比多进程要卡一些;进程的缺点就是进程间通信比较麻烦,需要通过一块内存来交换数据
  14. 进程间如何通信
    • 这我都不熟,所以直接凭印象瞎编了。我说:通过一块内存来交换数据
  15. 进程间通信的内存在哪
    • 应该是从内存池那里申请一个吧
  16. 如何申请一个新进程
    • 应该是用c++的进程库直接新开一个就行了吧
  17. 如何申请进程通信需要的内存
    • 用new就行了吧
  18. 如果只有一个核心,多线程还有意义吗
    • 肯定还是有的,因为在实际运行中,cpu不是一直在计算的,很多时间都是在io上,比如网络io等等,这样就可以开多线程去处理其它事情,其实协程也可以。
  19. 线程是越多越好吗
    • 肯定不是,首先cpu核心的计算能力是有限的,线程多到一定程度就没有收益了,然后实际业务中也不会出现非常多需要并行处理的事情。(其实这里我想表达线程多到一定程度,其本身就占满了核心的资源,但是这个想法卡在脑子里出不来,还是学少了)
  20. 你知道有哪些锁
    • c++的互斥锁,还有什么来着(这里我突然就忘了,毕竟这一块我没学又没用过),python的gil锁
  21. 听说过无锁语言吗
    • 这是什么?我不知道
  22. 除了互斥锁,还有什么锁
    • 呃……我不知道
  23. 原子操作的定义是什么
    • 我答不上来,又问我原子操作怎么实现的,也答不上来……
  24. python的GIL锁是什么
    • python的gil锁就是在程序运行的过程中,让cpu先跳转到其它的代码,那之前的代码就要上一个gil锁,我记得python3.14的移除gil锁已经是实验性功能了,不过我看到网上的技术分享,移除gil锁和没移除的在执行效率上居然没差多少,可能python的解释器十分优秀吧。

好像就这些,然后就是手撕环节,还是飞书的网页ide,没用过。

题目还算比较简单的,但是现场有点太紧张了,脑袋有点空空,好在思路没什么问题。顺便一提,手撕环节居然没有提交选项,就只有示例,我只能用示例测试。8个示例,前3个没问题,后面几个也大致没毛病,就是一些边缘情况和特殊情况。

具体是:

你拿到一段字符串,是一个json的部分,其缺失了尾部,让你尽可能补充回来。比如:

  • {"user": {"name": "李四", "tags": ["开发", "An -> {"user": {"name": "李四", "tags": ["开发", "An"]}}
  • {"items": [1, 2, 3, -> {"items": [1, 2, 3]}

全部示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{"name": "张三", "age": 25
{"message": "你好,我是智能助
{"user": {"name": "李四", "tags": ["开发", "An
{"na
{"text": "line1\nline2\
{"price": 99.
{"flag": tru
{"data": nul
{"items": [1, 2, 3, 

{"name": "张三", "age": 25}
{"message": "你好,我是智能助"}
{"user": {"name": "李四", "tags": ["开发", "An"]}}
{}
{"text": "line1\nline2"}
{"price": 99.0}
{"flag": true}
{"data": null}
{"items": [1, 2, 3]}

我是用栈记录来做的,基本属于模拟。

最后是简单的答疑,我就问了两个问题,一个是什么时候安排第二面,他说五天内给答复,其实当天下午就给答复了。

然后我问他,按照我的简历,其实投什么岗位会比较好,他说:校招不会对具体的技术有太多要求,只要你有扎实的工程基础、编程基础,然后这个方向是你感兴趣的、能接受的就行,毕竟学校学的都比较老。

后面HR也给我打电话,问我二面的时间安排了。他还在电话里面说:你是校招本科生,不需要有多少技术架构了解,努力展现自己的工程能力、编程基础就行。这几句话他重复了两三遍,应该非常重要,之后再思考一下,也许还可以打电话问问他?

comments powered by Disqus