agentic examples

The Gentle Deadline

The Gentle Deadline: most deadline code is violent - a timeout fires, everything dies, the user gets an error page at 30.0 seconds that could have been a good-enough answer at 29. This plan knows which of its tasks are ESSENTIAL and which are garnish, and when the time budget runs low it starts declining the garnish - politely, by name, with the meal still served on time.

Reliability & Recovery Round 16 Yukihiro Matsumoto exit 0

source on github

bundle exec ruby examples/gentle_deadline.rb

a real captured run

THE GENTLE DEADLINE (essential courses always; garnish as time allows)

  a leisurely evening (budget 500ms):
    served 6 courses in 256ms - status: completed
    everything made it, garnish and all

  a hurried lunch (budget 160ms):
    served 3 courses in 129ms - status: completed
    declined with regrets: garnish: related links; garnish: pull quotes; dessert: summary haiku
    (the meal was still served - nobody saw an error page)

  the design is one question asked politely: before an OPTIONAL
  task starts, is there comfortably time for it AND the essentials
  still owed? if not, it declines BY NAME and the plan flows on.
  compare the violent alternative - a global timeout that kills
  the render step because the pull quotes ran long, serving the
  user an error instead of a plainer dinner. deadlines are not
  the enemy of graciousness; treating every task as equally
  essential is. mark the garnish as garnish, and lateness becomes
  a menu decision instead of an outage.

source

# frozen_string_literal: true

# The Gentle Deadline: most deadline code is violent - a timeout
# fires, everything dies, the user gets an error page at 30.0
# seconds that could have been a good-enough answer at 29. This plan
# knows which of its tasks are ESSENTIAL and which are garnish, and
# when the time budget runs low it starts declining the garnish -
# politely, by name, with the meal still served on time.
#
#   bundle exec ruby examples/gentle_deadline.rb
#
# Runs offline; the same dinner is cooked twice, hurried once.

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

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

# Course list: essential tasks make the meal; optional ones make it lovely
COURSES = [
  {name: class="s">"stock: fetch data", essential: true, cost: 0.04},
  {name: class="s">"main: analyze", essential: true, cost: 0.06},
  {name: class="s">"garnish: related links", essential: false, cost: 0.05},
  {name: class="s">"garnish: pull quotes", essential: false, cost: 0.05},
  {name: class="s">"dessert: summary haiku", essential: false, cost: 0.03},
  {name: class="s">"serve: render answer", essential: true, cost: 0.02}
].freeze

def cook(budget_seconds)
  deadline = Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) + budget_seconds
  orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 1)
  declined = []
  previous = nil

  COURSES.each do |course|
    task = Agentic:class="y">:Task.new(description: course[class="y">:name], agent_spec: {class="s">"name" => course[class="y">:name], class="s">"instructions" => class="s">"cook"})
    orchestrator.add_task(task, previous ? [previous] : [], agent: ->(_t) {
      remaining = deadline - Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC)
      # The gentle part: an optional course checks whether there is
      # comfortably time for it AND the essentials still to come, and
      # bows out by name instead of being murdered mid-simmer
      essentials_owed = COURSES.drop(COURSES.index(course) + 1).select { |c| c[class="y">:essential] }.sum { |c| c[class="y">:cost] }
      if !course[class="y">:essential] && remaining < course[class="y">:cost] + essentials_owed + 0.01
        declined << course[class="y">:name]
        next class="y">:declined_with_regrets
      end

      sleep(course[class="y">:cost])
      class="s">"#{course[class="y">:name]} ready"
    })
    previous = task
  end

  started = Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC)
  result = orchestrator.execute_plan
  [result, Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) - started, declined]
end

puts class="s">"THE GENTLE DEADLINE (essential courses always; garnish as time allows)"
puts

[[class="s">"a leisurely evening", 0.5], [class="s">"a hurried lunch", 0.16]].each do |occasion, budget|
  result, elapsed, declined = cook(budget)
  served = result.results.values.count { |r| r.successful? && r.output != class="y">:declined_with_regrets }
  puts class="s">"  #{occasion} (budget #{(budget * 1000).round}ms):"
  puts format(class="s">"    served %d courses in %dms - status: %s", served, (elapsed * 1000).round, result.status)
  if declined.any?
    puts class="s">"    declined with regrets: #{declined.join("; class="s">")}"
    puts class="s">"    (the meal was still served - nobody saw an error page)"
  else
    puts class="s">"    everything made it, garnish and all"
  end
  puts
end

puts class="s">"  the design is one question asked politely: before an OPTIONAL"
puts class="s">"  task starts, is there comfortably time for it AND the essentials"
puts class="s">"  still owed? if not, it declines BY NAME and the plan flows on."
puts class="s">"  compare the violent alternative - a global timeout that kills"
puts class="s">"  the render step because the pull quotes ran long, serving the"
puts class="s">"  user an error instead of a plainer dinner. deadlines are not"
puts class="s">"  the enemy of graciousness; treating every task as equally"
puts class="s">"  essential is. mark the garnish as garnish, and lateness becomes"
puts class="s">"  a menu decision instead of an outage."