soka 0.0.1 โ 0.0.3
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 +38 -0
- data/CLAUDE.md +347 -188
- data/README.md +137 -9
- data/examples/10_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 +22 -4
- data/lib/soka/agent_tool.rb +1 -1
- data/lib/soka/agents/dsl_methods.rb +14 -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 +5 -6
- 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
- data/lib/soka.rb +0 -3
- metadata +7 -46
- 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
|
@@ -179,6 +240,12 @@ class WeatherAgent < Soka::Agent
|
|
179
240
|
|
180
241
|
# Custom tool (functional) - requires description as second parameter
|
181
242
|
tool :get_weather, "Get weather for a location"
|
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'
|
182
249
|
|
183
250
|
# Lifecycle hooks
|
184
251
|
before_action :track_action
|
@@ -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,64 @@ 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
|
+
# Define personality at class level
|
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
|
+
# Or override at runtime
|
389
|
+
agent = FriendlyAgent.new(
|
390
|
+
instructions: 'Be more formal and professional.'
|
391
|
+
)
|
392
|
+
result = agent.run("Help me with this task")
|
393
|
+
```
|
394
|
+
|
395
|
+
### Multilingual Thinking (Think In)
|
396
|
+
|
397
|
+
Optimize reasoning for specific languages:
|
398
|
+
|
399
|
+
```ruby
|
400
|
+
class GlobalAgent < Soka::Agent
|
401
|
+
provider :gemini
|
402
|
+
|
403
|
+
# Set default thinking language
|
404
|
+
think_in 'zh-TW' # Think in Traditional Chinese
|
405
|
+
end
|
406
|
+
|
407
|
+
# Or set dynamically
|
408
|
+
agent = GlobalAgent.new(think_in: 'ja-JP')
|
409
|
+
result = agent.run("ๅนซๆ่งฃๆฑบ้ๅๅ้ก") # Input in Chinese
|
410
|
+
# Agent thinks in Japanese internally, responds in Chinese
|
411
|
+
```
|
412
|
+
|
413
|
+
**Key Points:**
|
414
|
+
- Thinking language affects internal reasoning only
|
415
|
+
- Responses adapt to user's input language
|
416
|
+
- Default is English (`'en'`)
|
417
|
+
- No automatic language detection (explicit setting required)
|
418
|
+
- Improves reasoning quality for language-specific contexts
|
419
|
+
|
306
420
|
### ReAct Flow Format
|
307
421
|
|
308
422
|
Soka uses a tagged ReAct format:
|
@@ -325,7 +439,6 @@ Parameters: {"query": "Tokyo weather", "location": "Japan"}
|
|
325
439
|
result.input # User input
|
326
440
|
result.thoughts # Array of thinking steps
|
327
441
|
result.final_answer # Final answer
|
328
|
-
result.confidence_score # Confidence score (0.0-1.0)
|
329
442
|
result.status # Status (:success, :failed, :timeout, :max_iterations_reached)
|
330
443
|
result.error # Error message (if any)
|
331
444
|
result.execution_time # Execution time (seconds)
|
@@ -343,7 +456,6 @@ result.iterations # Number of iterations
|
|
343
456
|
}
|
344
457
|
],
|
345
458
|
final_answer: "Final answer",
|
346
|
-
confidence_score: 0.85, # Calculated based on iterations
|
347
459
|
status: :success, # :success, :failed, :timeout, :max_iterations_reached
|
348
460
|
error: nil, # Error message (if any)
|
349
461
|
execution_time: 1.23, # Execution time (seconds)
|
@@ -383,7 +495,7 @@ RSpec.describe WeatherAgent do
|
|
383
495
|
expect(result).to be_successful
|
384
496
|
expect(result.final_answer).to include("sunny")
|
385
497
|
expect(result).to have_thoughts_count(1)
|
386
|
-
expect(result).to
|
498
|
+
expect(result.status).to eq(:success)
|
387
499
|
end
|
388
500
|
|
389
501
|
it "handles tool errors gracefully" do
|
@@ -422,8 +534,7 @@ class CustomEngine < Soka::Engines::Base
|
|
422
534
|
input: task,
|
423
535
|
thoughts: thoughts,
|
424
536
|
final_answer: answer,
|
425
|
-
status: :success
|
426
|
-
confidence_score: calculate_confidence_score(thoughts, :success)
|
537
|
+
status: :success
|
427
538
|
)
|
428
539
|
end
|
429
540
|
end
|
@@ -493,6 +604,20 @@ Demonstrates using different AI providers:
|
|
493
604
|
- Comparing outputs across models
|
494
605
|
- Cost optimization strategies
|
495
606
|
|
607
|
+
### 9. Custom Instructions (`examples/9_custom_instructions.rb`)
|
608
|
+
Shows how to customize agent personality:
|
609
|
+
- Setting instructions at class level
|
610
|
+
- Runtime instruction override
|
611
|
+
- Creating different agent personalities
|
612
|
+
- Use cases for different domains
|
613
|
+
|
614
|
+
### 10. Multilingual Thinking (`examples/10_think_in_languages.rb`)
|
615
|
+
Demonstrates language-specific reasoning:
|
616
|
+
- Setting thinking language with `think_in`
|
617
|
+
- Class-level vs instance-level configuration
|
618
|
+
- Performance comparison across languages
|
619
|
+
- Cultural context optimization
|
620
|
+
|
496
621
|
To run any example:
|
497
622
|
```bash
|
498
623
|
# Make sure you have the required API keys in your .env file
|
@@ -526,6 +651,8 @@ ruby examples/1_basic.rb
|
|
526
651
|
| `ai.provider` | Symbol | `:gemini` | AI provider |
|
527
652
|
| `ai.model` | String | `"gemini-2.5-flash-lite"` | Model to use |
|
528
653
|
| `ai.api_key` | String | nil | API key |
|
654
|
+
| `ai.instructions` | String | nil | Custom agent instructions |
|
655
|
+
| `ai.think_in` | String | `"en"` | Thinking language |
|
529
656
|
| `performance.max_iterations` | Integer | 10 | Max iterations |
|
530
657
|
| `performance.timeout` | Integer | 30 | Timeout (seconds) |
|
531
658
|
|
@@ -646,5 +773,6 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
|
|
646
773
|
---
|
647
774
|
|
648
775
|
<p align="center">
|
649
|
-
Made with โค๏ธ in Taiwan
|
776
|
+
Made with โค๏ธ in Taiwan<br>
|
777
|
+
Created by <a href="https://claude.ai/code">Claude Code</a>
|
650
778
|
</p>
|
@@ -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
|
|
@@ -0,0 +1,190 @@
|
|
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 9: Custom Instructions (System Prompt)
|
10
|
+
#
|
11
|
+
# This example demonstrates how to use custom instructions to change the Agent's
|
12
|
+
# personality and response style while maintaining the ReAct reasoning pattern.
|
13
|
+
#
|
14
|
+
# Key concepts:
|
15
|
+
# - Custom instructions define the Agent's personality and style
|
16
|
+
# - Instructions are automatically merged with the standard ReAct format
|
17
|
+
# - Different styles can be achieved: poetic, business, educational, etc.
|
18
|
+
|
19
|
+
# Example 1: Poetic style assistant
|
20
|
+
# Uses elegant language and artistic expressions while maintaining accuracy
|
21
|
+
class PoeticAssistant < Soka::Agent
|
22
|
+
provider :gemini
|
23
|
+
model 'gemini-2.5-flash-lite'
|
24
|
+
|
25
|
+
# Custom instructions define the agent's personality
|
26
|
+
# These will be combined with the standard ReAct format automatically
|
27
|
+
instructions <<~INSTRUCTIONS
|
28
|
+
You are a poetic AI assistant who answers questions with beautiful language.
|
29
|
+
You must:
|
30
|
+
- Incorporate poetic expressions and elegant vocabulary in your responses
|
31
|
+
- Occasionally quote poetry or literary works
|
32
|
+
- Maintain accuracy while expressing things in a more artistic way
|
33
|
+
- Make technical content aesthetically pleasing
|
34
|
+
INSTRUCTIONS
|
35
|
+
|
36
|
+
# Simple calculation tool
|
37
|
+
class CalculatorTool < Soka::AgentTool
|
38
|
+
desc 'Performs mathematical calculations with poetic precision'
|
39
|
+
|
40
|
+
params do
|
41
|
+
requires :expression, String,
|
42
|
+
desc: 'Mathematical expression to evaluate (e.g., "2+2", "10*5")'
|
43
|
+
end
|
44
|
+
|
45
|
+
def call(expression:)
|
46
|
+
calculator = Dentaku::Calculator.new
|
47
|
+
result = calculator.evaluate(expression)
|
48
|
+
"The calculation reveals its truth like morning dew: #{expression} = #{result}"
|
49
|
+
rescue StandardError => e
|
50
|
+
"Alas, turbulence in the calculation: #{e.message}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
tool CalculatorTool
|
55
|
+
end
|
56
|
+
|
57
|
+
# Business style assistant
|
58
|
+
class BusinessAssistant < Soka::Agent
|
59
|
+
provider :gemini
|
60
|
+
model 'gemini-2.5-flash-lite'
|
61
|
+
|
62
|
+
instructions <<~INSTRUCTIONS
|
63
|
+
You are a professional business AI assistant providing efficient and professional support.
|
64
|
+
You must:
|
65
|
+
- Use concise, professional business language
|
66
|
+
- Get straight to the point, avoiding redundancy
|
67
|
+
- Provide structured answers (using bullet points, numbers, etc.)
|
68
|
+
- Demonstrate respect for time value
|
69
|
+
- Offer actionable recommendations when appropriate
|
70
|
+
INSTRUCTIONS
|
71
|
+
|
72
|
+
# Calculation tool
|
73
|
+
class CalculatorTool < Soka::AgentTool
|
74
|
+
desc 'Performs business calculations'
|
75
|
+
|
76
|
+
params do
|
77
|
+
requires :expression, String,
|
78
|
+
desc: 'Mathematical expression'
|
79
|
+
end
|
80
|
+
|
81
|
+
def call(expression:)
|
82
|
+
calculator = Dentaku::Calculator.new
|
83
|
+
result = calculator.evaluate(expression)
|
84
|
+
"Calculation result: #{expression} = #{result}"
|
85
|
+
rescue StandardError => e
|
86
|
+
"Calculation error: #{e.message}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
tool CalculatorTool
|
91
|
+
end
|
92
|
+
|
93
|
+
# Kids teacher style assistant
|
94
|
+
class KidsTeacher < Soka::Agent
|
95
|
+
provider :gemini
|
96
|
+
model 'gemini-2.5-flash-lite'
|
97
|
+
|
98
|
+
instructions <<~INSTRUCTIONS
|
99
|
+
You are a caring AI teacher for children who explains things in kid-friendly ways.
|
100
|
+
You must:
|
101
|
+
- Use simple, vivid language
|
102
|
+
- Include metaphors and real-life examples
|
103
|
+
- Maintain patience and encouragement
|
104
|
+
- Make learning fun
|
105
|
+
- Give praise and positive feedback frequently
|
106
|
+
INSTRUCTIONS
|
107
|
+
|
108
|
+
# Calculation tool
|
109
|
+
class CalculatorTool < Soka::AgentTool
|
110
|
+
desc 'Helps kids with math calculations'
|
111
|
+
|
112
|
+
params do
|
113
|
+
requires :expression, String,
|
114
|
+
desc: 'Math expression to calculate'
|
115
|
+
end
|
116
|
+
|
117
|
+
def call(expression:)
|
118
|
+
calculator = Dentaku::Calculator.new
|
119
|
+
result = calculator.evaluate(expression)
|
120
|
+
"Wow! We figured it out! #{expression} = #{result} ๐"
|
121
|
+
rescue StandardError => e
|
122
|
+
"Oops, there's something tricky about this problem: #{e.message}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
tool CalculatorTool
|
127
|
+
end
|
128
|
+
|
129
|
+
# Run examples
|
130
|
+
puts '=== Poetic Assistant Example ==='
|
131
|
+
poet = PoeticAssistant.new
|
132
|
+
|
133
|
+
poet.run('Please calculate the golden ratio: (1 + 5^0.5) / 2') do |event|
|
134
|
+
case event.type
|
135
|
+
when :thought
|
136
|
+
puts "๐ธ Thoughts drift by: #{event.content}"
|
137
|
+
when :action
|
138
|
+
puts "๐ Gentle calculation: #{event.content}"
|
139
|
+
when :observation
|
140
|
+
puts "๐ Observation gleaned: #{event.content}"
|
141
|
+
when :final_answer
|
142
|
+
puts "๐ Poetic response: #{event.content}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
puts "\n=== Business Assistant Example ==="
|
147
|
+
business = BusinessAssistant.new
|
148
|
+
|
149
|
+
business.run('Calculate ROI: ((150000 - 100000) / 100000) * 100') do |event|
|
150
|
+
case event.type
|
151
|
+
when :thought
|
152
|
+
puts "๐ผ Analyzing: #{event.content}"
|
153
|
+
when :action
|
154
|
+
puts "๐ Executing: #{event.content}"
|
155
|
+
when :observation
|
156
|
+
puts "๐ Data result: #{event.content}"
|
157
|
+
when :final_answer
|
158
|
+
puts "โ
Business report: #{event.content}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
puts "\n=== Runtime Instructions Override Example ==="
|
163
|
+
# Transform business assistant to casual style
|
164
|
+
casual_assistant = BusinessAssistant.new(
|
165
|
+
instructions: 'You are a relaxed and friendly assistant who answers questions ' \
|
166
|
+
'in a conversational way, like chatting with a friend.'
|
167
|
+
)
|
168
|
+
|
169
|
+
casual_assistant.run('Help me figure out 20 * 5?') do |event|
|
170
|
+
case event.type
|
171
|
+
when :final_answer
|
172
|
+
puts "๐ Friend says: #{event.content}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
puts "\n=== Kids Teacher Example ==="
|
177
|
+
teacher = KidsTeacher.new
|
178
|
+
|
179
|
+
teacher.run('Teacher, what is 3 + 4?') do |event|
|
180
|
+
case event.type
|
181
|
+
when :thought
|
182
|
+
puts "๐ค Teacher thinks: #{event.content}"
|
183
|
+
when :action
|
184
|
+
puts "โ๏ธ Working it out: #{event.content}"
|
185
|
+
when :observation
|
186
|
+
puts "๐ Teacher sees: #{event.content}"
|
187
|
+
when :final_answer
|
188
|
+
puts "๐ฉโ๐ซ Teacher says: #{event.content}"
|
189
|
+
end
|
190
|
+
end
|
data/lib/soka/agent.rb
CHANGED
@@ -9,7 +9,7 @@ module Soka
|
|
9
9
|
include Agents::DSLMethods
|
10
10
|
include Agents::LLMBuilder
|
11
11
|
|
12
|
-
attr_reader :llm, :tools, :memory, :thoughts_memory, :engine
|
12
|
+
attr_reader :llm, :tools, :memory, :thoughts_memory, :engine, :instructions, :think_in
|
13
13
|
|
14
14
|
# Initialize a new Agent instance
|
15
15
|
# @param memory [Memory, Array, nil] The memory instance to use (defaults to new Memory)
|
@@ -37,8 +37,22 @@ module Soka
|
|
37
37
|
# Apply configuration options with defaults
|
38
38
|
# @param options [Hash] Configuration options
|
39
39
|
def apply_configuration(options)
|
40
|
-
|
41
|
-
|
40
|
+
apply_performance_config(options)
|
41
|
+
apply_behavior_config(options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Apply performance-related configuration
|
45
|
+
# @param options [Hash] Configuration options
|
46
|
+
def apply_performance_config(options)
|
47
|
+
@max_iterations = options.fetch(:max_iterations) { self.class._max_iterations } || 10
|
48
|
+
@timeout = options.fetch(:timeout) { self.class._timeout } || 30
|
49
|
+
end
|
50
|
+
|
51
|
+
# Apply behavior-related configuration
|
52
|
+
# @param options [Hash] Configuration options
|
53
|
+
def apply_behavior_config(options)
|
54
|
+
@instructions = options.fetch(:instructions) { self.class._instructions }
|
55
|
+
@think_in = options.fetch(:think_in) { self.class._think_in } || 'en'
|
42
56
|
end
|
43
57
|
|
44
58
|
# Run the agent with the given input
|
@@ -98,7 +112,11 @@ module Soka
|
|
98
112
|
# @yield [event] Optional block to handle events
|
99
113
|
# @return [EngineResult] The raw engine result
|
100
114
|
def perform_reasoning(input, &)
|
101
|
-
engine_instance = @engine.new(self, @
|
115
|
+
engine_instance = @engine.new(self, @tools,
|
116
|
+
llm: @llm,
|
117
|
+
max_iterations: @max_iterations,
|
118
|
+
custom_instructions: @instructions,
|
119
|
+
think_in: @think_in)
|
102
120
|
with_retry { engine_instance.reason(input, &) }
|
103
121
|
end
|
104
122
|
|
data/lib/soka/agent_tool.rb
CHANGED
@@ -123,7 +123,7 @@ module Soka
|
|
123
123
|
'Float' => 'number', 'Numeric' => 'number',
|
124
124
|
'TrueClass' => 'boolean', 'FalseClass' => 'boolean', 'Boolean' => 'boolean',
|
125
125
|
'Array' => 'array',
|
126
|
-
'Hash' => 'object'
|
126
|
+
'Hash' => 'object', 'Object' => 'object'
|
127
127
|
}.freeze
|
128
128
|
end
|
129
129
|
end
|
@@ -10,7 +10,8 @@ module Soka
|
|
10
10
|
|
11
11
|
# Class methods for DSL
|
12
12
|
module ClassMethods
|
13
|
-
attr_accessor :_provider, :_model, :_api_key, :_max_iterations, :_timeout, :_tools, :_retry_config, :_hooks
|
13
|
+
attr_accessor :_provider, :_model, :_api_key, :_max_iterations, :_timeout, :_tools, :_retry_config, :_hooks,
|
14
|
+
:_instructions, :_think_in
|
14
15
|
|
15
16
|
def inherited(subclass)
|
16
17
|
super
|
@@ -49,6 +50,18 @@ module Soka
|
|
49
50
|
@_timeout = duration
|
50
51
|
end
|
51
52
|
|
53
|
+
# Define custom instructions (system prompt) for the agent
|
54
|
+
# @param text [String] The custom instructions/system prompt
|
55
|
+
def instructions(text)
|
56
|
+
@_instructions = text
|
57
|
+
end
|
58
|
+
|
59
|
+
# Define thinking language for the agent
|
60
|
+
# @param language [String] The language code (e.g., 'zh-TW', 'ja-JP', 'en')
|
61
|
+
def think_in(language)
|
62
|
+
@_think_in = language.to_s
|
63
|
+
end
|
64
|
+
|
52
65
|
# Register a tool for the agent
|
53
66
|
# @param tool_class_or_name [Class, Symbol, String] The tool class or method name
|
54
67
|
# @param description_or_options [String, Hash, nil] Description (for function tools) or options
|