agentic examples

The Telemetry Bus

The Telemetry Bus: lifecycle hooks are callbacks - one producer, one consumer, coupled at configuration time. A telemetry bus inverts that: the orchestrator emits NAMED EVENTS into a bus, and any number of handlers attach, detach, and crash independently. The producer never learns who is listening. This is the :telemetry pattern Elixir converged on, because every library inventing its own instrumentation callbacks was the worse world.

Observability & Ops Round 11 José Valim exit 0

source on github

bundle exec ruby examples/telemetry_bus.rb

a real captured run

TELEMETRY BUS (three handlers, one bridge, zero coupling)

  run 1 - all handlers attached:
    [trace] SLOW: enrich took 82ms
    [bus] handler exporter crashed (IOError) - detached, plan unharmed
    [metrics] {:tasks=>3}

  run 2 - tracer detached at runtime (ops got tired of it):
    [metrics] {:tasks=>6}

  the orchestrator emitted the same events both runs - it cannot
  tell that the tracer left or that the exporter crashed, and
  that ignorance is the feature. hooks couple one producer to
  one consumer at configuration time; a bus decouples N handlers
  at RUNTIME, with isolation (the Friday exporter died alone).
  event names are namespaced tuples, measurements are separated
  from metadata - steal the whole :telemetry design; it was
  right. the framework's hooks made the bridge ten lines, which
  is exactly what hooks are for: being the floor a bus stands on.

source

# frozen_string_literal: true

# The Telemetry Bus: lifecycle hooks are callbacks - one producer,
# one consumer, coupled at configuration time. A telemetry bus
# inverts that: the orchestrator emits NAMED EVENTS into a bus, and
# any number of handlers attach, detach, and crash independently.
# The producer never learns who is listening. This is the :telemetry
# pattern Elixir converged on, because every library inventing its
# own instrumentation callbacks was the worse world.
#
#   bundle exec ruby examples/telemetry_bus.rb
#
# Runs offline; three handlers listen, one detaches mid-flight.

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

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

# The bus: names, payloads, and isolation. A crashing handler is
# detached and reported - it never takes the plan down with it.
class TelemetryBus
  def initialize
    @handlers = Hash.new { |h, k| h[k] = {} }
  end

  def attach(id, event, &handler)
    @handlers[event][id] = handler
  end

  def detach(id)
    @handlers.each_value { |hs| hs.delete(id) }
  end

  def execute(event, measurements, metadata = {})
    @handlers[event].each do |id, handler|
      handler.call(measurements, metadata)
    rescue => e
      detach(id)
      puts class="s">"    [bus] handler #{id} crashed (#{e.class}) - detached, plan unharmed"
    end
  end
end

BUS = TelemetryBus.new

# The bridge: hooks in, events out. This is the ONLY place the
# orchestrator and the bus know about each other.
def telemetry_hooks(bus)
  {
    after_task_success: ->(task_id:, task:, result:, duration:) {
      bus.execute([class="y">:agentic, class="y">:task, class="y">:success], {duration: duration}, {task: task.description})
    },
    after_task_failure: ->(task_id:, task:, failure:, duration:) {
      bus.execute([class="y">:agentic, class="y">:task, class="y">:failure], {duration: duration}, {task: task.description, type: failure.type})
    },
    plan_completed: ->(plan_id:, status:, execution_time:, tasks:, results:) {
      bus.execute([class="y">:agentic, class="y">:plan, class="y">:completed], {execution_time: execution_time}, {status: status})
    }
  }
end

# Handler 1: a metrics counter - knows nothing about logging or tracing
metrics = Hash.new(0)
BUS.attach(class="y">:metrics, [class="y">:agentic, class="y">:task, class="y">:success]) { |m, _| metrics[class="y">:tasks] += 1 }
BUS.attach(class="y">:metrics2, [class="y">:agentic, class="y">:task, class="y">:failure]) { |m, _| metrics[class="y">:failures] += 1 }

# Handler 2: a slow-task tracer - only speaks when something is worth saying
BUS.attach(class="y">:tracer, [class="y">:agentic, class="y">:task, class="y">:success]) do |measurements, metadata|
  puts class="s">"    [trace] SLOW: #{metadata[class="y">:task]} took #{(measurements[class="y">:duration] * 1000).round}ms" if measurements[class="y">:duration] > 0.05
end

# Handler 3: a fragile exporter someone deployed on a Friday
BUS.attach(class="y">:exporter, [class="y">:agentic, class="y">:task, class="y">:success]) do |_m, metadata|
  raise IOError, class="s">"export endpoint down" if metadata[class="y">:task] == class="s">"enrich"
end

def run_plan(bus)
  orchestrator = Agentic:class="y">:PlanOrchestrator.new(lifecycle_hooks: telemetry_hooks(bus))
  fetch = Agentic:class="y">:Task.new(description: class="s">"fetch", agent_spec: {class="s">"name" => class="s">"w", class="s">"instructions" => class="s">"w"})
  enrich = Agentic:class="y">:Task.new(description: class="s">"enrich", agent_spec: {class="s">"name" => class="s">"w", class="s">"instructions" => class="s">"w"})
  publish = Agentic:class="y">:Task.new(description: class="s">"publish", agent_spec: {class="s">"name" => class="s">"w", class="s">"instructions" => class="s">"w"})
  orchestrator.add_task(fetch, agent: ->(_t) { sleep(0.01) })
  orchestrator.add_task(enrich, [fetch], agent: ->(_t) { sleep(0.08) })
  orchestrator.add_task(publish, [enrich], agent: ->(_t) { sleep(0.01) })
  orchestrator.execute_plan
end

puts class="s">"TELEMETRY BUS (three handlers, one bridge, zero coupling)"
puts
puts class="s">"  run 1 - all handlers attached:"
run_plan(BUS)
puts class="s">"    [metrics] #{metrics.inspect}"
puts

puts class="s">"  run 2 - tracer detached at runtime (ops got tired of it):"
BUS.detach(class="y">:tracer)
run_plan(BUS)
puts class="s">"    [metrics] #{metrics.inspect}"
puts
puts class="s">"  the orchestrator emitted the same events both runs - it cannot"
puts class="s">"  tell that the tracer left or that the exporter crashed, and"
puts class="s">"  that ignorance is the feature. hooks couple one producer to"
puts class="s">"  one consumer at configuration time; a bus decouples N handlers"
puts class="s">"  at RUNTIME, with isolation (the Friday exporter died alone)."
puts class="s">"  event names are namespaced tuples, measurements are separated"
puts class="s">"  from metadata - steal the whole class="y">:telemetry design; it was"
puts class="s">"  right. the framework's hooks made the bridge ten lines, which"
puts class="s">"  is exactly what hooks are for: being the floor a bus stands on."