LangChain+LangGraph 面试题 - 2026-06-14 23:49
【面试题】1. LangChain 中 Chain 和 LCEL(LangChain Expression Language)的区别是什么?什么时候该用 Chain 而不是 LCEL?
【标准答案】 简单说,Chain 是旧版写法,LCEL 是新版声明式语法。
Chain 是面向对象模式,定义类、重写方法,适合复杂定制;LCEL 是管道式,把 | 当作调用链串联。LCEL 有三大优势:内置流式输出、异步支持、并行执行自带,代码更简洁。
实际选择:快速原型用 LCEL,团队协作的复杂项目需要高度定制时才用 Chain。新项目默认 LCEL 就对了。
【面试题】2. LangGraph 中 add_edge 和 add_conditional_edges 的核心区别是什么?请举例说明conditional在什么场景下必须用?
【标准答案】 add_edge 是固定跳转,从节点 A 直接到节点 B,可控可预测。
add_conditional_edges 是动态路由,根据 state 内容决定下一步去哪。
必须用 conditional 的典型场景:
- 意图分类后分流:客服场景,识别到"投诉"走投诉链路,"查询"走查询链路
- LLM 返回的 action 判断:tool 调用返回结果后,判断是继续调用还是结束
- 循环上限控制:比如重试 3 次失败才走错误处理分支
核心区别就是:固定流程用 edge,动态决策用 conditional edges。
【面试题】3. 在 LangGraph 中实现多轮对话 Memory 记忆系统,你一般怎么设计状态(State)?
【标准答案】 State 设计三件套:
class AgentState(TypedDict):
messages: Annotated[list, add_messages] # 对话历史,自动增量
user_profile: dict # 用户画像,可跨会话
session_context: dict # 当前会话上下文关键点:add_messages 这个 Annotated 列表,底层是 MessagesToList,每次新消息 append 而不是覆盖,这样历史完整保留。检索时用滑动窗口或 token 上限截断。
跨会话的持久化用 Checkpointer,基于 SQLite 或 PostgreSQL,自动存图状态。下次用户来,load 快照直接恢复对话上下文,用户无感知。
【面试题】4. LangChain 的 Retriever 有哪些类型?在 RAG 场景下你如何选型?
【标准答案】 常见四类:
- VectorStoreRetriever —— 最常用,基于向量相似度查 top-k,秒级召回
- MultiQueryRetriever —— LLM 扩展 query,从多角度召回,解决表述差异问题
- ContextualCompressionRetriever —— 先查再压缩,过滤无关 context,节省 token
- EnsembleRetriever —— 混合召回,结合 BM25 稀疏检索 + 向量检索,取长补短
选型逻辑:
- 知识库规模大、语义相似重要 → VectorStoreRetriever + cosine 相似度
- 用户 query 表述随意 → MultiQueryRetriever 扩展
- context 经常超长 → 加 ContextualCompression
- 追求召回质量上限 → EnsembleRetriever
【面试题】5. 用 LangGraph 写一个 Agent 循环时,如何防止死循环?有哪些兜底机制?
【标准答案】 三层兜底:
第一层:循环次数限制
max_iterations = 5
def should_continue(state):
return "tool_call" if state["step"] < max_iterations else "end"通过 step 计数器控制,超过就强制退出。
第二层:重复检测 维护一个 seen_actions 集合,如果连续 3 次返回相同的 tool call,直接终止。可以用 add_messages 的 message 哈希来比对。
第三层:LLM 自我判断 在每次循环末尾,让 LLM 评估"当前结果是否已足够回答问题",返回置信度标识,低于阈值才继续。
工程上通常三层都上,再配一个超时兜底,稳如老狗。
【面试题】6. 场景题:让你从 0 搭建一个客服 Agent,处理售前咨询和售后投诉两类请求,你会如何设计 LangGraph 的图结构?
【标准答案】 三段式设计:
第一段:意图识别节点 单一 LLM 调用,返回结构化意图标签(pre_sale / after_sale / other)
第二段:条件分支
add_conditional_edges(
"intent_classifier",
lambda state: state["intent"].value,
{
"pre_sale": "pre_sale_flow",
"after_sale": "after_sale_flow",
"other": "handoff_to_human"
}
)第三段:各自子图
- pre_sale_flow:查产品库 → 匹配需求 → 推荐 → 转化
- after_sale_flow:查订单 → 诊断问题 → 退款/换货/工单
子图内部也用 conditional 处理分支。这样结构清晰,后续加新意图只要加分支,修改一个意图不影响另一个。
【面试题】7. LangChain 中 Tool 的返回值格式有什么讲究?为什么 Tool 返回值设计不好会导致 Agent 行为异常?
【标准答案】 Tool 返回值本质是给 LLM 看的文本,所以格式必须遵循两个原则:
原则一:结构化但简洁 返回 {"status": "success", "data": {...}} 比直接吐 dict 好。LLM 需要知道操作成功还是失败,也需要数据本身。
原则二:必须包含 action summary Tool 名 + 结果摘要,LLM 据此决定下一步。如果只返回原始数据,LLM 要自己理解,浪费 token 且容易误判。
常见坑:
- 返回 None 或空字符串 → LLM 不知道发生了什么,陷入困惑循环
- 返回超长原始输出 → 上下文爆炸,token 爆炸
- 不标记错误状态 → LLM 以为成功,继续用错误数据推理
最佳实践:所有 Tool 统一包装成 ToolMessage,带 status + summary + data 三个字段。
【面试题】8. LangGraph 的断点续跑(Breakpoint)是怎么实现的?生产环境中你用它解决什么问题?
【标准答案】 断点续跑靠 interrupts API 实现。原理是在某个节点前插入中断点,图执行到这里就暂停,状态持久化到存储(Redis/PostgreSQL)。外部可以检查状态、修改 state,甚至切换分支,然后手动 resume 继续执行。
生产场景三大用法:
- 人工审核节点:涉及金额、退款、敏感操作,先中断让客服人员确认后再继续
- 分支确认:LLM 分类不确定时,中断让用户二次确认意图
- 故障恢复:服务重启后,从最近的 checkpoint 恢复,不丢失长流程进度
实现上核心是 interrupts_before 或 interrupts_after,配合 state_graph.update_state() 可以中途改 state,非常灵活。
【面试题】9. 你在项目中遇到过 LangChain/LangGraph 的哪些坑?是如何解决的?
【标准答案】 三个高频坑:
坑一:Streaming 不生效 原因:用了 .invoke() 而不是 .stream(),或者 callback 没有正确传递。解决:确保用 stream() 配合 ConsoleCallbackHandler,或者在 LCEL 链里检查是否有 RunnableGenerator 节点。
坑二:多 Tool 调用时顺序执行慢 原因:默认串行。解决:用 RunnableParallel 或在 LangGraph 里把独立 Tool 用 node 并行执行,配合 asyncio.gather。
坑三:Memory 随对话无限增长 原因:messages 列表只增不减。解决:上线滑动窗口(保留最近 N 条)+ token 预算截断,或者定期 clear 历史。
核心原则:本地先跑通,再用 streaming 测试、压测内存占用,上线前必做边界 case 验证。
【面试题】10. 面试官追问:"如果让你优化一个慢速 RAG 链路,从 Query 进来到最终输出答案,你从哪些维度入手?"
【标准答案】 五个优化维度,逐层递进:
1. 召回层
- 换更快的 Embedding 模型(text-embedding-3-small)
- 改用 HNSW 索引代替 IVF,召回速度提升明显
- 加
reranker做二次排序,top-k 放大但最终精度高
2. Context 层
- 用 ContextualCompression,去掉无关 chunks
- 控制 chunk 大小(512~1024 tokens 最佳)
3. Prompt 层
- 精简 system prompt,去掉废话
- few-shot 示例控制在 3 个以内
4. LLM 推理层
- 开启 streaming,体感会快很多
- 用 vLLM / TGI 推理引擎,吞吐翻倍
5. 工程层
- 异步并行:召回和生成可以部分并行
- 缓存:热门 query 的 answer 缓存起来,命中率 30%+ 很常见
实际做法:先打日志找瓶颈在哪,瓶颈通常在召回或 LLM 推理,一个一个改,不要同时改。