rcrewai 0.2.1 → 0.3.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +99 -0
  4. data/CHANGELOG.md +24 -0
  5. data/README.md +2 -2
  6. data/Rakefile +53 -53
  7. data/bin/rcrewai +3 -3
  8. data/docs/mcp.md +109 -0
  9. data/docs/superpowers/plans/2026-05-11-llm-modernization.md +2753 -0
  10. data/docs/superpowers/specs/2026-05-11-llm-modernization-design.md +479 -0
  11. data/docs/upgrading-to-0.3.md +163 -0
  12. data/examples/async_execution_example.rb +82 -81
  13. data/examples/hierarchical_crew_example.rb +68 -72
  14. data/examples/human_in_the_loop_example.rb +73 -74
  15. data/examples/mcp_example.rb +48 -0
  16. data/examples/native_tools_example.rb +64 -0
  17. data/examples/streaming_example.rb +56 -0
  18. data/lib/rcrewai/agent.rb +148 -287
  19. data/lib/rcrewai/async_executor.rb +43 -43
  20. data/lib/rcrewai/cli.rb +11 -11
  21. data/lib/rcrewai/configuration.rb +14 -9
  22. data/lib/rcrewai/crew.rb +56 -39
  23. data/lib/rcrewai/events.rb +30 -0
  24. data/lib/rcrewai/human_input.rb +104 -114
  25. data/lib/rcrewai/legacy_react_runner.rb +172 -0
  26. data/lib/rcrewai/llm_client.rb +1 -1
  27. data/lib/rcrewai/llm_clients/anthropic.rb +174 -54
  28. data/lib/rcrewai/llm_clients/azure.rb +23 -128
  29. data/lib/rcrewai/llm_clients/base.rb +11 -7
  30. data/lib/rcrewai/llm_clients/google.rb +159 -95
  31. data/lib/rcrewai/llm_clients/ollama.rb +150 -106
  32. data/lib/rcrewai/llm_clients/openai.rb +140 -63
  33. data/lib/rcrewai/mcp/client.rb +101 -0
  34. data/lib/rcrewai/mcp/tool_adapter.rb +59 -0
  35. data/lib/rcrewai/mcp/transport/http.rb +53 -0
  36. data/lib/rcrewai/mcp/transport/stdio.rb +55 -0
  37. data/lib/rcrewai/mcp.rb +8 -0
  38. data/lib/rcrewai/memory.rb +45 -37
  39. data/lib/rcrewai/pricing.rb +34 -0
  40. data/lib/rcrewai/process.rb +86 -95
  41. data/lib/rcrewai/provider_schema.rb +38 -0
  42. data/lib/rcrewai/sse_parser.rb +55 -0
  43. data/lib/rcrewai/task.rb +56 -64
  44. data/lib/rcrewai/tool_runner.rb +132 -0
  45. data/lib/rcrewai/tool_schema.rb +97 -0
  46. data/lib/rcrewai/tools/base.rb +98 -37
  47. data/lib/rcrewai/tools/code_executor.rb +71 -74
  48. data/lib/rcrewai/tools/email_sender.rb +70 -78
  49. data/lib/rcrewai/tools/file_reader.rb +38 -30
  50. data/lib/rcrewai/tools/file_writer.rb +40 -38
  51. data/lib/rcrewai/tools/pdf_processor.rb +115 -130
  52. data/lib/rcrewai/tools/sql_database.rb +58 -55
  53. data/lib/rcrewai/tools/web_search.rb +26 -25
  54. data/lib/rcrewai/version.rb +2 -2
  55. data/lib/rcrewai.rb +18 -10
  56. data/rcrewai.gemspec +39 -39
  57. metadata +65 -47
data/lib/rcrewai/agent.rb CHANGED
@@ -4,6 +4,8 @@ require 'logger'
4
4
  require_relative 'llm_client'
5
5
  require_relative 'memory'
6
6
  require_relative 'tools/base'
7
+ require_relative 'tool_runner'
8
+ require_relative 'legacy_react_runner'
7
9
  require_relative 'human_input'
8
10
 
9
11
  module RCrewAI
@@ -20,7 +22,7 @@ module RCrewAI
20
22
  @tools = tools
21
23
  @verbose = options.fetch(:verbose, false)
22
24
  @allow_delegation = options.fetch(:allow_delegation, false)
