phronomy 0.3.0 → 0.5.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 +83 -0
- data/README.md +85 -16
- data/lib/phronomy/agent/base.rb +117 -382
- data/lib/phronomy/agent/checkpoint.rb +12 -5
- data/lib/phronomy/agent/concerns/before_completion.rb +105 -0
- data/lib/phronomy/agent/concerns/guardrailable.rb +42 -0
- data/lib/phronomy/agent/concerns/retryable.rb +88 -0
- data/lib/phronomy/agent/concerns/suspendable.rb +116 -0
- data/lib/phronomy/agent/orchestrator.rb +119 -0
- data/lib/phronomy/agent/react_agent.rb +37 -16
- data/lib/phronomy/agent/shared_state.rb +303 -0
- data/lib/phronomy/agent/team_coordinator.rb +285 -0
- data/lib/phronomy/{trust_pipeline.rb → generator_verifier.rb} +95 -108
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow_runner.rb +41 -22
- data/lib/phronomy.rb +17 -0
- metadata +12 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 138e6b7d6b59f34f827e39a43b86c6f30ea0dd80e936d11e326febad4d3217b0
|
|
4
|
+
data.tar.gz: fada502e034850a3162a488cb02fc195364fc93e72398e858a79058c005c2ad3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55526d56e69e328f9de38e75da98a9a1e0d206997f3463a18aa0481f18d978896f02567a0fefcb6ec4fe2a5f030d3829dde59c479305f6e3ce9d825b06222ce8
|
|
7
|
+
data.tar.gz: f58b275260866c5a7784c32c9846c9058cab815d6d294b92041dcf29525bbf76e1683c1151991c092eace65e5e55e41ad5390d6f6106f9306aa053bf42c5c0a8
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,89 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-05-20
|
|
11
|
+
|
|
12
|
+
### Breaking Changes
|
|
13
|
+
|
|
14
|
+
- **`Agent::Base#invoke` and `#stream` — `messages` and `thread_id` promoted to
|
|
15
|
+
top-level keyword arguments**:
|
|
16
|
+
Previously these values were passed inside the `config:` hash. They are now
|
|
17
|
+
explicit keyword arguments. The `config:` hash retains other runtime options
|
|
18
|
+
such as `:knowledge_sources`, `:user_id`, and `:session_id`.
|
|
19
|
+
|
|
20
|
+
**Before (v0.4.x)**:
|
|
21
|
+
```ruby
|
|
22
|
+
agent.invoke(input, config: { messages: prior_msgs, thread_id: "t1" })
|
|
23
|
+
agent.stream(input, config: { messages: prior_msgs, thread_id: "t1" }) { |e| ... }
|
|
24
|
+
```
|
|
25
|
+
**After (v0.5.0)**:
|
|
26
|
+
```ruby
|
|
27
|
+
agent.invoke(input, messages: prior_msgs, thread_id: "t1")
|
|
28
|
+
agent.stream(input, messages: prior_msgs, thread_id: "t1") { |e| ... }
|
|
29
|
+
```
|
|
30
|
+
Applications that only pass `:knowledge_sources`, `:user_id`, or `:session_id`
|
|
31
|
+
in `config:` require no changes.
|
|
32
|
+
|
|
33
|
+
- **`Agent::Checkpoint#initialize` — `original_input:` is now a required keyword
|
|
34
|
+
argument**: Applications that construct `Checkpoint` instances directly must
|
|
35
|
+
add `original_input: input`. Checkpoints produced by `#invoke` already include
|
|
36
|
+
this field automatically.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **`ReactAgent#step` — system instructions were never applied**: The first
|
|
41
|
+
iteration of the ReAct loop now calls `build_context` to assemble the system
|
|
42
|
+
prompt and history, matching the behaviour of `Agent::Base`. Subsequent
|
|
43
|
+
iterations re-apply instructions via `build_cached_system_text` before calling
|
|
44
|
+
`chat.complete`. Previously, all iterations silently omitted the system prompt.
|
|
45
|
+
|
|
46
|
+
- **`Agent::Base#resume` — system instructions were not re-applied after
|
|
47
|
+
suspension**: Resuming from a `Checkpoint` now calls `build_cached_system_text`
|
|
48
|
+
using the original input stored in the checkpoint, so the LLM receives the
|
|
49
|
+
correct system prompt when the conversation continues. Previously, the LLM was
|
|
50
|
+
called without any system instructions on resume.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## [0.4.0] - 2026-05-19
|
|
55
|
+
|
|
56
|
+
### Removed
|
|
57
|
+
|
|
58
|
+
- **`Phronomy::TrustPipeline` removed**: The `TrustPipeline` class and its inner
|
|
59
|
+
`TrustResult` value object have been deleted. Use `Phronomy::GeneratorVerifier`
|
|
60
|
+
instead, which provides the same generator-verifier pattern with a cleaner,
|
|
61
|
+
fully injectable API.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **`Phronomy::GeneratorVerifier`** — Generator-Verifier coordination loop
|
|
66
|
+
(Anthropic blog, Pattern 1). Wraps a generator agent and a verifier agent with
|
|
67
|
+
fully injectable prompt builders, response parsers, a configurable iteration
|
|
68
|
+
limit, and an approval-outcome raise policy.
|
|
69
|
+
- **`Phronomy::Agent::Orchestrator`** — Base class for orchestrator agents
|
|
70
|
+
(Anthropic blog, Pattern 2). Extends `Agent::Base` with a `subagent` DSL for
|
|
71
|
+
declarative subagent registration as LLM-callable tools, plus `dispatch_parallel`
|
|
72
|
+
and `fan_out` for programmatic parallel invocation.
|
|
73
|
+
- **`Phronomy::Agent::TeamCoordinator`** — Agent teams coordination pattern
|
|
74
|
+
(Anthropic blog, Pattern 3). An LLM-powered coordinator with a shared task
|
|
75
|
+
queue and a pool of worker agents that carry conversation history across task
|
|
76
|
+
assignments. Adds `coordinator_provider` DSL for independent LLM routing.
|
|
77
|
+
- **`Phronomy::Agent::SharedState`** — Shared-state coordination pattern
|
|
78
|
+
(Anthropic blog, Pattern 5). Peer agents collaborate via a `KnowledgeStore`;
|
|
79
|
+
the `member` DSL registers agents with per-agent instructions; `coordination`
|
|
80
|
+
sets the team protocol; `build_prompt` injects a tool-usage guide automatically.
|
|
81
|
+
- **`Phronomy::LowConfidenceError`** — Exception raised by `GeneratorVerifier`
|
|
82
|
+
when `raise_policy: :raise` and verification fails after exhausting the
|
|
83
|
+
iteration limit.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- **`Phronomy::Graph::StateGraph` event system refactored**: Per-node `advance`
|
|
88
|
+
events replaced with a unified `node_completed` event queue, reducing
|
|
89
|
+
event-handler registration overhead and simplifying listener registration.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
10
93
|
## [0.3.0] - 2026-05-18
|
|
11
94
|
|
|
12
95
|
### Removed
|
data/README.md
CHANGED
|
@@ -18,7 +18,10 @@ It provides composable building blocks — Workflows, Agents, Tools, Guardrails,
|
|
|
18
18
|
| **Context Management** — Token budget calculation, estimation, and pruning | Stable |
|
|
19
19
|
| **Knowledge/RAG** — Retrieval sources with pluggable loaders, splitters, and vector stores | Beta |
|
|
20
20
|
| **Multi-agent** — Agent-as-Tool pattern and hub-and-spoke handoff routing | Beta |
|
|
21
|
-
| **
|
|
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 |
|
|
22
25
|
| **Guardrails** — Input/output validation; built-in PII and prompt-injection detectors | Beta |
|
|
23
26
|
| **Output Parser** — JSON and Struct-mapped parsers for structured LLM responses | Stable |
|
|
24
27
|
| **Eval Framework** — Dataset-driven evaluation with multiple scorer types | Beta |
|
|
@@ -226,23 +229,88 @@ end
|
|
|
226
229
|
|
|
227
230
|
Hooks are called in order — global → class → instance — and deep-merged.
|
|
228
231
|
|
|
229
|
-
###
|
|
232
|
+
### GeneratorVerifier — Generator-Verifier loop with custom prompt builders
|
|
230
233
|
|
|
231
234
|
```ruby
|
|
232
|
-
pipeline = Phronomy::
|
|
233
|
-
draft_agent:
|
|
234
|
-
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
|
+
|
|
235
248
|
confidence_threshold: 0.7,
|
|
236
|
-
max_iterations: 3
|
|
249
|
+
max_iterations: 3,
|
|
250
|
+
raise_if_untrusted: false # set true to raise LowConfidenceError
|
|
237
251
|
)
|
|
238
252
|
|
|
239
253
|
result = pipeline.invoke("What is the refund policy?")
|
|
240
|
-
puts result.output
|
|
241
|
-
puts result.trusted?
|
|
242
|
-
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
|
+
```
|
|
243
269
|
|
|
244
|
-
|
|
245
|
-
|
|
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])
|
|
311
|
+
|
|
312
|
+
results.map { |r| r[:output] }.join("\n")
|
|
313
|
+
end
|
|
246
314
|
end
|
|
247
315
|
```
|
|
248
316
|
|
|
@@ -328,18 +396,19 @@ search_tool = Phronomy::Tool::McpTool.from_server(
|
|
|
328
396
|
|
|
329
397
|
### Conversation History — passing prior messages
|
|
330
398
|
|
|
331
|
-
Phronomy does not manage conversation history internally.
|
|
332
|
-
message array and passes it in via `
|
|
399
|
+
Phronomy does not manage conversation history internally. The application owns the
|
|
400
|
+
message array and passes it in via the `messages:` keyword argument:
|
|
333
401
|
|
|
334
402
|
```ruby
|
|
335
403
|
# First turn
|
|
336
|
-
result1 = MyAgent.new.invoke("Hello! I'm Alice.",
|
|
404
|
+
result1 = MyAgent.new.invoke("Hello! I'm Alice.", thread_id: "session-1")
|
|
337
405
|
prior_messages = result1[:messages] # Array<RubyLLM::Message>
|
|
338
406
|
|
|
339
407
|
# Second turn — pass prior messages so the agent has context
|
|
340
408
|
result2 = MyAgent.new.invoke(
|
|
341
409
|
"What is my name?",
|
|
342
|
-
|
|
410
|
+
messages: prior_messages,
|
|
411
|
+
thread_id: "session-1"
|
|
343
412
|
)
|
|
344
413
|
puts result2[:output] # => "Your name is Alice."
|
|
345
414
|
```
|
|
@@ -432,7 +501,7 @@ bundle exec ruby NN_example_name/run.rb
|
|
|
432
501
|
| 16 | `16_before_completion_hook/` | Global/class/instance before_completion hooks |
|
|
433
502
|
| 17 | `17_multi_agent_handoff/` | Hub-and-spoke agent routing via Runner |
|
|
434
503
|
| 18 | `18_rails_agent_job/` | Rails app with AgentJob + ActionCable streaming |
|
|
435
|
-
| 19 | `19_trust_pipeline/` |
|
|
504
|
+
| 19 | `19_trust_pipeline/` | Generator-Verifier pattern with citation tracking, self-review loop and confidence gate |
|
|
436
505
|
|
|
437
506
|
## Development
|
|
438
507
|
|