AgentSkillsCN

zap-cli

为代理调试引入zap可观测性CLI。当您需要实现事件日志记录、日志查看、查询预设、决策轨迹,或进行网络健康监测时,可选用此技能。该技能可在zap、可观测性、日志记录、决策轨迹、调试、代理事件等场景下自动触发。

SKILL.md
--- frontmatter
name: zap-cli
description: zap observability CLI for agent debugging. Use when implementing event logging, log viewing, query presets, decision traces, or network health monitoring. Triggers on zap, observability, logging, decision traces, debugging, agent events.

zap CLI

Agent-first observability CLI. JSON output by default, jq-friendly.

Event Schema

typescript
interface AgentEvent {
  id: string                    // ULID
  agent_did: string
  session_id: string
  event_type: EventType
  outcome: 'success' | 'error' | 'timeout' | 'skipped'
  timestamp: string             // ISO-8601
  duration_ms?: number
  
  // Trace correlation
  trace_id?: string
  parent_span_id?: string
  span_id: string
  
  // Flexible context
  context: Record<string, unknown>
  
  // Decision reasoning
  reasoning?: {
    decision: string
    rationale: string
    alternatives?: string[]
    confidence?: number
  }
  
  // Error details
  error?: {
    code: string
    message: string
    stack?: string
    retryable: boolean
  }
}

Event Types

TypeDescription
agent.startedAgent DO initialized
agent.promptReceived prompt
agent.responseCompleted response
agent.tool_callTool invoked
agent.tool_errorTool failed
memory.storeMemory stored
memory.searchMemory queried
memory.shareMemory shared
comms.message_sentSent message
comms.message_receivedReceived message
decisionDecision with reasoning
error.unhandledUnhandled error

Emit Events

typescript
import { ulid } from 'ulid'

class EventEmitter {
  constructor(
    private db: D1Database,
    private agentDid: string,
    private sessionId: string
  ) {}
  
