LangChain+LangGraph+LangSmith 面试题 - 2026-06-15 00:12
【面试题】1. LangChain Agent和LangGraph都能实现"智能决策",什么时候用Agent、什么时候用LangGraph?底层实现差异在哪?
【标准答案】选型口诀:简单路由用Agent,复杂编排用LangGraph。
LangChain Agent 本质是"LLM反复调用Tools直到认为任务完成",内部是一个while True循环:LLM决策→执行Tool→再决策→直到返回Final Answer。优点是开箱即用,适合工具数量少(≤5个)、逻辑简单的场景。
LangGraph 把决策流程变成了显式有向图,每一步是明确的Node,路由靠条件边(不是让LLM自己瞎猜下一步)。适合:多步骤编排、需要人工介入、有条件分支/循环、高可靠性要求的场景。
底层差异:Agent靠LLM的ReAct prompt自己决定下一步,不可控;LangGraph把"下一步去哪"写死在条件边里,可控性强得多。实际项目通常这样:小工具用Agent快速出活,核心业务流(风控审核、工单流转)用LangGraph精细控制每一步。
【面试题】2. LangGraph的持久化方案有几种?生产环境怎么选?
【标准答案】LangGraph的Checkpointer持久化,官方有三种方案:
- MemorySaver:进程内内存存储,重启就丢。只适合本地测试。
- SqliteSaver:单机SQLite文件,适合Demo和小规模MVP,不支持并发写。
- PostgresSaver / AsyncPostgresSaver:生产主力,支持事务、并发、水平扩展,数据可靠。
生产选型:直接上PostgresSaver。理由:
- 多LangGraph服务实例共享同一个状态库,支持水平扩展
- 支持
get_state_history()回溯全部执行记录,对排查问题极有用 - 配合LangSmith Trace,能精确定位"哪个用户的哪次图的哪一步出了问题"
注意数据量大时要给 checkpoint_ids 加索引,定期清理历史checkpoint(保留最近100次就够了)。
【面试题】3. LangSmith的Prompt Hub(提示词管理)怎么用?实际项目中如何做Prompt的版本管理和A/B测试?
【标准答案】Prompt Hub = 统一的提示词中心仓库,告别"Prompt写死在代码里"。
用法:通过 langsmith pull 把Prompt拉到本地,代码里用 from langsmith import Client 的 pull_prompt(hub_owner/prompt_name) 动态拉取,改了Prompt无需重新部署。
版本管理:每次修改Prompt自动生成新版本号(v1, v2, v3...),代码里可指定 version=2 锁定版本,也可以不指定用latest。
A/B测试实战:在LangSmith创建两个版本的Prompt(v1:标准格式,v2:加了Chain-of-Thought引导),用同一份Dataset分别跑Evaluation,对比correctness和latency_p99两个指标。数据说话,选效果好的版本推给生产。整个过程不需要改一行业务代码。
【面试题】4. LangGraph如何实现流式输出(Token-by-Token Streaming)?和LangChain的stream()有什么区别?
【标准答案】LangChain的stream() 是Runnable级别的,只输出LLM的token流,Chain的中间结果看不到,像黑盒。
LangGraph的流式输出 更强大,支持三种模式:
stream_mode="values":每次状态变更都输出(能看到每一步发生了什么)stream_mode="updates":只输出有变化的节点结果(少传数据,客户端好解析)stream_mode="custom"+StreamWriter:Node里手动对流写入指定内容
推荐做法:生产环境用 stream_mode="updates",在LLM Node里用 .astream_events() 把token逐个yield出去;前端实时展示打字机效果的同时,后端在并行处理后续节点。
关键点:单个Node内部用 astream 做token级流式输出,整个图层面用 stream_mode 做状态级流式输出,两者不冲突。
【面试题】5. 线上LangChain应用产生了明显幻觉(Hallucination),如何在LangSmith里定位根因并量化衡量?
【标准答案】定位根因,两招并行:
第一招:在LangSmith Trace里找到那个产生幻觉的Trace,展开LLM Call的Span,对比Input(用户问题+检索到的上下文)和Output(模型回答)。常见原因:
- 检索到的上下文不包含答案(知识库没覆盖),LLM强行编造 → 优化知识库
- 检索到的上下文被截断了(Token超限),LLM没看到关键信息 → 优化Chunk策略
- Prompt里没给"不知道就说不知道"的约束 → 加Prompt约束
第二招:跑自动化评估,用LangSmith的 hallucination_evaluator(基于NLI模型判断回答是否基于文档),批量跑线上日志或Dataset,算一个幻觉率基线。
量化衡量:把faithfulness_score和answer_relevancy_score作为核心指标,设阈值(比如faithfulness<0.8告警),每次发版对比这两个分数的漂移情况,追责到具体的检索/模型变化。
【面试题】6. LangGraph里如果某个Node抛异常了,怎么优雅地重试和容错?而不是整个图崩掉。
【标准答案】三层防护:
第一层:Node内部用try-except兜住,返回结构化错误状态:
def fragile_node(state: AgentState) -> AgentState:
try:
result = some_external_api(state.query)
return {"status": "ok", "data": result}
except Exception as e:
return {"status": "error", "error_msg": str(e), "retry_count": state.retry_count + 1}第二层:条件边根据retry_count路由:
retry_count < 3→ 返回"重试节点"(不污染主逻辑)retry_count >= 3→ 路由到"降级节点"(返回友好错误消息)- 重试之间用
time.sleep(2**retry_count)做指数退避
第三层:图级别设置 checkpointer,如果整个图崩了,可以从上次checkpoint恢复继续跑(而不是从头开始)。LangSmith Trace会记录每次重试的完整链路,排查问题一目了然。
核心哲学:不让异常传播到图级别,而是把异常当作一种"状态"来管理。
【面试题】7. LangChain的Callback机制在生产中怎么用?有没有踩过什么坑?
【标准答案】核心用途:打日志、埋点监控、Token统计、告警。
推荐用LangChain官方提供的回调:
ConsoleCallbackHandler(本地调试用,上线一定关掉)StdOutCallbackHandler(同上)- 自定义
BaseCallbackHandler:重写on_llm_start/end、on_chain_start/end、on_tool_start/end
生产实践:自定义一个 MetricsCallbackHandler,在 on_llm_end 里记录Token用量(response.llm_output.get("token_usage")),在 on_tool_end 里记录工具响应时间,统一推到Prometheus/阿里云监控。这样Grafana看板实时看到"每小时的Token消耗趋势"和"工具调用成功率"。
踩过的坑:
- 同步回调阻塞异步:异步代码里别用同步回调,用
AsyncCallbackHandler - 回调参数泄露敏感信息:Trace里可能包含用户手机号等PII,上层要做好脱敏
- 回调异常吃掉主流程异常:回调里自己抛异常会吞掉Chain的原始异常,一定要try-except兜底
【面试题】8. LangGraph的子图(Subgraph)是什么?什么场景下一定要用?怎么组织?
【标准答案】子图 = 把一个复杂图的局部逻辑抽成一个独立的小图,嵌入大图作为"一个Node"。
必须用的场景:
- 子流程复用:比如"用户信息核验"这个流程,在"下单"、"退款"、"改密"三个大图里都要用 → 抽成Subgraph,三处统一引用
- 逻辑分层:主图只关心"步骤之间的路由",不关心某个步骤内部的复杂逻辑 → 内部细节扔到Subgraph里
- 跨团队协作:A团队负责"风控评估"子图,B团队负责"客服对话"子图,各管各的,主图由C团队拼装
组织方式:
auth_subgraph = StateGraph(AuthState)
auth_subgraph.add_node("verify_identity", verify_identity)
auth_subgraph.add_node("check_risk", check_risk)
auth_subgraph.add_edge("verify_identity", "check_risk")
auth_subgraph.set_entry_point("verify_identity")
auth_subgraph.set_finish_point("check_risk")
# 主图里引用子图
main_graph.add_node("authentication", auth_subgraph.compile())关键设计:父图和子图的State类型可以不同,但要约定好Input/Output接口。就像微服务之间通过API通信一样。
【面试题】9. 做一套RAG系统,用LangGraph怎么编排一个"带自我反思"的检索流程?(如果检索结果不理想,能自动重检)
【标准答案】核心思路:让LLM自己做"考官",判断检索结果够不够好,不够就自动换策略重检。
图结构设计:
用户提问 → 检索器(初始检索) → 相关性判断(critique) → 如果不够 → 策略切换(改查询/换索引/扩Top-K)
↓ 够了
回复生成 → END关键实现:
- 初始检索:用标准向量检索Top-5
- Critique Node:用LLM判断"检索到的文档能否回答问题"(不是让LLM回答,是让它评分),输出
{"pass": True/False, "reason": "..."} - 策略切换(条件边三个分支):
- 不通过且
retry<=1:Rewrite query换角度查 - 不通过且
retry=2:用MutiQuery扩召回范围 - 不通过且
retry>=3:直接返回"知识库没有相关信息"(防死循环)
- 不通过且
- 回复生成:用最终的检索结果+Critique的reason一起喂给LLM,让LLM知道当前检索的置信度
这样设计后,RAG系统的首次回答准确率能从70%提升到90%+(测试数据),而且LangSmith Trace里能看到"这次回答经过了N次反思",对排查问题极有帮助。
【面试题】10. LangSmith线上排查场景:生产环境的Trace日志里,突然发现所有LLM调用的Latency暴涨10倍,你怎么通过LangSmith快速定位?
【标准答案】标准排查流程,5分钟定位:
第1步:打开LangSmith的Dashboard,查看LLM Latency时序图,确认故障开始时间点。
第2步:按时间点筛选该时段的所有Trace,按Latency降序排列,点开最慢的那几条。
第3步:看Timeline视图,检查"哪个Span变慢了"。通常两种情况:
- 模型层慢:所有LLM Call都慢 → 可能是模型服务端问题(API限流、模型负载高、网络抖动)
- 查看Trace的
model_id,看是不是QPS打满了;切到同时间段的Model Usage面板看有无429错误
- 查看Trace的
- 应用层慢:只有一种Chain变慢 → 可能是Prompt变长了(Token输入量暴涨)或Tool调用卡住
第4步:对比突然变慢和正常时段的Trace:看Input Token量是否有变化(比如某次发版改了Prompt或增加了Memory保留轮数),看Tool调用是否有超时(例如检索服务响应变慢)。
第5步:定位到根因后,用LangSmith的Compare Experiments功能,在测试环境复现问题,验证修复方案后再上线。
真实案例:我们当时遇到的是某个配置项改了Top-K从5变成50,导致每次RAG的输入Token涨了10倍,Latency自然暴涨。LangSmith的Input Token用量分析一眼就看到了。