Coding agents

A coding agent builds your software, and the AI rules end up everywhere.

A coding agent that reads one intent file knows the goal, the data model, the rules and the budget. Declare what each operation needs and let the model decline rather than invent; test every operation on scripted replies with no key; keep the facts in an immutable ledger and the decisions beside it; give a long chain a time budget and read what each step cost.

What it costs today

A coding agent that adds a model call puts the goal in a string at the call site, the expected data in a class, the checks in an if statement and the retry in a loop. The next agent, or the next person, has to find all four before changing one, and nothing says which facts the operation needs, so a fact the input leaves out becomes a value the model invents and the type accepts.

Tests that need a key do not run in CI, so each change is checked by hand or not at all. A chain of calls with no budget fails late, and nobody can say which step spent the time or the tokens, or which step changed a fact it should only have read.

How IntentSharp handles it

The ranking below is one step of a job's estimate. Its input type names the fact the ranking depends on, the delivery mode, and its result may leave the order empty and list what is missing: with no delivery mode the model declines and the code asks, and with one it ranks. The chain after it gives each of four calls its own budget, a deadline for the whole, and a report of what each step cost.

The ranking's input and result
/// <summary>One way to deliver a job's materials.</summary>public sealed record DeliveryOption(string Name, double Cost, int Days); /// <summary>What the ranking needs: the job's delivery mode, which decides what "best" means, and the options.</summary>public sealed record RankingInput(string? DeliveryMode, IReadOnlyList<DeliveryOption> Options); /// <summary>The options in order, or no order and the facts that were missing.</summary>public sealed record Ranking(IReadOnlyList<string>? Order, IReadOnlyList<string> Missing);
Ranking.cs
AIResult<Ranking> ranked = await input.AIAsync<Ranking>(client,    "Rank the delivery options, best first, for the job's delivery mode. If the input does not state a " +    "delivery mode, return no order and list what is missing. Do not assume one.");if (!ranked.Success) return null;if (ranked.Value!.Order is null)    Console.WriteLine($"Ask for: {string.Join(", ", ranked.Value.Missing)}");   // the model declined; ask, do not guess
Output: without the fact, then with it
The ranking, with no delivery mode in the input:  Order: none  Missing: delivery modeThe same ranking, with the delivery mode "needed on site tomorrow":  Order: Courier, Pickup, Freight
The chain's deadline
// The chain's own deadline. Each call keeps its own budget inside it.using var deadline = new CancellationTokenSource(limit);try{    return await chain.RunAsync(job, deadline.Token);}catch (OperationCanceledException) when (deadline.IsCancellationRequested){    // Every stage that finished is saved: the next run of this job starts at the stage that was running.    return null;}
A unit test that needs no key (13 lines)
A test on a scripted reply
[Fact]public async Task Urgency_is_read_from_the_reply(){    var provider = new ScriptedProvider("""{"value": 4}""");    using var client = ScriptedClient(provider);     var result = await "The line stops on Thursday without the belts.".AIAsync<int>(client,        "Rate the urgency of this order email from 1 (routine) to 5 (critical).");     Assert.True(result.Success);    Assert.Equal(4, result.Value);    Assert.Equal(1, provider.CallCount);}
Output: what each step cost
A run of job J-1042, on a scripted model:  extract   passed  1 attempt    333 tokens   4.0 s  estimate  passed  1 attempt    335 tokens   9.0 s  review    passed  1 attempt    390 tokens   3.0 s  revise    passed  1 attempt    413 tokens   8.0 s  The chain: 4 stages, 4 model calls, 1471 tokens, 24.0 s. Estimate: 9 hours. 

The full chain, its checkpoints and its timings are in the long chains guide. Compiled and run with every build of this site; the model's answers are scripted, and a pass means the answer met the checks, not that it is right.

What stays in your control

The agent writes the intent, the data model and the rules. IntentSharp checks every answer against them, and your code keeps what to do with the result.

  • One file per operation. An intent file holds the intent, its data model, its rules, its examples and its budget: attempts, a time limit and the tokens of each reply. An agent edits it like code, a person reviews the change like code, and a field the format does not know is reported, not ignored.
  • The criteria are judgement. Whether the model declines or guesses is the model's judgement, which the intent's words guide. Only the type, the rules and your registered checks are checked in your process, and a pass means the answer met them, not that it is right.
  • What may stay empty. A nullable member and a list of what is missing give the model a way to decline. A check of your own refuses what the model must not invent, such as a citation of a file the input does not hold, and its message goes back to the model on the next attempt.
  • Tests with no key. ScriptedProvider stands in for the model, with no network and no key, and records every request. The client on it is the real client, so a test sees the same checks, retries and limits a live call would.
  • Facts that do not change. The evidence ledger keeps each fact as it was recorded; a later answer is a record of its own that names the fact. Each step's intent says which component may change what, and a rule set refuses a write to the ledger's table.
  • A budget per call, a deadline per chain. Each call has its own time budget, attempts and model calls; your own token stops the chain; each step's Attempts and Usage say what it cost. Progress and checkpoints are your application's code.
  • Limits count calls, tokens and time. Not money. To bound a chain's spend, add up its calls' Usage and stop when the total passes your limit.

Give the agent one file to read per operation.

dotnet add package IntentSharp