框架化开发:Python 三件套
前三篇,你一直在「手搓」:自己拼 JSON 请求体、自己写 while 工具循环、自己管消息列表和记忆。这些代码有个共同点——它们离「AI 能力」很远,离「胶水」很近。 你花大量时间在拼协议、拼字段、拼循环上,而不是在业务逻辑上。
这一篇回答一个核心问题:怎么用框架把这些胶水代码收敛掉,让你只写业务逻辑。 主角是 Python 生态的「三件套」——LangChain、LangGraph、LangSmith/Langfuse。读完你会发现,框架的价值不在「更强的 API」,而在「更少的胶水」,以及一个更清晰的心智模型:把 Agent 画成一张图。
1. 从「手搓」到「声明式」
1.1 你前三篇写的,本质是「AI 的底层」
回顾一下前面手搓的代码,它们都在干什么:
- 02 篇:手拼
messages数组、手拼鉴权头、手发 HTTP 请求; - 03 篇:手动做 Embedding、手动查库、手动拼「检索结果 + 问题」的 Prompt;
- 04 篇:手写
while循环,反复「调用模型 → 解析工具调用 → 执行工具 → 喂回结果」。
这些代码跑得通,但每一行都在重复解决「基础设施」问题,而不是「你的业务」问题。 框架要做的,就是把这层基础设施收起来。
1.2 一个对照:同一件事,手搓 vs 框架
拿「带记忆的智能客服」举例,看看框架把手搓省掉了多少:
| 能力 | 手搓 | 框架 |
|---|---|---|
| 会话记忆 | 自己维护消息列表、手动裁剪、防溢出 | 一个 Memory 组件加上 |
| RAG 检索 | 手动 Embedding → 查库 → 拼 Prompt | 一个 Retriever 组件加上 |
| 工具调用 | 手写 while 循环 + 手解析 function call | 一个 @tool 装饰器标好 |
| 模型切换 | 改 URL + 改请求体 + 改字段名 | 改一行配置 |
这就是框架的价值:把那堆「零业务价值、高出错率、跨项目零复用」的胶水代码统一收起来,你只写业务。
这里必须点破一个关键转变——从「命令式」到「声明式」。手搓时代,你要一步步告诉程序「先做什么、再做什么」;框架时代,你声明「我要什么能力」,框架替你编排「怎么做」。这个转变,是理解框架的前提,否则你只会觉得「框架就是把我的代码搬进去重写了一遍」。
2. 为什么需要框架:三个痛点
框架不是凭空造出来的概念,它是三个真实痛点的解药:
痛点一:重复造胶水。 每个项目都在拼 messages、拼鉴权头、手写工具循环。这些代码毫无业务价值,却占了大量工期,还特别容易出错(字段名写错、漏拼某个 role)。
痛点二:多模型切换难。 各厂商的协议细节不完全一致——有的用 messages 数组、有的字段名不同、有的 function call 格式不同。手工切模型,要改一堆地方,还容易踩坑。
痛点三:RAG / Tool / Memory 各自为战。 三种能力的代码风格完全不同,散落在各个文件里,维护成本高、难复用、难组合。
框架用一个统一抽象,把这三类东西「变简单、可组合」。但这里有个必须先破除的误解,直接决定你能不能用好框架:
Python 生态的三个框架,分工是不同的,不是「一个套一个」,更不是「选一个就行」。 它们各管一层,合起来才是一个完整的 Agent 开发栈。
3. 三件套各管什么(本篇的关键心智模型)
先搞清楚三个框架各自的分工。这是本篇最重要的心智模型,记牢它,后面所有内容都围绕它展开:
| 框架 | 定位 | 一句话 |
|---|---|---|
| LangChain | 组件抽象层 | 把模型、工具、向量库包装成能组合的「积木」 |
| LangGraph | 编排运行时 | 把积木按「图」的方式连起来,管状态、管循环、管持久化 |
| LangSmith / Langfuse | 观测 + 评估 | 看清 Agent 每一步在干什么、量化好坏 |
3.1 为什么是「各管一层」,不是「一个套一个」
这是最容易搞混的地方,值得单独讲透。
LangChain 给你「积木」,但它自己跑不了一个复杂 Agent。 LangChain 的组件(ChatOpenAI、@tool、Retriever)是离散的、无状态的——它告诉你「有哪些零件」,但没告诉你「这些零件怎么连起来跑、状态放哪、循环怎么控」。
真正让 Agent「可控地跑起来」的,是 LangGraph。 LangGraph 把零件画成一张「图」:节点(node)是执行单元,边(edge)是流转关系。它负责状态管理、循环控制、条件路由、断点续跑这些「运行时」的事——而这些,恰恰是一个复杂 Agent 最难、最容易写崩的部分。
LangSmith / Langfuse 则站在「图」外面,盯着它别跑偏。 它不参与执行,只负责记录和评估(这正是 09 篇讲的那套 Trace/Metric),帮你事后看清「这张图跑得对不对」。
所以正确的一句话是:LangChain 给积木,LangGraph 搭结构并让它跑起来,LangSmith/Langfuse 盯着它跑得怎么样。 三者不是竞争关系,是分层协作。
一个高频误区:很多人以为「用 LangChain 就是在搭 Agent」。其实 LangChain 只是给你组件,真正让 Agent「可控地跑起来」的是 LangGraph。做复杂 Agent,你的核心工作,绝大部分花在 LangGraph 的「图」上,而不是 LangChain 的组件上。
4. 最小可运行骨架:一张「图」
光讲概念太虚。用 LangChain 的组件 + LangGraph 的编排,搭一个带工具的 ReAct Agent。这段代码的重点不在语法,而在**「图」这个心智模型**:
# 1. LangChain:定义模型 + 工具(组件层,离散的"积木")
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""用户询问气温、天气、冷暖时调用。"""
return f"{city} 当前 26°C,多云"
llm = ChatOpenAI(model="deepseek-chat").bind_tools([get_weather])
# 2. LangGraph:把"思考 → 调工具 → 再思考"画成一张图(编排层)
from langgraph.graph import StateGraph, MessagesState, END
def call_model(state: MessagesState):
return {"messages": [llm.invoke(state["messages"])]}
def call_tools(state: MessagesState):
last = state["messages"][-1]
results = []
for tc in last.tool_calls:
results.append(get_weather.invoke(tc["args"]))
return {"messages": results}
def should_continue(state: MessagesState):
# 条件路由:最后一条还有工具调用 → 去执行工具;否则结束
return "tools" if state["messages"][-1].tool_calls else END
graph = StateGraph(MessagesState)
graph.add_node("agent", call_model) # 节点:让模型思考
graph.add_node("tools", call_tools) # 节点:执行工具
graph.add_edge("agent", "tools") # 思考完 → 去执行
graph.add_conditional_edges("agent", should_continue) # 判断:够了吗?
graph.set_entry_point("agent")
app = graph.compile()
# 3. 跑起来
result = app.invoke({"messages": [("user", "深圳今天几度?")]})
print(result["messages"][-1].content)4.1 这段代码里,最该看懂的三个点
点一:状态(State)是显式的。 MessagesState 是一个明确的数据结构,所有节点读写同一个 state。这解决了手搓时代「状态散落在各处的局部变量里」的问题——现在状态是图的一部分,可见、可持久化、可断点续跑(这正是 07 篇「状态持久化」的基础)。
点二:控制流是显式的「图」。 Agent 被显式画成「节点 + 边」:思考节点 → 工具节点 → 再回思考节点。每一步该干嘛、下一步去哪、什么时候停,都是可见的、可控的——而不是藏在一个黑盒 while 循环里。
点三:条件路由写在你手里。 should_continue 这一小段,就是「什么时候该停」的判断逻辑。你能在这里插入任何自定义规则:「最多走 5 步」「某工具调完必须人工确认」「检测到循环就强制停」——这就是后面「护栏」「人工把关」的挂载点。
这三个点合起来,就是框架相对手搓的本质优势:它把 Agent 的「状态、控制流、停止条件」从隐式变成了显式,于是你能在任意节点插入检查、记录、人工确认、断点续跑。这才是框架真正的价值,而不是「少写了几行胶水」。
5. 怎么用得高效(本篇命门:工程效率)
框架最大的价值是「少写代码 + 少出错」,但要用对。四条落地原则:
一、别为了用框架而用框架。 只调一次模型的脚本,裸调反而更轻(02 篇那 10 行 httpx 就够了)。框架的价值在「多能力组合」(RAG + Tool + Memory 同时用)时才会凸显。判断标准:如果你的逻辑一句话能说清、只调一次模型,就别上框架。
二、工具用装饰器声明,别手拼 Schema。 @tool 装饰器自动把你的函数签名 + docstring 转成模型的工具描述。手拼 JSON Schema 又容易错又难维护——让框架代劳。
三、模型路由用配置切,业务代码不动。 在配置层区分「简单任务用便宜模型、复杂任务用旗舰模型」,而不是在业务代码里写死 if-else。这样换模型、调路由,只改配置,不碰逻辑。
四、善用框架的「可观测」红利。 用 LangGraph 搭的 Agent,天然能接 LangSmith/Langfuse 的自动埋点——你上一篇(或下一篇 09)那套手写 Tracer 的活,框架直接帮你干了。这是「用框架」的隐藏收益:观测成本大幅下降。
6. 什么时候别用框架
框架不是银弹。明明白白地列出「别用」的场景,避免变成「框架信徒」:
| 场景 | 建议 | 原因 |
|---|---|---|
| 只调一次 LLM 的脚本 | 别用,裸 HTTP 更轻 | 框架有抽象层开销,反而增负 |
| 需要深度魔改请求体、极致省 Token | 别用 | 抽象层让你碰不到底层细节 |
| 团队想完全掌控底层、不信任黑盒 | 慎用 | 抽象层升级可能带来 breaking change |
| 多能力组合(RAG+Tool+Memory)、要切换模型、要断点续跑 | 强烈推荐 | 框架优势正好覆盖这些场景 |
核心一句话:框架解决的是「胶水」和「编排复杂度」,不是「性能」和「极致控制」。 你的问题正好是前者,就用框架;是后者,就别硬套。
7. 记住这一句
框架不是「更强的 API」,是「更少的胶水」+「更清晰的图」。Python 三件套各管一层:LangChain 给积木、LangGraph 搭结构(让状态/控制流/停止条件都显式化)、LangSmith 盯运行。做复杂 Agent,核心功夫在 LangGraph 的「图」上——因为它让每一步都可控、可观察、可续跑。
下一篇预告:工具怎么写、框架怎么用都清楚了。但「工具」这个生态本身还各搞一套——每家服务的接口、鉴权、数据格式都不同。怎么让工具像 USB-C 一样即插即用?这就到了 06 · MCP 协议与工具生态。