φ
Philφιλία · dev agent
philia (φιλία)·a coding agent experiment

I built a coding agent to find out where the tokens go.

Phil is a terminal coding agent built on LangGraph and DeepAgents. It plans with you, works in its own git worktree, and stops at a budget you set. Every change I make to it runs against a live benchmark, and I write up what I learn.

$uv tool install --editable git+https://github.com/ahochoy/phil.git
12×
Faster routingp95 latency, Jev vs. an LLM router
4 to 12×
Cheaper plansFrom two model tiers and prompt caching
0
Unsafe routesAcross 50 live benchmark runs
0
API keys in agent shellsEnv stripped, keyring backend set to null
Example runs·Three paths
phil chat · volute @ main|budget: $2.00
›phil "add subtract function with test"
Router: TypeSafe Jev (confidence: 0.94)latency: 140ms · $0.0001
✓ Classified: Simple change · quick path (skips architect and critic)
▸ Worktree: .phil/worktrees/r-98ac (isolated branch: phil/r-98ac)
▸ CALC-001Add subtract and test case
  tool  write  tests/test_math.py#1
  run   pytest -q  → 1 failed (expected RED)#2
  tool  edit   src/math.py#3
  run   pytest -q  → 8 passed (GREEN)#4
✓ CALC-001 done· 1 task · 1 attempt · 0 open issues
Open a PR for r-98ac → main? [y/N]run complete · spend: $0.04 (budget: $2.00)
idlevolute @ main|model: low·gemini-flash
budget: $0.04 / $2.00|0 open issues
Origin·Devlog, Part 1

The one-line change that took fifty minutes.

I asked an early version of Phil to add one hidden <meta name="easter-egg" content="hello world"> tag to a one-page Astro site. Any current model writes that line in two seconds. Here is where the other fifty minutes went.

What the run cost
438
Model Calls
3M+
Input Tokens

The model wrote reasonable code. The harness around it tripped over its own guardrails, dropped context on every approval, and insisted on a failing test for a meta tag.

1. Every ls needed approval

The agent used ls, cat, and grep to inspect the repository. None were on the execution allowlist, so each call paused for human approval. After each pause, the agent forgot its state and re-explored from zero.

Fix: Read-only commands like ls, cat, and grep run without approval, inside the worktree, with symlink checks.

2. Each retry started from zero

Each approval, retry, or resume started a new agent session. Most of the 3 million tokens were the same context, sent again.

Fix: SQLite checkpoints. A retry gets the diff and a summary of what already happened.

3. TDD for a meta tag

The orchestrator wanted a failing test before any code. A meta tag can't fail a unit test, so the agent looped in the red phase and never got out.

Fix: A separate check task type, verified by a command like the build. No test required.

4. No spending limit

A small website change on Sonnet ran 46 model calls and 1.56M tokens in 90 seconds ($3.17) with nothing to stop it.

Fix: Model call caps, a dollar budget ($2 by default), and prompt caching.
The full write-up:Building Phil, Part 1: Stop the Waste walks through each root cause and the benchmark harness I built to catch the next one.
Read Part 1↗
Architecture·LangGraph and DeepAgents

Every step is a node you can inspect.

Phil runs each job as a LangGraph state machine. Intake, planning, implementation, and review are separate nodes, and each one hands the next a contract it can check. When something goes wrong, you can see which step did it.

01

LangGraph Core

Intake, routing, planning, implementation, verification, and PR creation are LangGraph nodes with conditional edges between them.

• SQLite state checkpointing
• /resume after a crash or stop
• Serializable run telemetry
02

DeepAgents Delegation

An agent can hand a search to a subagent. The subagent spends the tool-call tokens, and the parent gets back a short answer.

• Subagent token isolation
• Context summarized at a set limit
• Cost logged per call
03

Worktree Sandboxing

Phil never touches your working directory. Each run gets its own .phil/worktrees/<id> on its own branch.

