Compare

replay production sessions instead of scoring a dataset

Braintrust scores your task over a dataset. Kitaru re-executes sessions you already served, tool calls answered from the recording. Built-in Braintrust importer.

Braintrust is an eval and observability platform. It captures logs and traces, holds datasets and scorers, and runs experiments. You iterate on prompts in a playground, score production traffic online as it arrives, and queue outputs for human review. The task is your own function, so Braintrust runs your agent over a dataset and stores the result as an experiment you can compare with the last one.

Kitaru answers a narrower question. When you change the model, a prompt, or a tool, what happens to the runs your agent already did in production? A session is a recorded run. Replay re-executes your agent’s real code over that session. Under a history tool policy, the recording answers the tool calls, so issue_refund() returns what it returned the first time instead of touching a card twice. Fork the session with one override and you get the same run under the change, next to the faithful baseline: an unchanged replay that reproduces the original. And because kitaru/braintrust is a built-in importer, the logs you already send to Braintrust are the way in.

Kitaru

Use Kitaru if you are

  • Shipping a model swap, a prompt edit, or a tool change to an agent that is already in production, and want to know what breaks on real traffic before users find it
  • Testing multi-turn agents where what regressed is the path the agent took, not the final string
  • Running tools that write to real systems (refunds, tickets, emails) and can't let a test call them a second time
  • Already logging to Braintrust and want those logs runnable, not just readable
  • Self-hosting the whole loop under Apache 2.0 without an Enterprise contract, so traces and provider keys stay in your own systems

Braintrust

Use Braintrust if you are

  • Iterating on prompts and want a playground, a dataset editor, and side-by-side output review
  • Scoring single-turn completions where the output text is what gets judged
  • Scoring production traffic online as it arrives, with dashboards and alerts on top
  • Running a review queue for a team of reviewers, with assignees and keyboard-driven batches
  • Writing in Go, Java, Ruby, or C#: Kitaru's SDKs are Python and TypeScript
Braintrust scores what your agent produced on a dataset. Kitaru re-runs what your agent did in production, with one thing changed.

Evals over a dataset vs replay of a recorded session

Both products run your code. The difference is what they run it on, and what the tools do while it runs.

A Braintrust Eval takes a dataset, calls your task on every row, scores what came back, and stores the run as an experiment. The rows can be curated by hand or built from logs. The task can be anything from one model call to a multi-step agent.

A Kitaru replay takes a session that already happened in production and re-executes your agent’s code over it from the top. Under a history tool policy, the recording answers each tool call by name and arguments. You compare the replay against the faithful baseline of the same run, not two experiments over a dataset.

Braintrust · EvalYour task, over a dataset
Population
row 1row 2row 3+ 47
Curated by hand, or built from logs
Runs
task(row)your code, once per row
issue_refund()live, unless you stub it yourself
Compares
this experimentthe last one
Kitaru · replayYour code, over a recorded session
Population
#4821#4822#4830+ 47
Sessions your users already generated
Runs
support.pyre-executes from the top
issue_refund()answered from the recording, by name and arguments
Compares
the replaythe baseline of the same run
  • Population: Dataset rows you maintain or sessions your users generated.
  • Tools during the run: Live or hand-stubbed, or governed by a tool policy: answered from the recording (history), canned (static), or live only for safe read-only calls (passthrough). on_miss decides what happens to an unrecorded call: fail, error_result, or passthrough.
  • What gets compared: This experiment against the previous one, or the replay against the baseline of the same session.
  • What a change looks like: A new prompt in the playground or new code in the task, or one override on the experiment with everything else held fixed.

Your Braintrust logs are the way in

Braintrust stays your system of record. Kitaru takes a runnable copy of the runs you care about.

Export project logs as JSONL and import them with kitaru/braintrust@latest. Every event becomes a node, the span tree survives, and traces that share a session_id or thread_id group into one multi-turn session. The import runs on a worker in your environment.

