向 Specivo 上报 AI 使用¶
Specivo 可以记录你的 AI 编码工具用了什么:token、请求数、模型,以及这项工作对应的工作项(Issue)。Specivo 自行为这些用量计价,并把结果与工作项上记录的工时并列展示,同时计入项目的开支报表。用量通过唯一一个端点进入 Specivo,即 AI 使用上报 API。由一个上报程序读取工具在本地写入的日志,再发送到这个端点。
本页面向搭建或编写这类上报程序的开发者。完整契约(含 JSON Schema 和示例)发布在 Specivo 仓库的
docs/contracts/ai-usage/v1/ 目录下。任何人都可以实现它。
智能体无法统计自己的 token
通过 MCP 连接的 AI 智能体(AI agent)看不到自己的 token 计数器,所以无法自行上报准确的用量。用量来自工具的本地日志,由会话结束后或定时运行的上报程序读取。
开始之前¶
- API 密钥。在 个人资料 → API 密钥 下创建一个(见 API 密钥与作用域)。拥有该密钥的账户,就是它发送的每条记录的上报者。
- 权限。该账户在用量所归属的项目中需要拥有 记录 AI 使用 权限。没有项目的用量不需要权限。要为其他人上报,还需要 为他人记录 AI 使用 权限(见为队友上报)。
- 检查服务器。
GET /api/v1/ai-usage/ingest返回服务器接受的契约版本、限制、时钟,以及它认识的 AI 工具:
curl -s -H "Authorization: Bearer $SPECIVO_API_KEY" \
https://your-specivo-host/api/v1/ai-usage/ingest
{
"versions": ["specivo.ai-usage.v1"],
"max_records": 500,
"max_bytes": 1048576,
"max_age_days": 400,
"rate_limit": {"requests": 60, "window_seconds": 60},
"server_time": "2026-11-06T12:00:00Z",
"tools": ["claude-code", "codex", "gemini-cli"],
"currencies": ["EUR", "THB", "USD"]
}
发送批次¶
POST /api/v1/ai-usage/ingest 接受一个批次,最多 500 条记录:
curl -s -X POST https://your-specivo-host/api/v1/ai-usage/ingest \
-H "Authorization: Bearer $SPECIVO_API_KEY" \
-H "Content-Type: application/json" \
-d @batch.json
{
"schema": "specivo.ai-usage.v1",
"client": {"name": "my-usage-reporter", "version": "0.1.0"},
"records": [
{
"idempotency_key": "752cc282663658a7ec3ea666eb7b7aa62c4aeda5191f2594c426edb9c558bcc4",
"tool": "claude-code",
"provider": "anthropic",
"model": "claude-opus-4-6",
"session_id": "0f6c2a1e-session",
"usage_date": "2026-11-05",
"started_at": "2026-11-05T09:10:00+07:00",
"ended_at": "2026-11-05T10:40:00+07:00",
"request_count": 42,
"tokens": {"input": 1200, "output": 9000, "cache_write_5m": 30000, "cache_read": 900000},
"attribution": {"issue_ref": "ACME-12", "git_branch": "feat/acme-12-login"}
}
]
}
只有 idempotency_key、tool、model、usage_date 和 tokens 是必填的。响应为 HTTP 200,每条记录对应一个结果:
{
"schema": "specivo.ai-usage.v1",
"server_time": "2026-11-06T12:00:01Z",
"summary": {"received": 1, "created": 1, "updated": 0, "unchanged": 0, "rejected": 0},
"results": [
{
"index": 0,
"idempotency_key": "752cc282663658a7ec3ea666eb7b7aa62c4aeda5191f2594c426edb9c558bcc4",
"status": "created",
"entry_id": 1042,
"revision": 1,
"attribution": {"method": "explicit", "project_key": "ACME", "issue_ref": "ACME-12"},
"warnings": [],
"error": null
}
],
"warnings": []
}
每条记录单独存储或拒绝。被拒绝的记录带有 status: "rejected" 和一个 error,其中包含 code、message 和出错的 field;其余记录照常存储。
一条记录对应一个桶¶
一条记录保存一个桶的完整总量:一个工具的一个会话,使用一个模型,在一个 UTC 日内。Specivo 为每个桶只保留一个条目,以其 idempotency_key 为键(按用户区分)。
- 每次都发送整个桶的总量,而不是上次发送以来的增量。Specivo 会替换总量,绝不累加。
- 再次发送相同的总量不会有任何变化(
unchanged),所以重发是安全的。崩溃或断网之后,把当天重新发送一遍即可。 - 好的键可以取
v1|<tool>|<session_id>|<model>|<usage_date>的 SHA-256。 - 如果有人在 Specivo 中把该条目排除出报表,之后的发送会更新它,但保持排除状态。
日期和时间均为 UTC¶
usage_date是 UTC 日期。跨越 UTC 午夜的会话会变成两条记录,每天一条。例如在曼谷(UTC+7),UTC 日期在当地时间 07:00 切换。started_at和ended_at需要带时区偏移(Z或+07:00)。按 UTC 计算,started_at必须落在usage_date当天,ended_at不得晚于当天结束。usage_date不能是未来日期,也不能早于 400 天前。- Specivo 按
started_at(或其 UTC 日的开始时刻)为每条记录计价,使用该时刻有效的价格。
Specivo 仍按每个人自己的时区显示日期;在午夜前后,两者可能相差几个小时。
用量归属到哪个工作项¶
Specivo 按以下顺序尝试这些线索,并记录实际采用了哪一条:
attribution.issue_ref:你指定的工作项,例如来自环境变量SPECIVO_ISSUE(加上"issue_ref_source": "env")。如果上报程序是从智能体自己的 Specivo MCP 调用中读到工作项的,则随"issue_ref_source": "agent_log"一起发送;这样的工作项会先于下面各步尝试,但无法使用时会被跳过,而不是拒绝记录。- 智能体的 MCP 会话:如果连接到 Specivo MCP 服务器的智能体在
started_at和ended_at之间正在处理某个工作项,则归属到该工作项。 attribution.commits:会话中的提交所关联的工作项。attribution.git_branch:分支名中的工作项编号,例如feat/acme-12-login。attribution.project_key,或者attribution.repo(当它匹配到恰好只关联一个项目的仓库时)。- 否则该条目为 未分配,之后可以在 Specivo 中再分配。
你明确指定的内容(issue_ref、project_key)必须有效:未知的工作项,或无法记录用量的项目,会导致记录被拒绝。agent_log 工作项以及 Specivo 自行推断的结果(第 2 到 5 步)在无法使用时会被跳过,并给出警告 attribution_dropped,然后尝试下一条线索。
条目一旦确定归属,之后的发送会保留该归属,只有两个例外,而且都只是让归属更精确:
- 有项目但没有工作项的条目(例如智能体在选定工作项之前先做了调研),会采用后续发送中找到的该项目的工作项。
- Specivo 推断出的工作项(来自 MCP 会话、提交或分支),或者早先来自智能体日志的工作项,会被智能体日志中的工作项替换。
指定了不同工作项或项目的发送会以 attribution_conflict 被拒绝;请改在 Specivo 中移动该条目。有人在 Specivo 中手动移动过的条目,其归属方式显示为 manual,之后的发送不会再改变它。
为队友上报¶
一个共享账户可以在每条记录上加 "on_behalf_of": "<login>",为整个团队上报。为此,该账户需要在项目中拥有 为他人记录 AI 使用 权限,而且被代报的人必须是该项目的活跃成员。没有项目的用量,只有管理员才能代他人上报。
每个条目都显示是哪个账户和哪把密钥记录的,每个人都能看到以自己名义记录的所有用量,代他人记录会写入审计日志。
为防止有人利用 on_behalf_of 探测谁拥有账户,在记录所属项目中无权为他人上报的账户,无论登录名是否存在,都会得到同样的 forbidden 响应。代报记录请带上 project_key(或 issue_ref):只有在该项目中拥有 为他人记录 AI 使用 权限的账户或管理员,才会得到精确的 target_unknown、target_invalid 和 target_not_member 代码。
费用¶
只需发送 token,Specivo 会根据 AI 模型价格 计算费用。
cost_type说明用量如何付费:api、subscription、local、free或unknown。省略时,如果此人有覆盖该工具的 AI 订阅,Specivo 使用subscription,否则使用unknown。- 如果工具知道服务商实际收取的费用,以
reported_cost发送({"amount": "1.2345", "currency": "USD"})。对api用量来说,它就是实际开支。 inputtoken 不得包含缓存读写;缓存请放在cache_read、cache_write_5m和cache_write_1h中发送。- 对于有长上下文价格的模型,发送两个独立的 token 桶。
tokens存放低于模型长上下文阈值的请求,long_context_tokens存放超过阈值的请求。一个请求整体归入一个桶(它的全部输入、缓存和输出 token),并且long_context_tokens不是tokens的一部分:总用量是两者之和。Specivo 按普通价格为tokens计价,按长上下文价格为long_context_tokens计价,所以同时计入两个桶的请求会被重复收费。示例见契约。 - Specivo 中的 token 总数(条目列表、报表、CSV 导出)包含两个桶。有长上下文用量的条目会显示其中有多少 token 按长上下文价格计费。
读取、修正和排除条目¶
上报的用量存储为 AI 使用记录条目,与人和智能体手动记录的放在一起。REST API 与工时记录的接口对应;每个 URL 都需要末尾的斜杠:
| 请求 | 作用 |
|---|---|
GET /api/v1/my/ai-usage/ |
以你名义记录的条目,包括未分配的。筛选参数:project_key、unassigned、tool、model、cost_type、source、excluded、from_date、to_date、offset、limit。 |
GET /api/v1/projects/{key}/ai-usage/ |
项目中你有权读取的条目:你自己的和你上报的;如果 查看 AI 使用 设为全部,则是所有人的。筛选参数相同,另加 user_id 和 issue_id。 |
POST /api/v1/projects/{key}/ai-usage/ |
手动记录一个条目:tool、model、usage_date、tokens,可选 issue_id、cost_type、cost({"amount": "0.42", "currency": "USD"})、on_behalf_of 和 comments。POST /api/v1/my/ai-usage/ 记录一个不属于任何项目的条目。 |
GET、PATCH、DELETE /api/v1/ai-usage/{id}/ |
读取、修改或删除一个条目。带 project_key(或 null)的 PATCH 会移动它;tokens 只改变发送了的类别。 |
POST /api/v1/ai-usage/{id}/exclude/、/include/ |
把一个条目排除出报表,或重新计入。 |
以这种方式记录的条目,来源为 api(使用 API 密钥)或 manual(登录后操作)。智能体使用 MCP 工具 specivo_log_ai_usage(来源 mcp)和 specivo_ai_usage(op="list"、"get"、"update"、"exclude"、"include");见智能体能做什么。
- 你可以修改以你名义记录的条目;在项目中拥有 管理 AI 使用 权限,则可以修改该项目中任何人的条目。把条目移到另一个项目,会按在那里记录用量的权限来检查。
- 上报程序发送的条目会保留其 token、模型和工具:只有项目、工作项和评论可以修改(改其他字段会返回
ai_usage_field_locked)。错误的条目请排除而不是删除;之后再次发送同一个桶时,它会保持排除状态。只有管理员可以删除上报的条目。 - 修改手动记录条目的用量会重新计价。条目所有者以外的人进行的编辑、排除或删除会写入审计日志。
限制和错误¶
| 响应 | 含义 |
|---|---|
| 200 | 批次已读取。检查每个结果的 status。 |
| 400 | 请求体不是 JSON(invalid_json)、信封格式错误(invalid_batch),或服务器不接受该 schema 版本(unsupported_schema)。 |
| 401 | 没有 API 密钥,或密钥无效。 |
| 413 | 超过 500 条记录或超过 1 MiB(batch_too_large):请拆分批次。 |
| 429 | 批次过多;请等待 Retry-After 指定的秒数。默认限制为每个账户每分钟 60 个批次。 |
管理员可以通过环境变量 AI_USAGE_INGEST_RATE_LIMIT_PER_MINUTE 修改速率限制(见配置)。
契约的 README 列出了所有错误和警告代码,以及新版本的规则:v1 随时可能新增可选字段,所以请忽略响应中你不认识的字段。