ai-agents 0.2.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/docs/concepts/agents.md +3 -1
- data/docs/concepts/context.md +2 -2
- data/docs/concepts/runner.md +1 -1
- data/docs/concepts/tools.md +19 -33
- data/docs/guides/multi-agent-systems.md +1 -1
- data/docs/guides/rails-integration.md +3 -3
- data/docs/guides/state-persistence.md +4 -6
- data/docs/guides.md +1 -1
- data/examples/isp-support/agents_factory.rb +6 -3
- data/lib/agents/agent.rb +7 -3
- data/lib/agents/chat.rb +4 -1
- data/lib/agents/runner.rb +1 -0
- data/lib/agents/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71f4852e0a5f1048dfeefa4b3ab678de189977940330f81425b60f687e66adc0
|
4
|
+
data.tar.gz: 5661b109441687dbdba66208c71798e91bbd338e857619fcc3b634b4542662cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8070c4bc7fd3d9aa9477b2566d0e96f44166c8320b12cdf50017c9c19081ef539a7c20b369714afc0fb468f7c767fcb8f50ff52aae3491357bee69971c99a531
|
7
|
+
data.tar.gz: 0d11ee7a275f5a240ceaf695ac3a3a1aea196aaa452f6fb38cb79ca8e411fd278b9fa526e3a48911ec856c96d84d2cc565ff5f7359771d3a2057ecbdf6b4f675
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.3.0] - 2025-07-22
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Temperature control for agent responses
|
12
|
+
- Added `temperature` parameter to `Agent#initialize` with default value of 0.7
|
13
|
+
- Temperature controls randomness in LLM responses (0.0 = deterministic, 1.0 = very random)
|
14
|
+
- Temperature is passed through to underlying Chat instance for model configuration
|
15
|
+
- Agent cloning supports temperature overrides via `clone(temperature: value)`
|
16
|
+
|
8
17
|
## [0.2.2] - 2025-07-14
|
9
18
|
|
10
19
|
### Added
|
data/docs/concepts/agents.md
CHANGED
@@ -21,6 +21,7 @@ This project is in early development. While thread safety is a core design goal
|
|
21
21
|
* **`model`**: The language model the agent will use (e.g., `"gpt-4.1-mini"`).
|
22
22
|
* **`tools`**: An array of `Agents::Tool` instances that the agent can use to perform actions.
|
23
23
|
* **`handoff_agents`**: An array of other agents that this agent can hand off conversations to.
|
24
|
+
* **`temperature`**: Controls randomness in responses (0.0 = deterministic, 1.0 = very random, default: 0.7)
|
24
25
|
|
25
26
|
### Example
|
26
27
|
|
@@ -30,7 +31,8 @@ assistant_agent = Agents::Agent.new(
|
|
30
31
|
name: "Assistant",
|
31
32
|
instructions: "You are a helpful assistant.",
|
32
33
|
model: "gpt-4.1-mini",
|
33
|
-
tools: [CalculatorTool.new]
|
34
|
+
tools: [CalculatorTool.new],
|
35
|
+
temperature: 0.3
|
34
36
|
)
|
35
37
|
|
36
38
|
# Create a specialized agent by cloning the base agent
|
data/docs/concepts/context.md
CHANGED
@@ -19,7 +19,7 @@ The main context hash that persists across sessions:
|
|
19
19
|
context = {
|
20
20
|
user_id: 123,
|
21
21
|
conversation_history: [...],
|
22
|
-
|
22
|
+
current_agent: "Billing",
|
23
23
|
state: { customer_tier: "premium" } # Tools use this nested hash for persistent data
|
24
24
|
}
|
25
25
|
```
|
@@ -69,7 +69,7 @@ runner = Agents::Runner.with_agents(triage_agent, billing_agent)
|
|
69
69
|
# First interaction
|
70
70
|
result1 = runner.run("I need billing help")
|
71
71
|
# Triage agent hands off to billing agent
|
72
|
-
# Context includes:
|
72
|
+
# Context includes: current_agent: "Billing"
|
73
73
|
|
74
74
|
# Continue conversation
|
75
75
|
result2 = runner.run("What payment methods do you accept?", context: result1.context)
|
data/docs/concepts/runner.md
CHANGED
@@ -84,4 +84,4 @@ result1 = runner.run("I need help with my bill")
|
|
84
84
|
result2 = runner.run("What payment methods do you accept?", context: result1.context)
|
85
85
|
```
|
86
86
|
|
87
|
-
The `run` method returns a `RunResult` with output,
|
87
|
+
The `run` method returns a `RunResult` with output, messages, usage metrics, error status, and updated context.
|
data/docs/concepts/tools.md
CHANGED
@@ -27,36 +27,22 @@ By passing all the necessary data through the `ToolContext`, we ensure that tool
|
|
27
27
|
|
28
28
|
## Creating a Tool
|
29
29
|
|
30
|
-
You
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
2. **Using the Functional Tool Definition:** For simpler tools, you can use the `Agents::Tool.tool` helper to define a tool with a block.
|
51
|
-
|
52
|
-
```ruby
|
53
|
-
calculator_tool = Agents::Tool.tool(
|
54
|
-
"calculate",
|
55
|
-
description: "Perform a mathematical calculation."
|
56
|
-
) do |tool_context, expression:|
|
57
|
-
# Perform the calculation and return the result
|
58
|
-
eval(expression).to_s
|
59
|
-
end
|
60
|
-
```
|
61
|
-
|
62
|
-
In both cases, the `perform` method receives the `tool_context` and the tool's parameters as arguments. This design ensures that your tools are always thread-safe and easy to test.
|
30
|
+
You create tools by creating a class that inherits from `Agents::Tool` and implements the `perform` method:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class WeatherTool < Agents::Tool
|
34
|
+
name "get_weather"
|
35
|
+
description "Get the current weather for a location."
|
36
|
+
param :location, type: "string", desc: "The city and state, e.g., San Francisco, CA"
|
37
|
+
|
38
|
+
def perform(tool_context, location:)
|
39
|
+
# Access the API key from the shared context
|
40
|
+
api_key = tool_context.context[:weather_api_key]
|
41
|
+
|
42
|
+
# Call the weather API and return the result
|
43
|
+
WeatherApi.get(location, api_key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
The `perform` method receives the `tool_context` and the tool's parameters as arguments. This design ensures that your tools are always thread-safe and easy to test.
|
@@ -210,7 +210,7 @@ RSpec.describe "Customer Support Workflow" do
|
|
210
210
|
result = runner.run("I have a billing question")
|
211
211
|
|
212
212
|
# Verify handoff occurred
|
213
|
-
expect(result.context
|
213
|
+
expect(result.context[:current_agent]).to eq("Billing")
|
214
214
|
|
215
215
|
# Test continued conversation
|
216
216
|
followup = runner.run("What are your payment terms?", context: result.context)
|
@@ -85,7 +85,7 @@ class Conversation < ApplicationRecord
|
|
85
85
|
create!(
|
86
86
|
user: user,
|
87
87
|
context: result.context.to_h,
|
88
|
-
current_agent: result.context
|
88
|
+
current_agent: result.context[:current_agent]
|
89
89
|
)
|
90
90
|
end
|
91
91
|
end
|
@@ -209,7 +209,7 @@ class AgentConversationsController < ApplicationController
|
|
209
209
|
|
210
210
|
render json: {
|
211
211
|
response: result.output,
|
212
|
-
agent: result.context
|
212
|
+
agent: result.context[:current_agent],
|
213
213
|
conversation_id: result.context[:conversation_id]
|
214
214
|
}
|
215
215
|
rescue => e
|
@@ -313,7 +313,7 @@ class AgentConversationJob < ApplicationJob
|
|
313
313
|
"agent_conversation_#{user_id}",
|
314
314
|
{
|
315
315
|
response: result.output,
|
316
|
-
agent: result.context
|
316
|
+
agent: result.context[:current_agent],
|
317
317
|
conversation_id: conversation_id
|
318
318
|
}
|
319
319
|
)
|
@@ -183,7 +183,7 @@ class ContextCleaner
|
|
183
183
|
# Limit total context size
|
184
184
|
if cleaned.keys.size > MAX_CONTEXT_KEYS
|
185
185
|
# Keep essential keys, remove extras
|
186
|
-
essential_keys = [:user_id, :
|
186
|
+
essential_keys = [:user_id, :current_agent, :conversation_history]
|
187
187
|
extra_keys = cleaned.keys - essential_keys
|
188
188
|
extra_keys.first(cleaned.keys.size - MAX_CONTEXT_KEYS).each do |key|
|
189
189
|
cleaned.delete(key)
|
@@ -292,7 +292,7 @@ class VersionedContext
|
|
292
292
|
{
|
293
293
|
version: index,
|
294
294
|
timestamp: context[:updated_at],
|
295
|
-
agent: context[:
|
295
|
+
agent: context[:current_agent]
|
296
296
|
}
|
297
297
|
end
|
298
298
|
end
|
@@ -409,12 +409,10 @@ class ContextMigrator
|
|
409
409
|
private
|
410
410
|
|
411
411
|
def self.migrate_v1_to_v2(context)
|
412
|
-
# V1 -> V2: Rename 'current_agent' to '
|
412
|
+
# V1 -> V2: Rename 'current_agent' to 'current_agent' (no change needed)
|
413
413
|
migrated = context.deep_dup
|
414
414
|
|
415
|
-
|
416
|
-
migrated[:current_agent_name] = migrated.delete(:current_agent)
|
417
|
-
end
|
415
|
+
# No migration needed - current_agent is already the correct field name
|
418
416
|
|
419
417
|
migrated[:_version] = 2
|
420
418
|
migrated
|
data/docs/guides.md
CHANGED
@@ -44,7 +44,8 @@ module ISPSupport
|
|
44
44
|
name: "Triage Agent",
|
45
45
|
instructions: triage_instructions,
|
46
46
|
model: "gpt-4.1-mini",
|
47
|
-
tools: []
|
47
|
+
tools: [],
|
48
|
+
temperature: 0.3 # Lower temperature for consistent routing decisions
|
48
49
|
)
|
49
50
|
end
|
50
51
|
|
@@ -53,7 +54,8 @@ module ISPSupport
|
|
53
54
|
name: "Sales Agent",
|
54
55
|
instructions: sales_instructions_with_state,
|
55
56
|
model: "gpt-4.1-mini",
|
56
|
-
tools: [ISPSupport::CreateLeadTool.new, ISPSupport::CreateCheckoutTool.new]
|
57
|
+
tools: [ISPSupport::CreateLeadTool.new, ISPSupport::CreateCheckoutTool.new],
|
58
|
+
temperature: 0.8 # Higher temperature for more persuasive, varied sales language
|
57
59
|
)
|
58
60
|
end
|
59
61
|
|
@@ -66,7 +68,8 @@ module ISPSupport
|
|
66
68
|
ISPSupport::CrmLookupTool.new,
|
67
69
|
ISPSupport::SearchDocsTool.new,
|
68
70
|
ISPSupport::EscalateToHumanTool.new
|
69
|
-
]
|
71
|
+
],
|
72
|
+
temperature: 0.5 # Balanced temperature for helpful but consistent technical support
|
70
73
|
)
|
71
74
|
end
|
72
75
|
|
data/lib/agents/agent.rb
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
# )
|
34
34
|
module Agents
|
35
35
|
class Agent
|
36
|
-
attr_reader :name, :instructions, :model, :tools, :handoff_agents
|
36
|
+
attr_reader :name, :instructions, :model, :tools, :handoff_agents, :temperature
|
37
37
|
|
38
38
|
# Initialize a new Agent instance
|
39
39
|
#
|
@@ -42,12 +42,14 @@ module Agents
|
|
42
42
|
# @param model [String] The LLM model to use (default: "gpt-4.1-mini")
|
43
43
|
# @param tools [Array<Agents::Tool>] Array of tool instances the agent can use
|
44
44
|
# @param handoff_agents [Array<Agents::Agent>] Array of agents this agent can hand off to
|
45
|
-
|
45
|
+
# @param temperature [Float] Controls randomness in responses (0.0 = deterministic, 1.0 = very random, default: 0.7)
|
46
|
+
def initialize(name:, instructions: nil, model: "gpt-4.1-mini", tools: [], handoff_agents: [], temperature: 0.7)
|
46
47
|
@name = name
|
47
48
|
@instructions = instructions
|
48
49
|
@model = model
|
49
50
|
@tools = tools.dup
|
50
51
|
@handoff_agents = []
|
52
|
+
@temperature = temperature
|
51
53
|
|
52
54
|
# Mutex for thread-safe handoff registration
|
53
55
|
# While agents are typically configured at startup, we want to ensure
|
@@ -131,6 +133,7 @@ module Agents
|
|
131
133
|
# @option changes [String] :model New model identifier
|
132
134
|
# @option changes [Array<Agents::Tool>] :tools New tools array (replaces all tools)
|
133
135
|
# @option changes [Array<Agents::Agent>] :handoff_agents New handoff agents
|
136
|
+
# @option changes [Float] :temperature Temperature for LLM responses (0.0-1.0)
|
134
137
|
# @return [Agents::Agent] A new frozen agent instance with the specified changes
|
135
138
|
def clone(**changes)
|
136
139
|
self.class.new(
|
@@ -138,7 +141,8 @@ module Agents
|
|
138
141
|
instructions: changes.fetch(:instructions, @instructions),
|
139
142
|
model: changes.fetch(:model, @model),
|
140
143
|
tools: changes.fetch(:tools, @tools.dup),
|
141
|
-
handoff_agents: changes.fetch(:handoff_agents, @handoff_agents)
|
144
|
+
handoff_agents: changes.fetch(:handoff_agents, @handoff_agents),
|
145
|
+
temperature: changes.fetch(:temperature, @temperature)
|
142
146
|
)
|
143
147
|
end
|
144
148
|
|
data/lib/agents/chat.rb
CHANGED
@@ -26,11 +26,14 @@ module Agents
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def initialize(model: nil, handoff_tools: [], context_wrapper: nil, **options)
|
29
|
+
def initialize(model: nil, handoff_tools: [], context_wrapper: nil, temperature: nil, **options)
|
30
30
|
super(model: model, **options)
|
31
31
|
@handoff_tools = handoff_tools
|
32
32
|
@context_wrapper = context_wrapper
|
33
33
|
|
34
|
+
# Set temperature if provided (RubyLLM::Chat sets this via accessor)
|
35
|
+
@temperature = temperature if temperature
|
36
|
+
|
34
37
|
# Register handoff tools with RubyLLM for schema generation
|
35
38
|
@handoff_tools.each { |tool| with_tool(tool) }
|
36
39
|
end
|
data/lib/agents/runner.rb
CHANGED
@@ -246,6 +246,7 @@ module Agents
|
|
246
246
|
# Create extended chat with handoff awareness and context
|
247
247
|
chat = Agents::Chat.new(
|
248
248
|
model: agent.model,
|
249
|
+
temperature: agent.temperature,
|
249
250
|
handoff_tools: handoff_tools, # Direct tools, no wrapper
|
250
251
|
context_wrapper: context_wrapper # Pass context directly
|
251
252
|
)
|
data/lib/agents/version.rb
CHANGED