agentic examples

The Spend Ledger

The Spend Ledger: LLM plans spend real money, and money has rules older than software - integer cents (floats round YOUR money, never theirs), a ledger where every entry has a description, and a budget that stops the spending BEFORE the overdraft, not in the postmortem. The journal already receipts every task; this makes the receipts denominate.

Observability & Ops Round 16 Noel Rappin exit 0

source on github

bundle exec ruby examples/spend_ledger.rb

a real captured run

THE SPEND LEDGER (budget $45.00, prices in integer cents)

  plan status: partial_failure
  stopped at: budget: polish:tone costs $9.50 but only $5.10 remains

  the invoice (from the ledger, balances to the cent):
    item                     amount      running
    fetch:tickets             $0.00        $0.00
    classify:batch            $2.40        $2.40
    draft:responses          $18.75       $21.15
    review:drafts            $18.75       $39.90
    TOTAL                    $39.90   (budget $45.00)

  journal receipts: 4 spends, 1 declined - the money
  trail and the work trail live in ONE fsynced file, so 'what did
  this run cost' and 'what did this run do' are the same replay.

  three rules from every payments postmortem I've read: INTEGER
  CENTS (floats round your money eventually, and eventually is
  audit season); check affordability BEFORE the spend (a budget
  that only notices overdrafts is a historian); and classify
  budget-stop as RETRYABLE - tomorrow has a new budget, so the
  dead letter office requeues it instead of parking it with the
  revoked keys. the plan stopped at $39.90 of $45.00, which is
  the entire point: the overdraft that didn't happen is invisible
  in every metric except the one that matters.

source

# frozen_string_literal: true

# The Spend Ledger: LLM plans spend real money, and money has rules
# older than software - integer cents (floats round YOUR money, never
# theirs), a ledger where every entry has a description, and a budget
# that stops the spending BEFORE the overdraft, not in the postmortem.
# The journal already receipts every task; this makes the receipts
# denominate.
#
#   bundle exec ruby examples/spend_ledger.rb
#
# Runs offline; the invoice at the end balances to the cent.

require class="s">"bundler/setup"
require class="s">"agentic"
require class="s">"tmpdir"

Agentic.logger.level = class="y">:fatal

# Price list in INTEGER CENTS. 0.1 + 0.2 != 0.3 is a cute trivia
# question everywhere except billing, where it's a lawsuit.
PRICES = {
  class="s">"fetch:tickets" => 0,        # api call, free tier
  class="s">"classify:batch" => 240,     # cheap model
  class="s">"draft:responses" => 1875,   # expensive model, long output
  class="s">"review:drafts" => 1875,
  class="s">"polish:tone" => 950,
  class="s">"render:report" => 0
}.freeze

BUDGET_CENTS = 4_500

class SpendLedger
  attr_reader class="y">:entries

  def initialize(budget_cents:, journal:)
    @budget_cents = budget_cents
    @journal = journal
    @entries = []
  end

  def spent_cents = @entries.sum { |e| e[class="y">:cents] }

  def remaining_cents = @budget_cents - spent_cents

  # The affordability check runs BEFORE the work: a budget that only
  # notices overdrafts is a historian, not a control
  def afford!(description, cents)
    if cents > remaining_cents
      @journal.record(class="y">:spend_declined, description: description, cents: cents, remaining_cents: remaining_cents)
      raise Agentic:class="y">:Errors:class="y">:LlmRateLimitError, # budget exhaustion is transient: tomorrow has a new budget
        class="s">"budget: #{description} costs #{format_cents(cents)} but only #{format_cents(remaining_cents)} remains"
    end

    @entries << {description: description, cents: cents}
    @journal.record(class="y">:spend, description: description, cents: cents, remaining_cents: remaining_cents)
  end

  def format_cents(cents) = format(class="s">"$%.2f", cents / 100.0)

  def invoice
    puts format(class="s">"    %-20s %10s %12s", class="s">"item", class="s">"amount", class="s">"running")
    running = 0
    @entries.each do |e|
      running += e[class="y">:cents]
      puts format(class="s">"    %-20s %10s %12s", e[class="y">:description], format_cents(e[class="y">:cents]), format_cents(running))
    end
    puts format(class="s">"    %-20s %10s   (budget %s)", class="s">"TOTAL", format_cents(spent_cents), format_cents(@budget_cents))
  end
end

journal = Agentic:class="y">:ExecutionJournal.new(path: File.join(Dir.tmpdir, class="s">"agentic_spend.jsonl"))
File.delete(journal.path) if File.exist?(journal.path)
ledger = SpendLedger.new(budget_cents: BUDGET_CENTS, journal: journal)

orchestrator = Agentic:class="y">:PlanOrchestrator.new(
  concurrency_limit: 1, lifecycle_hooks: journal.lifecycle_hooks,
  retry_policy: {max_retries: 0, retryable_errors: []}
)
previous = nil
PRICES.each do |name, cents|
  task = Agentic:class="y">:Task.new(description: name, agent_spec: {class="s">"name" => name, class="s">"instructions" => class="s">"w"})
  orchestrator.add_task(task, previous ? [previous] : [], agent: ->(_t) {
    ledger.afford!(name, cents)
    class="s">"#{name} done"
  })
  previous = task
end

puts class="s">"THE SPEND LEDGER (budget #{ledger.format_cents(BUDGET_CENTS)}, prices in integer cents)"
puts
result = orchestrator.execute_plan
puts class="s">"  plan status: #{result.status}"
failed = result.results.values.find { |r| !r.successful? }
puts class="s">"  stopped at: #{failed.failure.message}" if failed
puts
puts class="s">"  the invoice (from the ledger, balances to the cent):"
ledger.invoice
puts
state = Agentic:class="y">:ExecutionJournal.replay(path: journal.path)
declined = state.events.count { |e| e[class="y">:event] == class="s">"spend_declined" }
puts class="s">"  journal receipts: #{state.events.count { |e| e[class="y">:event] == "spendclass="s">" }} spends, #{declined} declined - the money"
puts class="s">"  trail and the work trail live in ONE fsynced file, so 'what did"
puts class="s">"  this run cost' and 'what did this run do' are the same replay."
puts
puts class="s">"  three rules from every payments postmortem I've read: INTEGER"
puts class="s">"  CENTS (floats round your money eventually, and eventually is"
puts class="s">"  audit season); check affordability BEFORE the spend (a budget"
puts class="s">"  that only notices overdrafts is a historian); and classify"
puts class="s">"  budget-stop as RETRYABLE - tomorrow has a new budget, so the"
puts class="s">"  dead letter office requeues it instead of parking it with the"
puts class="s">"  revoked keys. the plan stopped at #{ledger.format_cents(ledger.spent_cents)} of #{ledger.format_cents(BUDGET_CENTS)}, which is"
puts class="s">"  the entire point: the overdraft that didn't happen is invisible"
puts class="s">"  in every metric except the one that matters."