Three limits:

  • Braintrust scores are dropped. Kitaru evaluators rescore every imported session, so baseline and replay are judged by the same code.
  • Replay needs your code. A trace export holds no code, so an imported session can be investigated, evaluated, and added to a cohort as it is. To fork it, register the agent’s run command or wrap it with a native adapter.
  • Use the API export. Flat UI exports lose the span tree, and one import is capped at 50 MiB.
Braintruststays your system of record
project logsspans, tokens, cost
datasetsscorers, playground
experimentstask over a dataset
Nothing changes in production. Keep logging as you do.
Kitarutakes a runnable copy
sessionsspan tree survives; turns grouped by thread
cohortsthe population, frozen
experimentscode re-run, one thing changed
The import runs on a worker in your environment.

A cohort is production, frozen

A dataset is a list of rows someone maintains. Braintrust makes that easy. You can build rows from logs, edit them in place, or let Loop propose new ones. The list is still yours to curate, and it drifts from real traffic the moment the product changes.

A cohort is a different kind of thing. It’s a named set of sessions belonging to one agent. A cohort version’s member list never changes after creation. An experiment run replays every session in one cohort version against one agent version and evaluates baseline and replay alike. So a result keeps meaning what it meant when you read it.

Dataset you curateBuilt from logs or by hand, then maintained
row 1row 2row 3row 4row 5row 6
Reflects productionas of when the row was added
Edit it in place, or let Loop propose new rows. It still drifts the moment the product changes.
Cohort version, frozenSessions that already happened
#4821#4822#4830#4844#4851#4862 ⚑
Reflects productionit is production
The member list never changes after creation. The flagged one is last Tuesday's incident, now a permanent regression case.
  • Selection: By tag, by filter, session id, or branching from an earlier version.
  • Incidents: The conversation that went wrong last Tuesday is a session. Add it to the next cohort version and it is a permanent regression case, not a note in a retro.
  • Gating: kitaru experiment run start with --wait exits nonzero when the run fails, so a pull request can block on it. Each evaluator writes its own pass or fail.
  • Drift: A dataset ages quietly but a cohort version is pinned, and you cut the next one on purpose.

Human judgment that calibrates the evaluator

Braintrust’s human review is good. Reviewers get structured scores, expected values, comments, and tags, in a kanban queue with assignees and keyboard navigation for big batches.

Kitaru asks a different question: what’s the annotation for? A coding agent authors the investigation, not you. It picks the sessions and phrases one question per session. A human answers, and each answer is pinned to the evidence that supports it: a node, a JSON path, or a character range. Every session gets a verdict of acceptable, problematic, or uncertain. Then the answers do work. Evaluators are checked against them, cohorts are justified by them, and an evaluator that gates a deploy can show the human judgments it was calibrated on.

Braintrust · review queueKanban, assignees, keyboard batches
span · llm_call
score0.4
expected"Refund issued for #4821"
comment"escalated instead of refunding"
refundescalation
Attached to the span
Kitaru · investigationOne question per session, authored by a coding agent
"Should this refund have been escalated?"
session #4862node 7$.output.reasonchars 40–71
verdictproblematic
the evaluator is checked against it
the cohort is justified by it

What makes Kitaru unique

FeatureKitaruBraintrustWhat that means
Logging and trace capture for LLM appsPartial supportYesKitaru records sessions through adapters and expects a trace store beside it.
Prompt playground and dataset editorNot supportedYesNo Kitaru equivalent.
Online scoring of production traffic as it arrivesNot supportedYesKitaru evaluates sessions on demand, on import, or inside an experiment run.
Human review queue with assignees and keyboard navigationPartial supportYesKitaru has an investigation review page with verdicts and pinned annotations; no assignees or shortcuts.
Runs your task over a dataset (Eval SDK, remote evals, sandboxes)Not supportedYesKitaru has no dataset object. The population is sessions.
Re-executes a recorded production session with tool calls answered from the recordingYesNot supportedThe core difference. Needs the agent's code or a native adapter.
Tool policy on replay: history, static, passthrough, with on_miss set to failYesNot supportedSo a refund is not issued twice.
Fork one session with one override and diff it against the faithful baselineYesPartial supportBraintrust compares two experiments over a dataset and re-runs prompts in the playground. It doesn't replay a stored trace.
Cohorts: immutable versions of production sessions as the test populationYesPartial supportBraintrust datasets can be built from logs, but they are curated rows.
Annotations pinned to a node, a JSON path, or a character range, used to calibrate evaluatorsYesPartial supportBraintrust scores, expected values, and comments attach to a span.
Imports Braintrust, Langfuse, LangSmith, Logfire, and Arize Phoenix tracesYesNot supportedkitaru/braintrust is built in. No importer code to write.
Open sourceYesNot supportedKitaru is Apache 2.0.
Self-hosting without an Enterprise tierYesNot supportedBraintrust self-hosts the data plane on Enterprise; the control plane stays hosted by Braintrust.

