agentic examples

The Plan Tour

The Plan Tour: hand any orchestrator to the guide and it narrates the plan as prose - first this, then that, meanwhile the other - read straight from graph[:order] and graph[:edges]. If the prose sounds wrong, your plan IS wrong, and you found out before running it.

Plans & Graphs Round 6 Matz exit 0

source on github

bundle exec ruby examples/plan_tour.rb

a real captured run

THE PLAN, AS PROSE (nobody has cooked anything yet)

  First, boil the kettle.
  Meanwhile, slice the bread.
  After "boil the kettle": soft-boil the eggs.
  After "boil the kettle": steep the tea.
  After "slice the bread": toast the bread.
  After "soft-boil the eggs" (your protein), "toast the bread" (your crunch) and "steep the tea" (your comfort): plate everything.

the narration is generated from graph[:order] and graph[:edges] -
the same topology the scheduler will execute. read your plan aloud
before you run it; ears catch what eyes skim.

source

# frozen_string_literal: true

# The Plan Tour: hand any orchestrator to the guide and it narrates the
# plan as prose - first this, then that, meanwhile the other - read
# straight from graph[:order] and graph[:edges]. If the prose sounds
# wrong, your plan IS wrong, and you found out before running it.
#
#   bundle exec ruby examples/plan_tour.rb
#
# Runs offline; narration only, no execution.

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

# A breakfast, planned properly
orchestrator = Agentic:class="y">:PlanOrchestrator.new

def step(name)
  Agentic:class="y">:Task.new(description: name, agent_spec: {class="s">"name" => name, class="s">"instructions" => class="s">"cook"})
end

kettle = step(class="s">"boil the kettle")
eggs = step(class="s">"soft-boil the eggs")
bread = step(class="s">"slice the bread")
toast = step(class="s">"toast the bread")
tea = step(class="s">"steep the tea")
plate = step(class="s">"plate everything")

orchestrator.add_task(kettle)
orchestrator.add_task(bread)
orchestrator.add_task(eggs, [kettle])
orchestrator.add_task(tea, [kettle])
orchestrator.add_task(toast, [bread])
orchestrator.add_task(plate, needs: {protein: eggs, crunch: toast, comfort: tea})

# --- the guide: graph in, prose out ------------------------------------------
def narrate(graph)
  names = graph[class="y">:tasks].transform_values(&class="y">:description)
  incoming = graph[class="y">:edges].group_by { |e| e[class="y">:to] }
  narrated = []
  sentences = []

  graph[class="y">:order].each do |task_id|
    edges_in = incoming[task_id] || []
    sentence =
      if edges_in.empty?
        opener = narrated.empty? ? class="s">"First" : class="s">"Meanwhile"
        class="s">"#{opener}, #{names[task_id]}."
      else
        reasons = edges_in.map { |e|
          e[class="y">:label] ? class="s">"\"#{names[e[class="y">:from]]}\class="s">" (your #{e[class="y">:label]})" : class="s">"\"#{names[e[class="y">:from]]}\class="s">""
        }
        joined = (reasons.size > 1) ? reasons[0..-2].join(class="s">", ") + class="s">" and #{reasons.last}" : reasons.first
        class="s">"After #{joined}: #{names[task_id]}."
      end
    sentences << sentence
    narrated << task_id
  end

  sentences
end

puts class="s">"THE PLAN, AS PROSE (nobody has cooked anything yet)"
puts
narrate(orchestrator.graph).each { |sentence| puts class="s">"  #{sentence}" }
puts
puts class="s">"the narration is generated from graph[class="y">:order] and graph[class="y">:edges] -"
puts class="s">"the same topology the scheduler will execute. read your plan aloud"
puts class="s">"before you run it; ears catch what eyes skim."