@agentskit/observability

中文文档
0.13.1发布于 2026/10/24MITTypeScriptESM + CJS构建来源已验证近 30 天下载 7630

Logging and tracing layer for AgentsKit agents.

原始使用文档
文档目录19 节
  1. Verified proof
  2. How this fits the ecosystem
  3. Why observability
  4. Install
  5. Quick example
  6. Token counting
  7. Fast approximate count
  8. Per-message breakdown
  9. Exact count with a real tokenizer
  10. Features
  11. Production lifecycle (sinks + SDK bridges)
  12. Cost guards
  13. Durable budgets with CostStore
  14. Ecosystem
  15. Contributors
  16. License
  17. Docs
  18. Maturity and compatibility
  19. Contributing

@agentskit/observability

Profile: beta-package

AgentsKit

See exactly what your agent does — every LLM call, tool execution, and reasoning step — with zero coupling to your agent code.

npm version npm downloads bundle size license stability GitHub stars

Tags: ai · agents · llm · agentskit · ai-agents · observability · tracing · opentelemetry · langsmith · logging · monitoring

Verified proof

How this fits the ecosystem

@agentskit/observability makes agent behavior inspectable with traces, costs, audit logs, devtools, and production telemetry hooks.

  • AgentsKit: compose it with the other packages in this repo to build agents from small, swappable parts.
  • Registry: look for ready agents and templates that already use this layer at registry.agentskit.io.
  • Playbook: learn the production patterns behind this layer at playbook.agentskit.io.

Docs: package guide · agent handoff

Why observability

  • Debug in minutes, not hours — trace the full ReAct loop: which tools were called, what the LLM received, where it went wrong, all in one place
  • Works with your existing tracing stack — LangSmith, OpenTelemetry (OTLP), or a simple console logger; observers are just { name, on(event) } objects
  • No coupling, no lock-in — observability attaches to AgentEvent emissions from the runtime; remove it and your agent code is unchanged
  • Non-blocking by design — observer errors never surface to the agent; production stability is not at risk

Install

npm install @agentskit/observability

Quick example

import { createRuntime } from '@agentskit/runtime'
import { anthropic } from '@agentskit/adapters'
import { consoleLogger, langsmith } from '@agentskit/observability'

const runtime = createRuntime({
  adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, model: 'claude-sonnet-4-6' }),
  observers: [
    consoleLogger({ format: 'human' }),
    langsmith({ apiKey: process.env.LANGSMITH_API_KEY }),
  ],
})

const result = await runtime.run('Analyze sales data in ./data/sales.csv')
console.log(result.content)
// Every step is now logged and traced automatically

Token counting

@agentskit/observability includes a zero-dependency token counting API — useful for context-window budget checks, cost estimation, and message trimming.

Fast approximate count

import { countTokens, approximateCounter } from '@agentskit/observability'

// async convenience function
const total = await countTokens(messages)
if (total > 120_000) trimOldMessages(messages)

// synchronous via counter directly
const syncTotal = approximateCounter.count(messages)

Uses the chars / 4 + 4 per message heuristic. Slightly over-estimates — intentional for budget guards.

Per-message breakdown

import { countTokensDetailed } from '@agentskit/observability'

const { total, perMessage } = await countTokensDetailed(messages)
// total      → number
// perMessage → number[]  (one entry per message, same order)

Exact count with a real tokenizer

import { createProviderCounter, countTokens } from '@agentskit/observability'
import { encoding_for_model } from 'tiktoken'

const enc = encoding_for_model('gpt-4o')
const tiktokenCounter = createProviderCounter({
  name: 'tiktoken',
  tokenize: (text) => [...enc.encode(text)],
})

const exact = await countTokens(messages, { counter: tiktokenCounter, model: 'gpt-4o' })

createProviderCounter wraps any tokenize(text, model?) function in a TokenCounter that conforms to the core contract and supports countDetailed automatically.

Features

  • consoleLogger({ format }) — pretty-print or JSON structured logs for local dev
  • langsmith({ apiKey }) — LangSmith lifecycle observer (optional langsmith peer)
  • opentelemetry(config) — OTLP lifecycle observer (optional OpenTelemetry peers)
  • datadogSink / axiomSink / newRelicSink — HTTP lifecycle sinks with managed batching
  • Cost guards — costGuard, multiTenantCostGuard, createAdvancedCostGuard
  • approximateCounter — zero-dep synchronous token counter (chars/4 heuristic)
  • countTokens / countTokensDetailed — async token counting with optional custom counter
  • createProviderCounter — factory to wrap tiktoken or any tokenizer in the TokenCounter contract
  • Observer interface: { name: string, on(event: AgentEvent): void } — write custom observers in minutes
  • Attaches via observers array on createRuntime — zero changes to agent logic