23
- @manager = options.fetch(:manager, false) # New manager flag
25
+ @manager = options.fetch(:manager, false) # New manager flag
24
26
  @max_iterations = options.fetch(:max_iterations, 10)
25
27
  @max_execution_time = options.fetch(:max_execution_time, 300) # 5 minutes
26
28
  @human_input_enabled = options.fetch(:human_input, false)
@@ -30,38 +32,48 @@ module RCrewAI
30
32
  @logger.level = verbose ? Logger::DEBUG : Logger::INFO
31
33
  @memory = Memory.new
32
34
  @llm_client = LLMClient.for_provider
33
- @subordinates = [] # For manager agents
35
+ @subordinates = [] # For manager agents
34
36
  end
35
37
 
36
- def execute_task(task)
38
+ def execute_task(task, stream: nil, **opts)
37
39
  @logger.info "Agent #{name} starting task: #{task.name}"
38
40
  start_time = Time.now
39
41
 
40
42
  begin
41
- # Build context for the agent
42
- context = build_context(task)
43
-
44
- # Execute task with reasoning loop
45
- result = reasoning_loop(task, context)
46
-
43
+ initial_messages = build_initial_messages(task)
44
+ sink = stream || ->(_) {}
45
+
46
+ runner_class = pick_runner_class
47
+ @logger.info "[rcrewai] agent=#{name} runner=#{runner_class.name.split('::').last}"
48
+
49
+ runner = runner_class.new(
50
+ agent: self, llm: @llm_client, tools: @tools,
51
+ max_iterations: opts.fetch(:max_iterations, max_iterations),
52
+ event_sink: sink
53
+ )
54
+
55
+ runner_result = runner.run(messages: initial_messages)
47
56
  execution_time = Time.now - start_time
48
57
  @logger.info "Task completed in #{execution_time.round(2)}s"
49
-
50
- # Store in memory
51
- memory.add_execution(task, result, execution_time)
52
-
53
- task.result = result
54
- result
55
58
 
56
- rescue => e
59
+ result_string = runner_result[:content].to_s
60
+ memory.add_execution(task, result_string, execution_time)
61
+ task.result = result_string
62
+
63
+ build_task_result(task, runner_result)
64
+ rescue StandardError => e
57
65
  @logger.error "Task execution failed: #{e.message}"
58
66
  task.result = "Task failed: #{e.message}"
59
67
  raise AgentError, "Agent #{name} failed to execute task: #{e.message}"
60
68
  end
61
69
  end
62
70
 
71
+ def require_approval_for_tools?
72
+ @require_approval_for_tools && @human_input_enabled
73
+ end
74
+
63
75
  def available_tools_description
64
- return "No tools available." if tools.empty?
76
+ return 'No tools available.' if tools.empty?
65
77
 
66
78
  tools.map do |tool|
67
79
  "- #{tool.name}: #{tool.description}"
@@ -82,23 +94,21 @@ module RCrewAI
82
94
  end
83
95
 
84
96
  @logger.debug "Using tool: #{tool_name} with params: #{params}"
85
-
97
+
86
98
  begin
87
99
  result = tool.execute(**params)
88
-
100
+
89
101
  # Store tool usage in memory
90
102
  memory.add_tool_usage(tool_name, params, result)
91
-
103
+
92
104
  result
93
- rescue => e
105
+ rescue StandardError => e
94
106
  @logger.error "Tool execution failed: #{e.message}"
95
-
107
+
96
108
  # Offer human intervention if tool fails and human input is enabled
97
- if @human_input_enabled
98
- handle_tool_failure(tool_name, params, e)
99
- else
100
- raise e
101
- end
109
+ raise e unless @human_input_enabled
110
+
111
+ handle_tool_failure(tool_name, params, e)
102
112
  end
103
113
  end
104
114
 
@@ -109,32 +119,31 @@ module RCrewAI
109
119
 
110
120
  def add_subordinate(agent)
111
121
  return unless is_manager?
122
+
112
123
  @subordinates << agent unless @subordinates.include?(agent)
113
124
  end
114
125
 
115
- def subordinates
116
- @subordinates
117
- end
126
+ attr_reader :subordinates
118
127
 
119
128
  def delegate_task(task, target_agent)
120
129
  return unless is_manager?
