Skip to content

适配器契约

本文档定义宿主系统如何把自己的事件流接到 @yingdao-ai/agent-observability

目标:让所有宿主系统按同一套生命周期接入,而不是各自发明一套约定。

边界划分

职责
SDK 核心run / generation / tool span / score 状态机、Langfuse transport、脱敏、flush gate
adapter(宿主侧)把宿主事件映射到 SDK 调用、提供 run id / user id / tags / metadata、提供鉴权和 environment

SDK 不应该知道:宿主事件名、宿主 session token、宿主代理地址、宿主的 plugin/hook 系统。

最小生命周期

宿主必须保证一条 run 至少按这个顺序闭合:

startRun
  → startGeneration
  → appendText / appendReasoning
  → endGeneration
  → startTool / endTool
  → score
  → endRun
  → gate.waitAll

允许省略某些步骤,但不允许跳过收尾:

  • 没有 tool 调用,可以不 startTool/endTool
  • 没有 reasoning,可以不 appendReasoning
  • 没有 score,可以不 score
  • 必须endRun
  • 进程回收前必须等待 gate.waitAll()

宿主必须提供的信息

Run 唯一标识

宿主要提供稳定的 RunInput.id

  • 同一次用户请求内稳定
  • 能唯一对应一条 trace
  • 子 session 如果要并到父 trace,adapter 自己维护 alias,并调用 observer.link(childId, rootId)

名称

RunInput.name 建议:

  • 直接用 agent 类型或 agent 名称
  • 不要把临时 UI 文案当 name

上下文

宿主负责决定是否写入 userIdtagsmetadata

延迟可得的上下文,可以:

ts
// 方式一:先 set context,再 startRun
observer.context.set(id, ctx)
const run = observer.startRun({ id, name })

// 方式二:run 过程中随时更新
run.setUserId("user-1")
run.setTags(["tag"])
run.setMetadata({ key: "val" })

Generation 约定

开始时机

宿主应在"已知模型和 LLM 调用输入"时调用 startGeneration()

  • 最小要求:model
  • 尽量传 input

文本流

  • assistant 输出增量 → run.appendText()
  • reasoning 增量 → run.appendReasoning()
  • 不要把用户原文再写回 output

结束时机

宿主应在 LLM 调用完成时调用 endGeneration(),并尽量带上 usagecost

若未调用 endGeneration() 而 run 已结束,SDK 会自动兜底结束 generation,但这只是保底,不推荐。

Named Generation

辅助调用走 startNamedGeneration(key, input)

  • 适用于 title / summary / route / classify 等场景
  • key 在单条 run 内稳定
  • 同一个 key 再次开始时,SDK 自动结束旧 generation

Tool Span 约定

开始时机

ts
run.startTool({
  id: callId,     // 必须稳定且与 endTool 一致
  name: toolName,
  input,
})

结束时机

ts
// 成功或失败都要调用
run.endTool(callId, { output, error })
  • callId 必须前后一致
  • 不要因工具报错就漏掉 endTool

若 run 结束时 tool 未结束,SDK 自动标为:

span leaked: run ended before tool completed

这说明 adapter 生命周期接法有问题。

Score 约定

score 是 run 级别附加信息,adapter 不需要传 environment(来自 transport)。

宿主只需决定:

  • 何时打分
  • 用什么 score name
  • score 类型(数值、布尔、类别)

推荐命名:

场景name
结果状态agent_outcome
执行时长session_duration_ms
工具错误数tool_error_count

并发和子 Session

若存在根 run + 多个子 agent / 子 session:

ts
observer.link(childId, rootId)
// 之后 observer.getRun(childId) 返回根 run

要求:

  • 子 session 不单独开新 trace,除非产品明确要求拆开
  • 一个 child id 只能指向一个 root id

退出与 Flush

ts
await observer.endRun(id)      // 结束 run,触发 transport flush
await observer.gate.waitAll()  // 等待所有数据上报完成

gate.waitAll() 是 adapter contract 里最不能省的一步

错误处理要求

adapter 必须满足两个原则:

  1. 观测失败不能影响主业务链路
  2. 生命周期错误不能悄悄吞掉

建议:

  • transport 网络错误打日志,不抛回主流程
  • adapter 发现缺少 callIdsessionId、事件顺序错乱时,至少打 warn

新宿主接入 Checklist

新宿主接入前,至少确认:

  • [ ] 有稳定的 run id
  • [ ] 能识别 LLM 调用开始和结束
  • [ ] 能识别 assistant text delta 和 reasoning delta
  • [ ] 能识别 tool begin/end
  • [ ] 能拿到 user/tags/metadata
  • [ ] 退出前能 await observer.gate.waitAll()
  • [ ] transport 能提供稳定的 environment()

MIT License