Production lifecycle (sinks + SDK bridges)

Datadog, Axiom, New Relic, LangSmith, and OpenTelemetry factories return lifecycle observers:

type LifecycleObserver = Observer & {
  flush(): Promise<void>
  shutdown(): Promise<void> // idempotent
}

HTTP sinks share bounded, best-effort export (not a delivery guarantee):

OptionDefaultRole
batchSize25Max events per POST
maxQueueSize1000Hard queue cap; drop oldest when full
flushIntervalMs2000Periodic drain
maxRetries3Retries after the initial attempt
retryBaseDelayMs100Full-jitter exponential backoff base, capped at 30s
requestTimeoutMs10000Per-request timeout
onError—Isolated error sink (throws/rejections never escape on)

HTTP sinks retry network errors and responses with status 408, 429, or 5xx. A valid Retry-After delta-seconds or HTTP-date is a minimum delay; each wait is capped at 30s. Missing or invalid headers use full-jitter exponential backoff.

Batching is single-flight. Optional SDK peers resolve lazily; the package owns flush/shutdown for SDKs it constructs. During graceful process or request termination, await shutdown() so in-flight batches have a chance to drain. Overflow still drops oldest under pressure — plan capacity and onError monitoring accordingly.

Cost guards

import {
  costGuard,
  multiTenantCostGuard,
  createAdvancedCostGuard,
} from '@agentskit/observability'

const controller = new AbortController()
const guard = costGuard({
  budgetUsd: 0.10,
  controller,
  // prices: { 'gpt-4o': { input: …, output: … } }, // override DEFAULT_PRICES
})

// Advanced modes
const advanced = createAdvancedCostGuard({
  budgets: { tenantA: 1 },
  mode: 'reject', // 'warn' | 'reject' | 'kill'
  // mode 'kill' requires disableRuntime(tenant, reason)
})
advanced.setTenant('tenantA')
// Host enforcement for reject:
if (advanced.isRejected('tenantA')) {
  // reject the request / stop scheduling work
}

Semantics (current hardening line):

  • Incremental accounting — each llm:end adds cost for tokens on that event using the active model for that event. Historical tokens are never repriced when the model changes. Token totals stay cumulative.
  • Hostile usage — NaN / Infinity / negative prompt or completion counts become 0 and never poison cost, state, or JSON payloads.
  • Zero budgets — first positive spend exceeds; utilization and callbacks stay finite (sentinel 1 when budget is 0 and spend is positive).
  • Isolation — onCost / onExceeded / alert sinks / disableRuntime / tenantOf / now failures are isolated; optional onError reports them without unhandled rejections.
  • Modes — warn observes only; reject is enforced by the host consulting isRejected(tenant) (window rejections clear when the window rolls; overall rejections until reset); kill calls persistent disableRuntime and exposes isDisabled (fail-closed if disable fails).
  • DEFAULT_PRICES is a baseline snapshot for convenience. Override prices for current provider rates — table numbers are not a stability contract. Guards fail closed on unknown models by default; the simple guard aborts its controller, while multi-tenant/advanced guards report or block according to their mode. Set unknownModelPolicy: 'allow-zero' only for an explicitly free/local model.

Simple costGuard aborts via the supplied AbortController when the run budget is exceeded (mark + abort before potentially hostile onExceeded). Multi-tenant and advanced guards do not abort the runtime by default.

Durable budgets with CostStore

A guard without a store keeps its totals in process memory: they reset on restart and are not shared between instances or isolates. That is fine in development and wrong on serverless or multi-instance hosts, so the guards warn once when no store is set and NODE_ENV=production, or on Cloudflare Workers when NODE_ENV is not set. Pass warnWithoutStore: true or false to decide it yourself.

CostStore is the durable port. reserve holds an estimate against the tenant cap, commit replaces it with the real spend and writes the usage ledger, release returns it when the call fails, and window reads the current state. All four take the tenant id; reserve, commit and release are idempotent per reservationId.

import { Pool } from 'pg'
import { drizzle } from 'drizzle-orm/node-postgres'
import { multiTenantCostGuard } from '@agentskit/observability'
import { postgresCostMigrationSql, postgresCostStore } from '@agentskit/observability/postgres'