121
130
  return unless @subordinates.include?(target_agent) || allow_delegation
122
131
 
123
132
  @logger.info "Manager #{name} delegating task '#{task.name}' to #{target_agent.name}"
124
-
133
+
125
134
  # Create delegation context
126
135
  delegation_prompt = build_delegation_prompt(task, target_agent)
127
-
136
+
128
137
  # Use LLM to create proper delegation
129
138
  response = llm_client.chat(
130
139
  messages: [{ role: 'user', content: delegation_prompt }],
131
140
  temperature: 0.2,
132
141
  max_tokens: 1000
133
142
  )
134
-
143
+
135
144
  delegation_instructions = response[:content]
136
145
  @logger.debug "Delegation instructions: #{delegation_instructions}"
137
-
146
+
138
147
  # Execute delegated task
139
148
  target_agent.execute_delegated_task(task, delegation_instructions, self)
140
149
  end
@@ -142,21 +151,21 @@ module RCrewAI
142
151
  def execute_delegated_task(task, delegation_instructions, manager_agent)
143
152
  @logger.info "Receiving delegation from manager #{manager_agent.name}"
144
153
  @logger.debug "Delegation instructions: #{delegation_instructions}"
145
-
154
+
146
155
  # Store delegation context in task
147
156
  original_description = task.description
148
157
  enhanced_description = "#{original_description}\n\nDelegation Instructions from #{manager_agent.name}:\n#{delegation_instructions}"
149
-
158
+
150
159
  # Temporarily modify task
151
160
  task.instance_variable_set(:@description, enhanced_description)
152
161
  task.instance_variable_set(:@manager, manager_agent)
153
-
162
+
154
163
  begin
155
164
  result = execute_task(task)
156
-
165
+
157
166
  # Report back to manager
158
167
  report_to_manager(task, result, manager_agent)
159
-
168
+
160
169
  result
161
170
  ensure
162
171
  # Restore original task description
@@ -198,240 +207,93 @@ module RCrewAI
198
207
  }
199
208
 
200
209
  # Add delegation capabilities if allowed
201
- if allow_delegation
202
- context[:delegation_note] = "You can delegate subtasks to other agents if needed."
203
- end
210
+ context[:delegation_note] = 'You can delegate subtasks to other agents if needed.' if allow_delegation
204
211
 
205
212
  context
206
213
  end
207
214
 
