# 用量、预算与终态

记录执行时长和模型用量，区分费用估计、回合限制与输出预算。

## 从最终结果记录用量

ResultMessage 包含 DurationMS、DurationAPIMS、NumTurns、TotalCostUSD、Usage 和 ModelUsage。宿主可以用它们展示本轮用量与执行时间，并与自己的任务 ID 关联。

```go
fmt.Printf("duration_ms=%d turns=%d cost_usd=%f\n",
    result.DurationMS, result.NumTurns, result.TotalCostUSD)
```

运行时或模型服务未提供某项统计时，不应把字段零值宣传成免费或零消耗。多模型任务需要看 ModelUsage 的实际内容；不要把不同模型的 token 简单相加后套用同一种价格。

## 区分几种限制

| 配置 | 控制范围 |
| --- | --- |
| `context.WithTimeout` | 宿主操作等待与取消期限 |
| `WithInitializeTimeout` | 运行时初始化等待 |
| `WithMaxTurns` | 运行时任务回合上限，不是用户消息条数 |
| `WithMaxBudgetUSD` | 运行时支持的任务费用预算 |
| `WithTaskBudget` | 运行时任务预算选项，使用前核对支持 |
| `OutboundMessageOptions.MaxOutputTokens` | 协商后的单条消息输出限制 |

这些限制不能互相替代。初始化很慢时增加输出 token 没有帮助；希望缩短工具执行时间时，只限制回答长度也不够。

## 预算设置示例

```go
options = options.WithMaxTurns(8).WithMaxBudgetUSD(1.0)
```

配置后，用小任务确认目标运行时如何报告达到预算上限的终态。费用预算依赖运行时计量，不等于支付账户上的硬性扣费上限。需要组织级额度时，宿主还要维护自己的准入与并发规则。

## 终态不是一条字符串

先看 Go error，再看 IsError，随后结合 Subtype、TerminalReason、StopReason 和 Errors。停止原因可能来自取消、预算、工具拒绝或模型结束，不应仅依赖 Result 文本里是否出现“完成”。

用 `result.TerminalCategory()` 可得到 success、interrupted、limit、error、cancelled 或 unknown 等归一化分类。保留原始 Subtype 和 TerminalReason 作为诊断信息；unknown 应交给待核对流程，不能归为成功。

`IsRetryable()` 只表达这类终态是否通常值得重试，不保证重复执行没有副作用。费用和任务状态应读取协议字段，不要从自然语言日志中猜测。字段细节见 [protocol GoDoc](https://pkg.go.dev/github.com/nexus-research-lab/nexus-agent-sdk-bridge/protocol)。

## 服务端记账

以业务任务和运行时会话关联每轮结果，避免页面刷新导致重复累计。同一任务重试可能产生新的费用记录；应保留尝试次数，而不是覆盖失败尝试。

工具调用外部付费服务的费用不一定包含在模型用量中。宿主工具自身的计量应单独记录，界面明确展示其来源。
