Skip to content

LangChain+LangGraph 面试题 - 2026-06-14 23:52 ​

【面试题】1. LangChain 中 Prompt Template 的 template 和 partial_variables 分别解决什么问题?实际项目中你怎么用 partial? ​

【标准答案】 template 定义变量槽位,比如 {question} 和 {context},是 Prompt 的骨架。partial_variables 是预填充部分变量,生成一个"半成品" Prompt,剩下的变量在运行时才填。

典型用法:system prompt 里有固定上下文(比如当前日期、用户角色),用 partial 先填上,question 留给用户输入时再注入。好处是链式调用时不用每次手动传那些固定值,代码更干净。

python
prompt = ChatPromptTemplate.from_template(
    "你是{role},今天是{date}。回答:{question}"
).partial(role="客服专员", date=get_today())
# 调用时只需传 question

面试加分点:提到 partial 适合环境变量、动态配置注入,避免每次 invoke 都传重复参数。


【面试题】2. LangGraph 的 StateGraph 和 MessageGraph 你怎么选?它们本质区别在哪? ​

【标准答案】 StateGraph 是自定义状态,State 是你定义的 TypedDict,灵活度高,适合复杂业务(订单状态、审批流程、多字段协同)。

MessageGraph 是 StateGraph 的特化版本,State 只维护 messages 列表,本质就是 Annotated[list, add_messages]。适合纯对话型 Agent,省去自己定义 State 的步骤。

选择逻辑:

  • 单轮或多轮对话、不需要额外状态字段 → MessageGraph,简洁
  • 需要跟踪步骤计数、中间结果、分支标记等业务字段 → StateGraph,自定义
  • 两者可以嵌套:StateGraph 内部某个节点跑一个 MessageGraph 子图处理对话细节

面试关键点:MessageGraph 底层就是 StateGraph,只是帮你省了定义步骤。理解了这层关系,就知道什么时候该"升级"到 StateGraph。


【面试题】3. LangChain 的 Output Parser 有哪些常见类型?如果 LLM 返回格式不稳定,你怎么兜底? ​

【标准答案】 常见三种:

  • StrOutputParser —— 直接拿文本,最简单
  • JsonOutputParser —— 期望 JSON,自动解析+校验
  • PydanticOutputParser —— 期望 JSON 但带 Pydantic schema 校验,字段类型、必填项全控

兜底三板斧:

  1. RetryWithErrorReparser:解析失败时把错误信息喂回 LLM,让它修正输出,最多重试 3 次
  2. OutputFixingParser:底层就是 Retry 逻辑,用另一个 LLM 修格式,比手工写 retry 更省事
  3. 降级策略:Pydantic 解析失败 → 降级到 JsonOutputParser → 再失败降级到 StrOutputParser,至少不挂

工程实践:Prompt 里必须加格式示例(few-shot),Parser 只兜底不治本。格式稳定性 90% 来自 Prompt 设计,10% 来自 Parser 重试。


【面试题】4. LangGraph 中如何实现子图(Subgraph)?子图和主图之间状态怎么传递? ​

【标准答案】 子图就是把一个完整的 StateGraph 作为主图的一个 Node 使用。定义子图的 State、Node、Edge,然后在主图里 add_node("sub_task", sub_graph) 直接注册。

状态传递关键点:

  • 子图 State 可以和主图 State 不同,通过 key mapping 对齐
  • 主图传入子图时,用 add_node("sub_task", sub_graph, input_map={"main_key": "sub_key"}) 映射字段
  • 子图输出回到主图时,同样用 output mapping 把子图结果写回主图 State

典型场景:客服主图 → 投诉子图(有独立的 step、complaint_type 等字段),子图跑完只把 resolution 结果写回主图。

面试加分:提到子图让团队分工更清晰,不同人维护不同子图,互不干扰,比一个巨型图好维护得多。


【面试题】5. LangChain 的 Callback System 是干什么用的?生产环境里你最常用它做哪两件事? ​

【标准答案】 Callback System 是 LangChain 的全局事件钩子,所有 Runnable 执行时都会触发回调事件(on_llm_start、on_tool_end、on_chain_error 等)。

生产环境最常用的两件事:

1. 日志追踪 用 CallbackHandler 记录每一步的输入输出、耗时、token 数,写入日志系统。排查问题时直接看链路追踪,不用翻代码。

2. 流式输出StreamingStdOutCallbackHandler 或自定义 Handler,把 LLM 的 token 逐个推给前端。用户体感从"等 5 秒看一坨"变成"边生成边看到"。

面试加分:提到 LangSmith 平台本质就是 Callback 的可视化上层,生产环境直接对接 LangSmith 做全链路可观测性,比自建 Handler 更省力。


【面试题】6. 场景题:你需要做一个"智能审批流"——员工提交报销单 → LLM 自动初审 → 不通过的退回修改 → 通过的进财务复核 → 超金额的需经理确认。用 LangGraph 怎么设计? ​

【标准答案】 四节点 + 三条件分支:

节点设计:

  1. auto_review —— LLM 审核报销合理性(金额、类别、发票完整度)
  2. finance_check —— 财务规则校验(发票号查重、预算匹配)
  3. manager_approve —— 人工确认,用 interrupt 断点暂停等经理操作
  4. notify_result —— 最终结果通知

条件分支:

