soka 0.0.2 โ 0.0.4
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 +33 -0
- data/CLAUDE.md +347 -188
- data/README.md +163 -8
- data/examples/10_dynamic_instructions.rb +248 -0
- data/examples/11_think_in_languages.rb +113 -0
- data/examples/2_event_handling.rb +5 -4
- data/examples/3_memory.rb +4 -2
- data/examples/4_hooks.rb +0 -1
- data/examples/9_custom_instructions.rb +190 -0
- data/lib/soka/agent.rb +51 -4
- data/lib/soka/agents/dsl_methods.rb +18 -1
- data/lib/soka/engines/base.rb +6 -4
- data/lib/soka/engines/concerns/prompt_template.rb +44 -53
- data/lib/soka/engines/concerns/response_parser.rb +60 -0
- data/lib/soka/engines/concerns/response_processor.rb +4 -2
- data/lib/soka/engines/concerns/result_builder.rb +25 -0
- data/lib/soka/engines/react.rb +4 -26
- data/lib/soka/engines/reasoning_context.rb +4 -2
- data/lib/soka/result.rb +1 -5
- data/lib/soka/thoughts_memory.rb +0 -12
- data/lib/soka/version.rb +1 -1
- metadata +6 -2
- data/lib/soka/test_helpers.rb +0 -162
data/README.md
CHANGED
@@ -98,6 +98,67 @@ puts result.final_answer
|
|
98
98
|
ruby examples/1_basic.rb
|
99
99
|
```
|
100
100
|
|
101
|
+
## Rails Integration
|
102
|
+
|
103
|
+
### Soka Rails
|
104
|
+
|
105
|
+
For Rails applications, we provide a dedicated gem `soka-rails` that offers seamless integration with Rails conventions:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# Gemfile
|
109
|
+
gem 'soka-rails'
|
110
|
+
```
|
111
|
+
|
112
|
+
#### Features
|
113
|
+
|
114
|
+
- ๐ **Native Rails Integration**: Following Rails conventions and best practices
|
115
|
+
- ๐ **Auto-loading Support**: Automatically loads the `app/soka` directory
|
116
|
+
- ๐ ๏ธ **Generator Support**: Quickly generate Agent and Tool templates
|
117
|
+
- โ๏ธ **Rails Configuration Integration**: Uses Rails' configuration system
|
118
|
+
- ๐งช **Rails Testing Integration**: Seamless integration with RSpec
|
119
|
+
- ๐ **Rails Lifecycle Hooks**: Integrates with Rails logging and error tracking
|
120
|
+
|
121
|
+
#### Quick Setup
|
122
|
+
|
123
|
+
```bash
|
124
|
+
# Install the gem
|
125
|
+
bundle add soka-rails
|
126
|
+
|
127
|
+
# Run the installation generator
|
128
|
+
rails generate soka:install
|
129
|
+
|
130
|
+
# Generate an agent
|
131
|
+
rails generate soka:agent customer_support
|
132
|
+
|
133
|
+
# Generate a tool
|
134
|
+
rails generate soka:tool order_lookup order_id:string
|
135
|
+
```
|
136
|
+
|
137
|
+
#### Basic Usage in Rails
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
# app/soka/agents/customer_support_agent.rb
|
141
|
+
class CustomerSupportAgent < ApplicationAgent
|
142
|
+
tool OrderLookupTool
|
143
|
+
tool UserInfoTool
|
144
|
+
end
|
145
|
+
|
146
|
+
# app/controllers/conversations_controller.rb
|
147
|
+
class ConversationsController < ApplicationController
|
148
|
+
def create
|
149
|
+
agent = CustomerSupportAgent.new
|
150
|
+
result = agent.run(params[:message])
|
151
|
+
|
152
|
+
render json: {
|
153
|
+
answer: result.final_answer,
|
154
|
+
status: result.status
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
For more details, visit the [soka-rails repository](https://github.com/jiunjiun/soka-rails).
|
161
|
+
|
101
162
|
## Core Concepts
|
102
163
|
|
103
164
|
### Global Configuration
|
@@ -180,6 +241,12 @@ class WeatherAgent < Soka::Agent
|
|
180
241
|
# Custom tool (functional) - requires description as second parameter
|
181
242
|
tool :get_weather, "Get weather for a location"
|
182
243
|
|
244
|
+
# Custom instructions (optional)
|
245
|
+
instructions "You are a weather expert. Provide detailed weather information."
|
246
|
+
|
247
|
+
# Thinking language (optional)
|
248
|
+
think_in 'en' # Default is 'en'
|
249
|
+
|
183
250
|
# Lifecycle hooks
|
184
251
|
before_action :track_action
|
185
252
|
after_action :update_metrics
|
@@ -246,7 +313,6 @@ result = agent.run('What is the weather in Tokyo today?')
|
|
246
313
|
|
247
314
|
# Result object provides rich information
|
248
315
|
puts result.final_answer # Final answer
|
249
|
-
puts result.confidence_score # Confidence score (0.0-1.0)
|
250
316
|
puts result.iterations # Number of iterations used
|
251
317
|
puts result.status # :success, :failed, :timeout, :max_iterations_reached
|
252
318
|
puts result.execution_time # Execution time (if recorded)
|
@@ -293,16 +359,85 @@ puts agent.memory
|
|
293
359
|
```ruby
|
294
360
|
# View complete thought process
|
295
361
|
puts agent.thoughts_memory
|
296
|
-
# <Soka::ThoughtsMemory> (3 sessions, 2 successful, 1 failed, avg
|
362
|
+
# <Soka::ThoughtsMemory> (3 sessions, 2 successful, 1 failed, avg iterations: 2.3)
|
297
363
|
|
298
364
|
# Get detailed information for specific session
|
299
365
|
last_session = agent.thoughts_memory.last_session
|
300
366
|
puts last_session[:thoughts] # All thinking steps
|
301
|
-
puts last_session[:
|
367
|
+
puts last_session[:final_answer] # Final answer for that execution
|
302
368
|
```
|
303
369
|
|
304
370
|
## Advanced Features
|
305
371
|
|
372
|
+
### Custom Instructions
|
373
|
+
|
374
|
+
Customize your agent's personality and response style:
|
375
|
+
|
376
|
+
```ruby
|
377
|
+
class FriendlyAgent < Soka::Agent
|
378
|
+
provider :gemini
|
379
|
+
|
380
|
+
# Method 1: Static string instructions
|
381
|
+
instructions <<~PROMPT
|
382
|
+
You are a friendly, helpful assistant.
|
383
|
+
Use casual language and be encouraging.
|
384
|
+
Add emojis when appropriate.
|
385
|
+
PROMPT
|
386
|
+
end
|
387
|
+
|
388
|
+
# Method 2: Dynamic instructions using method
|
389
|
+
class DynamicAgent < Soka::Agent
|
390
|
+
provider :gemini
|
391
|
+
|
392
|
+
# Reference a method for dynamic instructions
|
393
|
+
instructions :generate_instructions
|
394
|
+
|
395
|
+
private
|
396
|
+
|
397
|
+
def generate_instructions
|
398
|
+
hour = Time.now.hour
|
399
|
+
mood = hour < 12 ? 'cheerful morning' : 'relaxed afternoon'
|
400
|
+
|
401
|
+
<<~PROMPT
|
402
|
+
You are a #{mood} assistant.
|
403
|
+
Current Time: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}
|
404
|
+
Adjust your tone based on the time of day.
|
405
|
+
PROMPT
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Method 3: Override at runtime
|
410
|
+
agent = FriendlyAgent.new(
|
411
|
+
instructions: 'Be more formal and professional.'
|
412
|
+
)
|
413
|
+
result = agent.run("Help me with this task")
|
414
|
+
```
|
415
|
+
|
416
|
+
### Multilingual Thinking (Think In)
|
417
|
+
|
418
|
+
Optimize reasoning for specific languages:
|
419
|
+
|
420
|
+
```ruby
|
421
|
+
class GlobalAgent < Soka::Agent
|
422
|
+
provider :gemini
|
423
|
+
|
424
|
+
# Set default thinking language
|
425
|
+
think_in 'zh-TW' # Think in Traditional Chinese
|
426
|
+
end
|
427
|
+
|
428
|
+
# Or set dynamically
|
429
|
+
agent = GlobalAgent.new(think_in: 'ja-JP')
|
430
|
+
result = agent.run("ๅนซๆ่งฃๆฑบ้ๅๅ้ก") # Input in Chinese
|
431
|
+
# Agent thinks in Japanese internally, responds in Chinese
|
432
|
+
```
|
433
|
+
|
434
|
+
**Key Points:**
|
435
|
+
- Thinking language affects internal reasoning only
|
436
|
+
- Responses adapt to user's input language
|
437
|
+
- Default is English (`'en'`)
|
438
|
+
- No automatic language detection (explicit setting required)
|
439
|
+
- Improves reasoning quality for language-specific contexts
|
440
|
+
|
306
441
|
### ReAct Flow Format
|
307
442
|
|
308
443
|
Soka uses a tagged ReAct format:
|
@@ -325,7 +460,6 @@ Parameters: {"query": "Tokyo weather", "location": "Japan"}
|
|
325
460
|
result.input # User input
|
326
461
|
result.thoughts # Array of thinking steps
|
327
462
|
result.final_answer # Final answer
|
328
|
-
result.confidence_score # Confidence score (0.0-1.0)
|
329
463
|
result.status # Status (:success, :failed, :timeout, :max_iterations_reached)
|
330
464
|
result.error # Error message (if any)
|
331
465
|
result.execution_time # Execution time (seconds)
|
@@ -343,7 +477,6 @@ result.iterations # Number of iterations
|
|
343
477
|
}
|
344
478
|
],
|
345
479
|
final_answer: "Final answer",
|
346
|
-
confidence_score: 0.85, # Calculated based on iterations
|
347
480
|
status: :success, # :success, :failed, :timeout, :max_iterations_reached
|
348
481
|
error: nil, # Error message (if any)
|
349
482
|
execution_time: 1.23, # Execution time (seconds)
|
@@ -383,7 +516,7 @@ RSpec.describe WeatherAgent do
|
|
383
516
|
expect(result).to be_successful
|
384
517
|
expect(result.final_answer).to include("sunny")
|
385
518
|
expect(result).to have_thoughts_count(1)
|
386
|
-
expect(result).to
|
519
|
+
expect(result.status).to eq(:success)
|
387
520
|
end
|
388
521
|
|
389
522
|
it "handles tool errors gracefully" do
|
@@ -422,8 +555,7 @@ class CustomEngine < Soka::Engines::Base
|
|
422
555
|
input: task,
|
423
556
|
thoughts: thoughts,
|
424
557
|
final_answer: answer,
|
425
|
-
status: :success
|
426
|
-
confidence_score: calculate_confidence_score(thoughts, :success)
|
558
|
+
status: :success
|
427
559
|
)
|
428
560
|
end
|
429
561
|
end
|
@@ -493,6 +625,27 @@ Demonstrates using different AI providers:
|
|
493
625
|
- Comparing outputs across models
|
494
626
|
- Cost optimization strategies
|
495
627
|
|
628
|
+
### 9. Custom Instructions (`examples/9_custom_instructions.rb`)
|
629
|
+
Shows how to customize agent personality:
|
630
|
+
- Setting instructions at class level
|
631
|
+
- Runtime instruction override
|
632
|
+
- Creating different agent personalities
|
633
|
+
- Use cases for different domains
|
634
|
+
|
635
|
+
### 10. Dynamic Instructions (`examples/10_dynamic_instructions.rb`)
|
636
|
+
Demonstrates dynamic instruction generation:
|
637
|
+
- Using methods to generate instructions dynamically
|
638
|
+
- Time-based instruction changes
|
639
|
+
- Environment-based configuration
|
640
|
+
- Session-based personality switching
|
641
|
+
|
642
|
+
### 11. Multilingual Thinking (`examples/11_think_in_languages.rb`)
|
643
|
+
Demonstrates language-specific reasoning:
|
644
|
+
- Setting thinking language with `think_in`
|
645
|
+
- Class-level vs instance-level configuration
|
646
|
+
- Performance comparison across languages
|
647
|
+
- Cultural context optimization
|
648
|
+
|
496
649
|
To run any example:
|
497
650
|
```bash
|
498
651
|
# Make sure you have the required API keys in your .env file
|
@@ -526,6 +679,8 @@ ruby examples/1_basic.rb
|
|
526
679
|
| `ai.provider` | Symbol | `:gemini` | AI provider |
|
527
680
|
| `ai.model` | String | `"gemini-2.5-flash-lite"` | Model to use |
|
528
681
|
| `ai.api_key` | String | nil | API key |
|
682
|
+
| `ai.instructions` | String | nil | Custom agent instructions |
|
683
|
+
| `ai.think_in` | String | `"en"` | Thinking language |
|
529
684
|
| `performance.max_iterations` | Integer | 10 | Max iterations |
|
530
685
|
| `performance.timeout` | Integer | 30 | Timeout (seconds) |
|
531
686
|
|
@@ -0,0 +1,248 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'soka'
|
6
|
+
require 'dotenv/load'
|
7
|
+
require 'dentaku'
|
8
|
+
|
9
|
+
# Example 10: Dynamic Instructions via Method
|
10
|
+
#
|
11
|
+
# This example demonstrates how to use a method to generate instructions dynamically.
|
12
|
+
# Instead of static string instructions, you can define a method that returns
|
13
|
+
# instructions based on runtime conditions.
|
14
|
+
#
|
15
|
+
# Key concepts:
|
16
|
+
# - Using a Symbol to reference an instructions method
|
17
|
+
# - Dynamic instruction generation based on context
|
18
|
+
# - Maintaining compatibility with string-based instructions
|
19
|
+
|
20
|
+
# Example 1: Time-aware Assistant
|
21
|
+
# Instructions change based on time of day
|
22
|
+
class TimeAwareAssistant < Soka::Agent
|
23
|
+
provider :gemini
|
24
|
+
model 'gemini-2.5-flash-lite'
|
25
|
+
|
26
|
+
# Use a method to generate instructions
|
27
|
+
instructions :generate_time_based_instructions
|
28
|
+
|
29
|
+
class GreetingTool < Soka::AgentTool
|
30
|
+
desc 'Generate appropriate greetings'
|
31
|
+
|
32
|
+
params do
|
33
|
+
requires :name, String, desc: 'Name of the person to greet'
|
34
|
+
end
|
35
|
+
|
36
|
+
def call(name:)
|
37
|
+
"Greeting generated for #{name}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
tool GreetingTool
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def generate_time_based_instructions
|
46
|
+
hour = Time.now.hour
|
47
|
+
|
48
|
+
base_instructions = if hour < 12
|
49
|
+
'You are a cheerful morning assistant who is energetic and motivating.'
|
50
|
+
elsif hour < 17
|
51
|
+
'You are a focused afternoon assistant who is professional and efficient.'
|
52
|
+
else
|
53
|
+
'You are a relaxed evening assistant who is calm and supportive.'
|
54
|
+
end
|
55
|
+
|
56
|
+
<<~INSTRUCTIONS
|
57
|
+
#{base_instructions}
|
58
|
+
Current time: #{Time.now.strftime('%H:%M')}
|
59
|
+
|
60
|
+
You must:
|
61
|
+
- Acknowledge the time of day in your responses
|
62
|
+
- Adjust your tone based on the time period
|
63
|
+
- Be helpful while matching the appropriate energy level
|
64
|
+
INSTRUCTIONS
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Example 2: Environment-based Assistant
|
69
|
+
# Instructions change based on environment variables
|
70
|
+
class EnvironmentAwareAssistant < Soka::Agent
|
71
|
+
provider :gemini
|
72
|
+
model 'gemini-2.5-flash-lite'
|
73
|
+
|
74
|
+
# Use a method for environment-based instructions
|
75
|
+
instructions :build_environment_instructions
|
76
|
+
|
77
|
+
class CalculatorTool < Soka::AgentTool
|
78
|
+
desc 'Performs calculations'
|
79
|
+
|
80
|
+
params do
|
81
|
+
requires :expression, String, desc: 'Math expression'
|
82
|
+
end
|
83
|
+
|
84
|
+
def call(expression:)
|
85
|
+
calculator = Dentaku::Calculator.new
|
86
|
+
result = calculator.evaluate(expression)
|
87
|
+
"Result: #{expression} = #{result}"
|
88
|
+
rescue StandardError => e
|
89
|
+
"Error: #{e.message}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
tool CalculatorTool
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def build_environment_instructions
|
98
|
+
user_level = ENV['USER_LEVEL'] || 'beginner'
|
99
|
+
debug_mode = ENV['DEBUG'] == 'true'
|
100
|
+
|
101
|
+
style = case user_level
|
102
|
+
when 'expert'
|
103
|
+
'Use technical terminology and assume deep knowledge. Be concise.'
|
104
|
+
when 'intermediate'
|
105
|
+
'Balance technical accuracy with clear explanations. Provide context when needed.'
|
106
|
+
else
|
107
|
+
'Use simple language and explain concepts thoroughly. Avoid jargon.'
|
108
|
+
end
|
109
|
+
|
110
|
+
debug_info = if debug_mode
|
111
|
+
"\n- Include detailed technical information in responses"
|
112
|
+
else
|
113
|
+
"\n- Keep responses focused on the essential information"
|
114
|
+
end
|
115
|
+
|
116
|
+
<<~INSTRUCTIONS
|
117
|
+
You are an AI assistant adapting to the user's expertise level.
|
118
|
+
|
119
|
+
Communication style: #{style}
|
120
|
+
User level: #{user_level}
|
121
|
+
Debug mode: #{debug_mode}
|
122
|
+
|
123
|
+
Guidelines:#{debug_info}
|
124
|
+
- Adapt your responses to the #{user_level} level
|
125
|
+
- Maintain accuracy while adjusting complexity
|
126
|
+
INSTRUCTIONS
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Example 3: Session-based Assistant
|
131
|
+
# Instructions change based on a session ID
|
132
|
+
class SessionAssistant < Soka::Agent
|
133
|
+
provider :gemini
|
134
|
+
model 'gemini-2.5-flash-lite'
|
135
|
+
|
136
|
+
# Use method for session-based instructions
|
137
|
+
instructions :generate_session_instructions
|
138
|
+
|
139
|
+
class InfoTool < Soka::AgentTool
|
140
|
+
desc 'Provides information'
|
141
|
+
|
142
|
+
params do
|
143
|
+
requires :query, String, desc: 'Information query'
|
144
|
+
end
|
145
|
+
|
146
|
+
def call(query:)
|
147
|
+
"Information about: #{query}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
tool InfoTool
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def generate_session_instructions
|
156
|
+
# Simulate different session types based on time
|
157
|
+
session_type = Time.now.min % 3
|
158
|
+
|
159
|
+
personality = case session_type
|
160
|
+
when 0
|
161
|
+
'You are a formal, professional assistant.'
|
162
|
+
when 1
|
163
|
+
'You are a friendly, casual assistant.'
|
164
|
+
else
|
165
|
+
'You are an educational, patient assistant.'
|
166
|
+
end
|
167
|
+
|
168
|
+
<<~INSTRUCTIONS
|
169
|
+
#{personality}
|
170
|
+
Session ID: #{Time.now.to_i}
|
171
|
+
|
172
|
+
Guidelines:
|
173
|
+
- Maintain consistent personality throughout the session
|
174
|
+
- Provide helpful and accurate information
|
175
|
+
- Adapt your tone to match the session style
|
176
|
+
INSTRUCTIONS
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Run examples
|
181
|
+
puts '=== Time-Aware Assistant Example ==='
|
182
|
+
time_assistant = TimeAwareAssistant.new
|
183
|
+
|
184
|
+
time_assistant.run('Please greet John appropriately for the current time') do |event|
|
185
|
+
case event.type
|
186
|
+
when :thought
|
187
|
+
puts "โฐ Thinking: #{event.content}"
|
188
|
+
when :final_answer
|
189
|
+
puts "๐ Response: #{event.content}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
puts "\n=== Environment-based Assistant (Beginner) Example ==="
|
194
|
+
ENV['USER_LEVEL'] = 'beginner'
|
195
|
+
ENV['DEBUG'] = 'false'
|
196
|
+
|
197
|
+
beginner_assistant = EnvironmentAwareAssistant.new
|
198
|
+
|
199
|
+
beginner_assistant.run('Calculate 15% of 200') do |event|
|
200
|
+
case event.type
|
201
|
+
when :thought
|
202
|
+
puts "๐ Thinking: #{event.content}"
|
203
|
+
when :final_answer
|
204
|
+
puts "๐ Response: #{event.content}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
puts "\n=== Environment-based Assistant (Expert) Example ==="
|
209
|
+
ENV['USER_LEVEL'] = 'expert'
|
210
|
+
ENV['DEBUG'] = 'true'
|
211
|
+
|
212
|
+
expert_assistant = EnvironmentAwareAssistant.new
|
213
|
+
|
214
|
+
expert_assistant.run('Calculate 15% of 200') do |event|
|
215
|
+
case event.type
|
216
|
+
when :final_answer
|
217
|
+
puts "๐ผ Response: #{event.content}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
puts "\n=== Session Assistant Example ==="
|
222
|
+
session_assistant = SessionAssistant.new
|
223
|
+
|
224
|
+
session_assistant.run('Tell me about Ruby') do |event|
|
225
|
+
case event.type
|
226
|
+
when :thought
|
227
|
+
puts "๐ง Thinking: #{event.content}"
|
228
|
+
when :final_answer
|
229
|
+
puts "๐ฅ๏ธ Response: #{event.content}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
puts "\n=== Mixed: String and Method Instructions ==="
|
234
|
+
# You can still use string instructions
|
235
|
+
class StringInstructionsAgent < Soka::Agent
|
236
|
+
provider :gemini
|
237
|
+
model 'gemini-2.5-flash-lite'
|
238
|
+
|
239
|
+
instructions 'You are a helpful assistant that always responds concisely.'
|
240
|
+
end
|
241
|
+
|
242
|
+
puts "String instructions work as before."
|
243
|
+
|
244
|
+
# And override at runtime
|
245
|
+
TimeAwareAssistant.new(
|
246
|
+
instructions: 'Override with a custom string instruction at runtime.'
|
247
|
+
)
|
248
|
+
puts "Runtime overrides still work with both string and method-based instructions."
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/soka'
|
4
|
+
require 'dotenv/load'
|
5
|
+
require 'dentaku'
|
6
|
+
|
7
|
+
# Example 10: Think In Languages Feature
|
8
|
+
#
|
9
|
+
# This example demonstrates the think_in feature that allows Agents to use
|
10
|
+
# a specific language for their internal reasoning process.
|
11
|
+
#
|
12
|
+
# Key concepts:
|
13
|
+
# - think_in specifies the language for internal thoughts
|
14
|
+
# - Final answers typically match the user's input language
|
15
|
+
# - Default thinking language is 'en' (English)
|
16
|
+
# - No automatic language detection - you must specify explicitly
|
17
|
+
#
|
18
|
+
# Benefits:
|
19
|
+
# - Improved reasoning quality for specific languages
|
20
|
+
# - Better cultural context understanding
|
21
|
+
# - Consistent reasoning patterns across teams
|
22
|
+
# - Reduced API calls (no language detection)
|
23
|
+
|
24
|
+
# Example: Multilingual Agent with configurable thinking language
|
25
|
+
class MultilingualAgent < Soka::Agent
|
26
|
+
provider ENV.fetch('AGENT_PROVIDER', :gemini).to_sym
|
27
|
+
model ENV.fetch('AGENT_MODEL', nil)
|
28
|
+
|
29
|
+
# Define a simple calculator tool
|
30
|
+
class CalculateTool < Soka::AgentTool
|
31
|
+
desc 'Perform mathematical calculations'
|
32
|
+
|
33
|
+
params do
|
34
|
+
requires :expression, String, desc: 'Mathematical expression to evaluate'
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(expression:)
|
38
|
+
calculator = Dentaku::Calculator.new
|
39
|
+
result = calculator.evaluate(expression)
|
40
|
+
"The result of #{expression} is #{result}"
|
41
|
+
rescue StandardError => e
|
42
|
+
"Error calculating: #{e.message}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
tool CalculateTool
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "=== Think In Languages Example ==="
|
50
|
+
puts
|
51
|
+
|
52
|
+
# Example 1: Default behavior - thinks in English
|
53
|
+
# Without specifying think_in, the agent defaults to English thinking
|
54
|
+
# but still understands and responds appropriately to Chinese input
|
55
|
+
puts "1. Chinese Input (Default English thinking):"
|
56
|
+
agent = MultilingualAgent.new # No think_in specified, defaults to 'en'
|
57
|
+
result = agent.run("ๅนซๆ่จ็ฎ 123 + 456 ็็ตๆ")
|
58
|
+
puts "Answer: #{result.final_answer}"
|
59
|
+
puts
|
60
|
+
|
61
|
+
# Example 2: Explicit Japanese thinking
|
62
|
+
# Specifying think_in='ja-JP' makes the agent think in Japanese
|
63
|
+
# This can improve reasoning quality for Japanese cultural contexts
|
64
|
+
puts "2. English Input with Japanese Thinking:"
|
65
|
+
agent = MultilingualAgent.new(think_in: 'ja-JP')
|
66
|
+
result = agent.run("Calculate the sum of 789 and 321")
|
67
|
+
puts "Answer: #{result.final_answer}"
|
68
|
+
puts
|
69
|
+
|
70
|
+
# Example 3: DSL configuration for Korean thinking
|
71
|
+
# Using the DSL to set think_in at the class level
|
72
|
+
# All instances of KoreanAgent will think in Korean by default
|
73
|
+
puts "3. DSL Configuration (Korean thinking):"
|
74
|
+
class KoreanAgent < MultilingualAgent
|
75
|
+
think_in 'ko-KR' # Class-level configuration
|
76
|
+
end
|
77
|
+
|
78
|
+
agent = KoreanAgent.new
|
79
|
+
result = agent.run("What is 1000 minus 250?")
|
80
|
+
puts "Answer: #{result.final_answer}"
|
81
|
+
puts
|
82
|
+
|
83
|
+
# Example 4: Show the thinking process
|
84
|
+
puts "4. Showing Thinking Process:"
|
85
|
+
agent = MultilingualAgent.new(think_in: 'zh-TW')
|
86
|
+
result = agent.run("่ซ่จ็ฎ 50 ไนไปฅ 8") do |event|
|
87
|
+
case event.type
|
88
|
+
when :thought
|
89
|
+
puts "ๆ่: #{event.content}"
|
90
|
+
when :action
|
91
|
+
puts "่กๅ: ไฝฟ็จๅทฅๅ
ท #{event.content[:tool]}"
|
92
|
+
when :observation
|
93
|
+
puts "่งๅฏ: #{event.content}"
|
94
|
+
when :final_answer
|
95
|
+
puts "ๆ็ต็ญๆก: #{event.content}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
puts
|
99
|
+
|
100
|
+
# Example 5: Multiple languages in sequence
|
101
|
+
puts "5. Multiple Languages:"
|
102
|
+
languages = {
|
103
|
+
'en' => "Calculate 100 divided by 4",
|
104
|
+
'zh-TW' => "่ซ่จ็ฎ 100 ้คไปฅ 4",
|
105
|
+
'ja-JP' => "100ใ4ใงๅฒใฃใ็ตๆใ่จ็ฎใใฆใใ ใใ",
|
106
|
+
'es' => "Calcula 100 dividido por 4"
|
107
|
+
}
|
108
|
+
|
109
|
+
languages.each do |lang, question|
|
110
|
+
agent = MultilingualAgent.new(think_in: lang)
|
111
|
+
result = agent.run(question)
|
112
|
+
puts "#{lang}: #{result.final_answer}"
|
113
|
+
end
|
@@ -4,6 +4,7 @@
|
|
4
4
|
require 'bundler/setup'
|
5
5
|
require 'soka'
|
6
6
|
require 'dotenv/load'
|
7
|
+
require 'dentaku'
|
7
8
|
|
8
9
|
# Configure Soka
|
9
10
|
Soka.setup do |config|
|
@@ -21,14 +22,15 @@ end
|
|
21
22
|
|
22
23
|
# Simple calculation tool
|
23
24
|
class CalculatorTool < Soka::AgentTool
|
24
|
-
desc 'Perform calculations'
|
25
|
+
desc 'Perform calculations using Dentaku (safe math evaluator)'
|
25
26
|
|
26
27
|
params do
|
27
|
-
requires :expression, String, desc: 'Mathematical expression
|
28
|
+
requires :expression, String, desc: 'Mathematical expression (use ^ for power, e.g., "2^3" = 8)'
|
28
29
|
end
|
29
30
|
|
30
31
|
def call(expression:)
|
31
|
-
|
32
|
+
calculator = Dentaku::Calculator.new
|
33
|
+
result = calculator.evaluate(expression)
|
32
34
|
"Result: #{expression} = #{result}"
|
33
35
|
rescue StandardError => e
|
34
36
|
"Error: #{e.message}"
|
@@ -109,7 +111,6 @@ puts '-' * 50
|
|
109
111
|
|
110
112
|
result = agent.run('Calculate the area of a circle with radius 5')
|
111
113
|
puts "Direct result: #{result.final_answer}"
|
112
|
-
puts "Confidence: #{(result.confidence_score * 100).round(1)}%"
|
113
114
|
puts "Iterations: #{result.iterations}"
|
114
115
|
|
115
116
|
puts "\n=== Event Handling Benefits ==="
|
data/examples/3_memory.rb
CHANGED
@@ -5,6 +5,7 @@ require 'bundler/setup'
|
|
5
5
|
require 'soka'
|
6
6
|
require 'dotenv/load'
|
7
7
|
require 'singleton'
|
8
|
+
require 'dentaku'
|
8
9
|
|
9
10
|
# Configure Soka
|
10
11
|
Soka.setup do |config|
|
@@ -29,8 +30,9 @@ class CalculatorTool < Soka::AgentTool
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def call(expression:)
|
32
|
-
#
|
33
|
-
|
33
|
+
# Safe math expression evaluation using Dentaku
|
34
|
+
calculator = Dentaku::Calculator.new
|
35
|
+
result = calculator.evaluate(expression)
|
34
36
|
"Calculation result: #{expression} = #{result}"
|
35
37
|
rescue StandardError => e
|
36
38
|
"Calculation error: #{e.message}"
|
data/examples/4_hooks.rb
CHANGED
@@ -59,7 +59,6 @@ class HookedAgent < Soka::Agent
|
|
59
59
|
puts "๐ข [AFTER] Request completed"
|
60
60
|
puts "๐ข [AFTER] Final answer: #{result.final_answer[0..100]}..."
|
61
61
|
puts "๐ข [AFTER] Iterations: #{result.iterations}"
|
62
|
-
puts "๐ข [AFTER] Confidence: #{(result.confidence_score * 100).round(1)}%"
|
63
62
|
puts "๐ข [AFTER] Memory size: #{@memory.messages.size} messages"
|
64
63
|
end
|
65
64
|
|