agentic examples

Capability Evals

Capability Evals: golden test cases run against registered capabilities, scored, and gated. When you swap a lambda for an LLM (or one model for another), the eval suite is what tells you whether quality moved - contracts check shape, evals check SUBSTANCE.

Testing & Verification Round 7 Andrew Kane exit 1 (red by design)

source on github

bundle exec ruby examples/capability_evals.rb

a real captured run

CAPABILITY EVALS (pass threshold 90%)

  classify_sentiment   4/4 (100%)
  extract_amount       1/3 ( 33%) BELOW THRESHOLD
    FAIL "you owe $12.50 by friday"
         expected {:cents=>1250}, got {:cents=>1200}
    FAIL "refund of $0.99 issued"
         expected {:cents=>99}, got {:cents=>0}

  suite score: 67%
  the gate holds: extract_amount drops cents - a shape-valid,
  substance-wrong answer that no contract could catch. contracts
  check types; evals check truth. you need both.

source

# frozen_string_literal: true

# Capability Evals: golden test cases run against registered
# capabilities, scored, and gated. When you swap a lambda for an LLM
# (or one model for another), the eval suite is what tells you whether
# quality moved - contracts check shape, evals check SUBSTANCE.
#
#   bundle exec ruby examples/capability_evals.rb
#
# Runs offline; one capability has a bug the evals catch.

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

def capability(name, inputs:, outputs:, &impl)
  spec = Agentic:class="y">:CapabilitySpecification.new(
    name: name, description: name, version: class="s">"1.0.0", inputs: inputs, outputs: outputs
  )
  Agentic.register_capability(
    spec, Agentic:class="y">:CapabilityProvider.new(capability: spec, implementation: impl)
  )
end

capability(class="s">"classify_sentiment",
  inputs: {text: {type: class="s">"string", required: true}},
  outputs: {label: {type: class="s">"string", required: true, enum: %w[positive negative neutral]}}) do |i|
  text = i[class="y">:text].downcase
  label = if text.match?(/love|great|excellent|happy/)
    class="s">"positive"
  elsif text.match?(/hate|terrible|awful|angry/)
    class="s">"negative"
  else
    class="s">"neutral"
  end
  {label: label}
end

capability(class="s">"extract_amount",
  inputs: {text: {type: class="s">"string", required: true}},
  outputs: {cents: {type: class="s">"number", required: true}}) do |i|
  # The bug: parses dollars but drops the cents
  dollars = i[class="y">:text][/\$(\d+)/, 1].to_i
  {cents: dollars * 100}
end

EVALS = {
  class="s">"classify_sentiment" => [
    {input: {text: class="s">"I love this gem"}, expect: {label: class="s">"positive"}},
    {input: {text: class="s">"This is terrible"}, expect: {label: class="s">"negative"}},
    {input: {text: class="s">"It runs on Ruby 3.3"}, expect: {label: class="s">"neutral"}},
    {input: {text: class="s">"absolutely EXCELLENT work"}, expect: {label: class="s">"positive"}}
  ],
  class="s">"extract_amount" => [
    {input: {text: class="s">"invoice total $45"}, expect: {cents: 4500}},
    {input: {text: class="s">"you owe $12.50 by friday"}, expect: {cents: 1250}},
    {input: {text: class="s">"refund of $0.99 issued"}, expect: {cents: 99}}
  ]
}.freeze

registry = Agentic:class="y">:AgentCapabilityRegistry.instance
THRESHOLD = 0.9

puts class="s">"CAPABILITY EVALS (pass threshold #{(THRESHOLD * 100).round}%)"
puts

overall = []
EVALS.each do |name, cases|
  provider = registry.get_provider(name)
  results = cases.map do |eval_case|
    actual = provider.execute(eval_case[class="y">:input])
    passed = eval_case[class="y">:expect].all? { |key, expected| actual[key] == expected }
    [eval_case, actual, passed]
  end

  passed = results.count { |_, _, ok| ok }
  rate = passed.to_f / cases.size
  overall << rate
  puts format(class="s">"  %-20s %d/%d (%3.0f%%) %s", name, passed, cases.size, rate * 100,
    (rate >= THRESHOLD) ? class="s">"" : class="s">"BELOW THRESHOLD")

  results.reject { |_, _, ok| ok }.each do |eval_case, actual, _|
    puts class="s">"    FAIL #{eval_case[class="y">:input][class="y">:text].inspect}"
    puts class="s">"         expected #{eval_case[class="y">:expect].inspect}, got #{actual.inspect}"
  end
end

puts
score = overall.sum / overall.size
puts format(class="s">"  suite score: %.0f%%", score * 100)
if score < THRESHOLD
  puts class="s">"  the gate holds: extract_amount drops cents - a shape-valid,"
  puts class="s">"  substance-wrong answer that no contract could catch. contracts"
  puts class="s">"  check types; evals check truth. you need both."
  exit 1
end