python
# auto_review 后:
add_conditional_edges("auto_review", route_review, {
    "reject": "notify_result",       # 退回,直接通知
    "pass": "finance_check",         # 初审通过,进财务
})

# finance_check 后:
add_conditional_edges("finance_check", route_finance, {
    "pass": "notify_result",         # 财务通过,直接通知
    "over_limit": "manager_approve", # 超金额,等经理
    "reject": "notify_result",       # 财务驳回
})

State 里存 claim_data、review_result、finance_result、step_count。manager_approve 用 interrupt_before 断点,经理在前端点击确认后 update_state + resume。

面试亮点:提到断点续跑是审批流的核心能力,纯自动化会出风险,人机混合才是正解。


【面试题】7. LangGraph 的 Checkpointer 有哪些实现?你在生产环境怎么选?选错了会有什么后果? ​

【标准答案】 四种主要实现:

  • MemorySaver —— 内存存储,开发调试用,进程一挂状态全丢
  • SqliteSaver —— 本地文件级持久化,单机小项目够用
  • PostgresSaver —— 生产首选,支持并发、事务、高可用
  • RedisSaver —— 高频读写场景快,但要注意持久化配置(默认内存,重启丢数据)

选型逻辑:

  • 本地开发 → MemorySaver,快
  • 单机部署/低并发 → SqliteSaver
  • 生产环境/多实例 → PostgresSaver,稳定可靠
  • 需要极低延迟的实时场景 → RedisSaver(必须开启 AOF 持久化)

选错后果:

  • 用 MemorySaver 上线 → 服务重启后用户长流程全部丢失,投诉炸裂
  • 用 SqliteSaver 高并发 → 写锁竞争,性能崩溃
  • 用 RedisSaver 不开持久化 → 容器重启,状态全丢,跟 MemorySaver 一个下场

面试一句话:Checkpointer 不是可选件,是生产刚需,选 PostgresSaver 除非你有明确理由选别的。


【面试题】8. LangChain 中如何封装一个自定义 Tool?Tool 的 name、description、args_schema 三个属性分别影响什么? ​

【标准答案】 自定义 Tool 两种方式:

  • @tool 装饰器 —— 简单函数直接包装
  • 继承 BaseTool 类 —— 复杂逻辑、需要配置时用

三个属性的影响:

name:LLM 调用 Tool 时用的标识符。必须唯一、简短、语义清晰。比如 search_order 比 tool_1 好,LLM 看到 name 就知道该不该调。

description:告诉 LLM 这个 Tool 能干什么、什么时候该调。这是 LLM 决策的核心依据!写不好 LLM 就乱调或漏调。最佳写法:"搜索订单信息,输入订单号,返回订单状态和详情"。

args_schema:Pydantic 定义入参结构,LLM 据此生成调用参数。字段名、类型、description 都会影响 LLM 填参数的准确率。不定义 schema → LLM 猜参数格式 → 经常填错。

面试加分:强调 description 是最容易被忽略但影响最大的属性,花时间写好 description 比优化算法性价比高 10 倍。


【面试题】9. LangChain + LangGraph 做流式输出(Streaming)时,你遇到过哪些坑?怎么解决的? ​

【标准答案】 三个典型坑:

坑一:流式只到 LLM 输出,Tool 调用结果不流式 原因:Tool 返回是完整结果,不走 stream 通道。解决:在 Tool 内部用自定义 Callback 推送中间进度(比如"正在查询数据库…"),前端用 SSE 接收。

坑二:LangGraph stream 输出和 LCEL stream 行为不一致 原因:LangGraph 的 stream() 按 Node 粒度输出,每个 Node 完成才吐一块;LCEL 是 token 粒度。解决:用 astream_events() 统一事件流,拿到 on_chat_model_stream 事件做逐 token 推送。

坑三:中断点(interrupt)和 stream 冲突 原因:图在 interrupt 处暂停,stream 也暂停,前端不知道是"还在跑"还是"等你操作"。解决:stream 输出里加 __interrupt__ 标记事件,前端据此切换 UI 状态(从等待动画变成操作按钮)。

面试总结:流式不是加个 .stream() 就完事的,LangGraph 的粒度、Tool 的非流式、中断的语义,三件事要分别处理。


【面试题】10. 面试官追问:"多智能体协作在 LangGraph 里怎么实现?和 LangChain 的 Agent 嵌套有什么本质区别?" ​

【标准答案】 LangGraph 多智能体两种模式:

模式一:Supervisor 架构 一个中心调度节点(Supervisor),根据任务分发给不同专业 Agent(搜索 Agent、分析 Agent、写作 Agent),每个 Agent 是一个子图 Node,跑完结果回 Supervisor 汇总决策下一步。

模式二:Swarm 对等架构 没有中心节点,Agent 之间通过 State 中的 next_agent 字段互相交接,像一个接力赛。适合任务边界清晰的场景。

和 LangChain Agent 嵌套的区别:

  • LangChain 嵌套是串行调用 Agent.run(),控制流在外层 Agent 手里,内层 Agent 只是"工具",无法自主决策交接
  • LangGraph 多智能体每个 Agent 是独立图节点,有自己的 State 和分支逻辑,交接是通过条件边动态路由的,自主性更高

面试关键一句话:LangChain 多 Agent 是"老板指挥员工",LangGraph 多 Agent 是"团队成员自主交接",后者灵活度和可扩展性远高于前者。