soka 0.0.1.beta2
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +365 -0
- data/CHANGELOG.md +31 -0
- data/CLAUDE.md +213 -0
- data/LICENSE +21 -0
- data/README.md +650 -0
- data/Rakefile +10 -0
- data/examples/1_basic.rb +94 -0
- data/examples/2_event_handling.rb +120 -0
- data/examples/3_memory.rb +182 -0
- data/examples/4_hooks.rb +140 -0
- data/examples/5_error_handling.rb +85 -0
- data/examples/6_retry.rb +164 -0
- data/examples/7_tool_conditional.rb +180 -0
- data/examples/8_multi_provider.rb +112 -0
- data/lib/soka/agent.rb +130 -0
- data/lib/soka/agent_tool.rb +146 -0
- data/lib/soka/agent_tools/params_validator.rb +139 -0
- data/lib/soka/agents/dsl_methods.rb +140 -0
- data/lib/soka/agents/hook_manager.rb +68 -0
- data/lib/soka/agents/llm_builder.rb +32 -0
- data/lib/soka/agents/retry_handler.rb +74 -0
- data/lib/soka/agents/tool_builder.rb +78 -0
- data/lib/soka/configuration.rb +60 -0
- data/lib/soka/engines/base.rb +67 -0
- data/lib/soka/engines/concerns/prompt_template.rb +130 -0
- data/lib/soka/engines/concerns/response_processor.rb +103 -0
- data/lib/soka/engines/react.rb +136 -0
- data/lib/soka/engines/reasoning_context.rb +92 -0
- data/lib/soka/llm.rb +85 -0
- data/lib/soka/llms/anthropic.rb +124 -0
- data/lib/soka/llms/base.rb +114 -0
- data/lib/soka/llms/concerns/response_parser.rb +47 -0
- data/lib/soka/llms/concerns/streaming_handler.rb +78 -0
- data/lib/soka/llms/gemini.rb +106 -0
- data/lib/soka/llms/openai.rb +97 -0
- data/lib/soka/memory.rb +83 -0
- data/lib/soka/result.rb +136 -0
- data/lib/soka/test_helpers.rb +162 -0
- data/lib/soka/thoughts_memory.rb +112 -0
- data/lib/soka/version.rb +5 -0
- data/lib/soka.rb +49 -0
- data/sig/soka.rbs +4 -0
- metadata +158 -0
data/README.md
ADDED
@@ -0,0 +1,650 @@
|
|
1
|
+
# Soka
|
2
|
+
|
3
|
+
<p align="center">
|
4
|
+
<strong>Ruby AI Agent Framework based on ReAct Pattern</strong>
|
5
|
+
</p>
|
6
|
+
|
7
|
+
<p align="center">
|
8
|
+
<a href="#features">Features</a> •
|
9
|
+
<a href="#installation">Installation</a> •
|
10
|
+
<a href="#quick-start">Quick Start</a> •
|
11
|
+
<a href="#advanced-features">Advanced Features</a> •
|
12
|
+
<a href="#api-documentation">API Documentation</a> •
|
13
|
+
<a href="#examples">Examples</a> •
|
14
|
+
<a href="#contributing">Contributing</a>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
Soka is a Ruby AI Agent framework based on the ReAct (Reasoning and Acting) pattern, supporting multiple AI providers, offering an object-oriented tool system and intelligent memory management. It enables you to quickly build intelligent agents that handle complex reasoning and action tasks.
|
18
|
+
|
19
|
+
## Features
|
20
|
+
|
21
|
+
- 🤖 **Multi AI Provider Support**: Google Gemini, OpenAI, Anthropic
|
22
|
+
- 🛠️ **Object-Oriented Tool System**: Grape API-like parameter definition and validation
|
23
|
+
- 🧠 **Intelligent Memory Management**: Conversation history and thought process recording
|
24
|
+
- 🔄 **ReAct Reasoning Pattern**: Tagged thought-action-observation loop
|
25
|
+
- ⚡ **Flexible Configuration System**: Global and instance-level configuration options
|
26
|
+
- 🔁 **Error Handling and Retry**: Built-in exponential backoff retry mechanism
|
27
|
+
- 🧪 **Test Friendly**: Complete test helper tools
|
28
|
+
- 📝 **Full Type Support**: Using dry-rb ecosystem
|
29
|
+
- 🚀 **Modular Design**: Easy to extend and maintain
|
30
|
+
- 💾 **Built-in Caching Mechanism**: Improve performance and save costs
|
31
|
+
|
32
|
+
## Installation
|
33
|
+
|
34
|
+
Add the following to your Gemfile:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
gem 'soka'
|
38
|
+
```
|
39
|
+
|
40
|
+
Then execute:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
bundle install
|
44
|
+
```
|
45
|
+
|
46
|
+
Or install directly:
|
47
|
+
|
48
|
+
```bash
|
49
|
+
gem install soka
|
50
|
+
```
|
51
|
+
|
52
|
+
## Quick Start
|
53
|
+
|
54
|
+
### 1. Set up API Key
|
55
|
+
|
56
|
+
```bash
|
57
|
+
# Method 1: Environment variable
|
58
|
+
export GEMINI_API_KEY="your-api-key"
|
59
|
+
|
60
|
+
# Method 2: Create .env file
|
61
|
+
echo "GEMINI_API_KEY=your-api-key" > .env
|
62
|
+
```
|
63
|
+
|
64
|
+
Get API Keys:
|
65
|
+
- [Google AI Studio](https://aistudio.google.com/app/apikey)
|
66
|
+
- [OpenAI Platform](https://platform.openai.com/api-keys)
|
67
|
+
- [Anthropic Console](https://console.anthropic.com/settings/keys)
|
68
|
+
|
69
|
+
### 2. Basic Usage
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
require 'soka'
|
73
|
+
|
74
|
+
# Create a simple time tool
|
75
|
+
class TimeTool < Soka::AgentTool
|
76
|
+
desc "Get current time"
|
77
|
+
|
78
|
+
def call
|
79
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Create Agent
|
84
|
+
class SimpleAgent < Soka::Agent
|
85
|
+
tool TimeTool
|
86
|
+
end
|
87
|
+
|
88
|
+
# Execute
|
89
|
+
agent = SimpleAgent.new
|
90
|
+
result = agent.run("What time is it?")
|
91
|
+
puts result.final_answer
|
92
|
+
```
|
93
|
+
|
94
|
+
### 3. Run Examples
|
95
|
+
|
96
|
+
```bash
|
97
|
+
# Run full example (API key required)
|
98
|
+
ruby examples/1_basic.rb
|
99
|
+
```
|
100
|
+
|
101
|
+
## Core Concepts
|
102
|
+
|
103
|
+
### Global Configuration
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
Soka.setup do |config|
|
107
|
+
# AI Configuration
|
108
|
+
config.ai do |ai|
|
109
|
+
ai.provider = :gemini # :gemini, :openai, :anthropic
|
110
|
+
ai.model = 'gemini-2.5-flash-lite'
|
111
|
+
ai.api_key = ENV['GEMINI_API_KEY']
|
112
|
+
end
|
113
|
+
|
114
|
+
# Performance Configuration
|
115
|
+
config.performance do |perf|
|
116
|
+
perf.max_iterations = 10 # ReAct max iterations
|
117
|
+
perf.timeout = 30 # API call timeout (seconds)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Default tools
|
121
|
+
config.tools = [SearchTool, TimeTool]
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
### Defining Tools
|
126
|
+
|
127
|
+
Tools are functional modules that Agents can use:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class SearchTool < Soka::AgentTool
|
131
|
+
desc "Search the web for information"
|
132
|
+
|
133
|
+
params do
|
134
|
+
requires :query, String, desc: "The query to search for"
|
135
|
+
optional :location, String, desc: "Location context", default: "Global"
|
136
|
+
|
137
|
+
# Parameter validation
|
138
|
+
validates :query, presence: true, length: { minimum: 1, maximum: 500 }
|
139
|
+
validates :location, inclusion: { in: %w[Global US Europe Asia] }, allow_nil: true
|
140
|
+
end
|
141
|
+
|
142
|
+
def call(query:, location: "Global")
|
143
|
+
# Actual search logic
|
144
|
+
perform_search(query, location)
|
145
|
+
rescue => e
|
146
|
+
{ error: e.message, tool: self.class.name }
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def perform_search(query, location)
|
152
|
+
# Here you can call real search APIs
|
153
|
+
"Search results for #{query} in #{location}..."
|
154
|
+
end
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
### Defining Agents
|
159
|
+
|
160
|
+
Agents are the entities that perform ReAct reasoning:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class WeatherAgent < Soka::Agent
|
164
|
+
# AI settings (override global settings)
|
165
|
+
provider :gemini
|
166
|
+
model 'gemini-2.5-flash-lite'
|
167
|
+
max_iterations 10
|
168
|
+
timeout 30
|
169
|
+
|
170
|
+
# Register tools
|
171
|
+
tool SearchTool
|
172
|
+
tool TimeTool
|
173
|
+
|
174
|
+
# Conditional tool registration
|
175
|
+
tool CalculatorTool, if: -> { ENV['ENABLE_CALCULATOR'] == 'true' }
|
176
|
+
|
177
|
+
# Batch registration
|
178
|
+
tools SearchTool, TimeTool, WeatherTool
|
179
|
+
|
180
|
+
# Custom tool (functional) - requires description as second parameter
|
181
|
+
tool :get_weather, "Get weather for a location"
|
182
|
+
|
183
|
+
# Lifecycle hooks
|
184
|
+
before_action :track_action
|
185
|
+
after_action :update_metrics
|
186
|
+
on_error :handle_error
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
# Method implementation for functional tool
|
191
|
+
# Note: This is currently experimental and not fully implemented
|
192
|
+
def get_weather(location:)
|
193
|
+
"#{location} is currently sunny, temperature 25°C"
|
194
|
+
end
|
195
|
+
|
196
|
+
def track_action(action)
|
197
|
+
# Track action execution
|
198
|
+
@action_count ||= 0
|
199
|
+
@action_count += 1
|
200
|
+
end
|
201
|
+
|
202
|
+
def update_metrics(result)
|
203
|
+
# Update metrics
|
204
|
+
# metrics.record(result)
|
205
|
+
end
|
206
|
+
|
207
|
+
def handle_error(error, context)
|
208
|
+
# Handle errors
|
209
|
+
:continue # or :stop to interrupt execution
|
210
|
+
end
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
214
|
+
### Using Agents
|
215
|
+
|
216
|
+
#### Block Mode (Real-time Feedback)
|
217
|
+
|
218
|
+
Suitable for scenarios that need to display the execution process:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
agent = WeatherAgent.new
|
222
|
+
|
223
|
+
agent.run('What is the weather in Tokyo today?') do |event|
|
224
|
+
case event.type
|
225
|
+
when :thought
|
226
|
+
puts "💭 Thinking: #{event.content}"
|
227
|
+
when :action
|
228
|
+
puts "🔧 Action: Using tool #{event.content[:tool]}"
|
229
|
+
when :observation
|
230
|
+
puts "👀 Observation: #{event.content}"
|
231
|
+
when :final_answer
|
232
|
+
puts "✅ Answer: #{event.content}"
|
233
|
+
when :error
|
234
|
+
puts "❌ Error: #{event.content}"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
```
|
238
|
+
|
239
|
+
#### Direct Mode (Get Result)
|
240
|
+
|
241
|
+
Suitable for scenarios that only need the final result:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
agent = WeatherAgent.new
|
245
|
+
result = agent.run('What is the weather in Tokyo today?')
|
246
|
+
|
247
|
+
# Result object provides rich information
|
248
|
+
puts result.final_answer # Final answer
|
249
|
+
puts result.confidence_score # Confidence score (0.0-1.0)
|
250
|
+
puts result.iterations # Number of iterations used
|
251
|
+
puts result.status # :success, :failed, :timeout, :max_iterations_reached
|
252
|
+
puts result.execution_time # Execution time (if recorded)
|
253
|
+
|
254
|
+
# Check execution status
|
255
|
+
if result.successful?
|
256
|
+
puts "Success: #{result.final_answer}"
|
257
|
+
elsif result.failed?
|
258
|
+
puts "Failed: #{result.error}"
|
259
|
+
elsif result.timeout?
|
260
|
+
puts "Execution timeout"
|
261
|
+
elsif result.max_iterations_reached?
|
262
|
+
puts "Max iterations reached"
|
263
|
+
end
|
264
|
+
```
|
265
|
+
|
266
|
+
### Memory Management
|
267
|
+
|
268
|
+
#### Basic Conversation Memory
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# Initialize Agent with history
|
272
|
+
memory = [
|
273
|
+
{ role: 'user', content: 'My name is John' },
|
274
|
+
{ role: 'assistant', content: 'Hello John! Nice to meet you.' }
|
275
|
+
]
|
276
|
+
|
277
|
+
agent = WeatherAgent.new(memory: memory)
|
278
|
+
result = agent.run('What is my name?')
|
279
|
+
# => "Your name is John."
|
280
|
+
|
281
|
+
# Memory updates automatically
|
282
|
+
puts agent.memory
|
283
|
+
# <Soka::Memory> [
|
284
|
+
# { role: 'user', content: 'My name is John' },
|
285
|
+
# { role: 'assistant', content: 'Hello John! Nice to meet you.' },
|
286
|
+
# { role: 'user', content: 'What is my name?' },
|
287
|
+
# { role: 'assistant', content: 'Your name is John.' }
|
288
|
+
# ]
|
289
|
+
```
|
290
|
+
|
291
|
+
#### Thought Process Memory
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
# View complete thought process
|
295
|
+
puts agent.thoughts_memory
|
296
|
+
# <Soka::ThoughtsMemory> (3 sessions, 2 successful, 1 failed, avg confidence: 0.82, avg iterations: 2.3)
|
297
|
+
|
298
|
+
# Get detailed information for specific session
|
299
|
+
last_session = agent.thoughts_memory.last_session
|
300
|
+
puts last_session[:thoughts] # All thinking steps
|
301
|
+
puts last_session[:confidence_score] # Confidence score for that execution
|
302
|
+
```
|
303
|
+
|
304
|
+
## Advanced Features
|
305
|
+
|
306
|
+
### ReAct Flow Format
|
307
|
+
|
308
|
+
Soka uses a tagged ReAct format:
|
309
|
+
|
310
|
+
```xml
|
311
|
+
<Thought>I need to search for weather information in Tokyo</Thought>
|
312
|
+
<Action>
|
313
|
+
Tool: search
|
314
|
+
Parameters: {"query": "Tokyo weather", "location": "Japan"}
|
315
|
+
</Action>
|
316
|
+
<Observation>Tokyo today: Sunny, temperature 28°C, humidity 65%</Observation>
|
317
|
+
<Thought>I have obtained the weather information and can answer the user now</Thought>
|
318
|
+
<Final_Answer>Today in Tokyo it's sunny with a temperature of 28°C and humidity of 65%.</Final_Answer>
|
319
|
+
```
|
320
|
+
|
321
|
+
### Result Object Structure
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
# Result object attributes
|
325
|
+
result.input # User input
|
326
|
+
result.thoughts # Array of thinking steps
|
327
|
+
result.final_answer # Final answer
|
328
|
+
result.confidence_score # Confidence score (0.0-1.0)
|
329
|
+
result.status # Status (:success, :failed, :timeout, :max_iterations_reached)
|
330
|
+
result.error # Error message (if any)
|
331
|
+
result.execution_time # Execution time (seconds)
|
332
|
+
result.iterations # Number of iterations
|
333
|
+
|
334
|
+
# Complete structure
|
335
|
+
{
|
336
|
+
input: "User input",
|
337
|
+
thoughts: [
|
338
|
+
{
|
339
|
+
step: 1,
|
340
|
+
thought: "Thinking content",
|
341
|
+
action: { tool: "search", params: { query: "..." } },
|
342
|
+
observation: "Observation result"
|
343
|
+
}
|
344
|
+
],
|
345
|
+
final_answer: "Final answer",
|
346
|
+
confidence_score: 0.85, # Calculated based on iterations
|
347
|
+
status: :success, # :success, :failed, :timeout, :max_iterations_reached
|
348
|
+
error: nil, # Error message (if any)
|
349
|
+
execution_time: 1.23, # Execution time (seconds)
|
350
|
+
iterations: 2, # Number of iterations
|
351
|
+
created_at: Time # Creation time
|
352
|
+
}
|
353
|
+
```
|
354
|
+
|
355
|
+
### Test Support
|
356
|
+
|
357
|
+
Soka provides complete test helper tools:
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
RSpec.describe WeatherAgent do
|
361
|
+
include Soka::TestHelpers
|
362
|
+
|
363
|
+
it "answers weather questions" do
|
364
|
+
# Mock AI response
|
365
|
+
mock_ai_response({
|
366
|
+
thoughts: [
|
367
|
+
{
|
368
|
+
step: 1,
|
369
|
+
thought: "Need to search for weather information",
|
370
|
+
action: { tool: "search", params: { query: "Tokyo weather" } },
|
371
|
+
observation: "Tokyo is sunny today"
|
372
|
+
}
|
373
|
+
],
|
374
|
+
final_answer: "Tokyo is sunny today."
|
375
|
+
})
|
376
|
+
|
377
|
+
# Mock tool response
|
378
|
+
mock_tool_response(SearchTool, "Tokyo is sunny today")
|
379
|
+
|
380
|
+
agent = described_class.new
|
381
|
+
result = agent.run("What's the weather in Tokyo?")
|
382
|
+
|
383
|
+
expect(result).to be_successful
|
384
|
+
expect(result.final_answer).to include("sunny")
|
385
|
+
expect(result).to have_thoughts_count(1)
|
386
|
+
expect(result).to have_confidence_score_above(0.8)
|
387
|
+
end
|
388
|
+
|
389
|
+
it "handles tool errors gracefully" do
|
390
|
+
allow_tool_to_fail(SearchTool, StandardError.new("API error"))
|
391
|
+
|
392
|
+
agent = described_class.new
|
393
|
+
result = agent.run("Search test")
|
394
|
+
|
395
|
+
expect(result).to be_failed
|
396
|
+
expect(result.error).to include("API error")
|
397
|
+
end
|
398
|
+
end
|
399
|
+
```
|
400
|
+
|
401
|
+
### Custom Engines
|
402
|
+
|
403
|
+
You can implement your own reasoning engine:
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
class CustomEngine < Soka::Engines::Base
|
407
|
+
def reason(task, &block)
|
408
|
+
# Implement custom reasoning logic
|
409
|
+
context = Soka::Engines::ReasoningContext.new(
|
410
|
+
task: task,
|
411
|
+
event_handler: block,
|
412
|
+
max_iterations: max_iterations
|
413
|
+
)
|
414
|
+
|
415
|
+
# Use emit_event to send events
|
416
|
+
emit_event(:thought, "Starting reasoning...", &block)
|
417
|
+
|
418
|
+
# Perform reasoning...
|
419
|
+
|
420
|
+
# Return result (using Struct)
|
421
|
+
Soka::Engines::React::ReasonResult.new(
|
422
|
+
input: task,
|
423
|
+
thoughts: thoughts,
|
424
|
+
final_answer: answer,
|
425
|
+
status: :success,
|
426
|
+
confidence_score: calculate_confidence_score(thoughts, :success)
|
427
|
+
)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Use custom engine
|
432
|
+
agent = MyAgent.new(engine: CustomEngine)
|
433
|
+
```
|
434
|
+
|
435
|
+
## Examples
|
436
|
+
|
437
|
+
The `examples/` directory contains several examples demonstrating different features of Soka, ordered from basic to advanced:
|
438
|
+
|
439
|
+
### 1. Basic Example (`examples/1_basic.rb`)
|
440
|
+
Demonstrates the fundamental usage of Soka with simple tools:
|
441
|
+
- Creating basic tools (SearchTool, TimeTool)
|
442
|
+
- Setting up an agent
|
443
|
+
- Running queries with event handling
|
444
|
+
- Direct result access
|
445
|
+
|
446
|
+
### 2. Event Handling (`examples/2_event_handling.rb`)
|
447
|
+
Shows how to handle real-time events during agent execution:
|
448
|
+
- Event-based response handling
|
449
|
+
- Different event types (thought, action, observation, final_answer)
|
450
|
+
- Multi-step task processing
|
451
|
+
- Direct result mode vs event mode
|
452
|
+
|
453
|
+
### 3. Memory Management (`examples/3_memory.rb`)
|
454
|
+
Illustrates memory features and conversation context:
|
455
|
+
- Using Soka::Memory for conversation history
|
456
|
+
- Array format for initial memory
|
457
|
+
- Tool-based memory storage and recall
|
458
|
+
- Accessing complete conversation history
|
459
|
+
- Viewing thinking processes
|
460
|
+
|
461
|
+
### 4. Lifecycle Hooks (`examples/4_hooks.rb`)
|
462
|
+
Demonstrates lifecycle hooks for monitoring and control:
|
463
|
+
- `before_action` for pre-processing
|
464
|
+
- `after_action` for post-processing
|
465
|
+
- `on_error` for error handling
|
466
|
+
- Tracking agent activity and metrics
|
467
|
+
|
468
|
+
### 5. Error Handling (`examples/5_error_handling.rb`)
|
469
|
+
Shows robust error handling mechanisms:
|
470
|
+
- Tool errors and agent-level errors
|
471
|
+
- Using `on_error` hooks
|
472
|
+
- Continuing execution after errors
|
473
|
+
- Error result inspection
|
474
|
+
|
475
|
+
### 6. Retry Mechanisms (`examples/6_retry.rb`)
|
476
|
+
Demonstrates retry strategies for reliability:
|
477
|
+
- Handling transient failures
|
478
|
+
- Exponential backoff
|
479
|
+
- Rate limiting scenarios
|
480
|
+
- Configuring retry behavior
|
481
|
+
|
482
|
+
### 7. Conditional Tools (`examples/7_tool_conditional.rb`)
|
483
|
+
Shows dynamic tool loading based on conditions:
|
484
|
+
- Environment-based tool loading
|
485
|
+
- Role-based access control
|
486
|
+
- Feature flag integration
|
487
|
+
- Time-based availability
|
488
|
+
|
489
|
+
### 8. Multi-Provider Support (`examples/8_multi_provider.rb`)
|
490
|
+
Demonstrates using different AI providers:
|
491
|
+
- Configuring Gemini, OpenAI, and Anthropic
|
492
|
+
- Provider-specific features
|
493
|
+
- Comparing outputs across models
|
494
|
+
- Cost optimization strategies
|
495
|
+
|
496
|
+
To run any example:
|
497
|
+
```bash
|
498
|
+
# Make sure you have the required API keys in your .env file
|
499
|
+
ruby examples/1_basic.rb
|
500
|
+
```
|
501
|
+
|
502
|
+
## API Documentation
|
503
|
+
|
504
|
+
### Supported AI Providers
|
505
|
+
|
506
|
+
#### Google Gemini
|
507
|
+
- Models: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`
|
508
|
+
- Environment variable: `GEMINI_API_KEY`
|
509
|
+
- Features: Fast response, cost-effective
|
510
|
+
- Default model: `gemini-2.5-flash-lite`
|
511
|
+
|
512
|
+
#### OpenAI
|
513
|
+
- Models: `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`
|
514
|
+
- Environment variable: `OPENAI_API_KEY`
|
515
|
+
- Features: Streaming support, powerful reasoning
|
516
|
+
|
517
|
+
#### Anthropic
|
518
|
+
- Models: `claude-opus-4-0`, `claude-sonnet-4-0`, `claude-3-5-haiku-latest`
|
519
|
+
- Environment variable: `ANTHROPIC_API_KEY`
|
520
|
+
- Features: Long context support, excellent code understanding
|
521
|
+
|
522
|
+
### Configuration Options
|
523
|
+
|
524
|
+
| Option | Type | Default | Description |
|
525
|
+
|--------|------|---------|-------------|
|
526
|
+
| `ai.provider` | Symbol | `:gemini` | AI provider |
|
527
|
+
| `ai.model` | String | `"gemini-2.5-flash-lite"` | Model to use |
|
528
|
+
| `ai.api_key` | String | nil | API key |
|
529
|
+
| `performance.max_iterations` | Integer | 10 | Max iterations |
|
530
|
+
| `performance.timeout` | Integer | 30 | Timeout (seconds) |
|
531
|
+
|
532
|
+
### Tool Parameter Validation
|
533
|
+
|
534
|
+
| Validator | Options | Description |
|
535
|
+
|-----------|---------|-------------|
|
536
|
+
| `presence` | `true/false` | Value cannot be empty |
|
537
|
+
| `length` | `minimum`, `maximum` | String length limits |
|
538
|
+
| `inclusion` | `in`, `allow_nil` | Value must be in specified list |
|
539
|
+
| `format` | `with` | Match regular expression |
|
540
|
+
|
541
|
+
## Performance Optimization
|
542
|
+
|
543
|
+
1. **Use appropriate models**:
|
544
|
+
- Simple tasks: `gemini-2.5-flash-lite` or `gpt-4.1-mini` or `claude-3-5-haiku-latest`
|
545
|
+
- Complex reasoning: `gemini-2.5-pro` or `gpt-4.1` or `claude-sonnet-4-0`
|
546
|
+
|
547
|
+
2. **Control iterations**:
|
548
|
+
```ruby
|
549
|
+
agent = MyAgent.new(max_iterations: 5) # Limit iterations
|
550
|
+
```
|
551
|
+
|
552
|
+
|
553
|
+
## Troubleshooting
|
554
|
+
|
555
|
+
### Common Issues
|
556
|
+
|
557
|
+
1. **API Key Error**
|
558
|
+
```
|
559
|
+
Soka::LLMError: API key is required
|
560
|
+
```
|
561
|
+
Solution: Ensure correct environment variable is set or provide API key in configuration
|
562
|
+
|
563
|
+
2. **Timeout Error**
|
564
|
+
```
|
565
|
+
Soka::LLMError: Request timed out
|
566
|
+
```
|
567
|
+
Solution: Increase timeout or use a faster model
|
568
|
+
|
569
|
+
3. **Max Iterations Reached**
|
570
|
+
```
|
571
|
+
Status: max_iterations_reached
|
572
|
+
```
|
573
|
+
Solution: Simplify the problem or increase `max_iterations`
|
574
|
+
|
575
|
+
### Debugging Tips
|
576
|
+
|
577
|
+
```ruby
|
578
|
+
# Adjust max iterations
|
579
|
+
Soka.configure do |c|
|
580
|
+
c.performance.max_iterations = 20
|
581
|
+
end
|
582
|
+
|
583
|
+
# Use block mode to see execution process
|
584
|
+
agent.run(query) do |event|
|
585
|
+
p event # Print all events
|
586
|
+
end
|
587
|
+
|
588
|
+
# Inspect thought process
|
589
|
+
result = agent.run(query)
|
590
|
+
result.thoughts.each do |thought|
|
591
|
+
puts "Step #{thought[:step]}: #{thought[:thought]}"
|
592
|
+
puts "Action: #{thought[:action]}" if thought[:action]
|
593
|
+
puts "Observation: #{thought[:observation]}" if thought[:observation]
|
594
|
+
end
|
595
|
+
```
|
596
|
+
|
597
|
+
## Development
|
598
|
+
|
599
|
+
```bash
|
600
|
+
# Install dependencies
|
601
|
+
bundle install
|
602
|
+
|
603
|
+
# Run tests
|
604
|
+
bundle exec rspec
|
605
|
+
|
606
|
+
# Run Rubocop
|
607
|
+
bundle exec rubocop
|
608
|
+
|
609
|
+
# Open interactive console
|
610
|
+
bin/console
|
611
|
+
|
612
|
+
# Create new version
|
613
|
+
# 1. Update lib/soka/version.rb
|
614
|
+
# 2. Update CHANGELOG.md
|
615
|
+
# 3. Commit changes
|
616
|
+
# 4. Create tag
|
617
|
+
bundle exec rake release
|
618
|
+
```
|
619
|
+
|
620
|
+
## Contributing
|
621
|
+
|
622
|
+
We welcome all forms of contributions!
|
623
|
+
|
624
|
+
1. Fork the project
|
625
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
626
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
627
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
628
|
+
5. Open a Pull Request
|
629
|
+
|
630
|
+
Please ensure:
|
631
|
+
- Add appropriate tests
|
632
|
+
- Update relevant documentation
|
633
|
+
- Follow existing code style
|
634
|
+
- Pass Rubocop checks
|
635
|
+
|
636
|
+
## License
|
637
|
+
|
638
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
639
|
+
|
640
|
+
## Acknowledgments
|
641
|
+
|
642
|
+
- Thanks to the [ReAct paper](https://arxiv.org/abs/2210.03629) for the theoretical foundation
|
643
|
+
- Thanks to the [Regent](https://github.com/alextwoods/regent) project for architectural inspiration
|
644
|
+
- Thanks to all contributors for their efforts
|
645
|
+
|
646
|
+
---
|
647
|
+
|
648
|
+
<p align="center">
|
649
|
+
Made with ❤️ in Taiwan
|
650
|
+
</p>
|