Athena + S3: Query Coding-Agent Eval Traces Without a Warehouse

0 views

Every serious coding-agent team accumulates eval traces: prompt version, model id, tool sequence, pass/fail, latency, token counts, dollar cost, tenant, suite name. Dumping them into Postgres until it melts, or waiting for a full warehouse, are both failure modes. S3 + Athena (with Glue catalog or CTAS) gets you SQL over JSONL this week — pass rates by prompt version, cost outliers, latency regressions — without standing up Redshift. When joins and concurrency outgrow Athena, graduate; until then, ship visibility.

⚡ TL;DR: Write one JSONL line per eval case to s3://…/evals/dt=YYYY-MM-DD/. Glue crawler or explicit table DDL → Athena. SQL for pass rate, p95 latency, cost by prompt_version. Pair with EMF per-tenant tokens for live metrics and Bedrock ApplyGuardrail for safety filters on tool I/O. Related: tool schemas.

Land traces as partition-friendly JSONL

python
# ✅ One immutable line per eval case — easy for Athena SerDe
import json, datetime
from pathlib import Path

def emit_eval_trace(row: dict, out_dir: Path):
    dt = datetime.date.today().isoformat()
    path = out_dir / f"dt={dt}" / "traces.jsonl"
    path.parent.mkdir(parents=True, exist_ok=True)
    required = {
        "eval_id", "suite", "prompt_version", "model_id",
        "pass", "latency_ms", "tokens_in", "tokens_out", "cost_usd",
        "tenant_id", "ts",
    }
    missing = required - row.keys()
    if missing:
        raise ValueError(f"missing {missing}")
    with path.open("a") as f:
        f.write(json.dumps(row, separators=(",", ":")) + "\n")
bash
# Sync nightly (or from the Batch eval job) — hive-style partitions
aws s3 sync ./evals/out/ s3://acme-agent-evals/evals/ \
  --exclude "*" --include "dt=*/*"

❌ Nested giant JSON blobs per run with random keys; ✅ flat columns Athena can GROUP BY. Keep tool call detail in a nested array only if you need it — prefer a separate tool_events prefix if volume is huge.

Glue crawler vs explicit DDL vs CTAS

Approach When Tradeoff
Glue crawler Schema still evolving Easy; watch type guesses on pass/cost_usd
Explicit Athena DDL You own the contract Best for CI-enforced schemas
CTAS to Parquet Query volume grows Faster/cheaper scans; add a compact job
sql
-- Explicit external table over JSONL
CREATE EXTERNAL TABLE agent_evals (
  eval_id string,
  suite string,
  prompt_version string,
  model_id string,
  pass boolean,
  latency_ms int,
  tokens_in int,
  tokens_out int,
  cost_usd double,
  tenant_id string,
  ts string
)
PARTITIONED BY (dt string)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://acme-agent-evals/evals/'
TBLPROPERTIES ('has_encrypted_data'='false');

MSCK REPAIR TABLE agent_evals;
-- or ALTER TABLE agent_evals ADD PARTITION (dt='2026-09-21')
--   LOCATION 's3://acme-agent-evals/evals/dt=2026-09-21/';
sql
-- Compact hot partitions to Parquet for repeated dashboards
CREATE TABLE agent_evals_parquet
WITH (
  format = 'PARQUET',
  parquet_compression = 'SNAPPY',
  external_location = 's3://acme-agent-evals/evals_parquet/',
  partitioned_by = ARRAY['dt']
) AS
SELECT * FROM agent_evals WHERE dt >= '2026-09-01';

SQL that product and platform both care about

sql
-- Pass rate and cost by prompt version (last 7 days)
SELECT prompt_version,
       count(*) AS n,
       avg(CASE WHEN pass THEN 1.0 ELSE 0.0 END) AS pass_rate,
       approx_percentile(latency_ms, 0.95) AS p95_ms,
       sum(cost_usd) AS spend_usd
FROM agent_evals
WHERE dt BETWEEN '2026-09-14' AND '2026-09-21'
GROUP BY 1
ORDER BY pass_rate ASC, spend_usd DESC;
sql
-- Regressions: versions that got slower at same suite
WITH base AS (
  SELECT suite, prompt_version,
         approx_percentile(latency_ms, 0.95) AS p95
  FROM agent_evals
  WHERE dt = '2026-09-21'
  GROUP BY 1, 2
)
SELECT * FROM base ORDER BY p95 DESC LIMIT 20;
sql
-- Tenant cost outliers (eval farm abuse / misconfig)
SELECT tenant_id, sum(cost_usd) AS spend, count(*) AS cases
FROM agent_evals
WHERE dt = '2026-09-21'
GROUP BY 1
HAVING sum(cost_usd) > 50
ORDER BY spend DESC;

Live per-request token burn still belongs in CloudWatch EMF; Athena is for suite-level history and prompt A/B, not second-by-second paging.

Wire evals into the agent release train

  1. Eval Batch/CodeBuild job writes JSONL → S3.
  2. Partition repair (Lambda on s3:ObjectCreated or nightly MSCK).
  3. Athena saved queries + optional QuickSight/Grafana Athena plugin.
  4. Gate deploys: fail the pipeline if pass_rate for suite=core drops >2pp vs previous prompt_version.

Strict tool JSON contracts reduce flaky evals that look like model regressions. Guardrails on tool I/O (ApplyGuardrail) should be recorded as columns too (guardrail_intervened boolean) so you do not confuse blocked unsafe output with model failure.

typescript
// Minimal TypeScript emitter (same contract)
type EvalTrace = {
  eval_id: string;
  suite: string;
  prompt_version: string;
  model_id: string;
  pass: boolean;
  latency_ms: number;
  tokens_in: number;
  tokens_out: number;
  cost_usd: number;
  tenant_id: string;
  ts: string;
  guardrail_intervened?: boolean;
};

export function line(t: EvalTrace): string {
  return JSON.stringify(t) + "\n";
}

When to leave Athena for a warehouse

Stay on Athena while: scans stay under a few GB/day compressed, concurrency is analyst-scale, and schema is wide-but-flat. Move (or add) a warehouse when: you need sub-second BI for dozens of editors, heavy joins across product DBs, or fine-grained row-level security beyond S3/Lake Formation. Lake Formation + IAM still cover many multi-tenant read patterns before you pay warehouse tax.

Checklist: eval lake in a day

  • [ ] Freeze JSONL schema; enforce in emitter CI
  • [ ] Hive partitions dt= on S3; repair partitions automatically
  • [ ] Athena workgroup with result location + bytes-scanned cutoff
  • [ ] Three saved queries: pass rate, p95, tenant spend
  • [ ] Deploy gate on core suite pass_rate delta
  • [ ] Columns for guardrail intervention and model id
  • [ ] EMF for live cost; Athena for historical eval SQL

You do not need a warehouse to know which prompt version got worse overnight. You need honest traces, S3 partitions, and Athena.

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.