彩讯股份 AI 全栈开发管培生面经:LangGraph、Prompt 与后端稳定性
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- LangChain 与 LangGraph 的区别是什么?
- Prompt 怎么做(怎么设计)?
- 有 badcase 的情况怎么优化 Prompt?
- Skill 如何设计?
- Prompt 与 Skill 有什么区别?
- 看你写了 SQL 优化,能不能具体举点例子?
- SQL 优化除了索引问题还有哪些可能?比如知道了一些慢 SQL,如何分析排查?
- 线程池的问题如何分析排查?
- 线程池参数怎么设计?
- 作为后端,最重要的是稳定性,上线前如何尽可能保证稳定性?
《参考解析》
LangChain 与 LangGraph 的区别:一个是组件与调用链,一个是状态机
LangChain 提供的是搭 LLM 应用的积木:模型封装、Prompt 模板、输出解析器、检索器、工具、以及把若干步骤串起来的 Chain。它的基本假设是「流程大体是线性的、由代码决定下一步」——适合固定几步的问答、RAG、摘要这类任务,一旦流程里出现循环、分支、人工介入,用 Chain 表达就会很别扭。
LangGraph 把 Agent 建模成一张状态图:节点是函数(调模型、执行工具、写数据库),边是转移条件,整个图共享一份可持久化的 state,因此天然支持循环(ReAct 那种「想—做—看」反复迭代)、条件分支、并行分支与汇合、以及中断后恢复(human-in-the-loop)。它最有价值的两个能力是 checkpoint 和持久化线程:每一步状态落盘,可以断点续跑、回放、按 thread 隔离多轮会话;这在生产里等于给 Agent 装上了「可观测 + 可恢复」。
所以选型的判断标准是流程形态:线性几步用 LangChain(或直接手写调用)更简单;需要循环、分支、人审、长时任务恢复就用 LangGraph。两者也不互斥,LangGraph 的节点内部完全可以调用 LangChain 的组件。面试里能补一句「框架只是壳,真正的难点是状态设计与工具契约」通常会让回答更有区分度。
Prompt 与 Skill:一个是单次指令,一个是可复用的能力单元
Prompt 是给模型的这一次输入:角色设定、任务描述、约束、输出格式、示例。它解决的是「这一次怎么问」。Skill 更像是沉淀下来的能力包:一段带明确触发条件的说明文档 + 需要的工具/脚本 + 执行步骤 + 输出规范,可以按需被加载进上下文,反过来被多个流程复用。可以拿「写一个 SQL 查询」类比:Prompt 是「帮我查昨天的订单量,用 xxx 口径」;Skill 是「公司内一切订单类查询都走这套:读语义层定义 → 选指标 → 用模板生成 SQL → 带出处返回」——它规定了触发条件、步骤、依赖工具和验收标准。
这个区别带来几个工程结论:Prompt 的优化靠改措辞、加示例、加约束,改完只影响这一处调用;Skill 的设计靠拆职责边界、写清输入输出契约、控制加载时机(上下文有限,全塞进去既贵又容易互相干扰),改完影响所有调用它的地方。也因此 Skill 需要版本管理、需要评测集回归,不能像改 Prompt 那样随手调。被追问「badcase 怎么优化」时,可以按这个分层回答:先判断是模型能力问题、Prompt 表述问题,还是流程/工具契约问题——只有第二类才该改 Prompt,其余两类改 Prompt 是治不好的。
慢 SQL 排查:先定位再归因,别一上来就加索引
拿到一条慢 SQL,顺序是:① 确认口径——它慢在数据库执行本身,还是被锁/被排队/被网络拖慢;② 拿执行计划(MySQL 的 EXPLAIN / EXPLAIN ANALYZE,PG 的 EXPLAIN (ANALYZE, BUFFERS)),看是走了全表扫描、索引选择性差、还是回表次数太多;③ 看实际数据量与统计信息,很多时候是优化器估错行数选了坏计划,ANALYZE TABLE 或改写让估算更准就能见效;④ 看是不是「查询本身没问题、但被高频调用」——这类要加缓存或改批量;⑤ 看锁与并发,长事务、行锁等待、间隙锁都会让一条本来不慢的语句堵住。
除了加索引,常见手段还有:只查需要的列、避免 SELECT * 带来的大字段 IO;把大 OFFSET 分页改成基于游标(WHERE id > last_id LIMIT n);避免在索引列上做函数/隐式类型转换(会让索引失效);大表拆冷热、按时间分区做分区裁剪;把复杂聚合预计算到宽表/物化视图;用覆盖索引消掉回表;把一次大事务拆小降低锁持有时间。前提都是先有慢查询日志、performance_schema、APM 这类观测数据,没有观测就只能猜。
线程池参数怎么设计:按任务类型倒推,不背公式
线程池的核心参数是核心线程数、最大线程数、队列容量、空闲存活时间和拒绝策略,设计的起点是任务是 CPU 密集型还是 IO 密集型。CPU 密集型把线程数控制在核心数附近(多了只会增加上下文切换);IO 密集型可以放大(常见的经验值是核心数的若干倍,但更靠谱的做法是先看任务的等待比,再用压测找拐点)。队列容量和最大线程数是联动的:队列无限大时最大线程数永远不会被触发,请求只会在队列里堆积、延迟一路涨,最后超时——所以队列必须有界,并且要选择有界队列 + 明确的拒绝策略(快速失败、调用方降级、或落盘重试),而不是让请求无声无息地排队。
参数之外还有几件必须做的事:线程工厂里给线程起有意义的名字(排查时能一眼看出是哪个池)、用自定义的拒绝策略记录被拒的任务量、把队列长度、活跃线程数、拒绝数、任务耗时打进监控,让「池子满了」这件事在用户投诉之前就被发现。至于「线程池的问题如何排查」,常见的几类现象和原因是:任务堆积而 CPU 不高 → 下游依赖变慢导致线程都被阻塞;死锁或互相等待 → 看线程栈(jstack)找持锁关系;线程池打满触发拒绝 → 先限流保护下游,再谈扩容;内存持续上涨 → 队列无界导致任务对象积压。上线前的稳定性保障可以顺着这条线讲:容量评估与压测、超时与重试(带退避和上限)、熔断降级、限流、依赖隔离(不同下游用不同池,避免一个慢依赖拖垮全部)、灰度发布与可回滚、以及关键指标告警与值班预案。