Earlier this year, Andrej Karpathy (@karpathy) pointed an agent at his own training code and let it run for two days. It ran 700 experiments, kept the 20 that beat the benchmark, and made the model train 11% faster. Then he said something quite interesting: any metric you can evaluate cheaply can be handed to an agent swarm.
Reply rate is a metric you can evaluate cheaply. I have spent some time since working out what that loop looks like pointed at outbound.
My build:
Codex reads last week's outcomes, edits the scoring and play files the outbound system runs on, runs a test, and opens a pull request. It proposes a change to the playbook with the evidence and the score attached, then waits for a human to approve it. Sending and merging stay outside the loop.
I have built the first loop a few times: sense the market, score the account, write from the signal, check the message, log the outcome, learn from the reply. This article is about the second loop, the one that edits the first.
That is the build: GTM as versioned code that improves from the market.
The repo
Start with the folder. The shape matters because Codex can only improve what it can read and edit.
The repo is intentionally plain. config/scoring.yaml holds the rules that decide which signals matter. prompts/ holds the plays that write the messages. memory/outcomes.jsonl holds what the market did. evals/score.py is the gate that says whether a proposed change helped. AGENTS.md is the law Codex reads before it touches anything.
Run the first version offline. No CRM, no enrichment, no delivery system. The improvement loop should prove itself on local files before it gets anywhere near a real outbound machine.
Step 1. Write the law first
Before the scoring file, before the prompt files, write AGENTS.md. This is the file that keeps the agent useful and contained.
The law has one job: narrow the work. Without it, Codex will try to help by expanding scope. It will add more data, touch more files, call more tools, or automate a step that should stay under human control. Here the job is smaller: read outcomes, propose one file change, prove it helped, then wait.
What good looks like. You can read the law before approving a PR and know exactly what Codex was allowed to do.
Where it breaks. The law becomes a compliance document. If AGENTS.md needs a table of contents, it is already too large. Keep it operational.
Step 2. Move judgment into config
Most outbound judgment lives in someone's head. Then the team buys software and expects the software to improve a decision it cannot see.
Move the judgment into a file.
This file starts as a visible hypothesis. If a generic download should count for zero, the team can point to the exact line and change it. If an implementation page visit is a stronger signal than you thought, Codex can propose the diff and show the outcome rows that justify it.
Do not bury this logic in a Python function. If the rule is visible, the team can review it, argue with it, and improve it without turning a sales judgment into an engineering refactor.
What good looks like. The file is small enough to argue with. Five signals is a good first version.
Where it breaks. The scoring file becomes a junk drawer. Twenty signals, six thresholds, and exception rules for every edge case will make the improver overfit. Start narrow and let the outcomes tell you where the next knob belongs.
Step 3. Write outcomes as memory
The most important file is memory/outcomes.jsonl.
One line per touch, written when the outcome is known:
The reason field is the whole point. no_reply tells you almost nothing. content-only intent tells the next run that this signal might not deserve a draft. bad_fit is useful only when the reason explains why. asked about implementation timeline is the kind of detail that can change a weight.
Build the validator before you build the improver:
This is where the compounding starts. A dashboard can tell you a campaign is down. A clean outcome log can tell Codex which signal, play, or phrase should change before the next run.
What good looks like. After a week, a stranger can read the file and tell which signals created replies, which plays created bad-fit conversations, and which internal favorite the market ignored.
Where it breaks. The team backfills outcomes on Friday from memory. The wins survive, the bad-fit reasons blur, and the system learns from fiction. Write the row when the outcome lands.
Step 4. Build the eval gate
Before Codex edits anything, it needs a test it cannot explain away.
Create evals/fixtures.yaml:
Then create evals/score.py:
The first gate should be small enough to understand and sharp enough to catch a real miss. In my first run, the baseline failed one case:
That was good. The system had implementation intent below the draft threshold, so it ignored an account the fixture said deserved a message. Better to catch that in a test than after a month of missed accounts.
What good looks like. One command gives one number, and every failed case is easy to inspect.
Where it breaks. The fixture only includes obvious wins. Then every reckless change passes. Put ugly cases in the gate: weak intent, bad fit, no reply, stale signals, and the accounts you wish the system had skipped.
Step 5. Let Codex propose one scoring change
Now Codex can edit.
Create prompts/improve_scoring.md:
Run it through the repo wrapper:
The first version of my improver made a useful mistake. It chased the cleanest-looking reply signal. competitor_comparison had the strongest reply rate in the tiny outcome log, so the improver wanted to increase that weight. The eval stayed at 0.75, so the change was rejected.
That is exactly why the gate exists. A weaker system would have accepted the story because it sounded reasonable. This one asked a better question: did the change fix the known miss?
The second pass found the smallest edit that helped:
The eval passed:
That is the moment the loop becomes useful. It changed one rule, for one reason, and proved the change against a fixture.
What good looks like. The proposed diff is boring and traceable: one line changed, one outcome-backed reason attached, one eval improved.
Where it breaks. Codex changes three weights and two prompts at once. Now nobody can tell which change helped. Keep the law strict: one concept per proposal.
Step 6. Improve prompt files separately
Scoring is only half the system. The message templates decay too.
A line that worked last month starts sounding familiar. A question that earns replies in one segment gets ignored in another. A phrase that feels sharp internally gets punished by the market. Treat prompt improvement as a separate lane so Codex does not mix scoring and copy in the same PR.
Create config/plays.yaml:
Then create prompts/improve_prompt.md:
Some improvements can be scored automatically. Others still need taste. If there is no copy eval, Codex can propose the prompt edit, but it should mark the PR for review instead of pretending the edit is proven.
What good looks like. Codex says, "This phrase appeared in seven no-reply outcomes, so I added it to banned_lines," or "positive replies quoted the implementation detail in sentence one, so I tightened the play to require that."
Where it breaks. The improver rewrites the whole voice because one message got a reply. Prompt edits should be smaller than your instinct.
Step 7. Ship changes as pull requests
This is the control layer. Codex edits files, runs the eval, and writes the PR summary. A human reviews and merges.
Create prompts/pr_summary.md:
Create `scripts/open_pr.sh`:
The PR should read like a teammate wrote it:
That is the safety system. Codex does the tedious work. The operator keeps the standard.
What good looks like. One PR a week, small diff, clear reason, passing eval.
Where it breaks. Someone gives Codex permission to merge because review feels like friction. That minute separates a system that improves from a system that drifts.
Step 8. Put it on a cadence
Do not run this after every reply. That is how a system overfits to one loud account.
Let the week happen, let outcomes accumulate, then tune.
Create scripts/weekly_tune.sh:
Then cron:
If you use GitHub Actions, keep the same shape:
Run the first two tune-ups by hand. Read every diff. Watch what Codex tries to change when the sample is thin. Once the proposals are boring, put it on a schedule.
What good looks like. A weekly PR appears with the evidence, the diff, and the eval result. You merge, edit, or close it.
Where it breaks. The job runs, nobody reviews, and PRs pile up. A self-improving system still has one human habit: read the diff.
The clone-and-run version
The repo should ship with four commands:
Expected first run:
The offline demo proves the file contracts. The Codex run proves the editing loop. After that, replace the sample outcomes with your own, rename the signals, add your plays, and build a fixture that reflects the accounts you wish the system had routed differently.
Do not start by wiring delivery. Start by proving the improvement loop.
The full version: max
This repo is the manual layer. It runs from files, public signals, and your Codex plan. It teaches the shape because every rule is exposed.
yourmax.ai is the same system with the seams hidden.
Instead of a repo you wire together yourself, max is the agent you use directly. It detects movement in the market, decides who is worth contacting and why now, drafts the outreach across email and LinkedIn for your approval, and keeps improving from the outcomes.
The repo shows the self-tuning layer most teams never build: outcomes become proposed rule changes, proposed rule changes run through a gate, and the human merge decides what becomes live. max takes that same operating logic and runs it as a managed system.
If you want the full repo, you can let me know and I will send it your way.