Phronomy
⚠️ Development Notice This project is primarily developed and maintained by AI coding agents. As a result,
mainreceives 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 installThis 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
endSee 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 onlyAgent 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
- Getting started — installation, RubyLLM setup, Agent/Workflow basics, persistence, streaming.
- Features and API stability — public feature matrix and stability labels.
- Architecture — canonical current explanatory architecture entry and authority navigation.
- Runtime and concurrency — EventLoop, FSMSession, TaskResult, OffloadPool, cancellation, observability.
- Result composition and Execution — application JOBs, map/flat_map, fan-in snapshots, context ownership and migration.
- MCP client — Model Context Protocol (MCP) integration and supported schema subset.
- Migration from 0.15-era APIs.
- 0.16 cleanup migration.
- Persistence failure contract migration — domain rescue clauses and unchanged raw backend errors.
- 0.19 unified Persistence migration.
- 0.22 semantic Multi-Agent Handoff migration.
- Architecture Decision Records — design rationale and superseding decisions.
- CHANGELOG — current development and recent release history.
- Changelog archive: 0.14.0 and earlier.
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/phronomyIntegration tests can be run with:
bundle exec rspec spec/integration --tag integrationContributing
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.
-
PromptInjectionFilteris 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.