208
- def reasoning_loop(task, context)
209
- iteration = 0
210
- start_time = Time.now
211
-
212
- loop do
213
- iteration += 1
214
- current_time = Time.now
215
-
216
- # Check limits
217
- if iteration > max_iterations
218
- @logger.warn "Max iterations (#{max_iterations}) reached"
219
- break
220
- end
221
-
222
- if current_time - start_time > max_execution_time
223
- @logger.warn "Max execution time (#{max_execution_time}s) reached"
224
- break
225
- end
226
-
227
- # Human review of reasoning at key points
228
- if @human_input_enabled && (iteration == 1 || iteration % 3 == 0)
229
- review_result = request_reasoning_review(task, context, iteration)
230
- if review_result && review_result[:feedback]
231
- context[:human_guidance] = review_result[:feedback]
232
- @logger.info "Incorporating human guidance into reasoning"
233
- end
234
- end
235
-
236
- # Generate reasoning prompt
237
- prompt = build_reasoning_prompt(context, iteration)
238
-
239
- # Get LLM response
240
- @logger.debug "Iteration #{iteration}: Sending prompt to LLM"
241
- response = llm_client.chat(
242
- messages: [{ role: 'user', content: prompt }],
243
- temperature: 0.1,
244
- max_tokens: 2000
245
- )
246
-
247
- reasoning = response[:content]
248
- @logger.debug "LLM Response: #{reasoning[0..200]}..." if verbose
249
-
250
- # Parse and execute actions
251
- action_result = parse_and_execute_actions(reasoning, task)
252
-
253
- # Check if task is complete
254
- if task_complete?(reasoning, action_result)
255
- final_result = extract_final_result(reasoning, action_result)
256
-
257
- # Human approval of final result if required
258
- if @require_approval_for_final_answer && @human_input_enabled
259
- final_result = request_final_answer_approval(final_result)
260
- end
261
-
262
- @logger.info "Task completed successfully in #{iteration} iterations"
263
- return final_result
264
- end
265
-
266
- # Update context with new information
267
- context[:previous_reasoning] = reasoning
268
- context[:previous_result] = action_result
269
- context[:iteration] = iteration
270
- end
271
-
272
- # If we exit the loop without completion, return best effort result
273
- final_result = extract_final_result(context[:previous_reasoning], context[:previous_result]) ||
274
- "Task execution reached limits without clear completion"
275
-
276
- # Human approval even for incomplete results if required
277
- if @require_approval_for_final_answer && @human_input_enabled
278
- final_result = request_final_answer_approval(final_result)
279
- end
280
-
281
- final_result
282
- end
283
-
284
- def build_reasoning_prompt(context, iteration)
285
- prompt = <<~PROMPT
286
- You are #{context[:agent_role]}.
287
-
288
- Your goal: #{context[:agent_goal]}
289
-
290
- Background: #{context[:agent_backstory]}
291
-
292
- Current Task: #{context[:task_description]}
293
- Expected Output: #{context[:task_expected_output]}
294
-
295
- Available Tools:
296
- #{context[:available_tools]}
297
-
298
- #{context[:delegation_note] if context[:delegation_note]}
299
-
300
- #{build_context_section(context)}
301
-
302
- This is iteration #{iteration}. Think step by step about how to approach this task.
303
-
304
- You can:
305
- 1. Use tools by writing: USE_TOOL[tool_name](param1=value1, param2=value2)
306
- 2. Provide your final answer when ready: FINAL_ANSWER[your complete response]
307
- 3. Continue reasoning if you need more information
308
-
309
- What is your next step?
310
- PROMPT
311
-
312
- prompt
313
- end
314
-
315
- def build_context_section(context)
316
- sections = []
317
-
318
- if context[:context_data] && !context[:context_data].empty?
319
- sections << "Additional Context:\n#{context[:context_data]}"
320
- end
321
-
322
- if context[:previous_executions] && !context[:previous_executions].empty?
323
- sections << "Previous Similar Tasks:\n#{context[:previous_executions]}"
324
- end
325
-
326
- if context[:human_guidance]
327
- sections << "Human Guidance:\n#{context[:human_guidance]}"
215
+ def build_initial_messages(task)
216
+ ctx = build_context(task)
217
+ system = +"You are #{ctx[:agent_role]}. Goal: #{ctx[:agent_goal]}."
218
+ system << " #{ctx[:agent_backstory]}" if ctx[:agent_backstory]
219
+ if @tools.any?
220
+ system << "\nAvailable Tools:\n#{ctx[:available_tools]}"
221
+ system << "\nYou may call tools by name when needed."
328
222
  end
329
-
330
- if context[:previous_reasoning]
331
- sections << "Previous Reasoning:\n#{context[:previous_reasoning]}"
332
- end
333
-
334
- if context[:previous_result]
335
- sections << "Previous Action Result:\n#{context[:previous_result]}"
336
- end
337
-
338
- sections.join("\n\n")
339
- end
223
+ system << "\n#{ctx[:delegation_note]}" if ctx[:delegation_note]
340
224
 
341
- def parse_and_execute_actions(reasoning, task)
342
- results = []
343
-
344
- # Look for tool usage patterns
345
- tool_matches = reasoning.scan(/USE_TOOL\[(\w+)\]\(([^)]*)\)/)
346
- tool_matches.each do |tool_name, params_str|
347
- begin
348
- params = parse_tool_params(params_str)
349
- result = use_tool(tool_name, **params)
350
- results << "Tool #{tool_name} result: #{result}"
351
- rescue => e
352
- results << "Tool #{tool_name} failed: #{e.message}"
353
- @logger.error "Tool execution failed: #{e.message}"
354
- end
355
- end
225
+ user = +"Task: #{task.description}"
226
+ user << "\nExpected Output: #{task.expected_output}" if task.expected_output
227
+ user << "\nAdditional Context:\n#{ctx[:context_data]}" if ctx[:context_data] && !ctx[:context_data].to_s.empty?
356
228
 
357
- results.join("\n")
229
+ [
230
+ { role: 'system', content: system },
231
+ { role: 'user', content: user }
232
+ ]
358
233
  end
359
234
 
