从面试官的夺命连环问到我用LlamaIndex反杀:一个广州老广程序员的考公备考技术自救指南

技术达人App
2026-07-01 03:32
阅读 418

作者:一个在珠江边喝着凉茶刷LeetCode的老广码农 坐标:广州·荔湾老城区 状态:白天写CRUD,晚上刷行测,周末背申论


一、那碗牛腩粉,和那个让我破防的面试官

去年十月的一个周六下午,广州的天气终于不那么闷了。我坐在荔湾老城区一家吃了十几年的牛腩粉店里,面前是一碗加底加蛋的萝卜牛腩粉,18块。

手机震了一下,是Boss直聘的消息。

"您好,我们对您的简历很感兴趣,请问明天下午方便来面试吗?"

我嗦了一口粉,心里叹了口气。这已经是我这个月收到的第7个面试邀请了。

先交代一下背景吧。我,92年的老广,在这行摸爬滚打了快八年。坐标广州老城区,和老婆租了个一房一厅,月租3500。之前在一家做电商SaaS的公司写Java后端,月薪从刚毕业时的8k熬到了22k。听起来还行是吧?但去年年中,公司"优化"了一波,我光荣地在被优化的名单里。

失业头两个月,我还挺淡定的。想着凭自己八年经验,找个差不多的工作还不是手到擒来?结果投了三十多份简历,面试了七八家,发现事情没那么简单。

要么是公司要求全栈还要懂AI,要么是薪资直接砍半,要么就是那种"我们这边是大小周,偶尔加班到十点"的委婉说法。

更让我破防的是上周五那场面试。

面试官是个看起来比我小五六岁的技术总监,上来就问:"你了解大模型应用开发吗?RAG做过没有?LlamaIndex和LangChain有什么区别?"

我当时就愣住了。

不是完全不懂,但确实没在项目里实操过。我支支吾吾地说了一些概念性的东西,什么"检索增强生成"、"向量数据库"之类的。面试官微微一笑,那种微笑,懂的都懂——就是那种"你说的都对但我觉得你不行"的微笑。

然后他追了一个问题:"那你能不能现场说一下,用LlamaIndex搭一个基于本地文档的问答系统,核心流程是什么?"

我的大脑一片空白。

最后那场面试毫无意外地挂了。

回家的地铁上,我站在三号线的人堆里,被挤得动都动不了。脑子里全是那个面试官的问题。不是答不出来让我沮丧,而是我突然意识到——我已经很久没有做过技术探索了

每天就是写业务代码,CRUD,对接口,改Bug。技术栈停留在两三年前的水平,新东西一概不碰。八年的"经验",搞不好只是"一年的经验用了八年"。

那天晚上回到家,老婆看我脸色不好,问我怎么了。我把面试的事跟她说了,她沉默了一会儿,说:"要不……你考公吧?"

考公。

这两个字像一颗炸弹在我脑子里炸开。


二、考公?我?一个写了八年代码的程序员?

说实话,一开始我是拒绝的。

"我都三十多了,考什么公啊?再说了,我大学学的计算机,考公能报的岗位本来就少。"

老婆翻了个白眼:"你现在的状态,要么就老老实实去卷技术,把短板补上;要么就换个赛道。你现在这样高不成低不就的,最难受。"

她说得对。

但我心里还是不甘心。我想起刚入行的时候,为了搞懂一个Redis的底层原理,能翻一整天的源码。为了做一个个人项目,周末窝在出租屋里写代码,那种纯粹的兴奋感,现在好像已经找不到了。

我到底是真的不行了,还是只是太久没有逼自己了?

我决定给自己最后一次机会。

不是去找工作,也不是去报考公班,而是——用一个月时间,认认真真做一次技术探索。如果做完之后我还是觉得卷不动了,那就老老实实考公。

而我想探索的方向,就是面试时被问懵的那个东西:大模型应用开发,特别是RAG和LlamaIndex


三、从零开始:被LlamaIndex按在地上摩擦的第一周

说干就干。

十月的第二个周一,我请了个年假,窝在家里开始了我的"技术自救计划"。

先说结论:LlamaIndex的学习曲线比我想象的陡峭多了

我之前以为,不就是个框架嘛,看看文档,写几个Demo,不就上手了?

太天真了。

第一天,我打开LlamaIndex的官方文档,看了两个小时,脑子里全是浆糊。什么DocumentNodeIndexQueryEngineRetriever……概念一堆,每个概念下面还有一堆子概念。

我当时的心态是这样的:

"这玩意儿到底是干嘛的?我直接调OpenAI的API不就行了?为什么要搞这么复杂?"

这种心态持续了大概两天。直到第三天,我强迫自己静下心来,不看代码,先看原理。

我找了几篇讲RAG(Retrieval-Augmented Generation)原理的文章,又看了LlamaIndex创始人Jerry Liu的几篇博客。慢慢地,我开始理解这个东西的设计哲学了。

简单来说,RAG要解决的核心问题是:大模型有知识截止日期的问题,而且对你私有的数据一无所知。你不能把所有东西都塞进prompt里,因为上下文窗口有限,而且token要钱啊兄弟。