const pool = new Pool({ connectionString: process.env.DATABASE_URL })
await pool.query(postgresCostMigrationSql) // once, from your migration step
const store = postgresCostStore({ db: drizzle(pool) })

// Gate a request before calling the model.
const reserved = await store.reserve({ tenant, reservationId: turnId, amountUsd: 0.02, capUsd: planCapUsd })
if (!reserved.ok) return new Response('quota exceeded', { status: 402 })
try {
  const usage = await runTurn()
  await store.commit({ tenant, reservationId: turnId, actualUsd: usage.costUsd, usage: usage.calls })
} catch (error) {
  await store.release({ tenant, reservationId: turnId })
  throw error
}

// Or let a guard record every priced call and trip on the shared total.
const guard = multiTenantCostGuard({ budgets: { [tenant]: planCapUsd }, store })
  • Atomic cap. The PostgreSQL store admits a reservation in one statement (reserved + spent + amount <= cap), so concurrent requests cannot overshoot the cap together. The cap is an argument of reserve: read it from your plan or entitlements table.
  • Accounting window. Spend is grouped by windowKey, by default the UTC month (2026-10). Pass windowKey to the store options or per call for another period. With a store, a guard's budget applies to the current window.
  • Real spend wins. commit records actualUsd even when it is above the reserved estimate. Reserve an upper estimate if the cap must never be passed.
  • Guards record after the fact. A guard writes each priced llm:end to the store and trips when the stored total passes the budget. To stop a request before it spends, call reserve yourself as above.
  • Connection lifetime is yours. Use a pool on Node. On Workers, create a pg client or a small pool per request and close it in waitUntil. drizzle-orm and pg are optional peers, needed only for the /postgres subpath.
  • A released id stays released. reserve with the id of a released reservation throws AK_COST_RESERVATION_RELEASED instead of reporting it as admitted. Use a new id to retry a call.
  • Abandoned reservations. A reservation that is never committed or released (a crashed process) keeps its amount held; the store has no background reaper. Schedule expirePostgresCostReservations({ db, olderThan }) with a cutoff longer than your slowest call, for example new Date(Date.now() - 60 * 60_000).
  • Store failures. A guard never throws into the run when the store fails. It reports the error to onError; without onError it logs one warning for the first failure. Set onError in production so a store that stops recording is visible.
  • Match errors by code. In CommonJS each subpath bundles its own copy of CostReservationError, so instanceof fails across require('@agentskit/observability') and require('@agentskit/observability/postgres'). Compare error.code.
  • Transactions. Call reserve outside your own transaction, or inside a savepoint. When two calls race on one reservation id, the store absorbs the loser's unique violation, but PostgreSQL has already aborted the enclosing transaction.
  • Your own store. Implement CostStore over your tables and run costStoreContract from @agentskit/observability/cost-store-contract in your test suite. createInMemoryCostStore() is the development and test implementation.

Ecosystem

PackageRole
@agentskit/runtimeEmits steps for tracing
@agentskit/coreAgentEvent stream
@agentskit/evalQuality gates alongside traces

Contributors

AgentsKit contributors

License

MIT — see LICENSE.

Docs

Full documentation · GitHub

Maturity and compatibility

  • Stability: beta — see docs/STABILITY.md
  • Node.js 20+ and TypeScript strict mode
  • Published as @agentskit/observability

Contributing

See CONTRIBUTING.md and the monorepo LICENSE.

版本与发布记录

@agentskit/observability 共发布 42 个版本,首次发布于 2026/04/06;过去 12 个月发布了 42 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.13.1latest2026/10/2412 天前
0.13.02026/10/1026 天前
0.12.12026/10/031 个月前
0.12.02026/10/031 个月前
0.11.82026/09/291 个月前
0.11.72026/09/261 个月前

关于 @agentskit/observability 的常见问题

@agentskit/observability 是什么?

Logging and tracing layer for AgentsKit agents.

如何安装 @agentskit/observability?

在项目目录运行 npm install @agentskit/observability,或使用 pnpm add / yarn add / bun add。

@agentskit/observability 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@agentskit/observability 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@agentskit/observability 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.19。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@agentskit/observability 可以商用吗?

@agentskit/observability 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@agentskit/observability 的下载量有多少?

根据 npm 官方下载统计,@agentskit/observability 在 2026/10/05 – 2026/11/03 这 30 天内被下载 7,630 次,日均约 254 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@agentskit/observability 还在维护吗?

最新版本 0.13.1 发布于 2026/10/24(12 天前);至今共发布 42 个版本,其中过去 12 个月发布了 42 个;首次发布于 2026/04/06。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。