Project

phronomy

0.0
The project is in a healthy, maintained state
Phronomy is a Ruby AI agent framework that provides composable building blocks — Agents, Workflows, Tools, Filters, and Tracing — for building AI agents in Ruby. Powered by RubyLLM for LLM abstraction.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 2.6, < 3
~> 1.0
< 3
~> 2.0.0
 Project Readme

Phronomy

⚠️ Development Notice This project is primarily developed and maintained by AI coding agents. As a result, main receives frequent, large, and unannounced changes. External contributors should expect significant churn and potential conflicts at any time. We apologise for the instability this may cause.

Phronomy is a Ruby AI agent framework for stateful Agents, Workflows, Tools, context management, filtering, tracing, and multi-agent coordination. Large Language Model (LLM) access is provided through RubyLLM.

Phronomy is pre-1.0. Pin to a released gem version for production use rather than tracking main directly.

Core concepts

  • Agent — stateful, persistence-backed LLM agent with canonical execution history.
  • Persistence — unified durable backend for Agent state and Workflow workflow_states.
  • Workflow — state-machine-driven application workflow with explicit events and wait states.
  • Tool / Capability — callable application capability exposed to an Agent; application-defined Tools subclass Phronomy::Tool::Base.
  • Multi-Agent Handoff — semantic Source-to-Target responsibility transfer with policy-bounded Context projection and persisted active responsibility and exact Target recovery within one Persistence domain.
  • EventLoop + FSMSession — the framework control plane for logical lifecycle coordination.
  • OffloadPool — bounded operating-system-thread execution boundary for synchronous work that must not run on EventLoop.
  • TaskResult — the common thread-free completion handle returned by Phronomy asynchronous APIs, including OffloadPool-backed work.
  • TaskResult.completed / TaskResult.failed — already-settled application results without starting execution.
  • TaskResult.map / flat_map / all_settled — result transformation, asynchronous chaining and ordered all-settled observation.
  • Execution.run_async / run — start application JOBs and join their final results under a whole-execution timeout and cancellation scope.
  • Blocking.call_async — submits synchronous application work to the existing bounded OffloadPool without waiting for queue space.
  • Journal / Context Policy / Manifest — canonical history plus per-LLM-call context selection.

See Features and Application Programming Interface (API) stability for the full feature matrix.

Installation

Add Phronomy to your Gemfile:

gem "phronomy"

Then run:

bundle install

This refactoring branch requires RubyLLM 2.0.x. See the RubyLLM 2 and token-ownership migration for removed input-budget overrides and the in-flight Tool-manifest upgrade boundary.

Configure RubyLLM with the provider credentials and transport policy required by your application. Phronomy does not add another LLM transport retry/timeout layer.

RubyLLM.configure do |c|
  c.openai_api_key = ENV["OPENAI_API_KEY"]
  c.request_timeout = 120
  c.max_retries = 3
end

See Getting started for installation details, optional dependencies, stateful Agent setup, streaming, and Workflow examples.

Quick start

class WebSearch < Phronomy::Tool::Base
  description "Search the web"
  param :query, type: :string, desc: "Search query"

  def execute(query:)
    "Mock search result for: #{query}"
  end
end

class ResearchAgent < Phronomy::Agent::Base
  agent_definition id: "research-agent", version: 1
  model "gpt-4o"
  instructions "You are a research assistant. Use tools to answer questions."
  tools(WebSearch => nil)
  max_iterations 5
end

result = ResearchAgent.new.invoke("What happened in AI research this week?")
puts result[:output]

Phronomy::Tool::Base is the public authoring name for the existing Tool base class. The legacy Phronomy::Agent::Context::Capability::Base constant remains valid for compatibility.

For non-blocking top-level use, call invoke_async and keep the returned Phronomy::TaskResult. Inside Phronomy lifecycle callbacks, do not block waiting for another TaskResult; continue through explicit events instead.

task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks")
result = task.wait_result   # top-level/external caller only

Agent lifecycle events are bound to one live Agent Runtime incarnation. Register on_event: (or the equivalent construction block) when the Agent is created or loaded, then invoke it without a per-call listener:

agent = ResearchAgent.new(
  on_event: ->(event) {
    puts event.type   # :done, :error, :tool_call, :tool_result, etc.
  }
)

task = agent.invoke_async("Research Ruby AI frameworks")

The same listener registration is available on create and load. Public per-invocation on_event: / listener blocks are removed.

Runtime model

Phronomy uses one completion model with two execution mechanisms:

Runtime
├─ EventLoop
│  └─ FSMSession
│     ├─ Agent
│     ├─ Workflow
│     ├─ ToolInvocation
│     └─ MultiAgent fan-out
├─ OffloadPool
│  └─ synchronous off-EventLoop work
└─ EventLoop-driven timers

EventLoop / FSMSession ─┐
                       ├─> TaskResult = completion handle
OffloadPool ────────────┘

Logical waiting remains in EventLoop/FSMSession state. Synchronous work that would block EventLoop uses the bounded OffloadPool. OffloadPool-specific queue, worker, timeout, and abandonment state remains private runtime machinery; callers observe completion through Phronomy::TaskResult. See Runtime and concurrency for the detailed contracts, timeout/cancellation semantics, metrics, and callback rules.

Documentation

Examples

Runnable examples covering major features are maintained in the phronomy-examples repository.

Development

After checking out the repository:

bin/setup
bundle exec rspec spec/phronomy

Integration tests can be run with:

bundle exec rspec spec/integration --tag integration

Contributing

Bug reports and pull requests are welcome. See CONTRIBUTING.md.

Security and privacy

  • Provider credentials are handled by RubyLLM; Phronomy does not persist LLM API keys.
  • Trace payloads are redacted by default when trace_pii: false.
  • Tools and MCP servers are external trust boundaries; apply approval and application-specific policy to side-effecting capabilities.
  • PromptInjectionFilter is a useful baseline, not a complete untrusted-input defence.
  • Report vulnerabilities privately through GitHub Security Advisories rather than a public issue.

License

The gem is available as open source under the terms of the MIT License.