Skip to content

kulesh/recurgent

Recurgent

An agent runtime that grows software through use.

Three Core Ideas

  1. Interface: Model backed dynamic dispatch. Behavior is infered and created at runtime by a backing model.
  2. Environment: Agents compose themselves. Agents compose themselves by building other agents to delegate work. Humans just sketch intent and boundaries.
  3. Evolution: Outcome is shaped, not prescribed. Contracts and traces provide directional pressure so emergence stays reliable and intelligible.

Recurgent doesn't produce code. It produces a tool-building organism that produces code. You give it a role and an environment. It discovers what tools it needs, builds them, and gets better over time.

A Calculator

calc = Agent.for("calculator")

calc.memory = 5
calc.add(3)                                        # => 8
calc.multiply(4)                                   # => 32
calc.sqrt(144)                                     # => 12
calc.convert(100, from: "celsius", to: "fahrenheit") # => 212
calc.solve("2x + 5 = 17")                          # => x = 6
calc.history                                       # => [all of the above]

You didn't define add, sqrt, convert, or solve. There's no spec, no schema, no tool registration. The runtime synthesized behavior at call time, tracked state across calls, validated the results, and persisted stable implementations for reuse. Next time you call calc.add, it doesn't regenerate — it reuses what worked.

Personal Assistant with Conversation Memory

assistant = Agent.for(
  "personal assistant that remembers conversation history",
  log: "tmp/recurgent.jsonl",
  debug: true
)

news = assistant.ask("What's the latest on Google News?")
src  = assistant.ask("What's the source?")

puts news.value
puts src.value

Conversation state persists across calls. Source follow-ups resolve from concrete references when available — when they're not, the agent returns an explicit unknown instead of fabricating provenance.

Agents That Spawn Other Agents

debate = Agent.for("philosophy_symposium_host", verbose: true)

puts debate.host(
  question: "What is the good life?",
  thinkers: [
    "Stoic philosopher in the tradition of Marcus Aurelius",
    "Epicurean philosopher in the tradition of Epicurus",
    "Existentialist in the tradition of Simone de Beauvoir"
  ],
  rounds: 3
)

puts debate.debate_takeaways(10)

You defined a host and a question. Recurgent created three philosopher agents, gave each a contract (take a position, engage the others' claims), ran three rounds of structured debate where arguments sharpened against each other's actual responses, and synthesized takeaways. You didn't orchestrate any of that. The runtime figured out the delegation pattern, the turn structure, and the accumulation of conversational history on its own.

What Happens Under the Hood

  1. You call an Agent method naturally.
  2. The runtime synthesizes behavior — code, contracts, even other agents.
  3. Recurgent executes in a sandbox, validates outcomes, and logs traces.
  4. Useful behavior is persisted and reused on future calls.
  5. Failures trigger repair and evolution — not silent drift.

The output isn't a program. It's a living registry of capabilities, shaped by health metrics, contracts, failure histories, and evolutionary pressure. What works survives. What doesn't gets repaired or replaced.

Agents That Build Their Own Tools

When you asked the personal assistant "What's the latest on Google News?", it needed to fetch web content. You didn't write a tool for that. The assistant did:

fetcher = assistant.delegate(
  "web_fetcher",
  purpose: "fetch content from HTTP/HTTPS URLs with redirect handling",
  deliverable: { type: "object", required: ["status", "body"] },
  acceptance: [{ assert: "status code is present and body is string" }],
  failure_policy: { on_error: "return_error" }
)

result = fetcher.fetch_url("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en")
puts result.ok?

The runtime decided it needed a specialized tool, wrote the contract (what it should accept, what it should return, how to handle failure), generated the implementation, and validated the results. The web_fetcher now exists in the registry — tested, typed, and available for reuse by any agent that needs it. Tools that meet their contracts survive. Tools that don't get repaired or replaced.

Quickstart

Prerequisites

  • Ruby 3.4+
  • Bundler
  • One provider key:
    • ANTHROPIC_API_KEY, or
    • OPENAI_API_KEY

Setup

cd runtimes/ruby
bundle install

Run

cd runtimes/ruby
./bin/recurgent
ruby examples/assistant.rb
ruby examples/debate.rb

Verify

cd runtimes/ruby
bundle exec rspec
bundle exec rubocop

Where This Is Going

Recurgent is being built in public. Here's the arc:

Now: The Ruby runtime is actively developed and usable. Agents synthesize tools, persist what works, and evolve through use. This is the foundation.

Next: Recursim — a simulated environment where agents face synthetic scenarios designed to grow new capabilities and pressure-test existing ones. Think of it as a gym for agents: instead of waiting for real-world edge cases to surface organically, you manufacture them. The goal is to accelerate the evolutionary loop — building capabilities before users need them and finding gaps before users hit them.

Ahead: A Lua runtime for portability and embedding in constrained environments. The spec is runtime-agnostic by design — the contract and registry model should translate cleanly.

The long game is agents that don't just respond to instructions but develop genuine, persistent competence in their domain — shaped by real work, validated by contracts, and evolved through simulation. While you sleep, they exercise their capabilities, discover new ones, and get better at their job.

Trajectory: from single-agent emergence to measured, repeatable evolution loops.

Known Limitations

Documentation Map

References

Community and Policy

About

Recursive agents as skills for model harnesses

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages