phronomy 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +127 -30
- data/README.md +106 -122
- data/lib/phronomy/agent/base.rb +135 -57
- data/lib/phronomy/agent/checkpoint.rb +53 -0
- data/lib/phronomy/agent/orchestrator.rb +119 -0
- data/lib/phronomy/agent/react_agent.rb +18 -28
- data/lib/phronomy/agent/shared_state.rb +303 -0
- data/lib/phronomy/agent/suspend_signal.rb +35 -0
- data/lib/phronomy/agent/team_coordinator.rb +285 -0
- data/lib/phronomy/agent.rb +2 -1
- data/lib/phronomy/configuration.rb +0 -24
- data/lib/phronomy/generator_verifier.rb +250 -0
- data/lib/phronomy/guardrail/builtin/pii_pattern_detector.rb +10 -27
- data/lib/phronomy/railtie.rb +0 -6
- data/lib/phronomy/ruby_llm_patches.rb +20 -0
- data/lib/phronomy/tool/mcp_tool.rb +23 -26
- data/lib/phronomy/tracing/langfuse_tracer.rb +3 -6
- data/lib/phronomy/vector_store/redis_search.rb +4 -4
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow.rb +4 -7
- data/lib/phronomy/workflow_runner.rb +42 -30
- data/lib/phronomy.rb +18 -0
- data/scripts/check_readme_ruby.rb +38 -0
- metadata +12 -38
- data/docs/trustworthy_ai_enhancements.md +0 -332
- data/lib/phronomy/active_record/acts_as.rb +0 -48
- data/lib/phronomy/active_record/checkpoint.rb +0 -20
- data/lib/phronomy/active_record/extensions.rb +0 -14
- data/lib/phronomy/active_record/message.rb +0 -20
- data/lib/phronomy/actor.rb +0 -68
- data/lib/phronomy/memory/compression/base.rb +0 -37
- data/lib/phronomy/memory/compression/summary.rb +0 -107
- data/lib/phronomy/memory/compression/tool_output_pruner.rb +0 -67
- data/lib/phronomy/memory/compression.rb +0 -11
- data/lib/phronomy/memory/conversation_manager.rb +0 -213
- data/lib/phronomy/memory/retrieval/base.rb +0 -22
- data/lib/phronomy/memory/retrieval/composite.rb +0 -76
- data/lib/phronomy/memory/retrieval/recent.rb +0 -35
- data/lib/phronomy/memory/retrieval/semantic.rb +0 -114
- data/lib/phronomy/memory/retrieval.rb +0 -12
- data/lib/phronomy/memory/storage/active_record.rb +0 -248
- data/lib/phronomy/memory/storage/base.rb +0 -155
- data/lib/phronomy/memory/storage/in_memory.rb +0 -152
- data/lib/phronomy/memory/storage.rb +0 -11
- data/lib/phronomy/memory.rb +0 -21
- data/lib/phronomy/rails/agent_job.rb +0 -75
- data/lib/phronomy/state_store/active_record.rb +0 -76
- data/lib/phronomy/state_store/base.rb +0 -112
- data/lib/phronomy/state_store/encryptor/active_support.rb +0 -49
- data/lib/phronomy/state_store/encryptor/base.rb +0 -34
- data/lib/phronomy/state_store/encryptor.rb +0 -16
- data/lib/phronomy/state_store/file.rb +0 -85
- data/lib/phronomy/state_store/in_memory.rb +0 -53
- data/lib/phronomy/state_store/redis.rb +0 -70
- data/lib/phronomy/state_store.rb +0 -9
- data/lib/phronomy/thread_actor_registry.rb +0 -85
- data/lib/phronomy/trust_pipeline.rb +0 -264
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a5d8907f1d87037c09f78f63dd8ddbd21605536b1aa53c2d43e52be4a6ab6791
|
|
4
|
+
data.tar.gz: 37260258f4ece53e6e5b748cb2236f9c99f6f7e5c754fb0c2d72c415b0137386
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1533e2e0d82283066bab5f80b6583497330b274396640b91a6b27b1706533e4d25842600eb023c0fd469c8da076101f43e5691218703faedce194cd74476c590
|
|
7
|
+
data.tar.gz: af0513115bfcd23f786dfbedb7bfa7947346bb36168cc62175bd900f8536d984789469afc1fd929de90fca9a6b8cd8496451a054ea3ca86af401ef142b1c3e95
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,131 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-05-19
|
|
11
|
+
|
|
12
|
+
### Removed
|
|
13
|
+
|
|
14
|
+
- **`Phronomy::TrustPipeline` removed**: The `TrustPipeline` class and its inner
|
|
15
|
+
`TrustResult` value object have been deleted. Use `Phronomy::GeneratorVerifier`
|
|
16
|
+
instead, which provides the same generator-verifier pattern with a cleaner,
|
|
17
|
+
fully injectable API.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **`Phronomy::GeneratorVerifier`** — Generator-Verifier coordination loop
|
|
22
|
+
(Anthropic blog, Pattern 1). Wraps a generator agent and a verifier agent with
|
|
23
|
+
fully injectable prompt builders, response parsers, a configurable iteration
|
|
24
|
+
limit, and an approval-outcome raise policy.
|
|
25
|
+
- **`Phronomy::Agent::Orchestrator`** — Base class for orchestrator agents
|
|
26
|
+
(Anthropic blog, Pattern 2). Extends `Agent::Base` with a `subagent` DSL for
|
|
27
|
+
declarative subagent registration as LLM-callable tools, plus `dispatch_parallel`
|
|
28
|
+
and `fan_out` for programmatic parallel invocation.
|
|
29
|
+
- **`Phronomy::Agent::TeamCoordinator`** — Agent teams coordination pattern
|
|
30
|
+
(Anthropic blog, Pattern 3). An LLM-powered coordinator with a shared task
|
|
31
|
+
queue and a pool of worker agents that carry conversation history across task
|
|
32
|
+
assignments. Adds `coordinator_provider` DSL for independent LLM routing.
|
|
33
|
+
- **`Phronomy::Agent::SharedState`** — Shared-state coordination pattern
|
|
34
|
+
(Anthropic blog, Pattern 5). Peer agents collaborate via a `KnowledgeStore`;
|
|
35
|
+
the `member` DSL registers agents with per-agent instructions; `coordination`
|
|
36
|
+
sets the team protocol; `build_prompt` injects a tool-usage guide automatically.
|
|
37
|
+
- **`Phronomy::LowConfidenceError`** — Exception raised by `GeneratorVerifier`
|
|
38
|
+
when `raise_policy: :raise` and verification fails after exhausting the
|
|
39
|
+
iteration limit.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- **`Phronomy::Graph::StateGraph` event system refactored**: Per-node `advance`
|
|
44
|
+
events replaced with a unified `node_completed` event queue, reducing
|
|
45
|
+
event-handler registration overhead and simplifying listener registration.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## [0.3.0] - 2026-05-18
|
|
50
|
+
|
|
51
|
+
### Removed
|
|
52
|
+
|
|
53
|
+
- **`Phronomy::Memory` module fully removed**: `ConversationManager`, all
|
|
54
|
+
`Storage` backends (InMemory, ActiveRecord), all `Retrieval` strategies
|
|
55
|
+
(Recent, Semantic, Composite), and all `Compression` helpers (ToolOutputPruner,
|
|
56
|
+
Summary) have been deleted. Conversation history is now the responsibility of
|
|
57
|
+
the calling application — pass prior messages via `config[:messages]`
|
|
58
|
+
(`Array<RubyLLM::Message>`) and receive the updated array in `result[:messages]`.
|
|
59
|
+
- **`Phronomy::StateStore` module fully removed**: `InMemory`, `ActiveRecord`,
|
|
60
|
+
`Redis`, and `FileSystem` state-store backends have been deleted. The Workflow
|
|
61
|
+
halted-state object is now returned directly from `invoke` and `send_event`
|
|
62
|
+
and must be stored by the caller if resumption is needed.
|
|
63
|
+
- **`Phronomy::Configuration#default_state_store` removed**: No longer meaningful
|
|
64
|
+
without a built-in state store.
|
|
65
|
+
- **`Phronomy::Configuration#default_memory` / `#memory_async` / `#memory_job_queue` removed**:
|
|
66
|
+
No longer meaningful without the Memory module.
|
|
67
|
+
- **Rails integration removed**: `Railtie` initializers for `AgentJob` and
|
|
68
|
+
`acts_as_phronomy_message` no longer load. The `rails/` and `active_record/`
|
|
69
|
+
directories have been deleted.
|
|
70
|
+
- **`Phronomy::Actor` and `Phronomy::ThreadActorRegistry` deleted**: The Active
|
|
71
|
+
Object pattern implementation (`actor.rb`, `thread_actor_registry.rb`) has been
|
|
72
|
+
removed. It provided synchronous blocking only (no true async) and was
|
|
73
|
+
architecturally inconsistent with the `WorkflowRunner` halt/resume model. All
|
|
74
|
+
thread coordination now uses plain `Mutex` where needed.
|
|
75
|
+
- **`Phronomy.configuration.max_actors` removed**: The configuration option is no
|
|
76
|
+
longer meaningful without `ThreadActorRegistry`.
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- **`Agent::Base#invoke` and `#stream`** no longer route execution through a
|
|
81
|
+
per-thread Actor. Both methods now call `_invoke_impl` / `_stream_impl` directly
|
|
82
|
+
on the calling thread.
|
|
83
|
+
- **`Memory::Storage::InMemory`** now stores all thread data in an instance-level
|
|
84
|
+
`Hash` instead of `Thread.current` thread-local storage. The class-level
|
|
85
|
+
`THREAD_DATA_KEY` constant has been removed. `with_thread_lock` uses a
|
|
86
|
+
per-thread-id `Mutex` to preserve concurrent-compaction safety (issue #44).
|
|
87
|
+
- **`StateStore::InMemory`** now stores state in an instance-level `Hash`.
|
|
88
|
+
The `THREAD_DATA_KEY` constant has been removed.
|
|
89
|
+
- **`VectorStore::RedisSearch`** uses a `Mutex` for `ensure_index!` and `clear`
|
|
90
|
+
instead of an Actor, preserving the thread-safety invariant on `@index_created`.
|
|
91
|
+
- **`Tool::McpTool::StdioTransport`**, **`Tracing::LangfuseTracer`**,
|
|
92
|
+
**`TrustPipeline`**, and **`Memory::Retrieval::Semantic`** no longer hold a
|
|
93
|
+
dedicated Actor instance. All operations execute directly on the calling thread.
|
|
94
|
+
- **`PIIPatternDetector` — `:my_number` replaced by `:ssn`** ([#77]): The built-in PII
|
|
95
|
+
detector now checks for US Social Security Numbers (`\b\d{3}-\d{2}-\d{4}\b`) instead
|
|
96
|
+
of Japanese My Numbers. The JIS X 0076 check-digit validation and `my_number_valid?`
|
|
97
|
+
helper have been removed. Category key renamed from `:my_number` to `:ssn`.
|
|
98
|
+
- **`PIIPatternDetector` — phone pattern updated to international format** ([#77]):
|
|
99
|
+
The `:phone` pattern now matches 3-digit area code + 3–4-digit exchange + 4-digit
|
|
100
|
+
subscriber number with optional E.164 country-code prefix
|
|
101
|
+
(`(?:\+\d{1,3}[.\- ]?)?\(?\d{3}\)?[.\- ]?\d{3,4}[.\- ]?\d{4}\b`),
|
|
102
|
+
replacing the previous Japan-specific pattern.
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
|
|
106
|
+
- **`RubyLLM::Providers::OpenAI#handle_error_chunk` — `NoMethodError` on single-line SSE error chunks**:
|
|
107
|
+
Some models (e.g. Qwen running via LM Studio) return SSE error events as a
|
|
108
|
+
single line (`data: {...}`) without a preceding `event:` line. The upstream
|
|
109
|
+
implementation called `chunk.split("\n")[1].delete_prefix(...)`, which raised
|
|
110
|
+
`NoMethodError: undefined method 'delete_prefix' for nil` when the second
|
|
111
|
+
element was absent. A monkey-patch in `lib/phronomy/ruby_llm_patches.rb` guards
|
|
112
|
+
against this by returning an empty string when the split result has fewer than
|
|
113
|
+
two elements.
|
|
114
|
+
- **`README` — stale Memory API examples** ([#76]): All references to the
|
|
115
|
+
non-existent `WindowMemory`, `ActiveRecordMemory`, `SemanticMemory` classes and
|
|
116
|
+
`load_messages` / `memory_compression` API have been replaced with the correct
|
|
117
|
+
`ConversationManager`-based API.
|
|
118
|
+
- **`README` — `PIIPatternDetector` comment** ([#77]): Inline comment updated to
|
|
119
|
+
`# Detect SSNs, credit cards, emails, and phone numbers`.
|
|
120
|
+
- **`README` — Configuration block markdown** ([#80]): The `max_actors` Note block
|
|
121
|
+
was incorrectly placed inside the Ruby code fence; moved outside so it renders
|
|
122
|
+
as a blockquote.
|
|
123
|
+
- **`README` — `Guardrails` stability label** ([#76]): Changed from `Stable` to `Beta`
|
|
124
|
+
to reflect that the built-in detector patterns may evolve.
|
|
125
|
+
- **`CHANGELOG` — stale entries** ([#78]): Removed the orphaned `[Unreleased]` section
|
|
126
|
+
describing a never-released API, and replaced a forward `"As of 0.3.0"` reference
|
|
127
|
+
with future-tense wording.
|
|
128
|
+
- **`McpTool` — YARD class comment** ([#79]): Updated to document both the
|
|
129
|
+
`stdio://` and `http://`/`https://` transport schemes.
|
|
130
|
+
- **`README` — `max_actors` configuration reference** ([#80]): Added `c.max_actors`
|
|
131
|
+
example and LRU eviction note to the Configuration section.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
10
135
|
## [0.2.2] - 2026-05-17
|
|
11
136
|
|
|
12
137
|
### Fixed
|
|
@@ -61,8 +186,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
61
186
|
- **`WorkflowRunner` — state_machines fully drives execution** (architecture overhaul).
|
|
62
187
|
Previously `state_machines` was used only for post-hoc transition validation;
|
|
63
188
|
the next-node was calculated by Phronomy internally (`resolve_next_node`).
|
|
64
|
-
|
|
65
|
-
routing events —
|
|
189
|
+
After this change, all state transition decisions — including guard evaluation for
|
|
190
|
+
routing events — will be delegated entirely to `state_machines`.
|
|
66
191
|
- `PhaseTracker` now exposes `attr_accessor :context` so guard lambdas can
|
|
67
192
|
access the `WorkflowContext` via `m.context`.
|
|
68
193
|
- Guard bridge pattern: `if: ->(m) { guard_proc.call(m.context) }`.
|
|
@@ -87,34 +212,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
87
212
|
|
|
88
213
|
---
|
|
89
214
|
|
|
90
|
-
## [Unreleased]
|
|
91
|
-
|
|
92
|
-
### Added
|
|
93
|
-
|
|
94
|
-
- **`Phronomy::Graph::Context`** module — canonical module for defining workflow
|
|
95
|
-
context classes (replaces the removed `Phronomy::Graph::State`).
|
|
96
|
-
- **`Phronomy::Graph.register_context_class`** — registers context classes for
|
|
97
|
-
deserialization from external stores (Redis, DB).
|
|
98
|
-
- **`Phronomy::Workflow.define`** DSL — primary high-level API for declaring
|
|
99
|
-
stateful workflows (`state`, `wait_state`, `event`, `after`, `initial`).
|
|
100
|
-
- **`Phronomy::Graph::WorkflowRunner`** — state-machine execution engine backing
|
|
101
|
-
the Workflow DSL. Replaces the removed `CompiledGraph`.
|
|
102
|
-
- **`app.send_event(event, config:)`** — event-driven resume for workflows halted
|
|
103
|
-
at a `wait_state`.
|
|
104
|
-
- **`state.halted?`** — returns `true` when the workflow is paused at a `wait_state`.
|
|
105
|
-
- **`state.phase`** — single source of truth for execution state.
|
|
106
|
-
|
|
107
|
-
### Removed
|
|
108
|
-
|
|
109
|
-
- `Phronomy::Graph::StateGraph` / `CompiledGraph` — use `Phronomy::Workflow.define`.
|
|
110
|
-
- `Phronomy::Graph::State` — use `Phronomy::Graph::Context`.
|
|
111
|
-
- `Phronomy::Graph.register_state_class` — use `register_context_class`.
|
|
112
|
-
- `state.current_nodes` / `state.halted_before` — use `state.phase` / `state.halted?`.
|
|
113
|
-
- `compiled.interrupt_before` / `compiled.interrupt_after` — use `wait_state` + `event`.
|
|
114
|
-
- `compiled.resume` — use `app.send_event`.
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
215
|
## [0.2.0] - 2026-05-13
|
|
119
216
|
|
|
120
217
|
### Added
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Phronomy
|
|
2
2
|
|
|
3
3
|
**Phronomy** is a Ruby AI agent framework inspired by open-source AI agent frameworks.
|
|
4
|
-
It provides composable building blocks — Workflows, Agents, and
|
|
4
|
+
It provides composable building blocks — Workflows, Agents, Tools, Guardrails, RAG, and Tracing — all powered by [RubyLLM](https://github.com/crmne/ruby_llm) for LLM abstraction.
|
|
5
5
|
|
|
6
6
|
## Features
|
|
7
7
|
|
|
@@ -13,21 +13,20 @@ It provides composable building blocks — Workflows, Agents, and Memory — all
|
|
|
13
13
|
|---|---|
|
|
14
14
|
| **Workflow** — Stateful, branching workflows with wait_state/send_event | Stable |
|
|
15
15
|
| **Workflow Parallel Node** — Concurrent branches via application-level threads | Beta |
|
|
16
|
-
| **Agent** — ReAct-style tool-calling agents with
|
|
16
|
+
| **Agent** — ReAct-style tool-calling agents with guardrails and conversation history | Stable |
|
|
17
17
|
| **Before-Completion Hook** — Three-tier LLM parameter injection | Stable |
|
|
18
|
-
| **Memory** — Window, summary, ActiveRecord-backed, semantic, and composite memory | Stable |
|
|
19
|
-
| **Memory Compression** — Automatic summarisation and tool-output pruning | Beta |
|
|
20
18
|
| **Context Management** — Token budget calculation, estimation, and pruning | Stable |
|
|
21
19
|
| **Knowledge/RAG** — Retrieval sources with pluggable loaders, splitters, and vector stores | Beta |
|
|
22
20
|
| **Multi-agent** — Agent-as-Tool pattern and hub-and-spoke handoff routing | Beta |
|
|
23
|
-
| **
|
|
24
|
-
| **
|
|
21
|
+
| **GeneratorVerifier** — Generator-Verifier loop with injectable prompt builders/parsers | Beta |
|
|
22
|
+
| **Agent::Orchestrator** — Parallel subagent dispatch, fan-out, and `subagent` DSL | Beta |
|
|
23
|
+
| **Agent::TeamCoordinator** — Agent teams pattern: LLM coordinator + persistent worker pool with task queue | Beta |
|
|
24
|
+
| **Agent::SharedState** — Shared state pattern: peer agents collaborate via a shared KnowledgeStore; `member` DSL with per-agent instructions and `coordination` team protocol | Experimental |
|
|
25
|
+
| **Guardrails** — Input/output validation; built-in PII and prompt-injection detectors | Beta |
|
|
25
26
|
| **Output Parser** — JSON and Struct-mapped parsers for structured LLM responses | Stable |
|
|
26
27
|
| **Eval Framework** — Dataset-driven evaluation with multiple scorer types | Beta |
|
|
27
28
|
| **Tracing** — Pluggable span-based observability | Stable |
|
|
28
|
-
| **StateStore** — Persist graph state to memory, ActiveRecord, Redis, or file system | Stable |
|
|
29
29
|
| **MCP Tool** — Model Context Protocol server integration | Beta |
|
|
30
|
-
| **Rails integration** — `AgentJob`, `acts_as_phronomy_message`, and generators | Beta |
|
|
31
30
|
|
|
32
31
|
## Installation
|
|
33
32
|
|
|
@@ -49,7 +48,7 @@ For Rails apps, run the install generator after bundling:
|
|
|
49
48
|
rails generate phronomy:install
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
This creates
|
|
51
|
+
This creates a configuration initializer.
|
|
53
52
|
|
|
54
53
|
## Quick Start
|
|
55
54
|
|
|
@@ -99,8 +98,6 @@ app = Phronomy::Workflow.define(ReviewContext) do
|
|
|
99
98
|
event :reject, from: :awaiting_approval, to: :write
|
|
100
99
|
end
|
|
101
100
|
|
|
102
|
-
Phronomy.configure { |c| c.default_state_store = Phronomy::StateStore::InMemory.new }
|
|
103
|
-
|
|
104
101
|
# First run — halts at :awaiting_approval
|
|
105
102
|
state = app.invoke({ draft: "" }, config: { thread_id: "doc-1" })
|
|
106
103
|
puts "Halted: #{state.halted?}" # => true
|
|
@@ -160,7 +157,7 @@ agent.add_input_guardrail(NoSensitiveDataGuardrail.new)
|
|
|
160
157
|
### Built-in Guardrails — PII and prompt injection detection
|
|
161
158
|
|
|
162
159
|
```ruby
|
|
163
|
-
# Detect credit cards,
|
|
160
|
+
# Detect SSNs, credit cards, emails, and phone numbers
|
|
164
161
|
agent.add_input_guardrail(Phronomy::Guardrail::Builtin::PIIPatternDetector.new)
|
|
165
162
|
|
|
166
163
|
# Block common prompt-injection attempts
|
|
@@ -232,23 +229,88 @@ end
|
|
|
232
229
|
|
|
233
230
|
Hooks are called in order — global → class → instance — and deep-merged.
|
|
234
231
|
|
|
235
|
-
###
|
|
232
|
+
### GeneratorVerifier — Generator-Verifier loop with custom prompt builders
|
|
236
233
|
|
|
237
234
|
```ruby
|
|
238
|
-
pipeline = Phronomy::
|
|
239
|
-
draft_agent:
|
|
240
|
-
review_agent:
|
|
235
|
+
pipeline = Phronomy::GeneratorVerifier.new(
|
|
236
|
+
draft_agent: PolicyDraftAgent,
|
|
237
|
+
review_agent: PolicyReviewAgent,
|
|
238
|
+
|
|
239
|
+
# Full control over the LLM dialogue — supply your own prompts.
|
|
240
|
+
draft_prompt_builder: ->(input, feedback) {
|
|
241
|
+
base = "Answer precisely: #{input}"
|
|
242
|
+
feedback ? "#{base}\n\nPrevious feedback: #{feedback}" : base
|
|
243
|
+
},
|
|
244
|
+
review_prompt_builder: ->(input, draft, citations) {
|
|
245
|
+
"Is this draft accurate? Draft: #{draft}"
|
|
246
|
+
},
|
|
247
|
+
|
|
241
248
|
confidence_threshold: 0.7,
|
|
242
|
-
max_iterations: 3
|
|
249
|
+
max_iterations: 3,
|
|
250
|
+
raise_if_untrusted: false # set true to raise LowConfidenceError
|
|
243
251
|
)
|
|
244
252
|
|
|
245
253
|
result = pipeline.invoke("What is the refund policy?")
|
|
246
|
-
puts result.output
|
|
247
|
-
puts result.trusted?
|
|
248
|
-
puts result.confidence
|
|
254
|
+
puts result.output # final answer
|
|
255
|
+
puts result.trusted? # true when confidence >= 0.7
|
|
256
|
+
puts result.confidence # Float 0.0–1.0
|
|
257
|
+
result.citations.each { |c| puts "#{c[:source]}: #{c[:excerpt]}" }
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Optionally inject a custom result parser to decode non-JSON LLM output:
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
pipeline = Phronomy::GeneratorVerifier.new(
|
|
264
|
+
# ... (required params as shown above)
|
|
265
|
+
draft_result_parser: ->(text) { my_custom_draft_parser(text) },
|
|
266
|
+
review_result_parser: ->(text) { my_custom_review_parser(text) }
|
|
267
|
+
)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Raise on low confidence:
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
begin
|
|
274
|
+
result = pipeline.invoke("question")
|
|
275
|
+
rescue Phronomy::LowConfidenceError => e
|
|
276
|
+
puts "Untrusted (confidence #{e.result.confidence}): #{e.result.output}"
|
|
277
|
+
end
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Agent::Orchestrator — Parallel subagent dispatch
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
class ResearchOrchestrator < Phronomy::Agent::Orchestrator
|
|
284
|
+
model "gpt-4o"
|
|
285
|
+
instructions "Coordinate research tasks by dispatching to specialised agents."
|
|
286
|
+
|
|
287
|
+
# Each subagent is automatically exposed as an LLM-callable tool.
|
|
288
|
+
subagent :searcher, SearchAgent
|
|
289
|
+
subagent :summarizer, SummaryAgent, on_error: :skip
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
result = ResearchOrchestrator.new.invoke("Research the latest AI news.")
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Programmatic parallel dispatch (no LLM loop):
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
class MyOrchestrator < Phronomy::Agent::Orchestrator
|
|
299
|
+
model "gpt-4o"
|
|
300
|
+
instructions "Orchestrate."
|
|
301
|
+
|
|
302
|
+
def run(query)
|
|
303
|
+
# Heterogeneous agents in parallel
|
|
304
|
+
results = dispatch_parallel(
|
|
305
|
+
{agent: SearchAgent, input: "topic A"},
|
|
306
|
+
{agent: AnalysisAgent, input: query}
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Fan-out — same agent, multiple inputs
|
|
310
|
+
translations = fan_out(agent: TranslationAgent, inputs: %w[Hello World])
|
|
249
311
|
|
|
250
|
-
|
|
251
|
-
|
|
312
|
+
results.map { |r| r[:output] }.join("\n")
|
|
313
|
+
end
|
|
252
314
|
end
|
|
253
315
|
```
|
|
254
316
|
|
|
@@ -332,35 +394,28 @@ search_tool = Phronomy::Tool::McpTool.from_server(
|
|
|
332
394
|
)
|
|
333
395
|
```
|
|
334
396
|
|
|
335
|
-
###
|
|
397
|
+
### Conversation History — passing prior messages
|
|
336
398
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
# create_table :phronomy_messages ...
|
|
340
|
-
# create_table :phronomy_states ...
|
|
399
|
+
Phronomy does not manage conversation history internally. Instead, the application owns the
|
|
400
|
+
message array and passes it in via `config[:messages]`:
|
|
341
401
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
# Use in a controller:
|
|
354
|
-
agent = ResearchAgent.new
|
|
355
|
-
result = agent.invoke(
|
|
356
|
-
params[:message],
|
|
357
|
-
config: {
|
|
358
|
-
thread_id: "user_#{current_user.id}",
|
|
359
|
-
memory: PhronomyMessage.phronomy_memory
|
|
360
|
-
}
|
|
402
|
+
```ruby
|
|
403
|
+
# First turn
|
|
404
|
+
result1 = MyAgent.new.invoke("Hello! I'm Alice.", config: { thread_id: "session-1" })
|
|
405
|
+
prior_messages = result1[:messages] # Array<RubyLLM::Message>
|
|
406
|
+
|
|
407
|
+
# Second turn — pass prior messages so the agent has context
|
|
408
|
+
result2 = MyAgent.new.invoke(
|
|
409
|
+
"What is my name?",
|
|
410
|
+
config: { messages: prior_messages, thread_id: "session-1" }
|
|
361
411
|
)
|
|
412
|
+
puts result2[:output] # => "Your name is Alice."
|
|
362
413
|
```
|
|
363
414
|
|
|
415
|
+
`result[:messages]` contains the complete message history after each invocation.
|
|
416
|
+
Persist it however suits your application (in-memory hash, Redis, ActiveRecord, etc.).
|
|
417
|
+
|
|
418
|
+
|
|
364
419
|
## Configuration
|
|
365
420
|
|
|
366
421
|
```ruby
|
|
@@ -368,9 +423,7 @@ Phronomy.configure do |c|
|
|
|
368
423
|
c.default_model = "gpt-4o-mini"
|
|
369
424
|
c.recursion_limit = 25
|
|
370
425
|
c.tracer = Phronomy::Tracing::NullTracer.new
|
|
371
|
-
c.
|
|
372
|
-
c.memory_compression = [] # optional; Array of compressors
|
|
373
|
-
c.before_completion = nil # optional; global hook lambda
|
|
426
|
+
c.before_completion = nil # optional; global hook lambda
|
|
374
427
|
end
|
|
375
428
|
```
|
|
376
429
|
|
|
@@ -402,24 +455,6 @@ budget = Phronomy::Context::TokenBudget.new(
|
|
|
402
455
|
)
|
|
403
456
|
```
|
|
404
457
|
|
|
405
|
-
### Budget-aware Memory
|
|
406
|
-
|
|
407
|
-
Pass a budget to `load_messages` and only the newest messages that fit are returned:
|
|
408
|
-
|
|
409
|
-
```ruby
|
|
410
|
-
memory = Phronomy::Memory::WindowMemory.new
|
|
411
|
-
messages = memory.load_messages(thread_id: "t1", token_budget: budget)
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
`ActiveRecordMemory` also accepts `pruner:` to truncate oversized tool results:
|
|
415
|
-
|
|
416
|
-
```ruby
|
|
417
|
-
memory = Phronomy::Memory::ActiveRecordMemory.new(
|
|
418
|
-
model_class: PhronomyMessage,
|
|
419
|
-
pruner: Phronomy::Memory::Compression::ToolOutputPruner.new(max_chars: 4000)
|
|
420
|
-
)
|
|
421
|
-
```
|
|
422
|
-
|
|
423
458
|
### Agent DSL extensions
|
|
424
459
|
|
|
425
460
|
```ruby
|
|
@@ -430,59 +465,8 @@ class MyAgent < Phronomy::Agent::Base
|
|
|
430
465
|
end
|
|
431
466
|
```
|
|
432
467
|
|
|
433
|
-
`Agent::Base#invoke` builds a `TokenBudget` automatically
|
|
434
|
-
|
|
435
|
-
silently skipped.
|
|
436
|
-
|
|
437
|
-
### SemanticMemory
|
|
438
|
-
|
|
439
|
-
Embedding-based retrieval of relevant past messages:
|
|
440
|
-
|
|
441
|
-
```ruby
|
|
442
|
-
semantic = Phronomy::Memory::SemanticMemory.new(
|
|
443
|
-
embedding_model: "text-embedding-3-small",
|
|
444
|
-
k: 10
|
|
445
|
-
)
|
|
446
|
-
messages = semantic.load_messages(thread_id: "t1", query: "user's current question")
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
### Composite retrieval
|
|
450
|
-
|
|
451
|
-
Merge multiple retrieval strategies within a shared `ConversationManager`:
|
|
452
|
-
|
|
453
|
-
```ruby
|
|
454
|
-
composite_retrieval = Phronomy::Memory::Retrieval::Composite.new(
|
|
455
|
-
sources: [
|
|
456
|
-
{ retrieval: Phronomy::Memory::Retrieval::Recent.new(k: 5), weight: 0.4 },
|
|
457
|
-
{ retrieval: Phronomy::Memory::Retrieval::Semantic.new(k: 10), weight: 0.6 }
|
|
458
|
-
]
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
manager = Phronomy::Memory::ConversationManager.new(
|
|
462
|
-
storage: Phronomy::Memory::Storage::InMemory.new,
|
|
463
|
-
retrieval: composite_retrieval
|
|
464
|
-
)
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
### Memory Compression
|
|
468
|
-
|
|
469
|
-
Automatically shrink conversation history before it reaches the LLM.
|
|
470
|
-
|
|
471
|
-
```ruby
|
|
472
|
-
# Truncate oversized tool outputs (no LLM call, cheap)
|
|
473
|
-
pruner = Phronomy::Memory::Compression::ToolOutputPruner.new(max_chars: 4000)
|
|
474
|
-
|
|
475
|
-
# Summarise old messages when history exceeds max_tokens (calls summarizer_model)
|
|
476
|
-
summary = Phronomy::Memory::Compression::Summary.new(
|
|
477
|
-
max_tokens: 4000,
|
|
478
|
-
keep: 10, # always preserve the N most recent messages
|
|
479
|
-
summarizer_model: "gpt-4o-mini"
|
|
480
|
-
)
|
|
481
|
-
|
|
482
|
-
Phronomy.configure do |c|
|
|
483
|
-
c.memory_compression = [pruner, summary] # applied in order: pruner first, then summary
|
|
484
|
-
end
|
|
485
|
-
```
|
|
468
|
+
`Agent::Base#invoke` builds a `TokenBudget` automatically. When the model is not in the
|
|
469
|
+
registry the budget is silently skipped.
|
|
486
470
|
|
|
487
471
|
|
|
488
472
|
## Examples
|
|
@@ -512,11 +496,11 @@ bundle exec ruby NN_example_name/run.rb
|
|
|
512
496
|
| 12 | `12_prompt_template/` | Advanced prompt templates |
|
|
513
497
|
| 13 | `13_mcp_http_tool/` | HTTP-based MCP tool server |
|
|
514
498
|
| 14 | `14_code_review/` | Automated code review agent |
|
|
515
|
-
| 15 | `15_rails_secure_chat/` | Rails chat with PII guardrails
|
|
499
|
+
| 15 | `15_rails_secure_chat/` | Rails chat with PII guardrails |
|
|
516
500
|
| 16 | `16_before_completion_hook/` | Global/class/instance before_completion hooks |
|
|
517
501
|
| 17 | `17_multi_agent_handoff/` | Hub-and-spoke agent routing via Runner |
|
|
518
502
|
| 18 | `18_rails_agent_job/` | Rails app with AgentJob + ActionCable streaming |
|
|
519
|
-
| 19 | `19_trust_pipeline/` |
|
|
503
|
+
| 19 | `19_trust_pipeline/` | Generator-Verifier pattern with citation tracking, self-review loop and confidence gate |
|
|
520
504
|
|
|
521
505
|
## Development
|
|
522
506
|
|