Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Branching — fork an agent into strategies, compare, pick one

A runnable example of chidori.branch (see docs/branching-execution.md): an agent forks itself mid-run into N branches that each explore a strategy from the same anchored state, then compares the outcomes and picks one.

The agent (agent.ts) does shared "research" once, then forks:

const outcomes = await chidori.branch([
  { label: "outline-first", source: "examples/branching/strategies/outline_first.ts", input: { topic, research } },
  { label: "draft-direct",  source: "examples/branching/strategies/draft_direct.ts",  input: { topic, research } },
]);
const best = outcomes.filter((o) => o.status === "completed").reduce(pick);

Why this beats re-running the whole agent per idea:

  • The prefix is paid once. Branches act on the prefix's result (the parent's VFS plus the explicit input); they don't re-derive it — so the shared state is byte-identical across branches and the only variable is each branch's code. A controlled experiment, not a stochastic re-roll.
  • Each branch is its own editable source. Edit strategies/outline_first.ts and re-run: the branch re-anchors to the same shared state, the other strategy is untouched.
  • One durable record. The fan-out is recorded as a single branch call whose result is the outcomes array. chidori resume examples/branching/agent.ts <run-id> --dir examples/branching returns it from the call log without re-running either branch.
  • Branches nest in the trace. Each branch's host calls carry parent_seq = the branch call's seq, so an OTLP viewer (tael) shows a branch span with one subtree per strategy, side by side.

Run it

cargo run -- run examples/branching/agent.ts --input '{"topic": "incident postmortem"}'

Branch source paths resolve like callAgent paths (relative to the working directory), so run from the repository root.

With an OTLP endpoint configured the fork is visible live:

OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317 \
  cargo run -- run examples/branching/agent.ts

The strategies are offline-friendly local transforms; swap their bodies for chidori.prompt(...) calls to compare real model strategies (each branch's LLM spend is live — cap the fan-out accordingly).

Outcome shape

type BranchOutcome = {
  label: string;
  branchId: string;              // <parent run id>-op<branch seq>-branch-<k>
  status: "completed" | "paused" | "failed";
  output?: Json;                 // when completed
  pendingPrompt?: string;        // when paused (e.g. a chidori.input prompt)
  error?: string;                // when failed
};

Nested chidori.branch inside a branch is rejected. Pass { concurrency: N } as the second argument to run up to N branches at once (default 1 — sequential); outcome order always follows variant order.

The branch store: resume and edit-and-rerun

Each branch persists under the parent run (.chidori/runs/<run>/branches/op-<seq>/branch-<k>/): its own editable source.ts, its call log, and the fork-time anchor. That makes a branch independently operable after the parent has moved on:

# List this run's branches and their states:
chidori branches <run-id> --dir examples/branching

# A branch paused on chidori.input()? Answer it — the branch replays its
# checkpoint with the response and runs to its next outcome:
chidori branch-resume <run-id> <branch-id> --value "blue" --dir examples/branching

# Edit a strategy and re-run ONLY that branch from the same anchored state:
$EDITOR examples/branching/.chidori/runs/<run-id>/branches/op-*/branch-001/source.ts
chidori branch-rerun <run-id> <branch-id> --dir examples/branching

A resumed or re-run branch updates its own store; the parent's recorded outcome is immutable history (compare, don't merge).