• Your uncommitted work stays put
• Cleanup after merge
• Several runs at once, in the background
Stack:
Python 3.14+LangGraph 1.2DeepAgents 0.7Rich and Typerprompt_toolkit
Design·Four decisions

Four decisions, and what they bought.

Each one came out of a run that went wrong. Here is what changed and what it saved.

01 · ROUTING

A classifier for routing

Deciding whether you asked a question or want a refactor is a classification problem. Paying $3 per million tokens for a chat model to make that call is overkill, so Phil uses TypeSafe Jev, a small "System One" decision model.

  • ›$0.042 per million input tokens, with no charge for output. About 70× cheaper than a frontier chat model.
  • ›12× lower p95 latency. Routing takes under 200 ms.
  • ›If Jev times out, the low-tier model routes instead.
Benchmark: 0 unsafe routes in 50 live runs.
02 · MODEL TIERS

Two tiers instead of six roles

Early versions of Phil asked you to pick a model for each of six internal roles. That was a lot to ask, so now there are two tiers.

HIGH TIER
Judgment and review
• Architect
• Critic
• Reviewer
LOW TIER
Fast execution
• Orchestrator
• Implementer
• Tester
Works with Claude, GPT, Gemini, DeepSeek, or a local Ollama or vLLM server.
03 · SECURITY

Agent shells never see your keys

An agent that can run shell commands is one prompt injection away from reading your keys. Phil assumes that will happen.

  • ›Subshells set PYTHON_KEYRING_BACKEND=null, so code the agent runs can't read the OS keychain.
  • ›API keys and tokens are removed from the environment of every child command.
  • ›Commands with &&, |, or ;, symlinks that point outside the worktree, and mutating flags are rejected.
Four rounds of adversarial review against grep, rg, and git escapes.
04 · PROCESS

Small jobs get a small process

A typo fix doesn't need an intake interview, an architect, and a code review. Phil picks one of three paths.

  • ›Questions run against a read-only snapshot. No branch, no commits.
  • ›A one-task change gets one implementer and one review. The quick-path demo above cost four cents.
  • ›When a request is ambiguous, Phil asks one multiple-choice question instead of guessing.
Limits: 15 model calls per attempt on the quick path, and $2.00 per run by default.
Devlog·A five-part series

A rule written first.

Every change to Phil runs against a live benchmark, and I write down what I expect before I run it. This series is the record: what broke, what I changed, and what it cost.

All devlog posts↗
PART 01

Stop the Waste

Read↗

Fifty minutes and 3 million tokens on a one-line meta tag. Where they went, and the benchmark harness that came out of it.

PART 02

Configure Once, Any Model

Coming soon

Six internal model roles become two tiers. API keys move to the OS keychain and stay out of agent shells.

PART 03

The Right Amount of Process

Coming soon

Routing with TypeSafe Jev, a small decision model, and deciding how much process each request deserves.

PART 04

Paying for What You Use

Coming soon

How planning a small change cost $3.17, and the call caps, prompt caching, and intake questions that fixed it.

PART 05

A Rule Written First

Coming soon

Setting classifier thresholds on 50 live runs, with the pass/fail rule written down before the first one.

Install·Three commands

Install it and point it at a repo.

There are no config files to write. Run phil setup once to pick a provider and models and to store your key in the OS keychain. Then run phil in any git repo.

01 · INSTALL

From a clone of the repo, with uv:

uv tool install --editable .
02 · SET UP

Pick a model for each tier. Your key goes in the OS keychain.

phil setup
03 · RUN

Start a chat in any git repo:

phil
ABOUT

Hi, I'm Andrew.

I build AI systems and developer tools, and I care about what they cost to run. Phil started as a question: how much of what a coding agent spends goes to real work? So far, less than you'd hope, and the fixes are mostly boring engineering. If you're working on something similar, I'd like to compare notes.