Skip to content
Go
最佳实践与陷阱汇总

最佳实践与陷阱汇总

全手册最佳实践与常见陷阱汇总,按主题分类。


通用原则

💡 说明
Agent 必须通过 Runner 运行 不要直接调用 Agent.Run()——Runner 管理生命周期、Checkpoint 和中断
优先用 Handlers(ChatModelAgentMiddleware) AgentMiddleware 已废弃,新代码一律用 Handler 接口
自定义类型注册 gob 存入 InterruptInfo / RunLocalValue 的自定义类型需 schema.RegisterName[T]()
语言设置尽早调用 adk.SetLanguage() 应在 init()main() 开头调用
编译期类型安全 Graph 编译时检查节点间类型兼容,复杂编排优先用 Graph 而非手写

组件使用

💡 说明
优先使用 Stream model.Stream() 打字机效果体验更好;仅在不支持或无需流式时用 Generate
Tool 描述要清晰 LLM 靠 ToolInfo.Desc 判断何时调用,描述模糊则 Tool 白注册
🚨 说明
Stream 必须 Close stream.Close() 必须调用,即使提前退出循环,否则 goroutine 泄漏
ToolMessage 需正确 CallID ToolMessage("result", "call_id")call_id 须与 ToolCall.ID 一致
Embedding 维度不匹配 换模型后向量维度可能变化,须重建向量索引
混淆 Generate / Stream GenerateStream 的子集;框架对不支持 Stream 的组件自动降级

编排与 Graph

💡 说明
简单管道用 Chain,复杂编排用 Graph Chain 内部封装 Graph,代码更简洁但无分支能力
字段精确映射用 Workflow 控制流与数据流分离,避免大 struct 全量拷贝
ReAct 环中确保退出路径 分支函数必须保留通向 END 的路径
🚨 说明
StreamReader 未 Copy 就并发读 一个 StreamReader 单消费者;多消费者需先 Copy(n)
类型不匹配导致编译失败 Graph 编译期校验,上游输出必须与下游输入兼容
Graph 中手动管理 goroutine 编排框架自动处理并发;不要在图内自行 fork goroutine

ChatModelAgent

💡 说明
BaseChatModelAgentMiddleware 嵌入 只需覆盖需要的方法,避免实现全部接口
WrapModel 做重试 / Failover 这两个是模型调用维度的关注点,不应放在其他 Hook 中
BeforeModelRewriteState 修改工具列表 它是 ToolInfos 的 source of truth,不要在其他 Hook 中改
ToolSearch 减少常驻工具 工具过多时用 ToolSearch Middleware 而非全部塞入 prompt
🚨 说明
WrapModel 中修改 Messages 修改不被持久化,须在 BeforeModelRewriteState 中修改
MaxIterations 过小 复杂多步任务可能执行不完整
ReturnDirectly 误用 Tool 命中后直接返回,不再给模型修正机会
Tools 过多且无 ToolSearch 稀释模型注意力,导致调用决策出错

Model 容错 (Retry / Failover)

💡 说明
Retry + Failover 组合 顺序:Retry 耗尽 → Failover。ShouldFailover 收到的 err 是 *RetryExhaustedError
流式 Failover 去重 失败尝试已发送的事件不撤回,客户端应按元数据去重
ShouldFailoverGetFailoverModel 都是必填 缺一在 NewChatModelAgent 时返回错误
🚨 说明
流式 Failover 事件重复 客户端需自行处理;失败尝试的事件不会自动撤回
ErrStreamCanceled 触发 Failover 它不会触发 Failover,但需在 ShouldFailover 中排除

中断与恢复

💡 说明
CheckPointID 用业务语义 session-{id}-{step},便于恢复时定位
推荐实现 ResumableAgent 自定义 Agent 要支持 Resume 需实现此接口
🚨 说明
未配 CheckPointStore 用 Resume 必须创建 Runner 时配置;否则中断状态丢失
忘记 gob.RegisterName 序列化时 panic,排查困难
CheckPointID 复用覆盖 Resume 后旧状态被新状态覆盖,如需保留须用新 ID

多 Agent 协作

💡 说明
委派用 AgentAsTool 父 Agent 保持控制权,子 Agent 上下文隔离
固定流程用 Workflow Sequential / Parallel / Loop — 确定性编排
DeepAgent 适合复杂代码任务 内置文件系统 + Shell + 子 Agent + TODO 管理
🚨 说明
LoopAgent 无退出条件 必须产出 BreakLoop 或设 MaxIterations
Parallel 共享状态 并发写 SessionValue 需自行加锁
DeepAgent 中 WithoutWriteTodos 对简单任务可关闭 TODO 减少不必要的步骤

Callback 与可观测性

💡 说明
全局 Handler 一次性注册 AppendGlobalHandlers 非并发安全,初始化阶段调用
Context 传递 Span OnStart 返回的 ctx 会传入 OnEnd / OnError,用来关联 span
CozeLoop 用于可视化 字节跳动内部可直接用 eino-ext/callbacks/cozeloop
🚨 说明
StreamReader 在 Callback 中泄漏 流式回调中收到的 StreamReader 必须关闭
OnStart 中修改 input 值拷贝,修改不影响实际流程;不要在此做数据变更

生产部署清单

# 检查项
1 全局 Handler(Tracing / Logging / Metrics)已注册
2 adk.SetLanguage() 已调用
3 init() 中已注册所有自定义 gob 类型
4 所有 Stream 有对应的 defer Close()
5 CheckPointStore 已配置(如需中断恢复)
6 Middleware 使用 Handler 接口而非废弃的 AgentMiddleware
7 Graph / Chain 编译错误已处理(Compile 返回值)
8 模型容错配置完整(ShouldFailover + GetFailoverModel 双必填)
9 自定义 Agent 实现了 ResumableAgent(如需 Resume)
10 工具过多时启用 ToolSearch Middleware