ai-agents 0.1.1 → 0.1.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/README.md +29 -106
- data/docs/Gemfile +14 -0
- data/docs/Gemfile.lock +183 -0
- data/docs/_config.yml +53 -0
- data/docs/_sass/color_schemes/ruby.scss +72 -0
- data/docs/_sass/custom/custom.scss +93 -0
- data/docs/architecture.md +353 -0
- data/docs/assets/fonts/InterVariable.woff2 +0 -0
- data/docs/concepts/agent-tool.md +166 -0
- data/docs/concepts/agents.md +43 -0
- data/docs/concepts/context.md +110 -0
- data/docs/concepts/handoffs.md +81 -0
- data/docs/concepts/runner.md +87 -0
- data/docs/concepts/tools.md +62 -0
- data/docs/concepts.md +21 -0
- data/docs/guides/agent-as-tool-pattern.md +242 -0
- data/docs/guides/multi-agent-systems.md +261 -0
- data/docs/guides/rails-integration.md +440 -0
- data/docs/guides/state-persistence.md +451 -0
- data/docs/guides.md +18 -0
- data/docs/index.md +95 -0
- data/examples/collaborative-copilot/README.md +169 -0
- data/examples/collaborative-copilot/agents/analysis_agent.rb +48 -0
- data/examples/collaborative-copilot/agents/answer_suggestion_agent.rb +50 -0
- data/examples/collaborative-copilot/agents/copilot_orchestrator.rb +85 -0
- data/examples/collaborative-copilot/agents/integrations_agent.rb +58 -0
- data/examples/collaborative-copilot/agents/research_agent.rb +52 -0
- data/examples/collaborative-copilot/data/contacts.json +47 -0
- data/examples/collaborative-copilot/data/conversations.json +170 -0
- data/examples/collaborative-copilot/data/knowledge_base.json +58 -0
- data/examples/collaborative-copilot/data/linear_issues.json +83 -0
- data/examples/collaborative-copilot/data/stripe_billing.json +71 -0
- data/examples/collaborative-copilot/interactive.rb +90 -0
- data/examples/collaborative-copilot/tools/create_linear_ticket_tool.rb +58 -0
- data/examples/collaborative-copilot/tools/get_article_tool.rb +41 -0
- data/examples/collaborative-copilot/tools/get_contact_tool.rb +51 -0
- data/examples/collaborative-copilot/tools/get_conversation_tool.rb +53 -0
- data/examples/collaborative-copilot/tools/get_stripe_billing_tool.rb +44 -0
- data/examples/collaborative-copilot/tools/search_contacts_tool.rb +57 -0
- data/examples/collaborative-copilot/tools/search_conversations_tool.rb +54 -0
- data/examples/collaborative-copilot/tools/search_knowledge_base_tool.rb +55 -0
- data/examples/collaborative-copilot/tools/search_linear_issues_tool.rb +60 -0
- data/examples/isp-support/agents_factory.rb +57 -1
- data/examples/isp-support/tools/create_lead_tool.rb +16 -2
- data/examples/isp-support/tools/crm_lookup_tool.rb +13 -1
- data/lib/agents/agent.rb +52 -6
- data/lib/agents/agent_tool.rb +113 -0
- data/lib/agents/handoff.rb +8 -34
- data/lib/agents/tool_context.rb +36 -0
- data/lib/agents/version.rb +1 -1
- data/lib/agents.rb +1 -0
- metadata +44 -2
@@ -0,0 +1,451 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
title: State Persistence
|
4
|
+
parent: Guides
|
5
|
+
nav_order: 3
|
6
|
+
---
|
7
|
+
|
8
|
+
# State Persistence
|
9
|
+
|
10
|
+
The AI Agents library provides flexible mechanisms for persisting state between agent interactions and tool executions. This guide covers context serialization, cross-session persistence, and state management patterns.
|
11
|
+
|
12
|
+
## Context Serialization
|
13
|
+
|
14
|
+
The library's context system is designed to be fully serializable, enabling persistence across process boundaries.
|
15
|
+
|
16
|
+
### Basic Serialization
|
17
|
+
|
18
|
+
Context objects can be converted to and from JSON:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
# Run an agent
|
22
|
+
result = runner.run("Hello, my name is John")
|
23
|
+
|
24
|
+
# Serialize context to JSON
|
25
|
+
context_json = result.context.to_json
|
26
|
+
|
27
|
+
# Later, deserialize and continue conversation
|
28
|
+
restored_context = JSON.parse(context_json, symbolize_names: true)
|
29
|
+
next_result = runner.run("What's my name?", context: restored_context)
|
30
|
+
# => "Your name is John"
|
31
|
+
```
|
32
|
+
|
33
|
+
### Database Storage
|
34
|
+
|
35
|
+
Store context in your database for long-term persistence:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# Store context in database
|
39
|
+
class ConversationState < ActiveRecord::Base
|
40
|
+
def context=(hash)
|
41
|
+
self.context_data = hash.to_json
|
42
|
+
end
|
43
|
+
|
44
|
+
def context
|
45
|
+
JSON.parse(context_data, symbolize_names: true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Usage
|
50
|
+
conversation = ConversationState.create(
|
51
|
+
user_id: user.id,
|
52
|
+
context: result.context.to_h
|
53
|
+
)
|
54
|
+
|
55
|
+
# Restore later
|
56
|
+
restored_context = conversation.context
|
57
|
+
continued_result = runner.run("Continue conversation", context: restored_context)
|
58
|
+
```
|
59
|
+
|
60
|
+
## Cross-Session Persistence
|
61
|
+
|
62
|
+
### Session-Based State
|
63
|
+
|
64
|
+
Maintain state across HTTP requests using sessions:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
class ChatController < ApplicationController
|
68
|
+
def send_message
|
69
|
+
# Retrieve context from session
|
70
|
+
context = session[:agent_context] || {}
|
71
|
+
|
72
|
+
# Run agent
|
73
|
+
result = agent_runner.run(params[:message], context: context)
|
74
|
+
|
75
|
+
# Store updated context in session
|
76
|
+
session[:agent_context] = result.context.to_h
|
77
|
+
|
78
|
+
render json: { response: result.output }
|
79
|
+
end
|
80
|
+
|
81
|
+
def reset_conversation
|
82
|
+
session[:agent_context] = nil
|
83
|
+
render json: { message: "Conversation reset" }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### File-Based Persistence
|
89
|
+
|
90
|
+
For development or simple deployments:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class FileContextPersistence
|
94
|
+
def initialize(storage_path = "./contexts")
|
95
|
+
@storage_path = storage_path
|
96
|
+
FileUtils.mkdir_p(@storage_path)
|
97
|
+
end
|
98
|
+
|
99
|
+
def save_context(user_id, context)
|
100
|
+
file_path = context_file_path(user_id)
|
101
|
+
File.write(file_path, context.to_json)
|
102
|
+
end
|
103
|
+
|
104
|
+
def load_context(user_id)
|
105
|
+
file_path = context_file_path(user_id)
|
106
|
+
return {} unless File.exist?(file_path)
|
107
|
+
|
108
|
+
JSON.parse(File.read(file_path), symbolize_names: true)
|
109
|
+
end
|
110
|
+
|
111
|
+
def delete_context(user_id)
|
112
|
+
file_path = context_file_path(user_id)
|
113
|
+
File.delete(file_path) if File.exist?(file_path)
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def context_file_path(user_id)
|
119
|
+
File.join(@storage_path, "#{user_id}.json")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Usage
|
124
|
+
persistence = FileContextPersistence.new
|
125
|
+
context = persistence.load_context(user.id)
|
126
|
+
|
127
|
+
result = runner.run(message, context: context)
|
128
|
+
persistence.save_context(user.id, result.context.to_h)
|
129
|
+
```
|
130
|
+
|
131
|
+
## State Management Patterns
|
132
|
+
|
133
|
+
### Context Layering
|
134
|
+
|
135
|
+
Organize context data into logical layers:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
def build_layered_context(user, conversation_id)
|
139
|
+
{
|
140
|
+
# User layer - persistent across all conversations
|
141
|
+
user: {
|
142
|
+
id: user.id,
|
143
|
+
name: user.name,
|
144
|
+
preferences: user.preferences
|
145
|
+
},
|
146
|
+
|
147
|
+
# Conversation layer - specific to this conversation
|
148
|
+
conversation: {
|
149
|
+
id: conversation_id,
|
150
|
+
started_at: Time.current,
|
151
|
+
topic: nil
|
152
|
+
},
|
153
|
+
|
154
|
+
# Session layer - temporary data for current session
|
155
|
+
session: {
|
156
|
+
last_activity: Time.current,
|
157
|
+
interaction_count: 0
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
### Context Cleanup
|
164
|
+
|
165
|
+
Prevent context from growing indefinitely:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class ContextCleaner
|
169
|
+
MAX_HISTORY_SIZE = 20
|
170
|
+
MAX_CONTEXT_KEYS = 50
|
171
|
+
|
172
|
+
def self.clean_context(context)
|
173
|
+
cleaned = context.deep_dup
|
174
|
+
|
175
|
+
# Limit conversation history
|
176
|
+
if cleaned[:conversation_history]&.size > MAX_HISTORY_SIZE
|
177
|
+
cleaned[:conversation_history] = cleaned[:conversation_history].last(MAX_HISTORY_SIZE)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Remove old temporary data
|
181
|
+
cleaned.delete(:temp_data) if cleaned[:temp_data]
|
182
|
+
|
183
|
+
# Limit total context size
|
184
|
+
if cleaned.keys.size > MAX_CONTEXT_KEYS
|
185
|
+
# Keep essential keys, remove extras
|
186
|
+
essential_keys = [:user_id, :current_agent_name, :conversation_history]
|
187
|
+
extra_keys = cleaned.keys - essential_keys
|
188
|
+
extra_keys.first(cleaned.keys.size - MAX_CONTEXT_KEYS).each do |key|
|
189
|
+
cleaned.delete(key)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
cleaned
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Use in your service
|
198
|
+
result = runner.run(message, context: context)
|
199
|
+
cleaned_context = ContextCleaner.clean_context(result.context.to_h)
|
200
|
+
save_context(user_id, cleaned_context)
|
201
|
+
```
|
202
|
+
|
203
|
+
## Tool State Management
|
204
|
+
|
205
|
+
### Stateless Tool Design
|
206
|
+
|
207
|
+
Tools should be stateless and rely on context for all data:
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
class DatabaseTool < Agents::Tool
|
211
|
+
name "query_database"
|
212
|
+
description "Query the application database"
|
213
|
+
param :query, type: "string", desc: "SQL query to execute"
|
214
|
+
|
215
|
+
def perform(tool_context, query:)
|
216
|
+
# Get database connection from context, not instance variables
|
217
|
+
db_config = tool_context.context[:database_config]
|
218
|
+
connection = establish_connection(db_config)
|
219
|
+
|
220
|
+
# Execute query and return results
|
221
|
+
connection.execute(query)
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
def establish_connection(config)
|
227
|
+
# Create connection based on config
|
228
|
+
ActiveRecord::Base.establish_connection(config)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
### Tool State Persistence
|
234
|
+
|
235
|
+
Store tool-specific data in context:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
class FileProcessorTool < Agents::Tool
|
239
|
+
name "process_file"
|
240
|
+
description "Process uploaded files"
|
241
|
+
param :file_path, type: "string", desc: "Path to file"
|
242
|
+
|
243
|
+
def perform(tool_context, file_path:)
|
244
|
+
# Initialize tool state in context if needed
|
245
|
+
tool_context.context[:file_processor] ||= {
|
246
|
+
processed_files: [],
|
247
|
+
processing_status: {}
|
248
|
+
}
|
249
|
+
|
250
|
+
# Process file
|
251
|
+
result = process_file(file_path)
|
252
|
+
|
253
|
+
# Update tool state in context
|
254
|
+
tool_context.context[:file_processor][:processed_files] << file_path
|
255
|
+
tool_context.context[:file_processor][:processing_status][file_path] = result[:status]
|
256
|
+
|
257
|
+
result
|
258
|
+
end
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
## Advanced Persistence Patterns
|
263
|
+
|
264
|
+
### Context Versioning
|
265
|
+
|
266
|
+
Track context changes over time:
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
class VersionedContext
|
270
|
+
def initialize(initial_context = {})
|
271
|
+
@versions = [initial_context.deep_dup]
|
272
|
+
@current_version = 0
|
273
|
+
end
|
274
|
+
|
275
|
+
def update_context(new_context)
|
276
|
+
@versions << new_context.deep_dup
|
277
|
+
@current_version = @versions.size - 1
|
278
|
+
end
|
279
|
+
|
280
|
+
def current_context
|
281
|
+
@versions[@current_version]
|
282
|
+
end
|
283
|
+
|
284
|
+
def rollback(versions = 1)
|
285
|
+
target_version = [@current_version - versions, 0].max
|
286
|
+
@current_version = target_version
|
287
|
+
current_context
|
288
|
+
end
|
289
|
+
|
290
|
+
def context_history
|
291
|
+
@versions.map.with_index do |context, index|
|
292
|
+
{
|
293
|
+
version: index,
|
294
|
+
timestamp: context[:updated_at],
|
295
|
+
agent: context[:current_agent_name]
|
296
|
+
}
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
```
|
301
|
+
|
302
|
+
### Context Encryption
|
303
|
+
|
304
|
+
Encrypt sensitive context data:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
class EncryptedContextStorage
|
308
|
+
def initialize(encryption_key)
|
309
|
+
@cipher = OpenSSL::Cipher.new('AES-256-CBC')
|
310
|
+
@key = encryption_key
|
311
|
+
end
|
312
|
+
|
313
|
+
def encrypt_context(context)
|
314
|
+
@cipher.encrypt
|
315
|
+
@cipher.key = @key
|
316
|
+
|
317
|
+
encrypted_data = @cipher.update(context.to_json)
|
318
|
+
encrypted_data << @cipher.final
|
319
|
+
|
320
|
+
Base64.encode64(encrypted_data)
|
321
|
+
end
|
322
|
+
|
323
|
+
def decrypt_context(encrypted_data)
|
324
|
+
@cipher.decrypt
|
325
|
+
@cipher.key = @key
|
326
|
+
|
327
|
+
decoded_data = Base64.decode64(encrypted_data)
|
328
|
+
decrypted_data = @cipher.update(decoded_data)
|
329
|
+
decrypted_data << @cipher.final
|
330
|
+
|
331
|
+
JSON.parse(decrypted_data, symbolize_names: true)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# Usage
|
336
|
+
storage = EncryptedContextStorage.new(Rails.application.secret_key_base)
|
337
|
+
|
338
|
+
# Encrypt before storing
|
339
|
+
encrypted_context = storage.encrypt_context(result.context.to_h)
|
340
|
+
database_record.update(encrypted_context: encrypted_context)
|
341
|
+
|
342
|
+
# Decrypt when loading
|
343
|
+
encrypted_data = database_record.encrypted_context
|
344
|
+
context = storage.decrypt_context(encrypted_data)
|
345
|
+
```
|
346
|
+
|
347
|
+
### Distributed Context Storage
|
348
|
+
|
349
|
+
For multi-server deployments using Redis:
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
class RedisContextStorage
|
353
|
+
def initialize(redis_client = Redis.new)
|
354
|
+
@redis = redis_client
|
355
|
+
end
|
356
|
+
|
357
|
+
def save_context(user_id, context, ttl: 1.hour)
|
358
|
+
key = context_key(user_id)
|
359
|
+
@redis.setex(key, ttl, context.to_json)
|
360
|
+
end
|
361
|
+
|
362
|
+
def load_context(user_id)
|
363
|
+
key = context_key(user_id)
|
364
|
+
data = @redis.get(key)
|
365
|
+
return {} unless data
|
366
|
+
|
367
|
+
JSON.parse(data, symbolize_names: true)
|
368
|
+
end
|
369
|
+
|
370
|
+
def delete_context(user_id)
|
371
|
+
key = context_key(user_id)
|
372
|
+
@redis.del(key)
|
373
|
+
end
|
374
|
+
|
375
|
+
def extend_ttl(user_id, ttl: 1.hour)
|
376
|
+
key = context_key(user_id)
|
377
|
+
@redis.expire(key, ttl)
|
378
|
+
end
|
379
|
+
|
380
|
+
private
|
381
|
+
|
382
|
+
def context_key(user_id)
|
383
|
+
"agent_context:#{user_id}"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
```
|
387
|
+
|
388
|
+
## Context Migration
|
389
|
+
|
390
|
+
Handle context format changes across application versions:
|
391
|
+
|
392
|
+
```ruby
|
393
|
+
class ContextMigrator
|
394
|
+
CURRENT_VERSION = 2
|
395
|
+
|
396
|
+
def self.migrate_context(context)
|
397
|
+
version = context[:_version] || 1
|
398
|
+
|
399
|
+
case version
|
400
|
+
when 1
|
401
|
+
migrate_v1_to_v2(context)
|
402
|
+
when 2
|
403
|
+
context # Already current
|
404
|
+
else
|
405
|
+
raise "Unknown context version: #{version}"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
private
|
410
|
+
|
411
|
+
def self.migrate_v1_to_v2(context)
|
412
|
+
# V1 -> V2: Rename 'current_agent' to 'current_agent_name'
|
413
|
+
migrated = context.deep_dup
|
414
|
+
|
415
|
+
if migrated[:current_agent]
|
416
|
+
migrated[:current_agent_name] = migrated.delete(:current_agent)
|
417
|
+
end
|
418
|
+
|
419
|
+
migrated[:_version] = 2
|
420
|
+
migrated
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Use in context loading
|
425
|
+
def load_context(user_id)
|
426
|
+
raw_context = storage.load_context(user_id)
|
427
|
+
ContextMigrator.migrate_context(raw_context)
|
428
|
+
end
|
429
|
+
```
|
430
|
+
|
431
|
+
## Best Practices
|
432
|
+
|
433
|
+
### Context Size Management
|
434
|
+
- Regularly clean up old conversation history
|
435
|
+
- Remove temporary data after use
|
436
|
+
- Set reasonable size limits for context values
|
437
|
+
|
438
|
+
### Security Considerations
|
439
|
+
- Encrypt sensitive data in persistent storage
|
440
|
+
- Validate context data when loading from external sources
|
441
|
+
- Sanitize context data before serialization
|
442
|
+
|
443
|
+
### Performance Optimization
|
444
|
+
- Use lazy loading for large context objects
|
445
|
+
- Cache frequently accessed context data
|
446
|
+
- Consider context compression for large datasets
|
447
|
+
|
448
|
+
### Error Handling
|
449
|
+
- Always validate context structure after deserialization
|
450
|
+
- Provide fallback default contexts for corrupted data
|
451
|
+
- Log context-related errors for debugging
|
data/docs/guides.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
title: Guides
|
4
|
+
nav_order: 3
|
5
|
+
published: false
|
6
|
+
has_children: true
|
7
|
+
---
|
8
|
+
|
9
|
+
# Guides
|
10
|
+
|
11
|
+
Practical guides for building real-world applications with the AI Agents library. These guides provide step-by-step instructions, code examples, and best practices for common use cases.
|
12
|
+
|
13
|
+
## Available Guides
|
14
|
+
|
15
|
+
- **[Building Multi-Agent Systems](guides/multi-agent-systems.html)** - Design patterns and best practices for creating collaborative agent workflows
|
16
|
+
- **[Agent-as-Tool Pattern](guides/agent-as-tool-pattern.html)** - Enable agent collaboration behind the scenes without conversation handoffs
|
17
|
+
- **[Rails Integration](guides/rails-integration.html)** - Integrating agents with Ruby on Rails applications and ActiveRecord persistence
|
18
|
+
- **[State Persistence](guides/state-persistence.html)** - Managing conversation state and context across sessions and processes
|
data/docs/index.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
title: Home
|
4
|
+
nav_order: 1
|
5
|
+
description: "AI Agents is a Ruby SDK for building multi-agent AI workflows."
|
6
|
+
permalink: /
|
7
|
+
---
|
8
|
+
|
9
|
+
# AI Agents
|
10
|
+
|
11
|
+
A Ruby SDK for building sophisticated multi-agent AI workflows.
|
12
|
+
|
13
|
+
{: .fs-6 .fw-300 }
|
14
|
+
|
15
|
+
[Get started now](#getting-started){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
|
16
|
+
[View it on GitHub](https://github.com/chatwoot/ai-agents){: .btn .fs-5 .mb-4 .mb-md-0 }
|
17
|
+
|
18
|
+
---
|
19
|
+
|
20
|
+
## What is AI Agents?
|
21
|
+
|
22
|
+
AI Agents is a Ruby SDK that enables developers to create sophisticated multi-agent AI workflows. Build specialized AI agents that can collaborate, use tools, and seamlessly hand off conversations to solve complex tasks.
|
23
|
+
|
24
|
+
### Key Features
|
25
|
+
|
26
|
+
- **Multi-Agent Orchestration**: Define and manage multiple AI agents with distinct roles
|
27
|
+
- **Seamless Handoffs**: Transfer conversations between agents without user knowledge
|
28
|
+
- **Tool Integration**: Allow agents to use custom tools to interact with external systems
|
29
|
+
- **Shared Context**: Maintain state and conversation history across agent interactions
|
30
|
+
- **Thread-Safe Architecture**: Reusable agent runners that work safely across multiple threads
|
31
|
+
- **Provider Agnostic**: Support for OpenAI, Anthropic, and Gemini
|
32
|
+
|
33
|
+
## Getting Started
|
34
|
+
|
35
|
+
### Installation
|
36
|
+
|
37
|
+
Add this line to your application's Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'ai-agents'
|
41
|
+
```
|
42
|
+
|
43
|
+
And then execute:
|
44
|
+
|
45
|
+
```bash
|
46
|
+
bundle install
|
47
|
+
```
|
48
|
+
|
49
|
+
Or install it yourself as:
|
50
|
+
|
51
|
+
```bash
|
52
|
+
gem install ai-agents
|
53
|
+
```
|
54
|
+
|
55
|
+
### Quick Start
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
require 'agents'
|
59
|
+
|
60
|
+
# Configure your API keys
|
61
|
+
Agents.configure do |config|
|
62
|
+
config.openai_api_key = ENV['OPENAI_API_KEY']
|
63
|
+
# config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']
|
64
|
+
# config.gemini_api_key = ENV['GEMINI_API_KEY']
|
65
|
+
config.default_model = 'gpt-4o-mini'
|
66
|
+
end
|
67
|
+
|
68
|
+
# Create agents
|
69
|
+
triage = Agents::Agent.new(
|
70
|
+
name: "Triage",
|
71
|
+
instructions: "You help route customer inquiries to the right department."
|
72
|
+
)
|
73
|
+
|
74
|
+
support = Agents::Agent.new(
|
75
|
+
name: "Support",
|
76
|
+
instructions: "You provide technical support for our products."
|
77
|
+
)
|
78
|
+
|
79
|
+
# Set up handoffs
|
80
|
+
triage.register_handoffs(support)
|
81
|
+
|
82
|
+
# Create runner and start conversation
|
83
|
+
runner = Agents::AgentRunner.with_agents(triage, support)
|
84
|
+
result = runner.run("I need help with a technical issue")
|
85
|
+
|
86
|
+
puts result.output
|
87
|
+
```
|
88
|
+
|
89
|
+
## Next Steps
|
90
|
+
|
91
|
+
- [Learn about Agents](concepts/agents.html)
|
92
|
+
- [Understand Context](concepts/context.html)
|
93
|
+
- [Working with Tools](concepts/tools.html)
|
94
|
+
- [Agent Handoffs](concepts/handoffs.html)
|
95
|
+
- [Using the Runner](concepts/runner.html)
|