  async emit(event: Omit<AgentEvent, 'id' | 'agent_did' | 'session_id' | 'timestamp'>): Promise<string> {
    const id = ulid()
    
    await this.db.prepare(`
      INSERT INTO events (id, agent_did, session_id, event_type, outcome, timestamp, duration_ms, trace_id, span_id, parent_span_id, context, reasoning, error)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).bind(
      id,
      this.agentDid,
      this.sessionId,
      event.event_type,
      event.outcome,
      new Date().toISOString(),
      event.duration_ms,
      event.trace_id,
      event.span_id,
      event.parent_span_id,
      JSON.stringify(event.context),
      event.reasoning ? JSON.stringify(event.reasoning) : null,
      event.error ? JSON.stringify(event.error) : null
    ).run()
    
    return id
  }
  
  // Convenience methods
  async toolCall(name: string, params: unknown, result: unknown, durationMs: number): Promise<string> {
    return this.emit({
      event_type: 'agent.tool_call',
      outcome: 'success',
      duration_ms: durationMs,
      span_id: ulid(),
      context: { tool_name: name, params, result_summary: summarize(result) }
    })
  }
  
  async decision(type: string, decision: string, rationale: string, alternatives?: string[]): Promise<string> {
    return this.emit({
      event_type: 'decision',
      outcome: 'success',
      span_id: ulid(),
      context: { decision_type: type },
      reasoning: { decision, rationale, alternatives, confidence: 0.9 }
    })
  }
}

CLI Commands

zap logs

View and filter logs:

bash
# Recent logs (JSON output)
zap logs

# Human-friendly
zap logs --pretty

# Filter by time
zap logs --since 5m
zap logs --since 2h
zap logs --since "2025-01-01"

# Filter by type
zap logs --type tool_call
zap logs --type decision,memory.store

# Filter by outcome
zap logs --outcome error

# Watch mode (tail)
zap logs --watch
zap logs --watch --type error

# Complex filters (jq-style)
zap logs --filter '.duration_ms > 1000'
zap logs --filter '.context.tool_name == "remember"'

zap query

SQL analytics with presets:

bash
# Built-in presets
zap query --preset slow_tools
zap query --preset error_rate
zap query --preset decision_confidence
zap query --preset memory_hotspots

# Custom SQL
zap query --sql "SELECT event_type, COUNT(*) FROM events GROUP BY event_type"

# Output formats
zap query --preset error_rate --format json
zap query --preset error_rate --format csv

zap trace

Decision trace analysis:

bash
# View reasoning for event
zap trace <event-id>

# Find low-confidence decisions
zap traces --confidence-lt 0.5

# Replay decision sequence
zap replay --session sess-123 --type decision

zap doctor

Self-diagnosis:

bash
# Health check
zap doctor

# Storage usage
zap storage

# Index health
zap indexes

Query Presets

typescript
const PRESETS = {
  slow_tools: `
    SELECT 
      context->>'tool_name' as tool,
      COUNT(*) as calls,
      AVG(duration_ms) as avg_ms,
      MAX(duration_ms) as max_ms
    FROM events
    WHERE event_type = 'agent.tool_call'
    GROUP BY context->>'tool_name'
    HAVING AVG(duration_ms) > 100
    ORDER BY avg_ms DESC
  `,
  
  error_rate: `
    SELECT 
      event_type,
      COUNT(*) as total,
      SUM(CASE WHEN outcome = 'error' THEN 1 ELSE 0 END) as errors,
      ROUND(100.0 * SUM(CASE WHEN outcome = 'error' THEN 1 ELSE 0 END) / COUNT(*), 2) as error_pct
    FROM events
    GROUP BY event_type
    ORDER BY error_pct DESC
  `,
  
  decision_confidence: `
    SELECT 
      context->>'decision_type' as type,
      reasoning->>'decision' as decision,
      reasoning->>'confidence' as confidence,
      reasoning->>'rationale' as rationale
    FROM events
    WHERE event_type = 'decision'
    AND CAST(reasoning->>'confidence' AS REAL) < 0.7
    ORDER BY confidence ASC
    LIMIT 20
  `,
  
  memory_hotspots: `
    SELECT 
      context->>'collection' as collection,
      COUNT(*) as accesses,
      COUNT(DISTINCT session_id) as sessions
    FROM events
    WHERE event_type IN ('memory.store', 'memory.search')
    GROUP BY context->>'collection'
    ORDER BY accesses DESC
  `
}

Retention Policy

Compact events over time:

typescript
async function compactEvents(db: D1Database): Promise<void> {
  const now = Date.now()
  const oneDay = 24 * 60 * 60 * 1000
  const sevenDays = 7 * oneDay
  const thirtyDays = 30 * oneDay
  
  // 7-30 days: aggregate to hourly buckets
  await db.prepare(`
    INSERT INTO events_hourly (hour, agent_did, event_type, count, avg_duration, error_count)
    SELECT 
      strftime('%Y-%m-%d %H:00:00', timestamp) as hour,
      agent_did,
      event_type,
      COUNT(*),
      AVG(duration_ms),
      SUM(CASE WHEN outcome = 'error' THEN 1 ELSE 0 END)
    FROM events
    WHERE timestamp < datetime('now', '-7 days')
    AND timestamp >= datetime('now', '-30 days')
    GROUP BY hour, agent_did, event_type
    ON CONFLICT DO NOTHING
  `).run()
  
  // Delete raw events older than 7 days
  await db.prepare(`
    DELETE FROM events
    WHERE timestamp < datetime('now', '-7 days')
  `).run()
  
  // Delete hourly aggregates older than 30 days
  await db.prepare(`
    DELETE FROM events_hourly
    WHERE hour < datetime('now', '-30 days')
  `).run()
}

CLI Implementation

typescript
#!/usr/bin/env bun

import { Command } from 'commander'

const program = new Command()
  .name('zap')
  .description('Agent observability CLI')
  .version('1.0.0')

program
  .command('logs')
  .description('View agent logs')
  .option('--since <time>', 'Show logs since time (e.g., 5m, 2h)')
  .option('--type <types>', 'Filter by event type (comma-separated)')
  .option('--outcome <outcome>', 'Filter by outcome')
  .option('--filter <expr>', 'jq-style filter expression')
  .option('--watch', 'Watch mode (tail)')
  .option('--pretty', 'Human-friendly output')
  .option('--limit <n>', 'Max results', '50')
  .action(async (options) => {
    // Implementation
  })

program
  .command('query')
  .description('Run SQL analytics')
  .option('--preset <name>', 'Use preset query')
  .option('--sql <query>', 'Custom SQL query')
  .option('--format <fmt>', 'Output format (json, csv, table)', 'json')
  .action(async (options) => {
    // Implementation
  })

program
  .command('trace <event-id>')
  .description('View decision trace')
  .action(async (eventId) => {
    // Implementation
  })

program
  .command('doctor')
  .description('Health check')
  .action(async () => {
    // Implementation
  })

program.parse()

References