所以RAG的思路是:先把你的文档切成小块(chunk),把每个小块转成向量(embedding),存到向量数据库里。等用户提问的时候,先把问题也转成向量,然后在向量数据库里找到最相关的几个chunk,把这些chunk和问题一起塞给大模型,让它基于这些上下文来回答。

而LlamaIndex做的事情,就是把这个流程给你封装好,让你不用从零手写。

想通了这一点之后,我突然有种"任督二脉被打通"的感觉。

接下来的几天,我开始动手写代码。


四、一个真实到不能再真实的Demo:用LlamaIndex给我的考公资料做了个问答机器人

光看文档不实操,等于白学。这个道理我太懂了。

但我需要一个真实的场景来练手,不然写个"Hello World"级别的Demo,面试的时候也说不出来什么干货。

然后我灵机一动——我不是在备考公务员吗?我手头不是有一大堆考公资料吗?

行测的知识点总结、申论的范文集、常识判断的题库……这些资料加在一起有好几百页PDF。每次我要找某个知识点,都要翻来翻去的,效率很低。

不如用LlamaIndex搭一个考公资料问答机器人?

说干就干。

第一步:数据准备

我把所有的PDF资料整理到一个文件夹里,大概有二十多个文件,总共四百多页。

这里踩了第一个坑:LlamaIndex读取PDF需要用到PyPDF或者pdfplumber,有些PDF是扫描版的,根本读不出文字。我有一份申论范文集就是扫描版的,折腾了半天,最后用OCR工具转了一遍才搞定。

教训:数据质量决定一切。垃圾进,垃圾出。在RAG系统里更是如此。

第二步:文档切分(Chunking)

这是我觉得整个流程里最讲究的一步。

LlamaIndex默认的切分策略是按token数量切,比如每512个token切一块,相邻块之间重叠50个token。但对于我的考公资料来说,这种"暴力切分"有个问题——它可能会把一个知识点从中间切断

比如一道行测题的解析,可能刚好被切成两半,一半在这个chunk里,一半在那个chunk里。检索的时候如果只命中了其中一半,大模型拿到的上下文就是不完整的,回答自然就不靠谱。

后来我研究了一下LlamaIndex的高级切分策略,用了一个叫SentenceSplitter的东西,按句子边界来切,这样至少不会把一句话切断。效果好了不少。

from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter

# 读取文档
documents = SimpleDirectoryReader("./kaogong_docs").load_data()

# 按句子切分,每块大约512个token
splitter = SentenceSplitter(chunk_size=512, chunk_overlap=50)
nodes = splitter.get_nodes_from_documents(documents)

第三步:向量化和存储

这一步相对简单。我用的是OpenAI的text-embedding-ada-002来做embedding,向量数据库用的是Chroma,因为轻量,本地跑不需要额外部署。

from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext
import chromadb

# 创建Chroma客户端
chroma_client = chromadb.EphemeralClient()
chroma_collection = chroma_client.create_collection("kaogong_docs")

# 创建向量存储
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)

这里有个小细节:text-embedding-ada-002的调用是要花钱的。我那四百多页文档全部转成向量,大概花了不到两块钱。但如果是企业级的应用,文档量上去了,这个成本还是要考虑的。

第四步:构建QueryEngine并测试

最后一步,构建查询引擎,然后开始提问。

from llama_index.core import VectorStoreIndex

# 构建索引
index = VectorStoreIndex(nodes, storage_context=storage_context)

# 创建查询引擎
query_engine = index.as_query_engine(similarity_top_k=3)

# 开始提问
response = query_engine.query("行测资料分析中,增长率的计算公式是什么?")
print(response)

我满怀期待地按下了运行键。

然后……

它回答了一堆驴唇不对马嘴的东西。

我当时的心情,就像满怀期待地打开一个外卖,结果发现商家给你送了一份隔壁店的餐。

问题出在哪?

我debug了半天,终于找到了原因:similarity_top_k=3只检索了3个最相关的chunk,但对于一些需要综合多个知识点的复杂问题,3个chunk根本不够。而且,默认的QueryEngine是"一次性"回答,不会自我反思和迭代。

于是我又研究了一下LlamaIndex的高级功能,用了一个叫SubQuestionQueryEngine的东西。它的思路是:先把一个复杂问题拆成几个子问题,分别检索和回答,最后再综合起来。

from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.tools import QueryEngineTool

# 为不同类型的资料创建不同的查询引擎
xingce_engine = index.as_query_engine(similarity_top_k=5)
shenlun_engine = index.as_query_engine(similarity_top_k=5)

# 创建工具
tools = [
    QueryEngineTool.from_defaults(
        query_engine=xingce_engine,
        name="xingce",
        description="行测相关知识点查询"
    ),
    QueryEngineTool.from_defaults(
        query_engine=shenlun_engine,
        name="shenlun",
        description="申论相关知识点查询"
    ),
]

