rcrewai 0.6.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32edcb190b0d85db67d404b260fbb86d9b2821d6a89adf350d792b60a0e21a06
4
- data.tar.gz: 46d061ecebca5a67714a6a8139d41bf9d20ce60d4eb5f0a1505d965defffaeb8
3
+ metadata.gz: '0024080cd05b151b653bcedb4b29afea563e149f9d68dce1662d636a919020a9'
4
+ data.tar.gz: 53bb6b1376f88d1379cb0f877eeaa35aed0822f08df5f76d482e28d55101efe6
5
5
  SHA512:
6
- metadata.gz: a77a41b7fa6dad5de896c7b635777ee5f2429eb826e65516b2d3a5b2d9e9446ca8de0bf6fd0fd94f9efd4159c5080a94d534aa6c65447abd4d781b8d05a63ff4
7
- data.tar.gz: a65c248d53e9f346cd88c4f9f8e04280e89d67d5bf9695ff252ca14ee270864f23ee1f64395831ce7f2be95d682fc54ecb655d64a8c2b09b99800dcd561d3524
6
+ metadata.gz: b7923adbf161caa5361568268306513c92e5ffbddc3736cd91cd0fbccedd8cdd4230854f8310cf358964c7ccea09b1e43040e944f4c5472b0f5e58983bf37aaf
7
+ data.tar.gz: e4c9426c4e948fca50c3708a8a0e57588b4b18c51a5ac8df0bd2dd902aa2440a20d8d1f6a464333bb9c3244d5ff6901070645bb932fd5fb6e77f3d3f82ff9f83
data/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.0] - 2026-07-07
11
+
12
+ Turns the `:consensual` crew process from a stub into a real multi-agent
13
+ consensus (propose → vote → pick). This is a behavior change to an existing
14
+ process type — see `docs/upgrading-to-0.7.md`.
15
+
16
+ ### Changed
17
+ - 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`.
18
+
10
19
  ## [0.6.1] - 2026-07-06
11
20
 
12
21
  Follow-ups to the 0.6.0 Cognitive Memory release, deepening the areas that
@@ -208,7 +217,8 @@ output, guardrails, planning, and training/testing. See `ROADMAP.md`.
208
217
  - CLI usage documentation
209
218
  - Real-world use cases and examples
210
219
 
211
- [Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.6.1...HEAD
220
+ [Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.7.0...HEAD
221
+ [0.7.0]: https://github.com/gkosmo/rcrewAI/compare/v0.6.1...v0.7.0
212
222
  [0.6.1]: https://github.com/gkosmo/rcrewAI/compare/v0.6.0...v0.6.1
213
223
  [0.6.0]: https://github.com/gkosmo/rcrewAI/compare/v0.5.0...v0.6.0
214
224
  [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
@@ -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,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
@@ -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 collaborate on decisions'
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
- # For now, just execute normally
401
- # Future: implement actual consensus mechanisms
393
+ @logger.info "Consensus for task: #{task.name}"
394
+ participants = select_participants(task)
395
+ candidates = gather_proposals(task, participants)
402
396
 
403
- result = task.execute
404
- { task: task, result: result, status: :completed }
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))
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RCrewAI
4
- VERSION = '0.6.1'
4
+ VERSION = '0.7.0'
5
5
  end
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.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gkosmo
@@ -357,6 +357,7 @@ 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
361
362
  - docs/tutorials/custom-tools.md
362
363
  - docs/tutorials/deployment.md
@@ -366,8 +367,10 @@ files:
366
367
  - docs/upgrading-to-0.3.md
367
368
  - docs/upgrading-to-0.4.md
368
369
  - docs/upgrading-to-0.6.md
370
+ - docs/upgrading-to-0.7.md
369
371
  - examples/async_execution_example.rb
370
372
  - examples/cognitive_memory_example.rb
373
+ - examples/consensual_process_example.rb
371
374
  - examples/flow_example.rb
372
375
  - examples/hierarchical_crew_example.rb
373
376
  - examples/human_in_the_loop_example.rb