rcrewai 0.6.1 → 0.7.1
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 +21 -1
- data/README.md +20 -0
- data/docs/api/agent.md +8 -0
- data/docs/api/crew.md +53 -3
- data/docs/api/index.md +14 -13
- data/docs/api/task.md +7 -0
- data/docs/examples/async-execution.md +8 -5
- data/docs/examples/tool-composition.md +3 -3
- data/docs/index.md +22 -7
- data/docs/superpowers/specs/2026-07-07-consensual-process-design.md +82 -0
- data/docs/tutorials/agent-options.md +128 -0
- data/docs/tutorials/consensual-process.md +58 -0
- data/docs/tutorials/flows.md +135 -0
- data/docs/tutorials/index.md +45 -0
- data/docs/tutorials/knowledge.md +80 -0
- data/docs/tutorials/memory.md +90 -0
- data/docs/upgrading-to-0.7.md +36 -0
- data/examples/consensual_process_example.rb +63 -0
- data/lib/rcrewai/crew.rb +8 -1
- data/lib/rcrewai/process.rb +87 -19
- data/lib/rcrewai/task.rb +3 -2
- data/lib/rcrewai/version.rb +1 -1
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0a4a20865a08157cf9148d63439eeb653570c39f4f6e850bf6cc9f8e963a437
|
|
4
|
+
data.tar.gz: 4abf734ac77a3bdb1c43887b077cd99e1aea4ff9e2f8ba08b5fcac72a27ac7b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 01bd0f68ca18dcee76ccca334821bd1412695922b1a84fba092b7bf4686179401c581844b36094b9cf765415e8ce2652cfb7300b496d4c57c9b78dbcd6b7ac22
|
|
7
|
+
data.tar.gz: afdc22e50a5a61bc0e153c0e8a702e9e0aa465cbf268d06d71a82f372c626236a163ceab777f4eee5e8f6134740a8b54903fdb50cdebd38b6ae7b987d1394581
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.1] - 2026-08-13
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `Crew#execute` built its event sink but never delivered it to agents, so no agent-level events (`IterationStart`/`IterationEnd`, tool calls, token usage) reached a sink passed to `crew.execute(stream:)`. `Task` now carries a `stream_sink` that `Crew` populates at execute time and passes to `Agent#execute_task`; the two `Process` call sites that bypass `Task#execute` read `crew.stream_sink` directly. Subscribers now receive the full event stream on the sync, async, hierarchical, and consensual paths.
|
|
14
|
+
- Tasks retained a reference to the caller's sink after `execute` returned, keeping request-scoped subscribers reachable for the lifetime of the task object. The sink is now cleared in an `ensure`.
|
|
15
|
+
|
|
16
|
+
### Note
|
|
17
|
+
- `Events.fan_out` invokes sinks inline on the emitting thread with no serialization, so under `async: true` a sink may be called concurrently from multiple worker threads. Subscribers must do their own locking.
|
|
18
|
+
|
|
19
|
+
## [0.7.0] - 2026-07-07
|
|
20
|
+
|
|
21
|
+
Turns the `:consensual` crew process from a stub into a real multi-agent
|
|
22
|
+
consensus (propose → vote → pick). This is a behavior change to an existing
|
|
23
|
+
process type — see `docs/upgrading-to-0.7.md`.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- The `:consensual` crew process now performs real multi-agent consensus instead of silently running sequentially. For each task, up to `consensus_agents` agents (default 3) propose candidate answers, all participants score each candidate 0–10, and the highest-scored candidate wins (ties break toward the task's assigned agent). Configure via `Crew.new(process: :consensual, consensus_agents: N)`. A proposer that errors is dropped; if all proposals fail the task is marked failed. **Behavior change:** code relying on `:consensual`'s previous (accidental) sequential behavior should switch to `process: :sequential`. See `docs/upgrading-to-0.7.md`.
|
|
27
|
+
|
|
10
28
|
## [0.6.1] - 2026-07-06
|
|
11
29
|
|
|
12
30
|
Follow-ups to the 0.6.0 Cognitive Memory release, deepening the areas that
|
|
@@ -208,7 +226,9 @@ output, guardrails, planning, and training/testing. See `ROADMAP.md`.
|
|
|
208
226
|
- CLI usage documentation
|
|
209
227
|
- Real-world use cases and examples
|
|
210
228
|
|
|
211
|
-
[Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.
|
|
229
|
+
[Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.7.1...HEAD
|
|
230
|
+
[0.7.1]: https://github.com/gkosmo/rcrewAI/compare/v0.7.0...v0.7.1
|
|
231
|
+
[0.7.0]: https://github.com/gkosmo/rcrewAI/compare/v0.6.1...v0.7.0
|
|
212
232
|
[0.6.1]: https://github.com/gkosmo/rcrewAI/compare/v0.6.0...v0.6.1
|
|
213
233
|
[0.6.0]: https://github.com/gkosmo/rcrewAI/compare/v0.5.0...v0.6.0
|
|
214
234
|
[0.5.0]: https://github.com/gkosmo/rcrewAI/compare/v0.4.0...v0.5.0
|
data/README.md
CHANGED
|
@@ -445,6 +445,26 @@ flow.state.id # => automatic UUID
|
|
|
445
445
|
or your own `#save`/`#load`) and call `flow.restore(id)` to resume.
|
|
446
446
|
- Invoke a `Crew` inside any step, or pause with `human_feedback('Approve?')`.
|
|
447
447
|
|
|
448
|
+
## 🗳️ Consensual Process
|
|
449
|
+
|
|
450
|
+
For decisions where multiple perspectives matter, the `:consensual` process has
|
|
451
|
+
several agents propose competing answers and vote to pick the best:
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
crew = RCrewAI::Crew.new('panel', process: :consensual, consensus_agents: 3)
|
|
455
|
+
crew.add_agent(junior)
|
|
456
|
+
crew.add_agent(senior)
|
|
457
|
+
crew.add_task(task)
|
|
458
|
+
|
|
459
|
+
crew.execute # each task: agents propose → all score 0–10 → highest wins
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
For every task, up to `consensus_agents` agents (default 3) propose a candidate
|
|
463
|
+
answer, all participants score each candidate, and the highest-scored candidate
|
|
464
|
+
becomes the result (ties break toward the task's assigned agent). A proposer that
|
|
465
|
+
errors is dropped; if all proposals fail the task is marked failed. Consensus
|
|
466
|
+
multiplies LLM calls, so `consensus_agents` bounds the cost on larger crews.
|
|
467
|
+
|
|
448
468
|
## 💡 Examples
|
|
449
469
|
|
|
450
470
|
### Hierarchical Team with Human Oversight
|
data/docs/api/agent.md
CHANGED
|
@@ -28,6 +28,14 @@ Creates a new agent instance.
|
|
|
28
28
|
- `human_input` (Boolean, optional) - Enable human-in-the-loop interactions (default: false)
|
|
29
29
|
- `require_approval_for_tools` (Boolean, optional) - Require human approval for tool usage
|
|
30
30
|
- `require_approval_for_final_answer` (Boolean, optional) - Require human approval for final results
|
|
31
|
+
- `llm` (Symbol \| Hash \| client, optional) - Per-agent LLM override: a provider symbol (`:anthropic`), an options hash (`{ provider:, model:, api_key:, temperature: }`), or a pre-built client. Defaults to the global configuration. See [Advanced Agent Options]({{ site.baseurl }}/tutorials/agent-options)
|
|
32
|
+
- `reasoning` (Boolean, optional) - Run a reasoning/planning pass before answering (default: false); surfaced on the result as `:reasoning`
|
|
33
|
+
- `max_reasoning_attempts` (Integer, optional) - Reasoning retries on empty output (default: 3)
|
|
34
|
+
- `respect_context_window` (Boolean, optional) - Trim history to fit the model's context window (default: false)
|
|
35
|
+
- `max_rpm` (Integer, optional) - Throttle the agent's LLM calls to this many requests per minute (default: unlimited)
|
|
36
|
+
- `knowledge` (Knowledge::Base, optional) - A knowledge base to ground the agent (see [Knowledge (RAG)]({{ site.baseurl }}/tutorials/knowledge))
|
|
37
|
+
- `knowledge_sources` (Array, optional) - Knowledge sources the agent wraps in a base
|
|
38
|
+
- `memory` (Memory \| Hash, optional) - A pre-built `Memory`, or options (`{ embedder:, store:, scope:, short_term_limit:, entity_extractor: }`); defaults to zero-config in-memory (see [Cognitive Memory]({{ site.baseurl }}/tutorials/memory))
|
|
31
39
|
|
|
32
40
|
**Returns:** `RCrewAI::Agent` instance
|
|
33
41
|
|
data/docs/api/crew.md
CHANGED
|
@@ -10,18 +10,26 @@ The Crew class is the main orchestrator in RCrewAI. It manages a collection of a
|
|
|
10
10
|
|
|
11
11
|
## Class Methods
|
|
12
12
|
|
|
13
|
-
### `.new(name)`
|
|
13
|
+
### `.new(name, **options)`
|
|
14
14
|
|
|
15
15
|
Creates a new crew instance.
|
|
16
16
|
|
|
17
17
|
**Parameters:**
|
|
18
18
|
- `name` (String) - The name of the crew
|
|
19
|
+
- `process` (Symbol, optional) - `:sequential` (default), `:hierarchical`, or `:consensual`
|
|
20
|
+
- `consensus_agents` (Integer, optional) - For `:consensual`, how many agents propose and vote per task (default: 3). See [Consensual Process]({{ site.baseurl }}/tutorials/consensual-process)
|
|
21
|
+
- `planning` (Boolean, optional) - Run a planner pass that drafts a per-task plan before execution (default: false)
|
|
22
|
+
- `planning_llm` (Symbol \| Hash \| client, optional) - The planner's LLM (defaults to the global provider)
|
|
23
|
+
- `knowledge` (Knowledge::Base, optional) - A knowledge base shared with all agents
|
|
24
|
+
- `knowledge_sources` (Array, optional) - Sources the crew wraps in a shared base (see [Knowledge (RAG)]({{ site.baseurl }}/tutorials/knowledge))
|
|
25
|
+
- `verbose` (Boolean, optional) - Detailed logging (default: false)
|
|
26
|
+
- `max_iterations` (Integer, optional) - Max iterations per agent (default: 10)
|
|
19
27
|
|
|
20
28
|
**Returns:** `RCrewAI::Crew` instance
|
|
21
29
|
|
|
22
30
|
**Example:**
|
|
23
31
|
```ruby
|
|
24
|
-
crew = RCrewAI::Crew.new("research_team")
|
|
32
|
+
crew = RCrewAI::Crew.new("research_team", process: :consensual, consensus_agents: 3)
|
|
25
33
|
```
|
|
26
34
|
|
|
27
35
|
### `.create(name)`
|
|
@@ -342,4 +350,46 @@ crew.add_agent(manager)
|
|
|
342
350
|
# Add other agents...
|
|
343
351
|
|
|
344
352
|
crew.execute
|
|
345
|
-
```
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Consensual Process
|
|
356
|
+
|
|
357
|
+
Agents propose competing answers and vote to pick the best (see the
|
|
358
|
+
[Consensual Process]({{ site.baseurl }}/tutorials/consensual-process) tutorial).
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
crew = RCrewAI::Crew.new("panel", process: :consensual, consensus_agents: 3)
|
|
362
|
+
crew.add_agent(junior)
|
|
363
|
+
crew.add_agent(senior)
|
|
364
|
+
crew.add_task(task)
|
|
365
|
+
|
|
366
|
+
crew.execute # each task: propose → vote → pick
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Lifecycle, Batch, Training
|
|
370
|
+
|
|
371
|
+
### `#before_kickoff { |inputs| ... }` / `#after_kickoff { |result| ... }`
|
|
372
|
+
|
|
373
|
+
Register callbacks that run before/after execution. A `before_kickoff` hook
|
|
374
|
+
receives the inputs hash (from `execute(inputs:)`) and may transform it; an
|
|
375
|
+
`after_kickoff` hook receives and may transform the result. The resolved inputs
|
|
376
|
+
are exposed on `#last_inputs`.
|
|
377
|
+
|
|
378
|
+
### `#kickoff_for_each(inputs:)`
|
|
379
|
+
|
|
380
|
+
Runs the crew once per input set, returning one result per input in order. Runs
|
|
381
|
+
are isolated to their own inputs.
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
results = crew.kickoff_for_each(inputs: [{ topic: "ruby" }, { topic: "python" }])
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### `#train(n_iterations:, filename:, feedback: nil)`
|
|
388
|
+
|
|
389
|
+
Runs the crew repeatedly, collects feedback after each run (via a `feedback:`
|
|
390
|
+
callable, defaulting to a human prompt), and persists it to JSON.
|
|
391
|
+
|
|
392
|
+
### `#test(n_iterations:, scorer: nil)`
|
|
393
|
+
|
|
394
|
+
Runs the crew repeatedly and reports per-run and average scores (via a `scorer:`
|
|
395
|
+
callable, defaulting to the run's success rate).
|
data/docs/api/index.md
CHANGED
|
@@ -19,23 +19,24 @@ Individual AI agents with specific roles and capabilities.
|
|
|
19
19
|
### [RCrewAI::Task]({{ site.baseurl }}/api/task)
|
|
20
20
|
Tasks that agents execute to achieve goals.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Capabilities (0.4 – 0.7)
|
|
23
|
+
|
|
24
|
+
These subsystems are covered in depth by the tutorials (with runnable examples):
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
- **`RCrewAI::Flow`** — event-driven workflows. See [Flows]({{ site.baseurl }}/tutorials/flows)
|
|
27
|
+
- **`RCrewAI::Knowledge::Base`** and sources/embedders — RAG. See [Knowledge]({{ site.baseurl }}/tutorials/knowledge)
|
|
28
|
+
- **`RCrewAI::Memory`** (+ `SqliteStore`, memory types) — cognitive memory. See [Memory]({{ site.baseurl }}/tutorials/memory)
|
|
29
|
+
- **Consensual process** — multi-agent voting. See [Consensual Process]({{ site.baseurl }}/tutorials/consensual-process)
|
|
30
|
+
- **Advanced agent/task options** — per-agent LLM, reasoning, rate limiting, context window, multimodal, structured output, guardrails, hooks. See [Advanced Options]({{ site.baseurl }}/tutorials/agent-options)
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
Allow agents to read and write files.
|
|
32
|
+
## Tools
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
See the [Tools System]({{ site.baseurl }}/api/tools) reference for the full API.
|
|
35
|
+
Built-in tools include `WebSearch`, `FileReader`, `FileWriter`, `SqlDatabase`,
|
|
36
|
+
`EmailSender`, `PdfProcessor`, and `CodeExecutor` — plus native function calling
|
|
37
|
+
and MCP servers.
|
|
32
38
|
|
|
33
39
|
## Configuration
|
|
34
40
|
|
|
35
41
|
### [RCrewAI::Configuration]({{ site.baseurl }}/api/configuration)
|
|
36
|
-
Configure LLM providers and other settings.
|
|
37
|
-
|
|
38
|
-
## CLI
|
|
39
|
-
|
|
40
|
-
### [RCrewAI::CLI]({{ site.baseurl }}/api/cli)
|
|
41
|
-
Command-line interface for managing crews.
|
|
42
|
+
Configure LLM providers, models, and other settings.
|
data/docs/api/task.md
CHANGED
|
@@ -32,6 +32,13 @@ Creates a new task instance.
|
|
|
32
32
|
- `allow_guidance` (Boolean) - Allow human guidance during execution
|
|
33
33
|
- `human_review_points` (Array) - Points where human review is requested ([:completion, :error])
|
|
34
34
|
- `async` (Boolean) - Whether task can be executed asynchronously (default: false)
|
|
35
|
+
- `output_schema` (Hash) - A JSON-schema subset the output is validated/coerced against; parsed result on `#structured_output` (raw string on `#raw_result`). Non-conforming output re-runs the agent with the error fed back
|
|
36
|
+
- `guardrail` (callable) - `->(output) { [ok, value_or_error] }` to validate/transform output before it flows downstream
|
|
37
|
+
- `guardrail_max_retries` (Integer) - Guardrail retries with the reason fed back (default: 3)
|
|
38
|
+
- `output_file` (String) - Path to write the result to after completion
|
|
39
|
+
- `create_directory` (Boolean) - Create the output file's parent dirs (default: true)
|
|
40
|
+
- `markdown` (Boolean) - Prepend a heading when the output isn't already markdown (default: false)
|
|
41
|
+
- `attachments` (Array) - Multimodal image inputs, e.g. `[{ type: :image, path: 'x.png' }]` or `{ type: :image, url: '...' }` (OpenAI/Azure). See [Advanced Agent Options]({{ site.baseurl }}/tutorials/agent-options)
|
|
35
42
|
|
|
36
43
|
**Returns:** `RCrewAI::Task` instance
|
|
37
44
|
|
|
@@ -30,9 +30,10 @@ require 'benchmark'
|
|
|
30
30
|
RCrewAI.configure do |config|
|
|
31
31
|
config.llm_provider = :openai
|
|
32
32
|
config.temperature = 0.4
|
|
33
|
-
config.
|
|
34
|
-
config.task_timeout = 300 # 5-minute timeout per task
|
|
33
|
+
config.timeout = 300 # request timeout (seconds)
|
|
35
34
|
end
|
|
35
|
+
# Concurrency is a per-run option, not global config:
|
|
36
|
+
# crew.execute(async: true, max_concurrency: 8)
|
|
36
37
|
|
|
37
38
|
# ===== CONCURRENT PROCESSING TOOLS =====
|
|
38
39
|
|
|
@@ -885,9 +886,11 @@ context: [] # Dependency management continues with available results
|
|
|
885
886
|
Designed for horizontal and vertical scaling:
|
|
886
887
|
|
|
887
888
|
```ruby
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
889
|
+
# Concurrency is controlled per run via the async executor:
|
|
890
|
+
crew.execute(async: true, max_concurrency: 8) # up to 8 concurrent tasks
|
|
891
|
+
|
|
892
|
+
# Request timeout is global config:
|
|
893
|
+
RCrewAI.configure { |c| c.timeout = 300 }
|
|
891
894
|
```
|
|
892
895
|
|
|
893
896
|
This concurrent processing system provides a complete framework for optimizing performance through intelligent parallel execution while maintaining reliability and quality standards across all processing streams.
|
|
@@ -25,12 +25,12 @@ require 'time'
|
|
|
25
25
|
|
|
26
26
|
# Configure RCrewAI
|
|
27
27
|
RCrewAI.configure do |config|
|
|
28
|
-
config.
|
|
28
|
+
config.llm_provider = :openai
|
|
29
29
|
config.openai_api_key = ENV['OPENAI_API_KEY']
|
|
30
30
|
config.log_level = :info
|
|
31
|
-
config.
|
|
32
|
-
config.task_timeout = 300
|
|
31
|
+
config.timeout = 300 # request timeout (seconds)
|
|
33
32
|
end
|
|
33
|
+
# Concurrency is set per run: crew.execute(async: true, max_concurrency: 6)
|
|
34
34
|
|
|
35
35
|
# Base tool for common functionality
|
|
36
36
|
class CompositeToolBase < RCrewAI::Tools::Base
|
data/docs/index.md
CHANGED
|
@@ -19,16 +19,31 @@ Build powerful AI agent crews in Ruby that work together to accomplish complex t
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
21
|
- **🤖 Intelligent Agents**: AI agents with reasoning loops, memory, and tool usage capabilities
|
|
22
|
-
- **🔗 Multi-LLM Support**: OpenAI, Anthropic (Claude), Google (Gemini), Azure OpenAI, and Ollama
|
|
23
|
-
- **🛠️ Rich Tool Ecosystem**:
|
|
24
|
-
-
|
|
22
|
+
- **🔗 Multi-LLM Support**: OpenAI, Anthropic (Claude), Google (Gemini), Azure OpenAI, and Ollama — configurable per agent
|
|
23
|
+
- **🛠️ Rich Tool Ecosystem**: Native function calling, MCP servers, web search, file operations, SQL, email, code execution, PDF processing, and custom tools
|
|
24
|
+
- **🌊 Flows**: Event-driven workflows (`start`/`listen`/`router`) with branching and persistent state
|
|
25
|
+
- **📚 Knowledge (RAG)**: Ground agents in your own documents (string/file/PDF/CSV/URL) with built-in retrieval
|
|
26
|
+
- **🧠 Cognitive Memory**: Semantic recall (embeddings + cosine) with optional SQLite persistence and short-term/long-term/entity/tool memory types
|
|
27
|
+
- **📤 Structured Output & Guardrails**: Schema-validated task output and validate/transform guardrails
|
|
28
|
+
- **🗳️ Flexible Orchestration**: Sequential, hierarchical, and consensual (propose → vote → pick) processes, plus async execution
|
|
25
29
|
- **🤝 Human-in-the-Loop**: Interactive approval workflows, human guidance, and collaborative decision making
|
|
26
|
-
- **⚡ Advanced Task System**: Dependencies, retries, async/concurrent execution, and context sharing
|
|
27
|
-
-
|
|
28
|
-
- **🔒 Production Ready**: Security controls, error handling, logging, monitoring, and sandboxing
|
|
29
|
-
- **🎯 Flexible Orchestration**: Sequential, hierarchical, and concurrent execution modes
|
|
30
|
+
- **⚡ Advanced Task System**: Dependencies, retries, async/concurrent execution, planning, and context sharing
|
|
31
|
+
- **🎛️ Production Controls**: Rate limiting (`max_rpm`), context-window management, reasoning passes, multimodal input, and streaming events with cost tracking
|
|
30
32
|
- **💎 Ruby-First Design**: Built specifically for Ruby developers with idiomatic patterns
|
|
31
33
|
|
|
34
|
+
## What's new (0.4 – 0.7)
|
|
35
|
+
|
|
36
|
+
RCrewAI has expanded well beyond the classic crew model. Recent releases added:
|
|
37
|
+
|
|
38
|
+
- **[Flows]({{ site.baseurl }}/tutorials/flows)** — a second orchestration pillar for event-driven, stateful workflows
|
|
39
|
+
- **[Knowledge / RAG]({{ site.baseurl }}/tutorials/knowledge)** — retrieval-augmented grounding from your own documents
|
|
40
|
+
- **[Cognitive Memory]({{ site.baseurl }}/tutorials/memory)** — semantic, persistent, multi-type agent memory
|
|
41
|
+
- **[Consensual process]({{ site.baseurl }}/tutorials/consensual-process)** — multi-agent voting to pick the best answer
|
|
42
|
+
- **[Advanced agent options]({{ site.baseurl }}/tutorials/agent-options)** — per-agent LLM, reasoning, rate limiting, context-window management, multimodal, structured output, guardrails, and lifecycle hooks
|
|
43
|
+
|
|
44
|
+
See the [CHANGELOG](https://github.com/gkosmo/rcrewAI/blob/main/CHANGELOG.md) and the
|
|
45
|
+
per-release upgrade guides (e.g. [upgrading to 0.7](https://github.com/gkosmo/rcrewAI/blob/main/docs/upgrading-to-0.7.md)) for details.
|
|
46
|
+
|
|
32
47
|
## Quick Start
|
|
33
48
|
|
|
34
49
|
### Basic Agent Collaboration
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Consensual Process: propose → vote → pick
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-07-07
|
|
4
|
+
**Status:** Approved design (pending implementation)
|
|
5
|
+
**Target version:** `rcrewai` 0.7.0 (current is 0.6.1)
|
|
6
|
+
**Scope:** Replace the stubbed `Process::Consensual` (which silently runs tasks sequentially) with a real multi-agent consensus: agents propose candidate answers, score each other's candidates, and the highest-scored candidate wins.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Motivation
|
|
11
|
+
|
|
12
|
+
`Crew.new('c', process: :consensual)` is a public, validated, documented process type. But `Process::Consensual#execute` currently just runs each task once (`execute_with_consensus` calls `task.execute` with a "for now, just execute normally" comment). A user selecting `:consensual` silently gets sequential behavior — the feature is advertised but not implemented. This is a latent honesty gap: fix the code to do what its name and comments promise ("agent voting/discussion").
|
|
13
|
+
|
|
14
|
+
## Behavior
|
|
15
|
+
|
|
16
|
+
For each task in the crew, instead of a single execution:
|
|
17
|
+
|
|
18
|
+
1. **Propose** — up to `consensus_agents` agents (default 3, capped from the crew) each independently produce a candidate answer for the task.
|
|
19
|
+
2. **Vote** — each participating agent scores every candidate 0–10 for how well it satisfies the task's description and expected output.
|
|
20
|
+
3. **Pick** — the candidate with the highest total score wins. Ties break toward the task's assigned agent's candidate (or the first proposer if the assigned agent didn't propose).
|
|
21
|
+
|
|
22
|
+
## Components
|
|
23
|
+
|
|
24
|
+
All within `Process::Consensual`, replacing the stub. Result shape is unchanged
|
|
25
|
+
(`{ task:, result:, status: }`) so `Crew#format_execution_results` needs no changes.
|
|
26
|
+
|
|
27
|
+
- `execute` — iterate tasks, call `execute_with_consensus(task)`, log, return results.
|
|
28
|
+
- `select_participants(task)` — first N agents (`consensus_agents`), always including the task's assigned agent if present and not already in the first N.
|
|
29
|
+
- `gather_proposals(task, participants)` — each agent runs the task; returns `[{ agent:, content: }]`. A proposer that raises is dropped (logged), not fatal.
|
|
30
|
+
- `score_candidates(task, candidates, participants)` — each participant scores each candidate via an LLM call; returns per-candidate total. Non-numeric / failed scores count as 0.
|
|
31
|
+
- `pick_winner(task, scored)` — highest total; deterministic tie-break toward the task's assigned agent, else first proposer.
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
Crew.new('c', process: :consensual, consensus_agents: 3)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`consensus_agents` (default 3) caps how many agents propose and vote, bounding
|
|
40
|
+
cost regardless of crew size. Read by the process from the crew.
|
|
41
|
+
|
|
42
|
+
## Cost
|
|
43
|
+
|
|
44
|
+
Bounded per task: N proposals + N×N scoring calls, N defaulting to 3
|
|
45
|
+
(≈ 3 + 9 = 12 LLM calls/task worst case), independent of crew size.
|
|
46
|
+
|
|
47
|
+
## Edge cases
|
|
48
|
+
|
|
49
|
+
- **1 participant** → a single proposal, no meaningful vote; returns a valid result.
|
|
50
|
+
- **A proposer errors** → that candidate is dropped; consensus continues with the rest.
|
|
51
|
+
- **All proposals fail** → task marked `:failed` with the error, matching Sequential's rescue behavior.
|
|
52
|
+
- **No agents in crew** → task `:failed` (nothing can propose).
|
|
53
|
+
|
|
54
|
+
## Scoring mechanism
|
|
55
|
+
|
|
56
|
+
Each participant is prompted: given the task (description + expected output) and a
|
|
57
|
+
candidate answer, return a single integer 0–10. The response is parsed with a
|
|
58
|
+
tolerant integer extraction (first integer in the text; clamp to 0–10; default 0
|
|
59
|
+
on failure). Scores are summed across participants per candidate.
|
|
60
|
+
|
|
61
|
+
## Backward compatibility
|
|
62
|
+
|
|
63
|
+
`:consensual` already exists and validates. This changes only runtime behavior
|
|
64
|
+
(from "silently sequential" to "actual consensus"). The async consensual path
|
|
65
|
+
(`Crew#execute_consensual_async`, parallel aggregation) is intentionally left
|
|
66
|
+
unchanged for this pass and noted in docs; expanding it is a possible follow-up.
|
|
67
|
+
|
|
68
|
+
## Testing (TDD)
|
|
69
|
+
|
|
70
|
+
Fake agents returning canned proposals and scores (no live LLM) prove:
|
|
71
|
+
- highest-scored candidate wins
|
|
72
|
+
- deterministic tie-break toward the task's assigned agent
|
|
73
|
+
- `consensus_agents` cap respected on a larger crew
|
|
74
|
+
- single-agent degradation returns a valid result
|
|
75
|
+
- a proposer raising is dropped, consensus still completes
|
|
76
|
+
- all-proposals-fail marks the task `:failed`
|
|
77
|
+
|
|
78
|
+
## Rollout
|
|
79
|
+
|
|
80
|
+
Ship in `0.7.0` (behavior change to an existing feature warrants a minor bump).
|
|
81
|
+
`docs/upgrading-to-0.7.md` notes the behavior change; README documents the
|
|
82
|
+
consensus flow and `consensus_agents`.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: tutorial
|
|
3
|
+
title: Advanced Agent & Task Options
|
|
4
|
+
description: Per-agent LLM, reasoning, rate limiting, context window, multimodal, structured output, guardrails, and lifecycle hooks
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Advanced Agent & Task Options
|
|
8
|
+
|
|
9
|
+
A tour of the production controls added across 0.4–0.7. All are opt-in; agents
|
|
10
|
+
and tasks behave as before when you don't set them.
|
|
11
|
+
|
|
12
|
+
## Per-agent LLM
|
|
13
|
+
|
|
14
|
+
Give each agent its own provider/model instead of only the global default:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
worker = RCrewAI::Agent.new(name: 'worker', role: '...', goal: '...',
|
|
18
|
+
llm: { provider: :openai, model: 'gpt-4o-mini' })
|
|
19
|
+
manager = RCrewAI::Agent.new(name: 'manager', role: '...', goal: '...',
|
|
20
|
+
llm: { provider: :anthropic, model: 'claude-3-opus-20240229' })
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Accepts a provider symbol, an options hash, or a pre-built client. Overrides
|
|
24
|
+
never mutate the global configuration.
|
|
25
|
+
|
|
26
|
+
## Reasoning
|
|
27
|
+
|
|
28
|
+
Have an agent draft a plan before answering. The trace is exposed on the result
|
|
29
|
+
and doesn't pollute `task.result`:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
agent = RCrewAI::Agent.new(name: '...', role: '...', goal: '...',
|
|
33
|
+
reasoning: true, max_reasoning_attempts: 3)
|
|
34
|
+
result = agent.execute_task(task)
|
|
35
|
+
result[:reasoning] # the plan
|
|
36
|
+
result[:content] # the answer
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Rate limiting
|
|
40
|
+
|
|
41
|
+
Cap an agent's LLM calls to stay under provider limits (thread-safe, holds under
|
|
42
|
+
async execution):
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
agent = RCrewAI::Agent.new(name: '...', role: '...', goal: '...', max_rpm: 20)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Context-window management
|
|
49
|
+
|
|
50
|
+
Trim history to fit the model's context window (oldest non-system messages drop
|
|
51
|
+
first; system + latest always kept):
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
agent = RCrewAI::Agent.new(name: '...', role: '...', goal: '...',
|
|
55
|
+
respect_context_window: true)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Multimodal input
|
|
59
|
+
|
|
60
|
+
Pass images to a vision-capable model via task attachments (local files are
|
|
61
|
+
base64-encoded; URLs pass through). Supported on OpenAI/Azure.
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
task = RCrewAI::Task.new(
|
|
65
|
+
name: 'describe', description: 'What is in this chart?', agent: agent,
|
|
66
|
+
attachments: [
|
|
67
|
+
{ type: :image, path: 'chart.png' },
|
|
68
|
+
{ type: :image, url: 'https://example.com/photo.jpg' }
|
|
69
|
+
]
|
|
70
|
+
)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Structured output & guardrails
|
|
74
|
+
|
|
75
|
+
Validate, transform, and persist a task's result:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
task = RCrewAI::Task.new(
|
|
79
|
+
name: 'extract', description: '...', agent: agent,
|
|
80
|
+
|
|
81
|
+
output_schema: { type: 'object', properties: { title: { type: 'string' } },
|
|
82
|
+
required: ['title'] }, # -> task.structured_output
|
|
83
|
+
|
|
84
|
+
guardrail: ->(out) { [out.length < 5000, 'too long'] }, # [ok, value_or_error]
|
|
85
|
+
|
|
86
|
+
output_file: 'out/report.md', markdown: true
|
|
87
|
+
)
|
|
88
|
+
task.execute
|
|
89
|
+
task.structured_output # validated object
|
|
90
|
+
task.raw_result # unprocessed string
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Schema/guardrail failures re-run the agent with the error fed back.
|
|
94
|
+
|
|
95
|
+
## Planning
|
|
96
|
+
|
|
97
|
+
Run a planner pass that drafts a per-task plan before execution:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
crew = RCrewAI::Crew.new('research', planning: true) # optional planning_llm:
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Lifecycle hooks & batch runs
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
crew.before_kickoff { |inputs| inputs.merge(started_at: Time.now) }
|
|
107
|
+
crew.after_kickoff { |result| notify(result); result }
|
|
108
|
+
|
|
109
|
+
crew.execute(inputs: { topic: 'ruby' })
|
|
110
|
+
crew.last_inputs # the resolved inputs
|
|
111
|
+
|
|
112
|
+
# Run the crew once per input set:
|
|
113
|
+
results = crew.kickoff_for_each(inputs: [{ topic: 'ruby' }, { topic: 'python' }])
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Training & testing
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
crew.train(n_iterations: 3, filename: 'training.json') # collect feedback
|
|
120
|
+
crew.test(n_iterations: 5) # score repeated runs
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## See also
|
|
124
|
+
|
|
125
|
+
- [Flows]({{ site.baseurl }}/tutorials/flows)
|
|
126
|
+
- [Knowledge (RAG)]({{ site.baseurl }}/tutorials/knowledge)
|
|
127
|
+
- [Cognitive Memory]({{ site.baseurl }}/tutorials/memory)
|
|
128
|
+
- [Consensual Process]({{ site.baseurl }}/tutorials/consensual-process)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: tutorial
|
|
3
|
+
title: Consensual Process
|
|
4
|
+
description: Multi-agent consensus — agents propose competing answers and vote to pick one
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Consensual Process
|
|
8
|
+
|
|
9
|
+
For decisions where multiple perspectives matter, the `:consensual` process has
|
|
10
|
+
several agents propose competing answers and vote to pick the best one.
|
|
11
|
+
|
|
12
|
+
> **Since 0.7.0.** Earlier versions treated `:consensual` as a stub that ran
|
|
13
|
+
> tasks sequentially. It now performs real consensus — if you relied on the old
|
|
14
|
+
> behavior, use `process: :sequential`.
|
|
15
|
+
|
|
16
|
+
## How it works
|
|
17
|
+
|
|
18
|
+
For each task:
|
|
19
|
+
|
|
20
|
+
1. **Propose** — up to `consensus_agents` agents (default 3, capped from the
|
|
21
|
+
crew) each produce a candidate answer.
|
|
22
|
+
2. **Vote** — every participant scores each candidate 0–10 against the task's
|
|
23
|
+
description and expected output.
|
|
24
|
+
3. **Pick** — the highest total score wins. Ties break toward the task's
|
|
25
|
+
assigned agent.
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
crew = RCrewAI::Crew.new('panel', process: :consensual, consensus_agents: 3)
|
|
29
|
+
crew.add_agent(junior)
|
|
30
|
+
crew.add_agent(senior)
|
|
31
|
+
crew.add_task(task)
|
|
32
|
+
|
|
33
|
+
result = crew.execute # each task goes through propose → vote → pick
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Cost
|
|
37
|
+
|
|
38
|
+
Consensus multiplies LLM calls: roughly `N` proposals + `N × N` scoring calls per
|
|
39
|
+
task, where `N` is `consensus_agents` (default 3). The cap keeps cost bounded even
|
|
40
|
+
on large crews — raise or lower it to trade thoroughness for cost.
|
|
41
|
+
|
|
42
|
+
## Edge cases
|
|
43
|
+
|
|
44
|
+
- **One agent** → a single proposal (no meaningful vote), still a valid result.
|
|
45
|
+
- **A proposer errors** → that candidate is dropped; consensus continues with the
|
|
46
|
+
rest.
|
|
47
|
+
- **All proposals fail** → the task is marked failed.
|
|
48
|
+
|
|
49
|
+
## When to use it
|
|
50
|
+
|
|
51
|
+
Reach for `:consensual` when answer quality benefits from diversity and
|
|
52
|
+
cross-checking — design decisions, judgment calls, ambiguous tasks. For
|
|
53
|
+
straightforward pipelines, `:sequential` or `:hierarchical` is cheaper.
|
|
54
|
+
|
|
55
|
+
## Runnable example
|
|
56
|
+
|
|
57
|
+
See [`examples/consensual_process_example.rb`](https://github.com/gkosmo/rcrewAI/blob/main/examples/consensual_process_example.rb)
|
|
58
|
+
— runs without an API key.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: tutorial
|
|
3
|
+
title: Flows — Event-Driven Workflows
|
|
4
|
+
description: Build structured, stateful workflows with start/listen/router, combinators, and persistence
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Flows
|
|
8
|
+
|
|
9
|
+
Crews are great for "have these agents produce these outputs." **Flows** are the
|
|
10
|
+
second orchestration pillar — for workflows that need explicit branching, joins,
|
|
11
|
+
persistent state, or coordination across multiple crews and plain Ruby steps.
|
|
12
|
+
|
|
13
|
+
Subclass `RCrewAI::Flow` and wire methods together with a class-level DSL.
|
|
14
|
+
|
|
15
|
+
## A first flow
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require 'rcrewai'
|
|
19
|
+
|
|
20
|
+
class ArticleFlow < RCrewAI::Flow
|
|
21
|
+
start :outline
|
|
22
|
+
def outline
|
|
23
|
+
state.sections = %w[intro body conclusion]
|
|
24
|
+
state.sections.length # return value is passed to listeners of :outline
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
listen :outline
|
|
28
|
+
def draft(section_count)
|
|
29
|
+
state.words = section_count * 100
|
|
30
|
+
state.words
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
router :draft
|
|
34
|
+
def review(words)
|
|
35
|
+
words >= 250 ? :publish : :expand # a router returns a label
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
listen :publish
|
|
39
|
+
def publish = state.status = 'published'
|
|
40
|
+
|
|
41
|
+
listen :expand
|
|
42
|
+
def expand = state.status = 'needs more work'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
flow = ArticleFlow.new
|
|
46
|
+
flow.kickoff(inputs: { author: 'Ada' })
|
|
47
|
+
flow.state.status # => "published"
|
|
48
|
+
flow.state.id # => automatic UUID
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## The DSL
|
|
52
|
+
|
|
53
|
+
- **`start :method`** — an entry point. A flow can have several; all run first.
|
|
54
|
+
- **`listen :trigger`** — runs the following method after `:trigger` completes,
|
|
55
|
+
receiving its return value.
|
|
56
|
+
- **`router :trigger`** — like `listen`, but the method's return value becomes a
|
|
57
|
+
**label** that other `listen` methods can trigger on. This is how you branch.
|
|
58
|
+
|
|
59
|
+
### Combining triggers
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
listen and_(:fetch_a, :fetch_b) # fires once, after BOTH complete
|
|
63
|
+
def merge(...); end
|
|
64
|
+
|
|
65
|
+
listen or_(:cache_hit, :cache_miss) # fires when EITHER completes
|
|
66
|
+
def proceed(...); end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## State
|
|
70
|
+
|
|
71
|
+
`state` is a schemaless object with an automatic UUID. Read and write attributes
|
|
72
|
+
directly (`state.foo = 1`), and seed initial values via `kickoff(inputs:)`:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
flow.kickoff(inputs: { topic: 'ruby', max_words: 800 })
|
|
76
|
+
flow.state.topic # => "ruby"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Persistence — pause and resume
|
|
80
|
+
|
|
81
|
+
Pass a `state_store:` and a flow's state is saved after each run, so you can
|
|
82
|
+
restore it later by id:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
store = RCrewAI::Flow::FileStateStore.new('tmp/flows') # or your own #save/#load
|
|
86
|
+
flow = ArticleFlow.new(state_store: store)
|
|
87
|
+
flow.kickoff
|
|
88
|
+
id = flow.state.id
|
|
89
|
+
|
|
90
|
+
# ...later, even in a fresh process...
|
|
91
|
+
resumed = ArticleFlow.new(state_store: store)
|
|
92
|
+
resumed.restore(id)
|
|
93
|
+
resumed.state.status # => recovered
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Built-in stores: `RCrewAI::Flow::MemoryStateStore` (volatile) and
|
|
97
|
+
`RCrewAI::Flow::FileStateStore` (JSON on disk). Any object responding to
|
|
98
|
+
`#save(id, hash)` / `#load(id)` works.
|
|
99
|
+
|
|
100
|
+
## Running a crew inside a flow
|
|
101
|
+
|
|
102
|
+
A flow step is just a method, so it can kick off a whole crew:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
class ResearchFlow < RCrewAI::Flow
|
|
106
|
+
def initialize(crew:, **opts)
|
|
107
|
+
super(**opts)
|
|
108
|
+
@crew = crew
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
start :run
|
|
112
|
+
def run
|
|
113
|
+
state.crew_result = @crew.execute(inputs: { topic: state.topic })
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Human feedback
|
|
119
|
+
|
|
120
|
+
Pause a flow for input with `human_feedback`:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
listen :draft
|
|
124
|
+
def approve(_draft)
|
|
125
|
+
state.approved = human_feedback('Approve this draft?')
|
|
126
|
+
end
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Provide a handler for non-interactive runs:
|
|
130
|
+
`ArticleFlow.new(feedback_handler: ->(prompt) { auto_approve(prompt) })`.
|
|
131
|
+
|
|
132
|
+
## Runnable example
|
|
133
|
+
|
|
134
|
+
See [`examples/flow_example.rb`](https://github.com/gkosmo/rcrewAI/blob/main/examples/flow_example.rb)
|
|
135
|
+
— it runs without an API key.
|
data/docs/tutorials/index.md
CHANGED
|
@@ -62,6 +62,51 @@ Welcome to the RCrewAI tutorials! These step-by-step guides will take you from b
|
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
+
## ✨ Capabilities (0.4 – 0.7)
|
|
66
|
+
|
|
67
|
+
The features that grew RCrewAI beyond the classic crew model.
|
|
68
|
+
|
|
69
|
+
### [Flows — Event-Driven Workflows]({{ site.baseurl }}/tutorials/flows)
|
|
70
|
+
**Orchestrate with branching and state.** `start`/`listen`/`router`, `and_`/`or_`
|
|
71
|
+
combinators, schemaless state with a UUID, persistence and resume, running crews
|
|
72
|
+
as steps, and `human_feedback` pause points.
|
|
73
|
+
|
|
74
|
+
**Prerequisites:** Getting Started tutorial
|
|
75
|
+
**Difficulty:** Intermediate ⭐⭐
|
|
76
|
+
|
|
77
|
+
### [Knowledge (RAG)]({{ site.baseurl }}/tutorials/knowledge)
|
|
78
|
+
**Ground agents in your documents.** String/file/PDF/CSV/URL sources, chunking,
|
|
79
|
+
multi-provider embeddings, and agent- or crew-level attachment with automatic
|
|
80
|
+
retrieval into the prompt.
|
|
81
|
+
|
|
82
|
+
**Prerequisites:** Getting Started tutorial
|
|
83
|
+
**Difficulty:** Intermediate ⭐⭐
|
|
84
|
+
|
|
85
|
+
### [Cognitive Memory]({{ site.baseurl }}/tutorials/memory)
|
|
86
|
+
**Agents that remember.** Semantic recall (embeddings + cosine), optional SQLite
|
|
87
|
+
persistence, and short-term/long-term/entity/tool memory types — with a
|
|
88
|
+
zero-config default.
|
|
89
|
+
|
|
90
|
+
**Prerequisites:** Getting Started tutorial
|
|
91
|
+
**Difficulty:** Intermediate ⭐⭐
|
|
92
|
+
|
|
93
|
+
### [Consensual Process]({{ site.baseurl }}/tutorials/consensual-process)
|
|
94
|
+
**Multi-agent voting.** Agents propose competing answers and score each other to
|
|
95
|
+
pick the best; tune cost with `consensus_agents`.
|
|
96
|
+
|
|
97
|
+
**Prerequisites:** Getting Started tutorial
|
|
98
|
+
**Difficulty:** Beginner ⭐
|
|
99
|
+
|
|
100
|
+
### [Advanced Agent & Task Options]({{ site.baseurl }}/tutorials/agent-options)
|
|
101
|
+
**Production controls.** Per-agent LLM, reasoning passes, rate limiting,
|
|
102
|
+
context-window management, multimodal input, structured output, guardrails,
|
|
103
|
+
planning, lifecycle hooks, and batch runs.
|
|
104
|
+
|
|
105
|
+
**Prerequisites:** Getting Started tutorial
|
|
106
|
+
**Difficulty:** Intermediate ⭐⭐
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
65
110
|
## 🏗️ Architecture & Scaling
|
|
66
111
|
|
|
67
112
|
### [Working with Multiple Crews]({{ site.baseurl }}/tutorials/multiple-crews)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: tutorial
|
|
3
|
+
title: Knowledge (RAG)
|
|
4
|
+
description: Ground agents in your own documents with retrieval-augmented generation
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Knowledge (RAG)
|
|
8
|
+
|
|
9
|
+
Give agents access to your own documents. Sources are chunked, embedded, and
|
|
10
|
+
stored in a vector store; at execution time the most relevant chunks are
|
|
11
|
+
injected into the agent's task prompt automatically.
|
|
12
|
+
|
|
13
|
+
## Building a knowledge base
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
require 'rcrewai'
|
|
17
|
+
|
|
18
|
+
kb = RCrewAI::Knowledge::Base.new(sources: [
|
|
19
|
+
RCrewAI::Knowledge::StringSource.new('Refunds are available within 30 days.'),
|
|
20
|
+
RCrewAI::Knowledge::FileSource.new('docs/policy.txt'),
|
|
21
|
+
RCrewAI::Knowledge::PdfSource.new('handbook.pdf'),
|
|
22
|
+
RCrewAI::Knowledge::CsvSource.new('faq.csv'),
|
|
23
|
+
RCrewAI::Knowledge::UrlSource.new('https://example.com/faq')
|
|
24
|
+
])
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Attaching knowledge
|
|
28
|
+
|
|
29
|
+
**Agent-level** (role-specific):
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
support = RCrewAI::Agent.new(
|
|
33
|
+
name: 'support', role: 'Support specialist', goal: 'Answer using company policy',
|
|
34
|
+
knowledge: kb
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Or pass raw sources and let the agent build the base:
|
|
38
|
+
support = RCrewAI::Agent.new(name: 'support', role: '...', goal: '...',
|
|
39
|
+
knowledge_sources: [RCrewAI::Knowledge::StringSource.new('...')])
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Crew-level** (shared with every agent):
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
crew = RCrewAI::Crew.new('support_crew', knowledge: kb)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
When a task runs, chunks relevant to the task description are retrieved and added
|
|
49
|
+
to the prompt under a "Relevant Knowledge" heading.
|
|
50
|
+
|
|
51
|
+
## Embeddings — pick a provider
|
|
52
|
+
|
|
53
|
+
Embeddings default to OpenAI's `text-embedding-3-small`. Since 0.6.1 the embedder
|
|
54
|
+
is multi-provider:
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
# Local, no API key:
|
|
58
|
+
embedder = RCrewAI::Knowledge::Embedder.new(provider: :ollama, model: 'nomic-embed-text')
|
|
59
|
+
|
|
60
|
+
# Or :azure / :google. (:anthropic has no embeddings API and raises.)
|
|
61
|
+
kb = RCrewAI::Knowledge::Base.new(sources: [...], embedder: embedder)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Any object responding to `embed(texts) -> [[float, ...], ...]` can be substituted.
|
|
65
|
+
|
|
66
|
+
## Chunking and the vector store
|
|
67
|
+
|
|
68
|
+
`Knowledge::Base.new` accepts `chunk_size:` and `overlap:` to tune how documents
|
|
69
|
+
are split. The default vector store is in-memory with cosine similarity; the
|
|
70
|
+
store is pluggable if you need a different backend.
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
kb = RCrewAI::Knowledge::Base.new(sources: [...], chunk_size: 800, overlap: 100)
|
|
74
|
+
kb.search('what is the refund window?', k: 3) # => top-k relevant chunks
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Runnable example
|
|
78
|
+
|
|
79
|
+
See [`examples/knowledge_rag_example.rb`](https://github.com/gkosmo/rcrewAI/blob/main/examples/knowledge_rag_example.rb)
|
|
80
|
+
— it runs without an API key using a fake embedder.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: tutorial
|
|
3
|
+
title: Cognitive Memory
|
|
4
|
+
description: Semantic, persistent, multi-type agent memory
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cognitive Memory
|
|
8
|
+
|
|
9
|
+
Agents remember what they've done and recall it on future tasks. Memory is
|
|
10
|
+
**zero-config by default** (in-memory, word-overlap recall) and becomes far more
|
|
11
|
+
capable when you add an embedder (semantic recall) and a store (persistence).
|
|
12
|
+
|
|
13
|
+
## Zero-config
|
|
14
|
+
|
|
15
|
+
Every agent gets a `Memory` scoped to itself. Nothing to set up:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
agent = RCrewAI::Agent.new(name: 'engineer', role: '...', goal: '...')
|
|
19
|
+
# agent.memory records executions and recalls relevant ones automatically
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Semantic recall
|
|
23
|
+
|
|
24
|
+
Pass an embedder and recall becomes semantic — the agent finds conceptually
|
|
25
|
+
related past work even when the wording differs:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
embedder = RCrewAI::Knowledge::Embedder.new # or provider: :ollama
|
|
29
|
+
agent = RCrewAI::Agent.new(name: 'engineer', role: '...', goal: '...',
|
|
30
|
+
memory: { embedder: embedder })
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Recall falls back to word-overlap similarity without an embedder, and embedding
|
|
34
|
+
failures fall back gracefully — **memory never breaks agent execution.**
|
|
35
|
+
|
|
36
|
+
## Persistence
|
|
37
|
+
|
|
38
|
+
Give memory a SQLite store and it survives restarts:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
store = RCrewAI::Memory::SqliteStore.new(path: '~/.rcrewai/memory.db')
|
|
42
|
+
agent = RCrewAI::Agent.new(name: 'engineer', role: '...', goal: '...',
|
|
43
|
+
memory: { embedder: embedder, store: store })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The default store is `InMemoryStore` (volatile). `SqliteStore` accepts
|
|
47
|
+
`max_candidates:` (default 1000) to bound how many recent rows a search scans,
|
|
48
|
+
keeping recall fast as memory grows.
|
|
49
|
+
|
|
50
|
+
## Memory types
|
|
51
|
+
|
|
52
|
+
The `Memory` facade exposes four underlying types:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
agent.memory.short_term # recent executions (capped, semantic recall)
|
|
56
|
+
agent.memory.long_term # durable, deduped insights from successful runs
|
|
57
|
+
agent.memory.entity # facts about entities (people, systems) seen in work
|
|
58
|
+
agent.memory.tool # tool-call history + outcomes
|
|
59
|
+
|
|
60
|
+
agent.memory.entity.entities # => ["Alice", "AWS", ...]
|
|
61
|
+
agent.memory.long_term.recall('...', limit: 3)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Better entity extraction
|
|
65
|
+
|
|
66
|
+
By default entities are extracted heuristically (capitalized tokens). For
|
|
67
|
+
multi-word names, plug in an LLM extractor:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
extractor = RCrewAI::Memory::LlmEntityExtractor.new(agent.llm_client)
|
|
71
|
+
agent = RCrewAI::Agent.new(name: '...', role: '...', goal: '...',
|
|
72
|
+
memory: { entity_extractor: extractor })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Scoping
|
|
76
|
+
|
|
77
|
+
Memory is scoped per agent, so agents sharing a persistent store don't read each
|
|
78
|
+
other's memories. Override with `memory: { scope: 'shared' }` for deliberate
|
|
79
|
+
sharing.
|
|
80
|
+
|
|
81
|
+
## The classic API still works
|
|
82
|
+
|
|
83
|
+
`add_execution`, `add_tool_usage`, `relevant_executions`, `tool_usage_for`,
|
|
84
|
+
`clear_short_term!`, `clear_all!`, and `stats` behave as before — the cognitive
|
|
85
|
+
system is a drop-in upgrade.
|
|
86
|
+
|
|
87
|
+
## Runnable example
|
|
88
|
+
|
|
89
|
+
See [`examples/cognitive_memory_example.rb`](https://github.com/gkosmo/rcrewAI/blob/main/examples/cognitive_memory_example.rb)
|
|
90
|
+
— semantic recall + SQLite persistence, no API key required.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Upgrading to RCrewAI 0.7
|
|
2
|
+
|
|
3
|
+
RCrewAI 0.7 makes the `:consensual` crew process do what its name promises.
|
|
4
|
+
|
|
5
|
+
## Behavior change: `:consensual` process
|
|
6
|
+
|
|
7
|
+
Previously, `Crew.new('c', process: :consensual)` was a stub — it silently ran
|
|
8
|
+
tasks **sequentially**. As of 0.7 it performs real multi-agent consensus:
|
|
9
|
+
|
|
10
|
+
1. **Propose** — up to `consensus_agents` agents (default 3) each produce a
|
|
11
|
+
candidate answer for the task.
|
|
12
|
+
2. **Vote** — every participant scores each candidate 0–10.
|
|
13
|
+
3. **Pick** — the highest-scored candidate wins (ties break toward the task's
|
|
14
|
+
assigned agent).
|
|
15
|
+
|
|
16
|
+
### What you must do
|
|
17
|
+
|
|
18
|
+
Nothing to keep your code running — the API is unchanged. But be aware:
|
|
19
|
+
|
|
20
|
+
- If you were using `process: :consensual` and relying on its (accidental)
|
|
21
|
+
sequential behavior, switch to `process: :sequential` explicitly.
|
|
22
|
+
- Consensus multiplies LLM calls (≈ N proposals + N×N scoring per task, N=3 by
|
|
23
|
+
default). Tune or bound it with `consensus_agents:`.
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
crew = RCrewAI::Crew.new('panel', process: :consensual, consensus_agents: 3)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Edge cases
|
|
30
|
+
|
|
31
|
+
- One agent → a single proposal (no real vote), still a valid result.
|
|
32
|
+
- A proposer that errors is dropped; consensus continues with the rest.
|
|
33
|
+
- If every proposal fails, the task is marked failed.
|
|
34
|
+
|
|
35
|
+
The async consensual path (`crew.execute(async: true)` with `:consensual`) is
|
|
36
|
+
unchanged in this release (parallel aggregation).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Consensual process — agents propose competing answers and vote to pick one.
|
|
5
|
+
#
|
|
6
|
+
# For each task: up to `consensus_agents` agents each produce a candidate
|
|
7
|
+
# answer, every participant scores each candidate 0-10, and the highest-scored
|
|
8
|
+
# candidate wins (ties break toward the task's assigned agent).
|
|
9
|
+
#
|
|
10
|
+
# This example stubs the agents so it runs WITHOUT an API key. In real use each
|
|
11
|
+
# agent calls your configured LLM to propose and to score.
|
|
12
|
+
#
|
|
13
|
+
# Run:
|
|
14
|
+
# ruby examples/consensual_process_example.rb
|
|
15
|
+
|
|
16
|
+
require_relative '../lib/rcrewai'
|
|
17
|
+
|
|
18
|
+
RCrewAI.configure(validate: false) do |c|
|
|
19
|
+
c.llm_provider = :openai
|
|
20
|
+
c.api_key = 'demo-key'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# A stand-in agent: proposes a fixed answer, and scores candidates by a simple
|
|
24
|
+
# rubric (here: answers mentioning "trade-offs" are judged higher). A real
|
|
25
|
+
# Agent proposes via execute_task and scores via its llm_client.
|
|
26
|
+
class PanelAgent
|
|
27
|
+
attr_reader :name
|
|
28
|
+
|
|
29
|
+
def initialize(name, answer)
|
|
30
|
+
@name = name
|
|
31
|
+
@answer = answer
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def execute_task(_task)
|
|
35
|
+
{ content: @answer }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def llm_client
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def chat(messages:, **)
|
|
43
|
+
candidate = messages.first[:content]
|
|
44
|
+
score = candidate.include?('trade-offs') ? 9 : 5
|
|
45
|
+
{ content: score.to_s }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
junior = PanelAgent.new('junior', 'Use PostgreSQL.')
|
|
50
|
+
senior = PanelAgent.new('senior', 'Use PostgreSQL, but weigh the trade-offs vs. DynamoDB for scale.')
|
|
51
|
+
|
|
52
|
+
crew = RCrewAI::Crew.new('architecture_panel', process: :consensual, consensus_agents: 3)
|
|
53
|
+
crew.add_agent(junior)
|
|
54
|
+
crew.add_agent(senior)
|
|
55
|
+
|
|
56
|
+
task = RCrewAI::Task.new(name: 'db_choice', description: 'Recommend a database', agent: junior)
|
|
57
|
+
crew.add_task(task)
|
|
58
|
+
|
|
59
|
+
result = crew.execute
|
|
60
|
+
|
|
61
|
+
puts "process: #{result[:process]}"
|
|
62
|
+
puts "consensus winner: #{result[:results].first[:result].inspect}"
|
|
63
|
+
puts '(the answer weighing trade-offs scored higher and won)'
|
data/lib/rcrewai/crew.rb
CHANGED
|
@@ -22,6 +22,7 @@ module RCrewAI
|
|
|
22
22
|
@planning = options.fetch(:planning, false)
|
|
23
23
|
@planning_llm = options[:planning_llm]
|
|
24
24
|
@planned = false
|
|
25
|
+
@consensus_agents = options.fetch(:consensus_agents, 3)
|
|
25
26
|
@knowledge = build_knowledge(options[:knowledge], options[:knowledge_sources])
|
|
26
27
|
@before_kickoff_hooks = []
|
|
27
28
|
@after_kickoff_hooks = []
|
|
@@ -30,7 +31,7 @@ module RCrewAI
|
|
|
30
31
|
validate_process_type!
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
attr_reader :knowledge, :stream_sink, :last_inputs
|
|
34
|
+
attr_reader :knowledge, :stream_sink, :last_inputs, :consensus_agents
|
|
34
35
|
|
|
35
36
|
def planning?
|
|
36
37
|
@planning
|
|
@@ -63,6 +64,7 @@ module RCrewAI
|
|
|
63
64
|
sinks << block if block_given?
|
|
64
65
|
Array(stream).each { |s| sinks << s } if stream
|
|
65
66
|
@stream_sink = sinks.empty? ? nil : RCrewAI::Events.fan_out(sinks)
|
|
67
|
+
@tasks.each { |t| t.stream_sink = @stream_sink }
|
|
66
68
|
|
|
67
69
|
run_before_hooks(inputs)
|
|
68
70
|
|
|
@@ -71,6 +73,11 @@ module RCrewAI
|
|
|
71
73
|
|
|
72
74
|
result = async ? execute_async(**async_options) : execute_sync
|
|
73
75
|
run_after_hooks(result)
|
|
76
|
+
ensure
|
|
77
|
+
# Drop per-task references to the caller's sink so request-scoped
|
|
78
|
+
# collectors do not stay reachable after the run. The crew's own
|
|
79
|
+
# +stream_sink+ reader is left intact: Process reads it.
|
|
80
|
+
@tasks.each { |t| t.stream_sink = nil }
|
|
74
81
|
end
|
|
75
82
|
|
|
76
83
|
# Runs the crew once per input set, returning one result per input in order.
|
data/lib/rcrewai/process.rb
CHANGED
|
@@ -351,7 +351,7 @@ module RCrewAI
|
|
|
351
351
|
end
|
|
352
352
|
|
|
353
353
|
# Execute the task
|
|
354
|
-
agent.execute_task(enhanced_task)
|
|
354
|
+
agent.execute_task(enhanced_task, stream: crew.stream_sink)
|
|
355
355
|
end
|
|
356
356
|
|
|
357
357
|
def should_abort_execution?(failed_tasks, phase_number, _plan)
|
|
@@ -373,23 +373,16 @@ module RCrewAI
|
|
|
373
373
|
end
|
|
374
374
|
end
|
|
375
375
|
|
|
376
|
+
# Multi-agent consensus: for each task, several agents propose candidate
|
|
377
|
+
# answers, all participants score each candidate, and the highest-scored
|
|
378
|
+
# candidate wins (ties break toward the task's assigned agent).
|
|
376
379
|
class Consensual < Base
|
|
380
|
+
DEFAULT_CONSENSUS_AGENTS = 3
|
|
381
|
+
|
|
377
382
|
def execute
|
|
378
383
|
log_execution_start
|
|
379
|
-
@logger.info 'Consensual execution - agents
|
|
380
|
-
|
|
381
|
-
# For now, implement as enhanced sequential with collaboration
|
|
382
|
-
# Full consensual process would involve agent voting/discussion
|
|
383
|
-
results = []
|
|
384
|
-
|
|
385
|
-
crew.tasks.each do |task|
|
|
386
|
-
@logger.info "Collaborative execution of task: #{task.name}"
|
|
387
|
-
|
|
388
|
-
# Simple consensus: let multiple agents provide input
|
|
389
|
-
consensus_result = execute_with_consensus(task)
|
|
390
|
-
results << consensus_result
|
|
391
|
-
end
|
|
392
|
-
|
|
384
|
+
@logger.info 'Consensual execution - agents propose, vote, and pick'
|
|
385
|
+
results = crew.tasks.map { |task| execute_with_consensus(task) }
|
|
393
386
|
log_execution_end(results)
|
|
394
387
|
results
|
|
395
388
|
end
|
|
@@ -397,14 +390,89 @@ module RCrewAI
|
|
|
397
390
|
private
|
|
398
391
|
|
|
399
392
|
def execute_with_consensus(task)
|
|
400
|
-
|
|
401
|
-
|
|
393
|
+
@logger.info "Consensus for task: #{task.name}"
|
|
394
|
+
participants = select_participants(task)
|
|
395
|
+
candidates = gather_proposals(task, participants)
|
|
402
396
|
|
|
403
|
-
|
|
404
|
-
|
|
397
|
+
if candidates.empty?
|
|
398
|
+
return { task: task, result: 'All agents failed to produce a proposal', status: :failed }
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
scored = score_candidates(task, candidates, participants)
|
|
402
|
+
winner = pick_winner(task, scored)
|
|
403
|
+
task.result = winner[:content] if task.respond_to?(:result=)
|
|
404
|
+
|
|
405
|
+
{ task: task, result: winner[:content], status: :completed }
|
|
405
406
|
rescue StandardError => e
|
|
407
|
+
@logger.error "Consensus failed for #{task.name}: #{e.message}"
|
|
406
408
|
{ task: task, result: e.message, status: :failed }
|
|
407
409
|
end
|
|
410
|
+
|
|
411
|
+
# First N agents, always including the task's assigned agent if present.
|
|
412
|
+
def select_participants(task)
|
|
413
|
+
cap = consensus_agent_cap
|
|
414
|
+
chosen = crew.agents.first(cap)
|
|
415
|
+
assigned = task.respond_to?(:agent) ? task.agent : nil
|
|
416
|
+
if assigned && crew.agents.include?(assigned) && !chosen.include?(assigned)
|
|
417
|
+
chosen = ([assigned] + chosen).first(cap)
|
|
418
|
+
end
|
|
419
|
+
chosen
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def gather_proposals(task, participants)
|
|
423
|
+
participants.filter_map do |agent|
|
|
424
|
+
content = extract_content(agent.execute_task(task, stream: crew.stream_sink))
|
|
425
|
+
{ agent: agent, content: content }
|
|
426
|
+
rescue StandardError => e
|
|
427
|
+
@logger.warn "Agent #{agent.name} failed to propose: #{e.message}"
|
|
428
|
+
nil
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def score_candidates(task, candidates, participants)
|
|
433
|
+
candidates.map do |candidate|
|
|
434
|
+
total = participants.sum { |voter| score(voter, task, candidate[:content]) }
|
|
435
|
+
candidate.merge(score: total)
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def score(voter, task, candidate_content)
|
|
440
|
+
prompt = <<~PROMPT
|
|
441
|
+
Score how well the following answer satisfies the task, from 0 to 10.
|
|
442
|
+
Reply with just the integer.
|
|
443
|
+
|
|
444
|
+
Task: #{task.description}
|
|
445
|
+
Expected output: #{task.respond_to?(:expected_output) ? task.expected_output : 'n/a'}
|
|
446
|
+
|
|
447
|
+
Answer:
|
|
448
|
+
#{candidate_content}
|
|
449
|
+
PROMPT
|
|
450
|
+
response = voter.llm_client.chat(messages: [{ role: 'user', content: prompt }])
|
|
451
|
+
parse_score(response.is_a?(Hash) ? response[:content] : response)
|
|
452
|
+
rescue StandardError
|
|
453
|
+
0
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def pick_winner(task, scored)
|
|
457
|
+
assigned = task.respond_to?(:agent) ? task.agent : nil
|
|
458
|
+
scored.max_by { |c| [c[:score], c[:agent] == assigned ? 1 : 0] }
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def parse_score(text)
|
|
462
|
+
match = text.to_s[/-?\d+/]
|
|
463
|
+
return 0 unless match
|
|
464
|
+
|
|
465
|
+
match.to_i.clamp(0, 10)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def extract_content(agent_result)
|
|
469
|
+
agent_result.is_a?(Hash) ? agent_result[:content].to_s : agent_result.to_s
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def consensus_agent_cap
|
|
473
|
+
cap = crew.respond_to?(:consensus_agents) ? crew.consensus_agents : nil
|
|
474
|
+
[(cap || DEFAULT_CONSENSUS_AGENTS).to_i, 1].max
|
|
475
|
+
end
|
|
408
476
|
end
|
|
409
477
|
|
|
410
478
|
class ProcessError < RCrewAI::Error; end
|
data/lib/rcrewai/task.rb
CHANGED
|
@@ -10,7 +10,7 @@ module RCrewAI
|
|
|
10
10
|
include HumanInteractionExtensions
|
|
11
11
|
attr_reader :name, :description, :agent, :context, :expected_output, :tools, :async,
|
|
12
12
|
:raw_result, :structured_output, :attachments
|
|
13
|
-
attr_accessor :result, :status, :start_time, :end_time, :execution_time
|
|
13
|
+
attr_accessor :result, :status, :start_time, :end_time, :execution_time, :stream_sink
|
|
14
14
|
|
|
15
15
|
def initialize(name:, description:, agent: nil, **options)
|
|
16
16
|
@name = name
|
|
@@ -44,6 +44,7 @@ module RCrewAI
|
|
|
44
44
|
@start_time = nil
|
|
45
45
|
@end_time = nil
|
|
46
46
|
@execution_time = nil
|
|
47
|
+
@stream_sink = nil
|
|
47
48
|
@retry_count = 0
|
|
48
49
|
@max_retries = options.fetch(:max_retries, 2)
|
|
49
50
|
end
|
|
@@ -218,7 +219,7 @@ module RCrewAI
|
|
|
218
219
|
|
|
219
220
|
loop do
|
|
220
221
|
attempts += 1
|
|
221
|
-
raw = extract_content(agent.execute_task(self))
|
|
222
|
+
raw = extract_content(agent.execute_task(self, stream: @stream_sink))
|
|
222
223
|
@raw_result = raw
|
|
223
224
|
|
|
224
225
|
begin
|
data/lib/rcrewai/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rcrewai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- gkosmo
|
|
@@ -357,17 +357,25 @@ files:
|
|
|
357
357
|
- docs/superpowers/plans/2026-05-11-llm-modernization.md
|
|
358
358
|
- docs/superpowers/specs/2026-05-11-llm-modernization-design.md
|
|
359
359
|
- docs/superpowers/specs/2026-07-06-cognitive-memory-design.md
|
|
360
|
+
- docs/superpowers/specs/2026-07-07-consensual-process-design.md
|
|
360
361
|
- docs/tutorials/advanced-agents.md
|
|
362
|
+
- docs/tutorials/agent-options.md
|
|
363
|
+
- docs/tutorials/consensual-process.md
|
|
361
364
|
- docs/tutorials/custom-tools.md
|
|
362
365
|
- docs/tutorials/deployment.md
|
|
366
|
+
- docs/tutorials/flows.md
|
|
363
367
|
- docs/tutorials/getting-started.md
|
|
364
368
|
- docs/tutorials/index.md
|
|
369
|
+
- docs/tutorials/knowledge.md
|
|
370
|
+
- docs/tutorials/memory.md
|
|
365
371
|
- docs/tutorials/multiple-crews.md
|
|
366
372
|
- docs/upgrading-to-0.3.md
|
|
367
373
|
- docs/upgrading-to-0.4.md
|
|
368
374
|
- docs/upgrading-to-0.6.md
|
|
375
|
+
- docs/upgrading-to-0.7.md
|
|
369
376
|
- examples/async_execution_example.rb
|
|
370
377
|
- examples/cognitive_memory_example.rb
|
|
378
|
+
- examples/consensual_process_example.rb
|
|
371
379
|
- examples/flow_example.rb
|
|
372
380
|
- examples/hierarchical_crew_example.rb
|
|
373
381
|
- examples/human_in_the_loop_example.rb
|