# 构建子问题查询引擎
query_engine = SubQuestionQueryEngine.from_defaults(query_engine_tools=tools)

改完之后,效果好了很多。虽然不能说是完美,但至少能回答出个七八成了。


五、面试场上的反杀

搞完这个项目之后,已经是十一月初了。

我把它整理了一下,写了一篇技术博客,还放到了GitHub上。代码不算多优雅,但胜在场景真实,而且我把踩过的坑都记录下来了。

十一月中旬,我又收到了一家公司的面试邀请。做AI应用方向的,JD里明确写了"熟悉RAG、LlamaIndex或LangChain优先"。

面试那天,我特意提前半小时到了。公司在天河区珠江新城的一栋写字楼里,落地窗外能看到小蛮腰。

面试官是个三十出头的技术负责人,聊了几句基础之后,果然问到了RAG。

"你做过RAG相关的项目吗?"

我深吸一口气,开始讲我的考公问答机器人。

"我用LlamaIndex搭了一个基于本地考公文档的问答系统。整个流程包括文档解析、切分策略选择、向量化存储、检索和生成。其中我遇到的最大挑战是文档切分——默认的按token数量切分会破坏知识点的完整性,所以我改用了按句子边界切分的策略……"

面试官的眼睛亮了一下。

然后他开始追问:"那如果文档量很大,比如几万页,你的方案需要做哪些优化?"

这个问题我提前想过。

"首先,向量数据库不能再用本地的Chroma了,要换成Milvus或者Pinecone这种分布式的方案。其次,检索策略要优化,可以引入混合检索——向量检索加关键词检索,用RRF(Reciprocal Rank Fusion)来融合结果。另外,还可以加一层重排序(Rerank),在初步检索之后,用一个cross-encoder模型对结果重新打分排序……"

面试官点了点头,又问了一个更刁钻的:"LlamaIndex和LangChain你都了解吧?它们的核心区别是什么?"

"LangChain更像是一个大模型应用的'工具箱',它提供了各种组件和链式调用的能力,灵活性很高,但上手门槛也高,概念很多。而LlamaIndex更专注于数据连接和检索这个环节,它的核心抽象是Index和QueryEngine,对于RAG场景来说,开箱即用的体验更好。如果是纯RAG应用,我倾向于用LlamaIndex;如果是更复杂的Agent应用,可能需要LangChain或者两者结合。"

面试官笑了一下,说:"不错,看来是真的做过。"

那一刻,我心里长舒了一口气。


六、结果和反思

那场面试之后三天,我收到了offer。

薪资嘛,就不说了,反正是个能让我继续在广州活下去的数字。比上一份涨了那么一点点,但更重要的是——我终于找回了那种做技术探索的感觉

现在回想起来,这段经历给了我几个很深的感悟:

1. 面试题挑战是最好的学习驱动力

说实话,如果不是那次面试被问懵了,我可能永远不会主动去学LlamaIndex。人就是这样,没有压力的时候,总觉得"以后再说"。但"以后"永远不会来。面试题就像一面镜子,照出你到底会什么、不会什么。

2. 真实场景比Demo重要一百倍

如果我当时只是跟着官方文档跑了一个"Hello World",面试的时候根本说不出什么有深度的东西。但因为我用真实的考公资料做了这个项目,踩过了真实的坑(PDF解析失败、切分策略不对、检索效果不好),面试的时候才能言之有物。

面试官想听的不是你背了多少文档,而是你解决了什么真实的问题。

3. 技术焦虑的本质是"停滞焦虑"

我那段时间的焦虑,表面上看是找不到工作的焦虑,但本质上是"我在停滞"的焦虑。八年了,我的技术栈没有实质性的更新,每天都在舒适区里重复。这种停滞感,比失业本身更让人恐惧。

4. 考公和卷技术,不是非此即彼

老婆让我考公,我选择了先做技术探索。这两件事其实不矛盾。考公是一条路,卷技术也是一条路。关键是——你不能两条路都不走,站在原地焦虑

我现在的状态是:白天在新公司写代码(顺便把LlamaIndex用到了实际项目里),晚上继续备考公务员。两条腿走路,至少不会摔得太惨。


七、写在最后

写这篇文章的时候,是今年的一个周末。窗外广州的老城区,巷子里传来肠粉店的叫卖声,楼下阿伯们在下棋。

我合上电脑,喝了一口已经凉了的凉茶。

如果你也是一个在技术焦虑中挣扎的程序员,我想对你说:

别怕。你不是不行了,你只是太久没有逼自己了。

找一个你真正感兴趣的场景,做一个真实的项目,踩一些真实的坑。这个过程可能会很痛苦,但痛苦之后,你会发现自己还在成长。

至于考公嘛……

嗯,申论还没背完呢,先不聊了。

共勉。


附:文中提到的LlamaIndex学习资源

P.S. 如果你也在广州,欢迎来荔湾找我喝凉茶。我请客,你请我聊技术就行。😄

评论 0

最热最新
暂无评论
技术达人AppLv.1
0
影响力
0
文章
0
粉丝