How the two surfaces map

ConceptBraintrustKitaru
LayerEval and observability platform: log, curate, scoreReplay-based eval layer: re-run what happened
Core unitA span in a log, and a row in a datasetA session: a recorded run, re-executable
Test populationDataset you curate, or build from logsCohort version frozen from production sessions
What an experiment doesRuns your task over the dataset and scores itRe-executes your code over every session in a cohort version, one override applied
Tool calls in a testLive, or stubbed by youAnswered from the recording per tool policy
Human reviewQueue with scores, expected values, comments, tags, assigneesAgent-authored investigation; answers pinned to evidence; one verdict per session
Getting data inSDK logging, OpenTelemetry, or the APINative adapter, or a built-in trace importer
RelationshipSystem of recordTakes a runnable copy of it
SDKsTypeScript, Python, Go, Java, Ruby, C#Python and TypeScript, plus the CLI and an MCP server
Where it runsBraintrust cloud; hybrid self-hosting on Enterprise (data plane in your cloud, control plane hosted)Self-hosted under Apache 2.0 (Docker, Helm), or Kitaru Cloud
Price of entryStarter free (1 GB, 10k scores, 14-day retention); Pro $249 per monthOpen source free; Cloud $39 per month, 14-day trial, no card

Code comparison

Braintrust + Kitaru
# Braintrust keeps logging. Nothing changes in production.
# Export the project logs, then import them once.
kitaru session import braintrust-logs.jsonl \
--importer kitaru/braintrust@latest \
--agent support-agent@latest \
--media-type application/x-ndjson \
--tag imported-baseline --wait

# Freeze the sessions that matter into the test population.
kitaru cohort create refund-regression --agent support-agent \
--tag imported-baseline --display-version week-32

# Move one variable. Tool calls come from the recording, so
# nothing external fires and no card is refunded twice.
kitaru experiment create cheaper-model \
--agent support-agent \
--evaluator refund-check@latest \
--override '{"model": {"openai:gpt-5.4": "openai:gpt-5-nano"}}' \
--tool-policy '{"default": {"type": "history",
                "scope": "cohort_version", "on_miss": "fail"}}'

# Re-execute the whole cohort against the change.
# Exits nonzero on failure, so CI can gate on it.
kitaru experiment run start cheaper-model \
--cohort-version <cohort-version-id> \
--agent support-agent@1 --evaluate-baselines --wait
Braintrust alone
from braintrust import Eval
from autoevals import Factuality

# The dataset is the population. You write it, or build it from logs.
def load_cases():
  return [
      {"input": "Refund order #4821", "expected": "Refund issued for order #4821"},
      # ... and the rest, by hand
  ]

# task() is your code. Braintrust runs it on every row.
# Tools inside it are live unless you stub them yourself.
def task(input):
  return support_agent(input)

Eval(
  "support-agent",
  data=load_cases,
  task=task,
  scores=[Factuality],
)

# Scores this run and compares it with the last experiment.
# It cannot replay a production conversation with the tool
# results that conversation actually saw.

Make your Braintrust
logs runnable

Keep Braintrust. It’s the system of record, the playground, and the review queue. None of that has to move. Kitaru adds the step a dataset eval stops short of. It takes the runs you already served and re-executes your agent over them with one thing changed and the tool calls answered from the recording. Then it tells you what regressed before the change ships. One import, no instrumentation, and last Tuesday’s incident becomes a test you can never accidentally drop.