内容
# 代理账本
[](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/ci.yml)
[](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/codeql.yml)
[](https://pypi.org/project/agentic-ledger/)
[](https://pypi.org/project/agentic-ledger/)
[](https://ghcr.io/shekharbhardwaj/agentic-ledger)
[](LICENSE)
[](https://glama.ai/mcp/servers/ShekharBhardwaj/AgenticLedger)
AI 代理的运行时可观察性 - 精确查看代理执行了什么、为什么执行以及成本。
**网站:** [agentic-ledger.dev](https://agentic-ledger.dev)
> 数字旨在与提供商账单匹配。如果不匹配,[这是一个我们想要的错误](https://github.com/ShekharBhardwaj/AgenticLedger/issues/new/choose)。
与 **任何代理框架**、**任何 LLM 提供商**、**任何模型网关** 兼容。无需代码更改。只需将代理指向代理,自动捕获所有内容。
---
## 工作原理
Agentic Ledger 作为代理和 LLM 提供商之间的透明代理运行。它拦截每个请求和响应,分配一个 `action_id`,存储它,并返回未修改的上游响应。代理永远不知道代理的存在。
```
您的代理 → Agentic Ledger 代理 → OpenAI / Anthropic / LiteLLM / 任意 LLM
↓
SQLite 或 Postgres
↓
实时仪表盘 + API
```
---
## 快速入门
**步骤 1 — 启动代理**
使用 Docker(推荐,无需 Python):
```bash
docker run -p 8000:8000 \
-e AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com \
-v $(pwd)/data:/data \
ghcr.io/shekharbhardwaj/agentic-ledger:latest
```
> 镜像是多架构(amd64/arm64)的,以非 root 用户身份运行,每个版本都使用 Sigstore 签名并附带 SBOM。强化共享部署(TLS、身份验证密钥、数据删除、验证)?请参阅[部署指南](docs/deployment.md)。
> **使用 Anthropic / Claude?** 将上游设置为 Anthropic:
> `AGENTICLEDGER_UPSTREAM_URL=https://api.anthropic.com`。代理前端一个提供商一次 - 在另一个端口上运行第二个实例以覆盖两者。任何 OpenAI 兼容的网关 URL(LiteLLM、OpenRouter、...)都以相同方式工作。
或使用 docker compose(默认为 SQLite,请参阅 `docker-compose.yml`):
```bash
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com docker compose up
```
使用 `uv`:
```bash
uv add agentic-ledger
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com uv run python -m agenticledger.proxy
```
使用 `pip`:
```bash
python -m venv venv && source venv/bin/activate
pip install -U agentic-ledger
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com ./venv/bin/python -m agenticledger.proxy
```
> **Postgres?** 安装额外组件并设置 `AGENTICLEDGER_DSN`:
> ```bash
> pip install "agentic-ledger[postgres]"
> AGENTICLEDGER_DSN=postgresql://user:password@localhost/agenticledger
> ```
> 注意:Docker 镜像仅使用 SQLite。对于 Postgres,请使用 `pip` 安装。
> **OpenTelemetry?** 安装额外组件并设置 `AGENTICLEDGER_OTEL_ENDPOINT`:
> ```bash
> pip install "agentic-ledger[otel]"
> AGENTICLEDGER_OTEL_ENDPOINT=http://localhost:4318
> ```
代理启动在 `http://localhost:8000`。跟踪信息保存到当前文件夹中的 `agenticledger.db`(或 Docker 中的 `/data/agenticledger.db`)。
---
**步骤 2 — 将代理指向代理**
两个更改:设置 `base_url` 为代理并添加会话 ID 头以将调用分组到运行中。其他所有内容 - 您的 API 密钥、模型、消息 - 保持不变。
**OpenAI:**
```python
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1", # ← 代理
api_key="your-openai-key",
default_headers={"x-agenticledger-session-id": "run-1"},
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Research the top 3 AI trends in 2026"}],
)
```
**Anthropic** (使用 `AGENTICLEDGER_UPSTREAM_URL=https://api.anthropic.com` 启动代理):
```python
import anthropic
client = anthropic.Anthropic(
base_url="http://localhost:8000", # ← 代理
api_key="your-anthropic-key",
default_headers={"x-agenticledger-session-id": "run-1"},
)
```
**LiteLLM / OpenRouter / 任意网关:**
```bash
# 将 Agentic Ledger 指向您的网关
AGENTICLEDGER_UPSTREAM_URL=http://localhost:4000 uv run python -m agenticledger.proxy
# 然后将您的代理指向 Agentic Ledger
client = OpenAI(base_url="http://localhost:8000/v1", ...)
```
---
**步骤 3 — 打开仪表盘**
```
http://localhost:8000
```
Web 应用程序通过 WebSocket 实时更新跟踪信息。无需刷新。
- **循环镜头** - 每个循环运行状态(`running` / `flagged` / `complete`),每次迭代的成本图表,每次迭代的详细分解,以及每个标志的纯英文解释。选择任意两个运行,使用 **⇆** 以并排方式比较它们 - 成本、迭代次数、调用次数、标志、持续时间和带符号的增量 - 更改提示并重新运行的工作流。
- **会话** - 每个会话有三个视图:可展开的调用卡(响应、思考、工具调用、缓存令牌、交互徽章),**Flow** DAG 代理交接图,和 **Trace** 瀑布图,带有真实的父链接。从循环引擎。悬停会话卡可一键删除。
- **报告** - 资金去向:每日支出、模型混合、每代理总数和 **缓存节省** - 如果以完整输入速率运行,提示缓存流量将花费多少,与实际花费的成本(签名:缓存成本高于节省则显示为红色)
- **搜索** - 跨提示、输出和代理的全文搜索
经典的单文件仪表盘仍然位于 `http://localhost:8000/classic`:
- **调用选项卡** - 每个 LLM 调用,包含完整的提示、系统提示、工具调用、工具结果、输出、令牌、成本、延迟和错误
- **流程选项卡** - 多代理系统的视觉 DAG。每个代理是一个节点,带有聚合成本、延迟和调用计数。边代表交接。单击节点以高亮显示其调用。
- **跟踪选项卡** - Gantt/瀑布图时间线,显示每个调用为共享时间轴上的水平条。无需仪表。纯粹根据时间戳工作。单击任意条形图以跳转到完整的调用详细信息。预算警告在条形图上显示为琥珀色边框,而不隐藏代理颜色。
- **搜索** - 跨所有会话的全文搜索,按提示、输出、代理名称或用户 ID
---
## 编码代理 - Claude Code、Ralph 循环和朋友
Claude Code(和大多数编码代理)可以通过单个环境变量指向代理 - 无需头部,无需代码更改:
```bash
AGENTICLEDGER_UPSTREAM_URL=https://api.anthropic.com uv run python -m agenticledger.proxy
```
```bash
export ANTHROPIC_BASE_URL=http://localhost:8000
claude
```
Agentic Ledger 自动为 Claude Code 流量添加指纹:每个调用都标记为 `framework=claude-code`,并且每个 Claude Code 会话都出现在其 **真实会话 UUID** 下(与 `claude --resume` 显示的相同 ID),捕获并正确定价提示缓存读取/写入 - 缓存流量是大多数编码代理的实际支出。
运行夜间循环(Ralph 风格的 `while :; do cat PROMPT.md | claude -p; done`)?使用内置循环运行器 - 它重新执行您的命令每次迭代,将每个调用归因于运行(通过基本 URL,无需头部),并在完成承诺、预算上限或迭代上限时停止:
```bash
AGENTICLEDGER_UPSTREAM_URL=https://api.anthropic.com \
AGENTICLEDGER_COMPLETION_PROMISE="ALL TASKS COMPLETE" \
uv run python -m agenticledger.proxy
```
```bash
agenticledger run --max-iterations 50 --budget 25 -- \
claude -p "$(cat PROMPT.md)" --dangerously-skip-permissions
```
每次迭代在 `/api/runs` 中显示为 *迭代 N 次*;当代理打印响应中的完成承诺时,运行状态更改为 `complete`,循环退出并带有成本/令牌摘要。任何现有的循环脚本也适用 - 自己轮询 `GET /api/runs/{run_id}`,或让代理的预算(`AGENTICLEDGER_BUDGET_DAILY=25.00`)强制停止失控的循环。
迭代提示?重新运行并在 Loop Lens 中使用 **⇆ 比较** 以并排比较两个运行 - 成本、迭代次数、调用次数和标志 - 因此“新的提示是否真的有帮助”得到了一个数字而不是感觉。
相同的配方适用于具有基本 URL 覆盖的任何客户端(Codex CLI、opencode、OpenClaw、LiteLLM 基栈)- 将 OpenAI/Anthropic 基本 URL 设置为代理,流量将被捕获;当您想要明确的属性时添加 `x-agenticledger-*` 头。
**OTel 原生工具**(Gemini CLI、Codex `[otel]`、AutoGen/AG2、Pydantic AI、Vercel AI SDK)根本不需要代理 - 将它们的 OTLP 导出器指向账本,GenAI 跨度直接被摄取:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8000
```
同时接受 OTLP/HTTP 编码:JSON 始终,protobuf 当安装 `[otel]` 额外组件时(Docker 镜像包含它)。gRPC 导出器应切换到 HTTP:`OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`。
**框架指南** - [docs/integrations](docs/integrations/README.md) 中每个集成的指南:Claude Code、Codex CLI、opencode、OpenClaw、BMAD-METHOD、LangGraph/LangChain、CrewAI、OpenAI 代理 SDK、Gemini CLI、AutoGen/AG2、Pydantic AI、Vercel AI SDK、LiteLLM 和 OpenRouter。
**生产部署** - TLS 终止、身份验证密钥、数据删除、镜像签名/SBOM 验证、企业镜像和扩展指南在 [docs/deployment.md](docs/deployment.md) 中。
---
## 捕获内容
每个 LLM 调用都存储以下内容:
| 字段 | 内容 |
|---|---|
| `action_id` | 拦截时分配的 UUID |
| `session_id` | 运行分组(来自头部) |
| `timestamp` | 调用时间 |
| `model_id` | 使用的模型 |
| `provider` | `openai` 或 `anthropic` |
| `messages` | 发送到模型的完整消息历史记录 |
| `system_prompt` | 提取的系统提示 |
| `tools` | 模型可用的工具定义 |
| `tool_calls` | 模型选择的工具 |
| `tool_results` | 工具返回的内容(来自下一个调用的消息) |
| `content` | 模型的文本输出 |
| `stop_reason` | 模型停止的原因 |
| `tokens_in` / `tokens_out` | 令牌使用情况 |
| `cache_read_tokens` / `cache_write_tokens` | 提示缓存使用情况 - 读取和写入按提供商正确定价 |
| `thinking` | 扩展思考输出(Anthropic),与 `content` 分开捕获 |
| `cost_usd` | 基于模型定价的估计成本 |
| `latency_ms` | 端到端响应时间 |
| `status_code` | 上游的 HTTP 状态 - 捕获错误 |
| `error_detail` | 非 200 响应的上游错误消息 |
| `agent_name` | 来自 `x-agenticledger-agent-name` 头部,或自动检测(例如 `claude-code`) |
| `framework` | 来自 `x-agenticledger-framework` 头部,或指纹检测(例如 `claude-code`、`litellm`) |
| `user_id` | 来自 `x-agenticledger-user-id` 头部 |
| `app_id` | 来自 `x-agenticledger-app-id` 头部 |
| `environment` | 来自 `x-agenticledger-environment` 头部 |
| `parent_action_id` | 嵌套代理图中的父调用 |
| `handoff_from` / `handoff_to` | Flow DAG 的代理交接跟踪 |
---
## API 参考
| 方法 | 端点 | 描述 |
|---|---|---|
| `GET` | `/health` | 存活 - `{"status":"ok","version":"..."}`。无需身份验证,永不触及存储。 |
| `GET` | `/readyz` | 就绪 - ping 存储;当无法访问时返回 `503`。还报告 `capture_dropped`。 |
| `GET` | `/metrics` | Prometheus 指标(捕获的已持久化/已丢弃,队列深度)。 |
| `GET` | `/api/audit` | 敏感操作的审计跟踪(管理员)。 |
| `DELETE` | `/api/users/{user_id}` | 权利删除:删除用户的所有捕获调用(管理员)。 |
| `GET` | `/` | 实时仪表盘 |
| `WS` | `/ws` | WebSocket 流 - 为实时仪表盘更新提供支持 |
| `GET` | `/api/sessions` | 列出最近的会话,附带聚合统计数据 |
| `GET` | `/api/runs` | 列出循环运行(显式或自动推断),附带迭代、成本、状态和标记调用计数 |
| `GET` | `/api/runs/{run_id}` | 一个运行的状态(`running` / `flagged` / `complete`)- 从循环脚本中轮询此状态 |
| `GET` | `/api/sessions/{session_id}/tools` | 导出的工具执行 - 每个工具调用与结果、延迟和错误状态配对 |
| `DELETE` | `/api/sessions/{session_id}` | 删除会话及其所有调用 |
| `GET` | `/api/reports?days=30` | 支出洞察:每日趋势、模型混合、每代理总数 |
| `GET` | `/api/search?q=...` | 跨所有捕获调用的全文搜索 |
| `GET` | `/session/{session_id}` | 会话中的所有调用,按时间排序 |
| `GET` | `/explain/{action_id}` | 按操作 ID 单个调用 |
| `GET` | `/export/{session_id}` | 带有 SHA-256 完整性哈希的 JSON 合规导出 |
| `GET` | `/export/{session_id}/report` | 可打印的 HTML 审计报告 |
| `POST` | `/mcp` | MCP 工具服务器 - `list_sessions`、`explain`、`get_session`、`search`、`list_runs`、`get_run_status` |
| `POST` | `/v1/traces` | OTLP/HTTP JSON 收集 - 来自 OTel 原生工具的 GenAI 跨度成为账本调用(`/v1/logs`、`/v1/metrics` 确认) |
**示例:**
```bash
# 会话中的所有调用
curl http://localhost:8000/session/run-1
# 跨所有会话搜索
curl "http://localhost:8000/api/search?q=failed+to+connect"
# 下载 JSON 审计跟踪(包含完整性标签;配置时为密钥 HMAC)
curl http://localhost:8000/export/run-1 -o audit-run-1.json
# 可打印的 HTML 报告 - 在浏览器中打开,打印为 PDF
open http://localhost:8000/export/run-1/report
```
---
## MCP 服务器
Agentic Ledger 通过 `POST /mcp` 将其捕获的数据暴露为 MCP(模型上下文协议)工具服务器。将 Claude Desktop、Cursor 或任何 MCP 兼容的客户端指向它,直接从 AI 助手查询跟踪。
**可用工具:
| 工具 | 描述 |
|---|---|
| `list_sessions` | 列出最近的会话,附带成本、令牌和调用次数摘要 |
| `explain(action_id)` | 单个LLM调用的完整跟踪 - 提示、工具调用、输出、令牌、成本 |
| `get_session(session_id)` | 会话中的所有调用,按时间顺序 |
| `search(query)` | 跨所有捕获调用的全文搜索 |
| `list_runs` | 循环运行,附带迭代、成本和状态 |
| `get_run_status(run_id)` | 运行状态 - 允许代理检查其自己的循环并决定是否继续 |
**在 `claude_desktop_config.json` 中配置(HTTP,对话中运行的代理):**
```json
{
"mcpServers": {
"agenticledger": {
"url": "http://localhost:8000/mcp"
}
}
}
```
**或作为stdio子进程** - 适用于以命令形式启动服务器的客户端(无需运行代理;读取相同的数据库):
```json
{
"mcpServers": {
"agenticledger": {
"command": "agenticledger",
"args": ["mcp"],
"env": { "AGENTICLEDGER_DSN": "sqlite:////绝对路径/到/agenticledger.db" }
}
}
}
```
如果设置了 `AGENTICLEDGER_API_KEY`,请将其作为头部:
```json
{
"mcpServers": {
"agenticledger": {
"url": "http://localhost:8000/mcp",
"headers": { "x-agenticledger-api-key": "your-key" }
}
}
}
```
连接后,您可以向您的助手提问,例如:
- “上次会话中 SearchAgent 执行了什么?”
- “显示所有提及速率限制错误的调用”
- “会话 run-abc123 的总成本是多少?”
---
## 配置
(待翻译)
### 环境变量
**核心:**
| 变量名 | 是否必需 | 默认值 | 描述 |
|---|---|---|---|
| `AGENTICLEDGER_UPSTREAM_URL` | **是** | `https://api.openai.com` | 转发请求的LLM端点。接受OpenAI、Anthropic、LiteLLM、OpenRouter或任何OpenAI兼容的URL。 |
| `AGENTICLEDGER_DSN` | 否 | `sqlite:///agenticledger.db`(Docker:`sqlite:////data/agenticledger.db`) | 数据库。本地开发使用SQLite,生产环境使用Postgres URL。 |
| `AGENTICLEDGER_HOST` | 否 | `0.0.0.0` | 绑定主机。使用`127.0.0.1`仅限本地访问。 |
| `AGENTICLEDGER_PORT` | 否 | `8000` | 运行端口。 |
| `AGENTICLEDGER_API_KEY` | 否 | _(无)_ | 主管理员密钥。设置后,仪表盘、读取和管理端点需要身份验证;该密钥授予`admin`角色并引导API令牌(如下)。跳过本地开发;设置时使用服务器上的代理 — 您选择值。 |
| `AGENTICLEDGER_INGEST_KEY` | 否 | _(无)_ | 设置后,代理仅在请求携带匹配的`x-agenticledger-ingest-key`头部时转发请求 — 关闭开放中继。默认关闭;未设置时触发响亮的启动警告。 |
| `AGENTICLEDGER_EXPORT_HMAC_KEY` | 否 | _(无)_ | 设置后,合规导出携带防篡改的密钥`hmac-sha256`完整性标签,而不是纯`sha256`校验和。 |
| `AGENTICLEDGER_EXTRA_PATHS` | 否 | _(无)_ | 逗号分隔的附加请求路径,用于捕获,例如`v1/responses,v1/custom`。内置路径(`v1/chat/completions`、`v1/messages`、`v1/responses`,以及记录为免费调用的`v1/messages/count_tokens`)始终被捕获。 |
| `AGENTICLEDGER_ASYNC_CAPTURE` | 否 | `off` | 在后台工作线程中持久化捕获,以便存储不会为代理调用添加延迟。权衡:读取变为**最终一致性**(刚刚捕获的调用可能在短时间内无法查询)。推荐高吞吐量。 |
| `AGENTICLEDGER_CAPTURE_QUEUE_MAX` | 否 | `10000` | 异步模式下缓冲的最大捕获量,然后释放负载(丢弃计数在`/metrics`中)。 |
| `AGENTICLEDGER_CAPTURE_LEVEL` | 否 | `full` | `full`存储所有内容;`metadata`仅存储指标/元数据(模型、令牌、成本、延迟、代理、状态)并丢弃提示、响应和工具。 |
| `AGENTICLEDGER_REDACT` | 否 | _(关闭)_ | 存储的数据中删除PII/密钥:`all`或逗号列表`email,ssn,credit_card,ip,api_key`。替换匹配项为`[REDACTED:<label>]`。仅影响存储的副本 — 代理的响应不受影响。 |
| `AGENTICLEDGER_REDACT_PATTERNS` | 否 | _(无)_ | 额外的删除正则表达式为JSON:`{"label": "regex", ...}`或`["regex", ...]`。 |
| `AGENTICLEDGER_RETENTION_DAYS` | 否 | _(永久保存)_ | 通过后台清除工作线程删除捕获的调用,该调用超过N天。 |
| `AGENTICLEDGER_AUDIT_LOG` | 否 | `on` | 记录审计跟踪,记录谁查看/导出/删除了什么以及令牌/擦除操作。设置为`0`以禁用。 |
**成本预算** — 阻止超过支出限制的调用(返回HTTP 429):
| 变量名 | 默认值 | 描述 |
|---|---|---|
| `AGENTICLEDGER_BUDGET_SESSION` | _(无)_ | 每个`session_id`在整个生命周期内的最大美元数。 |
| `AGENTICLEDGER_BUDGET_AGENT` | _(无)_ | 每个`agent_name`每天(UTC)的最大美元数。 |
| `AGENTICLEDGER_BUDGET_DAILY` | _(无)_ | 所有调用每天(UTC)的最大美元数。 |
| `AGENTICLEDGER_BUDGET_ACTION` | `block` | 当超出预算时,会发生什么:`block`返回HTTP 429(调用从未到达LLM),`warn`允许调用通过并触发Webhook警报,`both`阻止并触发Webhook。 |
**速率限制** — 阻止超过请求频率的调用(返回HTTP 429,滑动60秒窗口):
| 变量名 | 默认值 | 描述 |
|---|---|---|
| `AGENTICLEDGER_RATE_LIMIT_RPM` | _(无)_ | 全球每分钟最大请求数。 |
| `AGENTICLEDGER_RATE_LIMIT_SESSION_RPM` | _(无)_ | 每个`session_id`每分钟最大请求数。 |
| `AGENTICLEDGER_RATE_LIMIT_AGENT_RPM` | _(无)_ | 每个`agent_name`每分钟最大请求数。 |
| `AGENTICLEDGER_RATE_LIMIT_USER_RPM` | _(无)_ | 每个`user_id`每分钟最大请求数。 |
**循环引擎** — 每个调用都缝合到ReAct线程(`thread_id`、`step_index`、`prev_action_id`)和新鲜上下文循环迭代分组为运行,具有卡住循环检测:
| 变量名 | 默认值 | 描述 |
|---|---|---|
| `AGENTICLEDGER_LOOP_ACTION` | `warn` | `warn`记录`loop_flags`并触发`loop_flag`Webhook警报;`block`另外返回HTTP 429(`loop_detected`)用于跳过守卫的会话;`off`禁用推理。 |
| `AGENTICLEDGER_LOOP_REPEAT_THRESHOLD` | `3` | 连续相同的工具调用(相同的工具,相同的参数)之前,将线程标记为卡住。 |
| `AGENTICLEDGER_LOOP_MAX_STEPS` | _(无)_ | 标记(并在阻止模式下停止)超过此许多ReAct步骤的线程。 |
| `AGENTICLEDGER_LOOP_RUN_GAP_SECONDS` | `900` | 新上下文生成之间的最大间隔(相同系统)仍然计为一次运行的迭代。 |
| `AGENTICLEDGER_COMPLETION_PROMISE` | _(无)_ | 与响应文本匹配的正则表达式。匹配时,调用被标记为`completion_promise`,运行状态变为`complete` — 循环运行器轮询`GET /api/runs/{run_id}`并停止。 |
**警报** — 当阈值被突破时,向您的Webhook发送POST请求(不会阻止调用 — 请参见[警报](#alerts)):
| 变量名 | 默认值 | 描述 |
|---|---|---|
| `AGENTICLEDGER_ALERT_WEBHOOK_URL` | _(无)_ | 要发送警报负载的POST请求URL。任何警报触发都需要此项。 |
| `AGENTICLEDGER_DIGEST_HOUR` | _(关闭)_ | UTC小时(0-23)发送每日消费摘要 — 最近24小时总计、缓存节省、热门模型/代理 — 到警报Webhook。 Slack-incoming-webhook友好(`text`)。 |
| `AGENTICLEDGER_ALERT_COST_PER_CALL` | _(无)_ | 当单个调用成本超过`$X`时触发警报。 |
| `AGENTICLEDGER_ALERT_LATENCY_MS` | _(无)_ | 当单个调用时间超过`Xms`时触发警报。 |
| `AGENTICLEDGER_ALERT_ERROR_RATE` | _(无)_ | 当会话错误率超过`X`(例如`0.5` = 50%)时触发警报。 |
| `AGENTICLEDGER_ALERT_DAILY_SPEND` | _(无)_ | 当每日消费超过`$X`时触发警报。与预算不同,这不会阻止调用。 |
**OpenTelemetry** — 将范围发送到任何OTLP兼容的收集器(需要`pip install "agentic-ledger[otel]"` — 请参见[OpenTelemetry导出](#opentelemetry-export)):
| 变量名 | 默认值 | 描述 |
|---|---|---|
| `AGENTICLEDGER_OTEL_ENDPOINT` | _(无)_ | OTLP/HTTP基本URL,例如`http://localhost:4318`。当未设置时,OTel导出被禁用。 |
| `AGENTICLEDGER_OTEL_SERVICE_NAME` | `agenticledger` | 报告给收集器的`service.name`值。 |
| `AGENTICLEDGER_OTEL_HEADERS` | _(无)_ | 逗号分隔的`key=value`身份验证头部,例如`x-honeycomb-team=abc123`。 |
**定价覆盖** — 覆盖或扩展内置的每个令牌定价表(在启动时合并):
| 变量名 | 默认值 | 描述 |
|---|---|---|
| `AGENTICLEDGER_PRICING` | _(无)_ | 内联JSON模型映射 → `[input_per_million, output_per_million]`美元。例如`'{"gpt-4o": [2.50, 10.00], "my-model": [1.00, 2.00]}'`。 |
| `AGENTICLEDGER_PRICING_FILE` | _(无)_ | 具有相同格式的JSON文件路径。在`AGENTICLEDGER_PRICING`之后应用。 |
---
### 常见启动示例
```bash
# 本地开发 — OpenAI(默认)
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com uv run python -m agenticledger.proxy
# 本地开发 — Anthropic
AGENTICLEDGER_UPSTREAM_URL=https://api.anthropic.com uv run python -m agenticledger.proxy
# 本地开发 — LiteLLM网关(任意模型)
AGENTICLEDGER_UPSTREAM_URL=http://localhost:4000 uv run python -m agenticledger.proxy
# 生产环境 — Postgres + 身份验证 + 预算 + 速率限制 + 警报
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com \
AGENTICLEDGER_DSN=postgresql://user:password@localhost/agenticledger \
AGENTICLEDGER_API_KEY=my-secret \
AGENTICLEDGER_BUDGET_DAILY=20.00 \
AGENTICLEDGER_BUDGET_SESSION=2.00 \
AGENTICLEDGER_RATE_LIMIT_SESSION_RPM=20 \
AGENTICLEDGER_RATE_LIMIT_USER_RPM=60 \
AGENTICLEDGER_ALERT_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz \
AGENTICLEDGER_ALERT_COST_PER_CALL=0.50 \
AGENTICLEDGER_ALERT_DAILY_SPEND=15.00 \
uv run python -m agenticledger.proxy
```
当设置`AGENTICLEDGER_API_KEY`时,请传递它以访问受保护的端点:
```bash
# 头部
curl -H "x-agenticledger-api-key: my-secret" http://localhost:8000/session/run-1
# 查询参数(浏览器)
http://localhost:8000?api_key=my-secret
```
#### 有范围的API令牌(RBAC)
主密钥很方便,但很粗糙。对于团队访问,铸造**有范围的、可撤销的令牌**,具有角色而不是共享主密钥。令牌是创建时显示的随机密钥;只有它们的SHA-256哈希被存储。
角色是分层的:
| 角色 | 可以 |
|---|---|
| `viewer` | 读取捕获的数据 — 仪表盘、API、导出、MCP |
| `editor` | viewer + 删除会话 |
| `admin` | editor + 管理API令牌 |
```bash
# 铸造一个viewer令牌(仅限管理员 — 使用主密钥启动)
curl -X POST http://localhost:8000/api/tokens \
-H "x-agenticledger-api-key: my-secret" \
-H "content-type: application/json" \
-d '{"name": "grafana-readonly", "role": "viewer", "expires_in_days": 90}'
# → {"token_id": "...", "token": "agl_…", "role": "viewer", ...} (令牌仅显示一次)
# 使用它(Bearer头部,x-agenticledger-token,或?token=)
curl -H "Authorization: Bearer agl_…" http://localhost:8000/api/sessions
# 列出和撤销
curl -H "x-agenticledger-api-key: my-secret" http://localhost:8000/api/tokens
curl -X DELETE -H "x-agenticledger-api-key: my-secret" http://localhost:8000/api/tokens/<token_id>
```
> 仅当设置了`AGENTICLEDGER_API_KEY`时,才强制执行身份验证;主密钥是用于铸造令牌的管理员引导程序。实时`/ws` feed接受相同的凭证(`?api_key=`,`?token=`,`Authorization: Bearer`或`x-agenticledger-token`)并以关闭代码1008拒绝未经身份验证的连接 — 仪表盘将其页面凭证转发到套接字自动。
---
### 请求头部
从您的代理在每个LLM调用中传递这些请求头部。所有可选。这些头部丰富了捕获的数据,为Flow选项卡提供支持,并启用每个维度预算和速率限制。
| 头部 | 默认值 | 描述 |
|---|---|---|
| `x-agenticledger-session-id` | _(无)_ | 分组运行中的所有调用。使用一致的ID每个代理执行(例如UUID或`"run-1"`)。如果没有,调用存储但在仪表盘中不分组。 |
| `x-agenticledger-user-id` | _(无)_ | 触发此运行的最终用户。启用每个用户速率限制和审计。 |
| `x-agenticledger-agent-name` | _(无)_ | 进行此调用的代理名称(例如`"orchestrator"`、`"researcher"`)。支持Flow选项卡DAG和基于代理的预算和速率限制。 |
| `x-agenticledger-app-id` | _(无)_ | 应用程序名称或ID。当多个应用程序共享一个代理时很有用|`application`)。 |
| `x-agenticledger-parent-action-id` | _(无)_ | 调用此调用的`action_id`。设置后,Trace选项卡绘制明确的父→子连接器。如果没有,它会自动从时间戳推断关系。 |
| `x-agenticledger-environment` | `development` | `production`、`staging`或`development`。显示在仪表盘中。 |
| `x-agenticledger-handoff-from` | _(无)_ | 代理放弃控制权(例如`"orchestrator"`)。在Flow DAG中呈现为有向边。 |
| `x-agenticledger-handoff-to` | _(无)_ | 接收代理控制权(例如`"researcher"`)。在Flow DAG中呈现为有向边。 |
| `x-agenticledger-framework` | _(自动检测)_ | 调用框架/工具(例如`"langgraph"`、`"bmad"`)。如果不存在,则自动指纹识别已知客户端(Claude Code,LiteLLM)。 |
| `x-agenticledger-run-id` | _(自动推断)_ | 将会话分组为循环运行(例如Ralph夜间运行)。如果不存在,共享一个系统提示的会话在`AGENTICLEDGER_LOOP_RUN_GAP_SECONDS`内自动分组。 |
| `x-agenticledger-iteration` | _(自动推断)_ | 运行中的迭代次数。 |
**单个代理 — 完全注释:**
```python
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-openai-key",
default_headers={
"x-agenticledger-session-id": "run-abc123",
"x-agenticledger-user-id": "user-42",
"x-agenticledger-agent-name": "researcher",
"x-agenticledger-app-id": "my-app",
"x-agenticledger-environment": "production",
},
)
```
**多代理系统 — 跟踪移交:**
```python
from openai import OpenAI
# Orchestrator
orchestrator_client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-openai-key",
default_headers={
"x-agenticledger-session-id": "run-abc123",
"x-agenticledger-agent-name": "orchestrator",
},
)
# Researcher(从orchestrator接收移交)
researcher_client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-openai-key",
default_headers={
"x-agenticledger-session-id": "run-abc123",
"x-agenticledger-agent-name": "researcher",
"x-agenticledger-handoff-from": "orchestrator",
"x-agenticledger-handoff-to": "researcher",
},
)
```
Flow选项卡呈现`orchestrator → researcher`为带有每个节点上的成本和延迟的DAG。
**OpenAI Agents SDK (`openai-agents`) — 每个代理客户端:**
`openai-agents` SDK使用其自己的内部OpenAI客户端。要传递Agentic Ledger头部,您需要为每个代理创建一个客户端,使用`OpenAIResponsesModel`并将其设置为代理的`model`。
```python
import uuid
import os
from openai import AsyncOpenAI
from agents import Agent
from agents.models.openai_responses import OpenAIResponsesModel
SESSION_ID = f"run-{uuid.uuid4().hex[:8]}" # 一个运行一个
BASE_URL = os.getenv("OPENAI_BASE_URL") # 例如http://localhost:8000/v1
def al_model(agent_name: str, model: str = "gpt-4o-mini",
handoff_from: str | None = None, handoff_to: str | None = None):
"""创建发送Agentic Ledger元数据头部的模型实例。"""
if not BASE_URL:
return model # 未配置代理 — 使用默认客户端
headers = {
"x-agenticledger-session-id": SESSION_ID,
"x-agenticledger-agent-name": agent_name,
}
if handoff_from:
headers["x-agenticledger-handoff-from"] = handoff_from
if handoff_to:
headers["x-agenticledger-handoff-to"] = handoff_to
client = AsyncOpenAI(base_url=BASE_URL, api_key=os.getenv("OPENAI_API_KEY", ""),
default_headers=headers)
return OpenAIResponsesModel(model=model, openai_client=client)
planner = Agent(name="PlannerAgent", model=al_model("PlannerAgent", handoff_to="SearchAgent"), ...)
searcher = Agent(name="SearchAgent", model=al_model("SearchAgent", handoff_from="PlannerAgent", handoff_to="WriterAgent"), ...)
writer = Agent(name="WriterAgent", model=al_model("WriterAgent", handoff_from="SearchAgent", handoff_to="EmailAgent"), ...)
emailer = Agent(name="EmailAgent", model=al_model("EmailAgent", handoff_from="WriterAgent"), ...)
```
每个代理的调用都用其名称和管道位置标记。Flow选项卡自动呈现完整的`PlannerAgent → SearchAgent → WriterAgent → EmailAgent` DAG。
> **为什么每个代理客户端?** `set_default_openai_client()`设置一个单一的全局客户端 — 对于单代理应用程序来说已经足够,但它无法为多代理系统中的每个代理携带不同的`agent_name`或`handoff_*`头部。每个代理的`OpenAIResponsesModel`实例是正确的方法。
---
## 警报
Agentic Ledger在您的Webhook URL被突破时触发`POST`请求。您可以将其连接到您已经使用的任何内容 — Slack,PagerDuty,Discord,电子邮件或自定义端点。Agentic Ledger发送负载;集成由您负责。
**负载格式:**
```json
{
"type": "high_cost",
"message": "单个调用成本$0.1842超过阈值$0.10",
"value": 0.1842,
"threshold": 0.10,
"action_id": "a1b2c3d4-...",
"session_id": "run-1",
"agent_name": "researcher",
"timestamp": "2026-04-03T12:00:00+00:00"
}
```
**警报类型:**
| 类型 | 触发条件 |
|---|---|
| `high_cost` | 单个调用超过`AGENTICLEDGER_ALERT_COST_PER_CALL` |
| `high_latency` | 单个调用时间超过`AGENTICLEDGER_ALERT_LATENCY_MS` |
| `high_error_rate` | 会话错误率超过`AGENTICLEDGER_ALERT_ERROR_RATE` |
| `daily_spend` | 每日总支出超过`AGENTICLEDGER_ALERT_DAILY_SPEND` |
| `budget_exceeded` | 达到预算限制,并且`AGENTICLEDGER_BUDGET_ACTION`为`warn`或`both` |
| `loop_flag` | 循环引擎在调用中触发了标志(`repeat_tool_calls`,`step_budget_exceeded`,`completion_promise`) |
| `run_complete` | 运行的完成承诺被看到 — 负载携带完整的运行摘要(迭代,成本,令牌,标志的调用) |
**预算 vs 警报:**
- **预算** (`AGENTICLEDGER_BUDGET_*`) — 在LLM到达之前阻止调用。代理收到HTTP 429。
- **警报** (`AGENTICLEDGER_ALERT_*`) — 调用继续,您在之后收到通知。
**Slack** — 创建一个[传入Webhook](https://api.slack.com/messaging/webhooks),并将`AGENTICLEDGER_ALERT_WEBHOOK_URL`指向它。添加`AGENTICLEDGER_DIGEST_HOUR=8`,同一个Webhook也会收到每日早晨的摘要:过去24小时的支出,缓存节省和热门模型和代理。
**PagerDuty** — 使用[Events API v2](https://developer.pagerduty.com/docs/events-api-v2/) URL或一个将`type`映射到PagerDuty严重程度的薄适配器。
**Discord** — 直接使用Discord频道Webhook URL。
**自定义** — 任何接受JSON `POST`请求的HTTP端点。
---
## OpenTelemetry 导出
Agentic Ledger 可以将每个拦截的 LLM 调用作为 OTel span 发送到任何兼容 OTLP 的收集器:Grafana Tempo、Jaeger、Honeycomb、Datadog、Dynatrace 或任何支持 OTLP/HTTP 的供应商。
**安装额外组件**(Docker 镜像已包含 OTel,使用 Docker 时无需额外步骤):
```bash
pip install "agentic-ledger[otel]"
# 或
uv add "agentic-ledger[otel]"
```
**配置:**
| 变量 | 默认值 | 描述 |
|---|---|---|
| `AGENTICLEDGER_OTEL_ENDPOINT` | (无) | OTLP/HTTP 基地址,例如 `http://localhost:4318`。如果未设置,则 OTel 导出被禁用。 |
| `AGENTICLEDGER_OTEL_SERVICE_NAME` | `agenticledger` | 发出资源中 `service.name` 的值。 |
| `AGENTICLEDGER_OTEL_HEADERS` | (无) | 授权头部的逗号分隔 `key=value` 对,例如 `x-honeycomb-team=abc123,x-honeycomb-dataset=llm`。 |
**示例 - Grafana Tempo:**
```bash
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com \
AGENTICLEDGER_OTEL_ENDPOINT=http://localhost:4318 \
AGENTICLEDGER_OTEL_SERVICE_NAME=my-agent \
uv run python -m agenticledger.proxy
```
**示例 - Honeycomb:**
```bash
AGENTICLEDGER_OTEL_ENDPOINT=https://api.honeycomb.io \
AGENTICLEDGER_OTEL_HEADERS=x-honeycomb-team=YOUR_API_KEY,x-honeycomb-dataset=llm-traces \
uv run python -m agenticledger.proxy
```
**发出的 Span 属性(GenAI 语义约定):**
| 属性 | 来源 |
|---|---|
| `gen_ai.system` | 提供者(`openai` / `anthropic`) |
| `gen_ai.operation.name` | 始终为 `chat` |
| `gen_ai.request.model` | 模型 ID |
| `gen_ai.request.temperature` | 如果设置 |
| `gen_ai.request.max_tokens` | 如果设置 |
| `gen_ai.usage.input_tokens` | 输入令牌 |
| `gen_ai.usage.output_tokens` | 输出令牌 |
| `gen_ai.response.finish_reasons` | 停止原因 |
| `agenticledger.action_id` | 唯一调用 ID |
| `agenticledger.session_id` | 运行分组 |
| `agenticledger.agent_name` | 从头部 |
| `agenticledger.user_id` | 从头部 |
| `agenticledger.cost_usd` | 预估成本 |
| `agenticledger.latency_ms` | 端到端延迟 |
| `agenticledger.environment` | 从头部 |
| `agenticledger.handoff_from` / `agenticledger.handoff_to` | 代理交接 |
| `http.status_code` | 上游的 HTTP 状态码 |
Span 按 `session_id` 分组为 traces - 会话中的所有调用在后端显示为一个 trace。父子关系遵循 `x-agenticledger-parent-action-id`。错误 Span(`status_code != 200`)被标记为 `StatusCode.ERROR`。
---
## 合规导出
每个会话都可以导出为具有完整性标签的审计轨迹 - 可用于受监管的行业、内部审计或将轨迹传递给外部工具。
```bash
# 带有调用数组完整性标签的机器可读 JSON
curl http://localhost:8000/export/run-1 -o audit-run-1.json
# 可打印的 HTML - 在浏览器中打开并打印为 PDF
open http://localhost:8000/export/run-1/report
```
JSON 导出带有调用数组的完整性标签。默认情况下,这是 `sha256` **校验和** - 它可以捕获意外损坏但不是签名(任何编辑调用的人都可以重新计算它)。设置 `AGENTICLEDGER_EXPORT_HMAC_KEY` 以切换到密钥 **`hmac-sha256`** 标签,该标签是防篡改的:持有密钥的接收者可以检测任何修改,并且标签无法在没有密钥的情况下伪造。
---
## 发布
标记版本会触发完整的发布管道自动:
```bash
git tag v0.2.0
git push origin v0.2.0
```
这运行三个作业:
1. **Docker** - 构建 `ghcr.io/shekharbhardwaj/agentic-ledger:{version}` 和 `:latest`,适用于 linux/amd64 + linux/arm64,推送时带有 SBOM + 源代码证明 attestations,用 Sigstore cosign(无密钥)签名摘要,并镜像到 Docker Hub(当配置 `DOCKERHUB_USERNAME`/`DOCKERHUB_TOKEN` 机密时)
2. **PyPI** - 使用受信任的发布(无需 API 令牌)将 `agentic-ledger=={version}` 构建并发布到 PyPI,带有 PEP 740 attestations
3. **GitHub Release** - 创建一个发布版本,带有自动生成的更改日志,并附上镜像 SBOM(SPDX)
**首次 PyPI 设置**(仅需一次):
1. 进入 [pypi.org/manage/account/publishing/](https://pypi.org/manage/account/publishing/)
2. 添加新的待处理发布者:
```
PyPI 项目名称:agentic-ledger
所有者:ShekharBhardwaj
仓库:AgenticLedger
工作流名称:release.yml
环境名称:pypi
```
3. 在 GitHub 中创建 `pypi` 环境:仓库 → 设置 → 环境 → 新环境 → 命名为 `pypi`
4. 完成 - 无需密钥
---
## 故障排除
**旧版本 / 命令或环境变量命名为 `agentledger`(无“ic”)** —
您正在运行一个预-0.4 版本,很可能是从已经安装了该软件包的 venv 运行:普通的 `pip install agentic-ledger` 表示“需求已经满足”并且不会升级。运行 `pip install -U agentic-ledger` 然后重启。代理在启动时在其第一行打印其版本,并且 `curl localhost:8000/health` 也会报告它。自 0.4.0 以来,所有内容都命名为 `agenticledger` - 请参见 CHANGELOG 中的迁移说明。
**macOS 上出现 `incompatible architecture (have 'arm64', need 'x86_64')`** —
您的终端正在 Rosetta 下运行,因此 Python 选择其 x86_64 切片,而 pip 安装了 arm64 原生轮。请使用 `arch` 检查(应该在 Apple Silicon 上打印 `arm64`)。快速解决方法:在命令前加上 `arch -arm64`。永久解决方法:取消选中终端应用程序上的“使用 Rosetta”,使用 Apple Silicon 构建您的编辑器,然后重启任何长时间运行的 `tmux` 服务器。
**`module 'httpx' has no attribute 'AsyncClient'`** — 在 `0.3.0-alpha.2` 中修复;使用 `pip install --upgrade agentic-ledger` 升级。
**端口 8000 已被占用** — 另一个代理实例(或应用程序)正在运行;停止它或设置 `AGENTICLEDGER_PORT`。
**来自 Claude Code 的 `401 OAuth access token has expired`** — 代理未修改地传递了 Anthropic 的答案;使用 `claude` → `/login` 重新进行身份验证。错误调用仍会被捕获,因此您将在仪表板中看到 401。
**`/` 显示经典仪表板而不是 Web 应用程序** — 您正在从源代码 checkout 运行 Web 应用程序。`cd dashboard-app && npm ci && npm run build` 然后重启。PyPI 和 Docker 安装始终包含该应用程序。
---
## License
MIT
<!-- MCP registry ownership verification -->
`mcp-name: io.github.ShekharBhardwaj/agentic-ledger`
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器