Agent 框架赛道 — 核心技术难点
基于 trpc-agent-go / trpc-agent-python / LangChain / LangGraph / CrewAI / Agno 源码调研 调研时间:2026-06-22
难点总览
| # | 难点 | 涉及框架 | 严重度 |
|---|---|---|---|
| 1 | 状态管理与一致性 | LangGraph / tRPC-Agent-Go / Agno | ★★★★★ |
| 2 | Tool Calling 可靠性 | 全部六个 | ★★★★ |
| 3 | 多 Agent 协作控制 | tRPC-Agent-Go / CrewAI / LangGraph | ★★★★★ |
| 4 | 记忆系统设计 | tRPC-Agent-Go / Agno / LangGraph | ★★★★ |
| 5 | 可观测性与调试 | 全部六个 | ★★★ |
| 6 | 生产环境扩展性 | tRPC-Agent-Go / LangGraph | ★★★★★ |
| 7 | 规划与推理质量 | tRPC-Agent-Go / LangGraph / CrewAI | ★★★★ |
| 8 | Human-in-the-Loop | LangGraph / tRPC-Agent-Go | ★★★ |
1. 状态管理与一致性(★★★★★)
核心矛盾:Agent 需要在多步推理中维护全局状态(对话历史、工具结果、计划进度),但 LLM 本身是无状态的——每次调用都从头开始。框架必须在 LLM 的”失忆”本质之上人工构建状态一致性。
日常类比:一个每天失忆的侦探在破案。他每天醒来忘记昨天做了什么,只能靠墙上的便利贴(State)回忆进度。如果有人在他睡觉时偷偷移动了便利贴(并发写入),第二天他会做出错误判断。
真实案例
LangGraph 的 Channel 冲突:当两个并行节点同时向同一个 LastValue Channel 写入不同的值时,最终状态取决于哪个节点先完成——这是非确定性的。
class State(TypedDict):
answer: str # LastValue——只保留最后写入
# 两个并行节点同时写 answer
def node_a(state):
return {"answer": "A 的答案"}
def node_b(state):
return {"answer": "B 的答案"}
# 最终 state["answer"] 是 A 还是 B?取决于执行顺序——非确定性!
解决方案:用 BinaryOperatorAggregate + operator.add 做累积,但这要求开发者预判哪些字段会被并发写入——设计时的认知负担。
tRPC-Agent-Go 的分布式状态:当 Agent 部署在多个实例上,Runner 的 Invocation.State 是实例本地的。如果一个请求被负载均衡到不同实例,状态会丢失。
请求 1 → 实例 A → State = {step: 3, plan: "research"}
请求 2 → 实例 B → State = {} ← 丢失了!必须从 Session 后端重新加载
9 种 Session 后端的存在正是为了解决这个问题,但引入了新问题:Session 读写延迟、后端故障时的降级策略。
根本难点
状态管理的三难选择(类似 CAP 定理):
- 一致性(所有组件看到相同状态)
- 性能(状态读写不成为瓶颈)
- 简洁性(开发者不需要手动管理状态同步)
LangGraph 选择了一致性(Pregel 超步同步),代价是性能(不能跨超步流水线化)。tRPC-Agent 选择了性能(分布式部署),代价是需要额外的 Session 后端。Agno/CrewAI 选择了简洁性(状态就是对象字段),代价是一致性弱。
没有框架同时解决了三者。
2. Tool Calling 可靠性(★★★★)
核心矛盾:LLM 必须正确生成 tool call 的参数(JSON 格式、类型匹配、语义正确),但 LLM 本质是概率生成模型——它在”理解”和”瞎编”之间没有明确界限。
日常类比:你让一个从未用过计算器的孩子按计算器。他知道”要算加法”,但可能按错键(参数类型错)、按多了(多余参数)、或根本没按到正确的键(调错工具)。
真实案例
参数类型错误:LLM 经常把数字生成为字符串,或把列表生成为逗号分隔的字符串。
// LLM 生成的 tool call(错误)
{"name": "search", "arguments": {"limit": "10", "tags": "python, ai"}}
// 期望的格式
{"name": "search", "arguments": {"limit": 10, "tags": ["python", "ai"]}}
每个框架都需要做参数修复(coercion),但修复策略不一致——有的默默修复(Agno),有的报错重试(LangChain),有的让开发者自定义(tRPC-Agent)。
工具选择错误:当可用工具超过 15 个时,LLM 选错工具的概率显著上升。CrewAI 的 allow_delegation=True 允许 Agent 把任务委派给其他 Agent,但委派决策本身也依赖 LLM 判断——形成了”用不可靠的系统做可靠性决策”的悖论。
循环调用:Agent 反复调用同一工具但参数略有不同,期望得到不同结果(类似”精神错乱的定义”)。
Step 1: search("agent framework comparison") → 没找到想要的
Step 2: search("agent framework comparison 2026") → 还是不满意
Step 3: search("best agent framework comparison 2026") → 继续...
Step 4: search("top agent framework comparison 2026 latest") → 死循环
tRPC-Agent-Go 的 MaxSteps 限制了总步数,但这是粗粒度保护——它不能阻止 Agent 在限制内浪费所有步数。
根本难点
Tool Calling 可靠性问题的本质是 LLM 没有”执行语义”的概念。它生成的是”看起来像正确 JSON 的文本”,不是”经过类型检查的函数调用”。框架能做的只有:
- Schema 验证 + 自动重试(所有框架都做了)
- 工具描述优化(减少歧义)
- 工具数量控制(保持在 LLM 可靠处理的范围内)
- Few-shot 示例注入(教 LLM 正确格式)
但这些都是”缓解”而非”解决”。真正的解决需要 LLM 架构层面的突破(如 constrained decoding / structured output 原生支持)。
3. 多 Agent 协作控制(★★★★★)
核心矛盾:多 Agent 系统需要在”自主性”和”可控性”之间找平衡。Agent 太自主则行为不可预测,太受控则失去协作的价值(直接写脚本更好)。
日常类比:公司管理。完全自主(每个人自己决定做什么)→ 混乱;完全受控(每一步都由经理审批)→ 效率极低。好的管理是”明确目标 + 适度授权 + 关键节点汇报”。
真实案例
CrewAI Hierarchical 模式的 Manager 失控:Manager Agent 本身也是 LLM 驱动的——它可能做出错误的任务分配。
# Manager 的"决策"是 LLM 生成的——不可靠
crew = Crew(
agents=[researcher, writer, designer],
process=Process.hierarchical # Manager 自动分配任务
)
# 可能出现:Manager 把设计任务分给了 researcher
# 原因:LLM 对"谁擅长什么"的判断基于 Role/Backstory 的文本相似度
tRPC-Agent-Go Swarm 模式的死循环:Agent A 认为该转交给 B,B 处理后认为该转回 A——形成 ping-pong。
Agent A: "这个任务需要更多研究" → 转交 Agent B
Agent B: "研究完了,需要写成报告" → 转交 Agent A
Agent A: "报告需要更多数据支撑" → 转交 Agent B
... 无限循环
Coordinator 模式通过中心调度者避免了这个问题,但引入了单点瓶颈——所有任务都经过 Coordinator,Coordinator 本身的 LLM 调用成为延迟瓶颈。
LangGraph 子图嵌套的状态隔离:子图有自己的 State,父图无法直接观察子图内部状态。当子图执行出错时,父图只能看到”子图失败了”,不知道失败在哪一步。
# 父图只能看到子图的最终输出——内部执行过程是黑盒
parent_graph.add_node("sub_task", sub_graph.compile())
# 如果 sub_graph 内部第 5 步失败,parent_graph 只知道"sub_task failed"
根本难点
多 Agent 协作的根本难点是 涌现行为不可预测。单个 Agent 的行为已经难以完全预测(因为 LLM 是概率模型),多个 Agent 交互时,行为空间呈指数增长。
经典分布式系统用协议(如 Raft/Paxos)保证一致性,但 Agent 系统的”协议”是 LLM 生成的自然语言——它不具备协议的确定性保证。
当前的缓解手段:
- 限制协作拓扑(Sequential 比 Swarm 更可控)
- 限制交互轮次(MaxSteps/MaxDelegations)
- 中心化协调(Coordinator/Hierarchical)
- 状态机约束(StateGraph 强制转移条件)
但这些都在牺牲灵活性换可控性——本质是把问题从”解决”降级为”约束”。
4. 记忆系统设计(★★★★)
核心矛盾:Agent 需要”记住”过去的交互以做出更好的决策,但 LLM 的 context window 是有限的。把所有历史都塞进 prompt 会超出 token 限制,选择性存储又可能遗漏关键信息。
日常类比:学生做笔记。全部逐字记录(全量记忆)→ 笔记本用完了也记不完;只记结论不记过程(压缩记忆)→ 考试时想不起推导过程;什么都不记(无记忆)→ 每次从零开始。
真实案例
tRPC-Agent-Go Fact vs Episode 的边界模糊:什么该存为 Fact(提炼后的知识),什么该存为 Episode(原始对话)?
// 这句话应该存为 Fact 还是 Episode?
"用户说他的项目用 React + TypeScript,但他考虑迁移到 Vue"
// 作为 Fact: "用户使用 React + TypeScript" — 丢失了"考虑迁移"的动态信息
// 作为 Episode: 保留原文 — 但 Episode 可能很长,检索时不好匹配
Agno LearningMachine 的知识漂移:Agent 从成功案例中学到的策略可能在场景变化后不再适用,但系统没有”遗忘”机制。
# Agent 学到:"用户问代码问题时,先搜 Stack Overflow"
# 存入 learning_db
# 3 个月后 Stack Overflow 改版,很多答案过时了
# 但 Agent 仍然优先搜 SO — 过时的学习策略变成了负资产
LangGraph Checkpoint 的存储膨胀:每个超步都保存完整 Channel 快照(不是增量 diff),长对话的 checkpoint 数据量线性增长。
对话 100 轮 × 每轮 State 约 5KB = 500KB checkpoint 数据
× 1000 并发用户 = 500MB
× 保留 30 天 = 15GB
PostgreSQL 后端可以处理,但 Redis(内存型)就吃不消了。
根本难点
记忆系统的根本难题是 相关性判断本身需要智能。”什么信息在未来有用”是一个需要预测能力的问题——而预测恰恰是 AI 最不确定的能力。
人类大脑通过睡眠时的”记忆固化”(memory consolidation)解决这个问题:重要记忆被反复激活,不重要的逐渐淡忘。当前没有 Agent 框架实现了类似的自适应遗忘机制。
5. 可观测性与调试(★★★)
核心矛盾:Agent 的执行路径是动态的(由 LLM 实时决定),传统的断点调试、日志追踪方法在面对非确定性系统时效果大减。
日常类比:调试一个”每次都走不同路”的迷宫机器人。你设置断点在某个路口,但机器人下次可能根本不经过那个路口。传统调试假设”相同输入→相同路径”,Agent 打破了这个假设。
真实案例
LangSmith trace 的信息过载:一个复杂的 LangGraph 执行可能产生 50+ 个 span,每个 span 包含完整的 prompt/response。人类审计者面对如此多的信息很难定位问题。
Trace:
├── graph.invoke (2.3s)
│ ├── node.research (0.8s)
│ │ ├── llm.call (0.6s) — 2000 token prompt, 500 token response
│ │ └── tool.search (0.2s)
│ ├── node.plan (0.5s)
│ │ └── llm.call (0.5s) — 3000 token prompt, 800 token response
│ ├── node.execute (0.7s)
│ │ ├── llm.call (0.4s)
│ │ ├── tool.code_run (0.2s)
│ │ └── llm.call (0.1s)
│ └── node.review (0.3s)
│ └── llm.call (0.3s)
问题出在哪一步?可能是 research 搜到了错误信息,可能是 plan 制定了错误策略,可能是 execute 的工具调用出错——需要人工逐步排查。
tRPC-Agent-Go 跨服务 trace 的因果链断裂:Agent A 调用 Agent B,B 调用 Agent C。当 C 的结果有误时,trace 能告诉你”C 返回了什么”,但不能告诉你”为什么 A 会选择调用 B 而不是直接调用 C”——决策推理链在 trace 中不可见。
复现困难:由于 LLM 的温度参数(temperature > 0),同样的输入可能产生不同的输出。一个生产环境中偶尔出现的 bug 可能在调试环境中无法复现。
根本难点
Agent 系统的可观测性难题源于 非确定性 + 高维状态空间。传统可观测性三支柱(Metrics/Logs/Traces)假设系统行为是确定性的——相同条件下可复现。Agent 打破了这个假设。
需要新的可观测性范式:
- 决策路径可视化(不只是执行路径)
- LLM 注意力/置信度可视化
- 反事实分析(”如果这一步选了另一个工具会怎样”)
6. 生产环境扩展性(★★★★★)
核心矛盾:Agent 的单次执行涉及多次 LLM 调用 + 工具调用 + 状态读写,延迟高、资源占用重。高并发场景下,传统 Web 服务的扩展模式(无状态 + 负载均衡)不直接适用于有状态的 Agent。
日常类比:餐厅服务。普通菜品(无状态 API)→ 任何服务员都能上菜;定制大餐(有状态 Agent)→ 同一个厨师必须从头跟到尾(因为他记得你的口味偏好和之前做了哪些菜)。后者很难通过”多招几个厨师”来线性扩展。
真实案例
LangGraph Cloud 的冷启动延迟:每次新对话开始时,需要初始化 Graph + 加载 Checkpoint + 预热连接。
冷启动:Graph 编译 (50ms) + Checkpoint 加载 (100ms) + LLM 连接 (200ms) = 350ms
热路径:LLM 调用 (500-3000ms/步) × 5-10 步 = 2.5-30s 总延迟
对于实时交互场景(如客服 chatbot),350ms 冷启动已经可感知。
tRPC-Agent-Go 的 Session 后端瓶颈:9 种 Session 后端中,Redis 最快但容量有限,PostgreSQL 容量大但每次状态读写有 2-5ms 网络延迟。在 ReAct 循环的每一步都需要读写 Session 时,延迟累积显著。
5 步 ReAct 循环 × 每步 2 次 Session 读写 × 3ms/次 = 30ms 额外延迟
看起来不多,但在 1000 QPS 下,Session 后端承受 10000 次/秒读写
LLM API 的 rate limit:所有框架共同面临的瓶颈——上游 LLM API 有 TPM(tokens per minute)和 RPM(requests per minute)限制。多 Agent 系统的 LLM 调用量是单 Agent 的 N 倍。
Coordinator + 3 Worker Agents × 5 步/Agent = 20 次 LLM 调用/请求
× 100 并发请求 = 2000 RPM
GPT-4 Tier 1 限制 = 500 RPM → 必须排队
根本难点
生产扩展性的根本挑战是 有状态 + 长执行时间 + 外部依赖三重叠加:
- 有状态:不能简单水平扩展(Session Affinity 或分布式状态)
- 长执行:一次 Agent 执行可能持续 30+ 秒,占用连接和内存
- 外部依赖:LLM API rate limit 是不可控的外部瓶颈
tRPC-Agent-Go 通过微服务框架继承了负载均衡和熔断限流,但本质上只是把问题推迟到了 Session 后端——后端本身的扩展性仍需要工程投入。
7. 规划与推理质量(★★★★)
核心矛盾:Agent 需要在执行前规划(先想后做),但规划本身依赖 LLM——而 LLM 在长链推理中的可靠性会随步数增加而下降。
日常类比:让一个五年级学生规划一场公司年会。他可能列出”订酒店、请嘉宾、准备节目”等合理步骤,但细节(预算分配、时间冲突处理、Plan B)往往不够周全。更致命的是,计划写得头头是道,执行时才发现很多步骤不可行。
真实案例
tRPC-Agent-Go Planner 的规划脆弱性:Planner 生成的 Plan 可能在执行第 3 步时发现前提不成立,需要 Replan。但 Replan 依赖 LLM 对”哪里出了问题”的正确诊断——而这个诊断本身也可能出错。
// 原始计划
Plan: [
Step 1: 搜索用户的历史订单 ← 成功
Step 2: 分析消费趋势 ← 成功
Step 3: 生成推荐方案 ← 失败(数据不足)
]
// Replan
// LLM 诊断:"数据不足是因为搜索范围太窄" ← 可能是错误诊断
// 实际原因:用户是新用户,根本没有历史订单
CrewAI Sequential 的计划刚性:任务执行顺序在定义时固定,无法根据运行时情况动态调整。
# 定义时:research → write → review
# 运行时:research 发现"这个话题不值得写"
# 问题:仍然会继续执行 write 和 review——浪费 token
LangGraph 的条件边爆炸:为了处理各种异常情况,条件边会越加越多,最终图的复杂度超出人类理解能力。
# 开始:2 个条件边,清晰易懂
graph.add_conditional_edges("check", route, {"pass": "next", "fail": "retry"})
# 6 个月后:12 个条件边,没人敢改
graph.add_conditional_edges("check", route, {
"pass": "next", "fail": "retry", "timeout": "fallback",
"rate_limit": "wait", "auth_error": "reauth", "partial": "supplement",
"ambiguous": "clarify", "duplicate": "dedupe", "stale": "refresh",
"forbidden": "escalate", "not_found": "search_alt", "error": "log_and_skip"
})
根本难点
规划质量的根本问题是 LLM 不真正”理解”因果关系。它生成的计划是基于训练数据中”看起来像合理计划”的模式匹配,不是基于对任务的深层理解。
这意味着:
- 简单、常见的任务(LLM 见过很多类似案例)→ 规划质量高
- 复杂、罕见的任务(训练数据中缺乏先例)→ 规划质量骤降
- 需要多步因果推理的任务 → 每一步的误差会累积
8. Human-in-the-Loop(★★★)
核心矛盾:把人类引入 Agent 循环可以提升可靠性(人审批关键操作),但同时破坏了自动化的核心价值(效率)。找到”什么时候该问人”的平衡点极其困难。
日常类比:自动驾驶的 L3 级别。系统处理 95% 的路况,但偶尔让你”接管方向盘”。问题是:你不知道什么时候会被要求接管,必须时刻保持注意力——这比自己开还累。
真实案例
LangGraph GraphInterrupt 的时机判断:什么操作需要人审批?框架无法自动判断——必须开发者手动在代码中埋入 raise GraphInterrupt()。
def execute_action(state):
if state["action"] == "delete_all_data":
raise GraphInterrupt(value="确认删除所有数据?") # 开发者手动判断
if state["action"] == "send_email":
raise GraphInterrupt(value="确认发送邮件?") # 开发者手动判断
# 忘记在 "transfer_money" 加 interrupt → 直接执行 → 出事了
中断恢复的状态一致性:人类可能在中断后数小时甚至数天才恢复——这期间系统状态可能已经变化。
# T=0: Agent 查询库存 = 100,触发 GraphInterrupt("确认下单 50 件?")
# T=2h: 人类点击"确认"
# 但 T=1h 时另一个订单已经消耗了 80 件库存 → 实际库存 = 20
# 恢复执行时下单 50 件 → 超卖!
审批疲劳:如果 interrupt 太频繁,人类会形成”无脑点确认”的习惯——失去了 human-in-the-loop 的意义。
Agent 执行 1000 个任务/天,每个任务 2 次 interrupt = 2000 次审批
人类平均 3 秒/次 = 100 分钟/天花在"点确认"上
第一个月:认真看每个审批请求
第三个月:条件反射式点"approve"
→ Human-in-the-loop 退化为 Human-rubber-stamp
根本难点
Human-in-the-loop 的根本困境是 信任校准:
- 系统对自己的置信度估计不准确(LLM 不知道自己不知道什么)
- 人类对系统的信任会随时间变化(从不信任到过度信任)
- 没有通用方法判断”这个决策是否需要人参与”
理想状态是”系统在不确定时自动请求人审批,确定时自动执行”——但”确定/不确定”的判断本身就是最难的 AI 问题之一。
难度分层总结
第一层:工程可解(★★★)— 有成熟方案,投入人力即可
| 难点 | 当前最佳实践 | 成熟度 |
|---|---|---|
| 可观测性 | OpenTelemetry + LangSmith + Langfuse | 80%(缺决策推理可视化) |
| Human-in-the-Loop | GraphInterrupt + Checkpoint | 70%(缺自动置信度评估) |
这一层的问题本质上是”已知解法但实现不完善”。给够工程资源就能解决。
第二层:设计权衡(★★★★)— 没有完美解,只有适合场景的取舍
| 难点 | 核心 trade-off | 为什么无法同时满足 |
|---|---|---|
| Tool Calling 可靠性 | 灵活性 vs 类型安全 | LLM 概率生成本质 |
| 记忆系统 | 完整性 vs context window | 信息量 vs 有限注意力 |
| 规划质量 | 规划深度 vs 执行速度 | 规划本身消耗推理资源 |
这一层的问题需要根据具体场景做取舍决策——没有通用最优解。
第三层:基础研究(★★★★★)— 当前技术栈无法根本解决
| 难点 | 为什么是基础问题 | 突破方向 |
|---|---|---|
| 状态管理与一致性 | Agent 有状态 + LLM 无状态的根本矛盾 | 新型状态机模型(不依赖 LLM 做一致性保证) |
| 多 Agent 协作控制 | 涌现行为 + 非确定性 = 不可预测 | 形式化验证 + 协议约束(但会牺牲灵活性) |
| 生产环境扩展性 | 有状态 + 长执行 + 外部限制三重约束 | 需要新的 Agent 原生调度范式 |
这一层的问题不是”工程做得不够好”,而是”当前的技术基础有结构性限制”。解决它们可能需要:新的 LLM 架构(如原生支持状态持久化的模型)、新的分布式计算范式(为 Agent 设计而非为请求-响应设计)、新的形式化方法(证明多 Agent 系统的安全性属性)。
商业级产品的应对策略:Codex 与 Claude Code
上述 8 大挑战在商业级 coding agent 中有了实战解法。以下对照表展示 OpenAI Codex 和 Claude Code 如何应对:
| 挑战 | Codex 解法 | Claude Code 解法 |
|---|---|---|
| 状态管理 | Rollout JSONL + SQLite 持久化;auto-compaction 压缩长对话 | 推测:conversation state + checkpoint |
| Tool Calling 可靠性 | ExecPolicy 规则引擎预过滤 + Sandbox 物理隔离 + Escalation 重试 | ToolUseBlock 标准化 + 权限分类器 |
| 多 Agent 协作 | 子进程 spawn + Guardian 审查(弱多 Agent) | Subagent → Coordinator → Swarm 四层递进 |
| 记忆系统 | 两阶段 pipeline(并行提取 + git-baseline 串行合并)→ MEMORY.md | 推测:conversation memory + project indexing |
| 可观测性 | App-Server JSON-RPC 事件流 + 客户端 UI 实时展示 | 推测:内部遥测 + CLI 输出 |
| 生产扩展性 | 本地执行(无分布式);Cloud Tasks 异步卸载 | 推测:单进程 + batch mode |
| 规划能力 | Plan tool(显式规划步骤)+ 模型自由循环 | 推测:模型自由规划 |
| Human-in-the-Loop | 5 级审批(UnlessTrusted→Never)+ Hooks 自定义 | 四层权限(Allow/PowerUser/Normal/Deny) |
详见:Codex 工具循环 / Codex 沙箱 / Codex 记忆
对犀牛鸟参与者的启示
理解这些难点的价值不在于”解决它们”——大部分是开放研究问题。价值在于:
- 选择贡献方向时有判断力:第一层问题适合工程贡献(PR),第三层适合研究贡献(论文/设计文档)
- 阅读源码时知道在看什么:StateGraph 的 6192 行为什么那么长?因为状态管理本身就是这个难度
- 设计自己的 Agent 时少踩坑:知道 trade-off 就不会追求”完美方案”,而是根据场景做最合理的妥协
- 和 mentor 讨论时有深度:能聊”为什么 Coordinator 模式有单点瓶颈但仍然是主流选择”,比”Coordinator 模式是怎么工作的”高一个层次
- 有商业级参考:对照 Codex/Claude Code 的实战解法,理解”工业界选了什么 trade-off”