1.3 选型方法:按可观测/持久化/生态/锁定度打分
1.1 给了四层地图,1.2 给了统一抽象。但真到项目里,你还是要面对那个原始问题:「我该用哪个框架」。常见的回答是列一张特性表,然后靠感觉。
本节把这件事变成可计算的——四个维度、一组权重、一个能跑的脚本,让选型结论可复现、可讨论、可调参。
本节把「选型」推进到可量化:定义可观测 / 持久化 / 生态 / 锁定度四维及其权重(附理由)与 1–5 分标准,给六个候选框架打分,用一个最小脚本算出加权总分与排序并真跑,再做权重重排的敏感度分析,最后给出「不同场景怎么调权重」的决策清单。
1.3.1 为什么是这四个维度
维度不是越多越好。维度一多,权重就成了玄学,结论反而不可信。经过卷二《AI Agent 工程实战》的落地检验,真正会决定「这个框架能不能上线、上线后痛不痛」的,是这四件事:
| 维度 | 含义 | 高分的表现 | 为什么它重要 |
|---|---|---|---|
| 可观测 observability | 能否看清每次模型/工具调用 | 原生 OTel / trace 导出 | 生产事故定位的唯一依据 |
| 持久化 durability | 崩溃/重启后能否续跑 | 检查点、可恢复、确定性重放 | 长任务能不能上线的硬门槛 |
| 生态 ecosystem | 集成、文档、人才、社区 | 工具多、示例多、招得到人 | 决定交付速度与维护成本 |
| 锁定度 lock-in | 迁移到别家的成本 | 抽象清晰、无私有格式 | 决定未来议价权(5 = 低锁定) |
为什么不是「性能」「上手难度」「代码量」这些常见维度?因为:
- 性能在 Agent 场景里几乎总是被模型延迟主导,框架自身的开销可忽略——区分度太低。
- 上手难度是短期成本,且与「生态」高度相关,重复计分。
- 代码量是过程指标,不是结果指标;卷二的工程经验告诉我们,少写的那几十行代码,往往在三个月后以「框架升级破坏兼容」的形式还回来。
四个维度覆盖了「上线前能不能用」「上线后痛不痛」「未来换不换得动」三条主线,且互相独立、不重复计分。
注意 锁定度是反向计分的:5 分表示「几乎不锁定、随便换」,1 分表示「深度绑定、换不动」。这样四个维度都是「分数越高越好」,加权求和才有统一方向——这是打分模型最容易搞错的地方,一旦方向反了,结论会完全颠倒。
权重及其理由(权重和为 1):
| 维度 | 权重 | 理由 |
|---|---|---|
| 可观测 | 0.30 | 生产环境里「不知道发生了什么」是最贵的故障,权重最高 |
| 持久化 | 0.25 | 长任务场景下这是 0 和 1 的区别,没有它连上线资格都没有 |
| 生态 | 0.25 | 直接换算成交付人天,和持久化同等重要 |
| 锁定度 | 0.20 | 重要但可延后——多数团队先上线再谈迁移,故略低 |
权重不是真理,是默认值。真正的方法论在 1.3.4:不同场景把权重调成不同形状。
1.3.2 1–5 分怎么定:评分标准
打分最容易「各打各的」,所以必须给锚点。每维 5 分对应「行业领先」,1 分对应「基本缺失」:
| 维度 | 1 分 | 3 分 | 5 分 |
|---|---|---|---|
| 可观测 | 无 trace,只能 print | 有日志,无结构化 trace | 原生 OTel,trace 可直接导出 |
| 持久化 | 进程挂了从头来 | 支持会话恢复 | 检查点 + 确定性重放 |
| 生态 | 几乎无集成 | 有主流模型适配 | 工具/示例/社区齐全,招得到人 |
| 锁定度 | 深度绑定、换不动 | 有抽象但仍有私有格式 | 纯标准接口,随时可换 |
有了锚点,打分就从「我觉得」变成「对照标准」。锚点越具体,不同人打出的分越接近,这正是选型会议最需要的东西。
实操建议:打分时每人独立打一遍再对表,分歧大的维度优先讨论——分歧点往往暴露了团队对「生产要求」的认知差异,比分数本身更有信息量。
1.3.3 六个候选的打分表
分数由本卷后续各章的真实体验给出(第 2 章 PydanticAI、第 3 章 Claude Agent SDK、第 4 章 OpenAI Agents SDK 与 smolagents、第 5 章 LangGraph 源码、第 6 章持久化)。这里是先验打分,后续章节会回头修正:
| 框架 | 层 | 可观测 | 持久化 | 生态 | 锁定度 | 说明 |
|---|---|---|---|---|---|---|
| minagent(自研) | 引擎 | 3 | 2 | 2 | 5 | 全可控、零锁定,但什么都得自己写 |
| pydantic-ai | SDK | 5 | 3 | 4 | 4 | OTel 原生、类型强,检查点需自建 |
| langgraph | 编排 | 4 | 5 | 5 | 2 | 检查点最强,但 Graph 抽象绑定较深 |
| claude-agent-sdk | SDK | 3 | 3 | 4 | 1 | 内置能力多,深度绑定 Claude 生态 |
| openai-agents | SDK | 5 | 2 | 4 | 3 | tracing 一流,持久化弱 |
| smolagents | SDK | 2 | 1 | 3 | 4 | 极简「代码即行动」,生产化最少 |
这张表本身就有信息量:没有全能冠军。langgraph 持久化/生态满分但锁定度高;claude-agent-sdk 锁定度只有 1;自研 minagent 锁定度满分却样样垫底。选型永远是取舍。
1.3.4 可运行的最小打分脚本
把上表变成代码。脚本的设计要点:权重与分数分离,改场景只动 WEIGHTS;排序函数与打印分离,方便复用。下面这段在本机真跑:
"""四维选型打分:可观测 / 持久化 / 生态 / 锁定度。每维 1–5 分,5 分最好。"""
WEIGHTS = {
"observability": 0.30, # 生产事故定位靠它,权重最高
"durability": 0.25, # 长任务/崩溃恢复,直接决定能不能上线
"ecosystem": 0.25, # 集成与人才供给,决定交付速度
"lock_in": 0.20, # 迁移成本,5 = 低锁定(易替换)
}
assert abs(sum(WEIGHTS.values()) - 1.0) < 1e-9
FRAMEWORKS = {
"minagent(自研)": {"observability": 3, "durability": 2, "ecosystem": 2, "lock_in": 5},
"pydantic-ai": {"observability": 5, "durability": 3, "ecosystem": 4, "lock_in": 4},
"langgraph": {"observability": 4, "durability": 5, "ecosystem": 5, "lock_in": 2},
"claude-agent-sdk": {"observability": 3, "durability": 3, "ecosystem": 4, "lock_in": 1},
"openai-agents": {"observability": 5, "durability": 2, "ecosystem": 4, "lock_in": 3},
"smolagents": {"observability": 2, "durability": 1, "ecosystem": 3, "lock_in": 4},
}
def total(row: dict) -> float:
return round(sum(WEIGHTS[k] * v for k, v in row.items()), 3)
def rank(frameworks: dict) -> list[tuple[float, str]]:
return sorted(((total(r), n) for n, r in frameworks.items()), reverse=True)
def main() -> None:
print(f"{'framework':18s} {'obs':>3s} {'dur':>3s} {'eco':>3s} {'lock':>4s} total")
for score, name in rank(FRAMEWORKS):
r = FRAMEWORKS[name]
print(f"{name:18s} {r['observability']:>3d} {r['durability']:>3d} "
f"{r['ecosystem']:>3d} {r['lock_in']:>4d} {score:>5.3f}")
print("rank:", [n for _, n in rank(FRAMEWORKS)])
if __name__ == "__main__":
main()
真跑输出(/tmp/agentbook/venv3/bin/python):
framework obs dur eco lock total
langgraph 4 5 5 2 4.100
pydantic-ai 5 3 4 4 4.050
openai-agents 5 2 4 3 3.600
minagent(自研) 3 2 2 5 2.900
claude-agent-sdk 3 3 4 1 2.850
smolagents 2 1 3 4 2.400
rank: ['langgraph', 'pydantic-ai', 'openai-agents', 'minagent(自研)', 'claude-agent-sdk', 'smolagents']
注意 assert 那一行:它守住了权重和必须为 1。这类不变量在打分脚本里比结果本身更重要——一旦有人手改权重忘了配平,脚本立刻报错,而不是悄悄算出一个错的排序。
1.3.5 敏感度分析:换个权重,冠军会变吗
默认权重下 langgraph 险胜 pydantic-ai(4.100 vs 4.050)。但结论稳不稳,要看它对权重的敏感度。把权重按场景重排,再跑一遍:
SCENARIOS = {
"默认": {"observability": 0.30, "durability": 0.25, "ecosystem": 0.25, "lock_in": 0.20},
"长任务": {"observability": 0.20, "durability": 0.50, "ecosystem": 0.15, "lock_in": 0.15},
"强合规": {"observability": 0.50, "durability": 0.15, "ecosystem": 0.20, "lock_in": 0.15},
"快速原型": {"observability": 0.15, "durability": 0.10, "ecosystem": 0.45, "lock_in": 0.30},
"抗锁定": {"observability": 0.20, "durability": 0.15, "ecosystem": 0.15, "lock_in": 0.50},
}
真跑输出:
默认 冠军=langgraph 4.100 前三=['langgraph', 'pydantic-ai', 'openai-agents']
长任务 冠军=langgraph 4.350 前三=['langgraph', 'pydantic-ai', 'openai-agents']
强合规 冠军=pydantic-ai 4.350 前三=['pydantic-ai', 'openai-agents', 'langgraph']
快速原型 冠军=pydantic-ai 4.050 前三=['pydantic-ai', 'langgraph', 'openai-agents']
抗锁定 冠军=pydantic-ai 4.050 前三=['pydantic-ai', 'minagent(自研)', 'openai-agents']
三条读得出来的结论:
- 「长任务」场景 langgraph 优势扩大(4.100 → 4.350),因为它是唯一持久化 5 分的——这正是第 6 章要展开的能力。
- 「强合规」场景冠军易主为 pydantic-ai,因为它可观测 5 分。冠军会变,说明选型必须先说清场景,再谈框架。
- 「抗锁定」冠军不是 minagent 而是 pydantic-ai——反直觉但有道理:minagent 虽然锁定度满分,但生态/可观测太弱,总分仍不及「锁定度 4 分、其余均衡」的 pydantic-ai。单项满分救不了全面平庸。
这三条如果只靠感觉,是得不出来的。这就是把选型变成计算的意义。
还值得留意「默认」与「长任务」两行的前三名完全相同——说明当候选之间的差距大于权重扰动带来的变化时,结论是稳健的。反之,「快速原型」一行里 langgraph 掉到第二,说明它的领先是场景依赖的,不是普适的。判断一个排序稳不稳,就看它在几组权重下是否守住名次。
1.3.6 打分之外:三条硬否决项
打分模型有个陷阱:它会把「绝对不能选」的选项算出个中等分数,让你误以为可以接受。所以打分之前,先用三条硬否决过滤:
| 硬否决项 | 判定 | 为什么是「一票否决」而非扣分 |
|---|---|---|
| 许可证不合规 | 与你的分发方式冲突 | 法律风险无法用分数补偿 |
| 关键能力缺失 | 场景必需项(如持久化)为 1 分 | 缺的不是「一点」,是「能不能用」 |
| 已停止维护 | 半年无提交、无安全更新 | 供应链风险,越用越危险 |
举例:某框架若许可证不允许商用,它哪怕四维全 5 分也不能选——分数再高也过不了法务。同理,一个「持久化 1 分」的框架,在小时级长任务场景里直接出局,无论它生态多好。硬否决先于打分:先筛掉不能用的,再给能用的排序。
这条顺序不能反。现实中很多选型事故,正是「总分排第一的选项,其实踩了硬否决」——因为打分表里没有「许可证」这一行,也没人先做否决检查。
补充一点:硬否决项本身也要随场景变。对闭源 SaaS,许可证是硬否决;对内部工具,它可能完全不相关。所以「否决清单」和「权重」一样,属于场景,不属于框架。
1.3.7 怎么用这张表做决策
默认权重下的结论是「langgraph ≈ pydantic-ai 领先」,但真正的用法是调权。把 WEIGHTS 改成你的场景,排序可能完全翻盘:
| 场景 | 该调高的维度 | 调整后倾向 |
|---|---|---|
| 快速原型 / Demo | 生态↑、锁定度↑ | pydantic-ai、openai-agents |
| 长任务(小时级) | 持久化↑↑ | langgraph(唯一 5 分) |
| 强合规 / 可审计 | 可观测↑↑ | pydantic-ai、openai-agents |
| 深度绑定某云 / 某模型 | 锁定度↓(不介意) | claude-agent-sdk |
| 教学 / 最小依赖 | 锁定度↑、生态↓ | smolagents、自研 minagent |
三个使用原则:
- 分数要来自实测,不是宣传。本节的分是后续章节真跑后的体验值,你换成自己的项目时,也应在小规模试跑后再填。
- 权重必须团队共识。权重之争其实是「我们最怕什么」之争,吵清楚权重比吵清楚分数更有价值。
- 不要追总分最高。第 4 名可能恰好是「锁定度最高」的那个——如果迁移能力对你是硬需求,它才是对的。
- 分层选型,不要选「一个」。1.1 的四层图提醒你:编排层选 langgraph、SDK 层选 pydantic-ai 是完全合理的组合,你不需要在同一个框架里找齐所有能力。本卷后续每一章都会回到这个组合视角。
这套方法替你做了什么:把「选型」从主观争论变成可复现的计算,让不同人用同一张表得出可比的结论。你让渡了什么:打分简化了真实世界的复杂性——有些框架的杀手级能力(比如某家的内置 subagent)无法被四个数字概括。打分是决策的起点,不是终点;第 11.3 节全景回顾时,我们会用真实项目数据回头检验这张表。
1.3.8 把结论固化:一份可复现的选型记录
打分的价值只有在被记录下来时才可持续。建议每次选型产出一段简短记录,格式如下:
选型记录 · <日期>
场景: 小时级研究任务,可中断可恢复
硬否决检查: 许可证✓ 关键能力(持久化≥3)✓ 维护中✓
权重: 可观测0.20 / 持久化0.50 / 生态0.15 / 锁定度0.15
候选与得分: langgraph 4.350 | pydantic-ai 4.050 | openai-agents 3.150
结论: 选 langgraph(编排层),SDK 层用 pydantic-ai 配合
被否理由: openai-agents 持久化 2 分,不满足关键能力门槛
复评时间: 3 个月后(若 langgraph 锁定度问题暴露则重评)
这份记录回答了三件事:为什么选、为什么没选别的、什么时候该重评。三个月后有人问「当初为什么用 langgraph」,翻出这段就够了;框架升级或场景变化时,「复评时间」会提醒你重跑脚本。
把它和 1.2 的统一抽象接起来,一个完整的选型闭环就成型了:打分选框架 → Protocol 包住它 → 记录留痕 → 场景变了重评。这也是本卷后续所有章节的元方法——每一章你都会看到「某框架替你做了什么」,而决定用不用它,回到这张四维表就够了。
最后提醒一句:这张表是活文档。第 2–6 章跑完后,某些分数一定会被修正(比如你可能会发现某框架的持久化比预想的更弱)。届时不要改历史记录,而是**追加一行「修订」**并注明依据——这样三个月后回看,你能看到的不只是结论,还有结论是怎么演化的。这比一张「看起来永远正确」的表有价值得多。
阅读导航:上一节:1.2 统一抽象:一个 Protocol 适配多家并写适配层 · 下一节:2.1 核心模型:Agent/RunContext/依赖注入重写问答路径 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。