360
- def parse_tool_params(params_str)
361
- params = {}
362
- return params if params_str.strip.empty?
363
-
364
- param_pairs = params_str.split(',').map(&:strip)
365
- param_pairs.each do |pair|
366
- key, value = pair.split('=', 2).map(&:strip)
367
- if key && value
368
- # Remove quotes if present
369
- value = value.gsub(/^["']|["']$/, '')
370
- params[key.to_sym] = value
371
- end
372
- end
373
-
374
- params
235
+ def build_task_result(task, runner_result)
236
+ {
237
+ task: task.name,
238
+ agent: name,
239
+ content: runner_result[:content],
240
+ tool_calls_history: runner_result[:tool_calls_history] || [],
241
+ usage: runner_result[:usage] || {},
242
+ iterations: runner_result[:iterations],
243
+ finish_reason: runner_result[:finish_reason]
244
+ }
375
245
  end
376
246
 
377
- def task_complete?(reasoning, action_result)
378
- reasoning.include?('FINAL_ANSWER[') ||
379
- reasoning.downcase.include?('task complete') ||
380
- reasoning.downcase.include?('finished')
247
+ def pick_runner_class
248
+ schemas_ok = @tools.empty? || @tools.all? { |t| t.respond_to?(:json_schema) && t.json_schema }
249
+ native = @llm_client.respond_to?(:supports_native_tools?) &&
250
+ safe_supports_native?(@llm_client)
251
+ schemas_ok && native ? ToolRunner : LegacyReactRunner
381
252
  end
382
253
 
383
- def extract_final_result(reasoning, action_result)
384
- # Look for FINAL_ANSWER pattern
385
- if match = reasoning.match(/FINAL_ANSWER\[(.*?)\]$/m)
386
- return match[1].strip
387
- end
388
-
389
- # Otherwise try to extract meaningful result from reasoning
390
- lines = reasoning.split("\n").map(&:strip).reject(&:empty?)
391
- final_lines = lines.last(3).join(" ")
392
-
393
- return final_lines if final_lines.length > 20
394
-
395
- # Fallback to action result
396
- action_result
254
+ def safe_supports_native?(llm)
255
+ model = llm.respond_to?(:config) && llm.config.respond_to?(:model) ? llm.config.model : nil
256
+ llm.supports_native_tools?(model: model)
257
+ rescue StandardError
258
+ false
397
259
  end
398
260
 
399
261
  # Manager-specific private methods
400
262
  def build_delegation_prompt(task, target_agent)
401
263
  <<~PROMPT
402
264
  You are #{name}, a #{role}.
403
-
265
+
404
266
  You need to delegate the following task to #{target_agent.name} (#{target_agent.role}):
405
-
267
+
406
268
  Task: #{task.name}
407
269
  Description: #{task.description}
408
270
  Expected Output: #{task.expected_output || 'Not specified'}
409
-
271
+
410
272
  Target Agent Profile:
411
273
  - Role: #{target_agent.role}
412
274
  - Goal: #{target_agent.goal}
413
275
  - Available Tools: #{target_agent.available_tools_description}
414
-
276
+
415
277
  Create clear, specific delegation instructions that:
416
278
  1. Explain why this agent is the right choice for this task
417
279
  2. Provide any additional context or requirements
418
280
  3. Set clear expectations for deliverables
419
281
  4. Include any coordination notes with other team members
420
-
282
+
421
283
  Keep instructions concise but comprehensive.
422
284
  PROMPT
423
285
  end
424
286
 
425
287
  def report_to_manager(task, result, manager_agent)
426
288
  @logger.info "Reporting task completion to manager #{manager_agent.name}"
427
-
289
+
428
290
  # Store the delegation result in manager's memory
429
291
  manager_agent.memory.add_execution(
430
- task,
292
+ task,
431
293
  "Delegated to #{name}: #{result}",
432
294
  task.execution_time || 0
433
295
  )
434
-
296
+
435
297
  # Could enhance with formal reporting mechanism
436
298
  end
437
299
 
@@ -440,34 +302,33 @@ module RCrewAI
440
302
  message = "Agent #{name} wants to use tool '#{tool_name}'"
441
303
  context = "Parameters: #{params.inspect}"
442
304
  consequences = "This will execute the #{tool_name} tool with the specified parameters."
443
-
444
- request_human_approval(message,
445
- context: context,
446
- consequences: consequences,
447
- timeout: 60
448
- )
305
+
306
+ request_human_approval(message,
307
+ context: context,
308
+ consequences: consequences,
309
+ timeout: 60)
449
310
  end
450
311
 
451
312
  def handle_tool_failure(tool_name, params, error)
452
- @logger.warn "Requesting human intervention for tool failure"
453
-
313
+ @logger.warn 'Requesting human intervention for tool failure'
314
+
454
315
  choices = [
455
- "Retry with same parameters",
456
- "Retry with different parameters",
457
- "Skip this tool and continue",
458
- "Abort task execution"
316
+ 'Retry with same parameters',
317
+ 'Retry with different parameters',
318
+ 'Skip this tool and continue',
319
+ 'Abort task execution'
459
320
  ]
460
-
321
+
461
322
  choice_result = request_human_choice(
462
323
  "Tool '#{tool_name}' failed with error: #{error.message}. How should I proceed?",
463
324
  choices,
464
325
  timeout: 120
465
326
  )
466
-
327
+
467
328
  case choice_result[:choice_index]
468
329
  when 0
469
330
  # Retry with same parameters
470
- @logger.info "Human requested retry with same parameters"
331
+ @logger.info 'Human requested retry with same parameters'
471
332
  tool = tools.find { |t| t.name == tool_name }
472
333
  tool.execute(**params)
473
334
  when 1
@@ -475,11 +336,11 @@ module RCrewAI
475
336
  new_params_result = request_human_input(
476
337
  "Please provide new parameters for #{tool_name} (JSON format):",
477
338
  type: :json,
478
- help_text: "Enter parameters as JSON, e.g. {\"param1\": \"value1\"}"
339
+ help_text: 'Enter parameters as JSON, e.g. {"param1": "value1"}'
479
340
  )
480
-
341
+
481
342
  if new_params_result[:valid]
482
- @logger.info "Human provided new parameters, retrying tool"
343
+ @logger.info 'Human provided new parameters, retrying tool'
483
344
  tool = tools.find { |t| t.name == tool_name }
484
345
  tool.execute(**new_params_result[:processed_input])
485
346
  else
@@ -487,43 +348,43 @@ module RCrewAI
487
348
  end
488
349
  when 2
489
350
  # Skip tool
490
- @logger.info "Human requested to skip failed tool"
491
- "Tool execution skipped by human intervention"
351
+ @logger.info 'Human requested to skip failed tool'
352
+ 'Tool execution skipped by human intervention'
492
353
  else
493
354
  # Abort
494
- @logger.error "Human requested task abortion due to tool failure"
355
+ @logger.error 'Human requested task abortion due to tool failure'
495
356
  raise AgentError, "Task aborted by human due to tool failure: #{error.message}"
496
357
  end
497
358
  end
498
359
 
499
360
  def request_final_answer_approval(proposed_answer)
500
361
  return proposed_answer unless @require_approval_for_final_answer && @human_input_enabled
501
-
362
+
502
363
  review_result = request_human_review(
503
364
  proposed_answer,
504
- review_criteria: ["Accuracy", "Completeness", "Clarity", "Relevance"],
365
+ review_criteria: %w[Accuracy Completeness Clarity Relevance],
505
366
  timeout: 180
506
367
  )
507
-
368
+
508
369
  if review_result[:approved]
509
- @logger.info "Final answer approved by human"
370
+ @logger.info 'Final answer approved by human'
510
371
  proposed_answer
511
372
  else
512
- @logger.info "Human provided feedback on final answer"
513
-
373
+ @logger.info 'Human provided feedback on final answer'
374
+
514
375
  if review_result[:suggested_changes].any?
515
376
  @logger.info "Suggested changes: #{review_result[:suggested_changes].join('; ')}"
516
-
377
+
517
378
  # Ask human what to do with the feedback
518
379
  choice_result = request_human_choice(
519
- "How should I handle your feedback?",
380
+ 'How should I handle your feedback?',
520
381
  [
521
- "Revise the answer based on feedback",
522
- "Use the answer as-is despite feedback",
523
- "Let me provide a completely new answer"
382
+ 'Revise the answer based on feedback',
383
+ 'Use the answer as-is despite feedback',
384
+ 'Let me provide a completely new answer'
524
385
  ]
525
386
  )
526
-
387
+
527
388
  case choice_result[:choice_index]
528
389
  when 0
529
390
  # Revise based on feedback
@@ -535,8 +396,8 @@ module RCrewAI
535
396
  when 2
536
397
  # Get new answer from human
537
398
  new_answer_result = request_human_input(
538
- "Please provide the final answer:",
539
- help_text: "Provide the complete answer for this task"
399
+ 'Please provide the final answer:',
400
+ help_text: 'Provide the complete answer for this task'
540
401
  )
541
402
  new_answer_result[:input] || proposed_answer
542
403
  else
@@ -550,29 +411,29 @@ module RCrewAI
550
411
  end
551
412
 
552
413
  def revise_answer_with_feedback(feedback_context)
553
- @logger.info "Revising answer based on human feedback"
554
-
414
+ @logger.info 'Revising answer based on human feedback'
415
+
555
416
  revision_prompt = <<~PROMPT
556
417
  You are #{name}, a #{role}.
557
-
418
+
558
419
  You need to revise your previous answer based on human feedback.
559
-
420
+
560
421
  #{feedback_context}
561
-
422
+
562
423
  Please provide a revised answer that addresses the feedback while maintaining accuracy and completeness.
563
-
424
+
564
425
  Revised answer:
565
426
  PROMPT
566
-
427
+
567
428
  response = llm_client.chat(
568
429
  messages: [{ role: 'user', content: revision_prompt }],
569
430
  temperature: 0.2,
570
431
  max_tokens: 1000
571
432
  )
572
-
433
+
573
434
  revised_answer = response[:content]
574
435
  @logger.debug "Revised answer based on feedback: #{revised_answer[0..100]}..."
575
-
436
+
576
437
  revised_answer
577
438
  end
578
439
 
@@ -582,36 +443,36 @@ module RCrewAI
582
443
  review_content = <<~CONTENT
583
444
  Task: #{task.name}
584
445
  Description: #{task.description}
585
-
446
+
586
447
  Current Iteration: #{iteration}
587
-
448
+
588
449
  Agent Analysis:
589
450
  - Role: #{role}
590
451
  - Current Progress: #{context[:previous_result] || 'Starting task'}
591
452
  - Previous Reasoning: #{context[:previous_reasoning] || 'No previous reasoning'}
592
-
453
+
593
454
  The agent is about to continue reasoning for this task.
594
455
  CONTENT
595
456
 
596
457
  request_human_review(
597
458
  review_content,
598
- review_criteria: ["Task approach", "Progress assessment", "Strategic guidance"],
459
+ review_criteria: ['Task approach', 'Progress assessment', 'Strategic guidance'],
599
460
  timeout: 30,
600
461
  optional: true
601
462
  )
602
- rescue => e
463
+ rescue StandardError => e
603
464
  @logger.warn "Failed to get human reasoning review: #{e.message}"
604
465
  nil
605
466
  end
606
467
 
607
468
  class CLI < Thor
608
- desc "new NAME", "Create a new agent"
469
+ desc 'new NAME', 'Create a new agent'
609
470
  option :role, type: :string, required: true
610
471
  option :goal, type: :string, required: true
611
472
  option :backstory, type: :string
612
473
  option :verbose, type: :boolean, default: false
613
474
  def new(name)
614
- agent = Agent.new(
475
+ Agent.new(
615
476
  name: name,
616
477
  role: options[:role],
617
478
  goal: options[:goal],
@@ -621,16 +482,16 @@ module RCrewAI
621
482
  puts "Agent '#{name}' created with role: #{options[:role]}"
622
483
  end
623
484
 
624
- desc "list", "List all agents"
485
+ desc 'list', 'List all agents'
625
486
  def list
626
- puts "Available agents:"
627
- puts " - researcher (Role: Research Specialist)"
628
- puts " - writer (Role: Content Writer)"
629
- puts " - analyst (Role: Data Analyst)"
487
+ puts 'Available agents:'
488
+ puts ' - researcher (Role: Research Specialist)'
489
+ puts ' - writer (Role: Content Writer)'
490
+ puts ' - analyst (Role: Data Analyst)'
630
491
  end
631
492
  end
632
493
  end
633
494
 
634
495
  class AgentError < Error; end
635
496
  class ToolNotFoundError < AgentError; end
636
- end
497
+ end