ai-agents 0.1.2 → 0.2.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/bump-version.md +44 -0
  3. data/CHANGELOG.md +35 -0
  4. data/CLAUDE.md +59 -15
  5. data/README.md +29 -106
  6. data/docs/Gemfile +14 -0
  7. data/docs/Gemfile.lock +183 -0
  8. data/docs/_config.yml +53 -0
  9. data/docs/_sass/color_schemes/ruby.scss +72 -0
  10. data/docs/_sass/custom/custom.scss +93 -0
  11. data/docs/architecture.md +353 -0
  12. data/docs/assets/fonts/InterVariable.woff2 +0 -0
  13. data/docs/concepts/agent-tool.md +166 -0
  14. data/docs/concepts/agents.md +43 -0
  15. data/docs/concepts/callbacks.md +42 -0
  16. data/docs/concepts/context.md +110 -0
  17. data/docs/concepts/handoffs.md +81 -0
  18. data/docs/concepts/runner.md +87 -0
  19. data/docs/concepts/tools.md +62 -0
  20. data/docs/concepts.md +22 -0
  21. data/docs/guides/agent-as-tool-pattern.md +242 -0
  22. data/docs/guides/multi-agent-systems.md +261 -0
  23. data/docs/guides/rails-integration.md +440 -0
  24. data/docs/guides/state-persistence.md +451 -0
  25. data/docs/guides.md +18 -0
  26. data/docs/index.md +97 -0
  27. data/examples/collaborative-copilot/README.md +169 -0
  28. data/examples/collaborative-copilot/agents/analysis_agent.rb +48 -0
  29. data/examples/collaborative-copilot/agents/answer_suggestion_agent.rb +50 -0
  30. data/examples/collaborative-copilot/agents/copilot_orchestrator.rb +85 -0
  31. data/examples/collaborative-copilot/agents/integrations_agent.rb +58 -0
  32. data/examples/collaborative-copilot/agents/research_agent.rb +52 -0
  33. data/examples/collaborative-copilot/data/contacts.json +47 -0
  34. data/examples/collaborative-copilot/data/conversations.json +170 -0
  35. data/examples/collaborative-copilot/data/knowledge_base.json +58 -0
  36. data/examples/collaborative-copilot/data/linear_issues.json +83 -0
  37. data/examples/collaborative-copilot/data/stripe_billing.json +71 -0
  38. data/examples/collaborative-copilot/interactive.rb +90 -0
  39. data/examples/collaborative-copilot/tools/create_linear_ticket_tool.rb +58 -0
  40. data/examples/collaborative-copilot/tools/get_article_tool.rb +41 -0
  41. data/examples/collaborative-copilot/tools/get_contact_tool.rb +51 -0
  42. data/examples/collaborative-copilot/tools/get_conversation_tool.rb +53 -0
  43. data/examples/collaborative-copilot/tools/get_stripe_billing_tool.rb +44 -0
  44. data/examples/collaborative-copilot/tools/search_contacts_tool.rb +57 -0
  45. data/examples/collaborative-copilot/tools/search_conversations_tool.rb +54 -0
  46. data/examples/collaborative-copilot/tools/search_knowledge_base_tool.rb +55 -0
  47. data/examples/collaborative-copilot/tools/search_linear_issues_tool.rb +60 -0
  48. data/examples/isp-support/interactive.rb +43 -4
  49. data/lib/agents/agent.rb +34 -0
  50. data/lib/agents/agent_runner.rb +66 -1
  51. data/lib/agents/agent_tool.rb +113 -0
  52. data/lib/agents/callback_manager.rb +54 -0
  53. data/lib/agents/handoff.rb +8 -34
  54. data/lib/agents/message_extractor.rb +82 -0
  55. data/lib/agents/run_context.rb +5 -2
  56. data/lib/agents/runner.rb +16 -27
  57. data/lib/agents/tool_wrapper.rb +11 -1
  58. data/lib/agents/version.rb +1 -1
  59. data/lib/agents.rb +3 -0
  60. metadata +48 -1
data/docs/_config.yml ADDED
@@ -0,0 +1,53 @@
1
+ title: AI Agents
2
+ description: A Ruby SDK for building multi-agent AI workflows
3
+ baseurl: ""
4
+ url: ""
5
+
6
+ # Theme
7
+ theme: just-the-docs
8
+
9
+ # Color scheme
10
+ color_scheme: ruby
11
+
12
+ # Search
13
+ search_enabled: true
14
+ search:
15
+ heading_level: 2
16
+ previews: 3
17
+ preview_words_before: 5
18
+ preview_words_after: 10
19
+ tokenizer_separator: /[\s/]+/
20
+ rel_url: true
21
+ button: false
22
+
23
+ # Navigation
24
+ nav_sort: case_insensitive
25
+ nav_external_links:
26
+ - title: GitHub
27
+ url: https://github.com/chatwoot/ai-agents
28
+
29
+ # Footer
30
+ footer_content: "Copyright © 2025 Chatwoot Inc."
31
+
32
+ # Plugins
33
+ plugins:
34
+ - jekyll-remote-theme
35
+ - jekyll-seo-tag
36
+ - jekyll-sitemap
37
+
38
+ # Markdown
39
+ markdown: kramdown
40
+ highlighter: rouge
41
+
42
+ # Exclude from processing
43
+ exclude:
44
+ - .sass-cache/
45
+ - .jekyll-cache/
46
+ - gemfiles/
47
+ - Gemfile
48
+ - Gemfile.lock
49
+ - node_modules/
50
+ - vendor/bundle/
51
+ - vendor/cache/
52
+ - vendor/gems/
53
+ - vendor/ruby/
@@ -0,0 +1,72 @@
1
+ // Ruby red color scheme for AI Agents documentation
2
+ // Inspired by Ruby's signature red color
3
+
4
+ // Define red color variables
5
+ $red-000: #fff5f5;
6
+ $red-100: #fed7d7;
7
+ $red-200: #feb2b2;
8
+ $red-300: #fc8181;
9
+ $red-400: #e53e3e;
10
+ $red-500: #d30001; // Primary Ruby red
11
+ $red-600: #b30001;
12
+ $red-700: #930001;
13
+ $red-800: #730001;
14
+ $red-900: #530001;
15
+
16
+ // Grey colors with subtle reddish tint for text and backgrounds
17
+ $grey-dk-000: #959396;
18
+ $grey-dk-100: #5c5962;
19
+ $grey-dk-200: #44434d;
20
+ $grey-dk-250: #302d36;
21
+ $grey-dk-300: #27262b;
22
+
23
+ $grey-lt-000: #faf6f6; // Very slightly reddish background
24
+ $grey-lt-100: #f7f2f2; // Very subtle reddish tint
25
+ $grey-lt-200: #f4efef; // Very subtle reddish tint
26
+ $grey-lt-300: #f0e8e8; // Very subtle reddish tint
27
+
28
+ // Override theme variables
29
+ $link-color: $red-500;
30
+ $btn-primary-color: $red-500;
31
+ $base-button-color: #f7f7f7;
32
+
33
+ // Navigation
34
+ $nav-child-link-color: $grey-dk-100;
35
+ $nav-list-item-color: $grey-dk-100;
36
+ $nav-list-item-active-color: $red-500;
37
+
38
+ // Sidebar background
39
+ $sidebar-color: $grey-lt-100; // Subtle reddish tint for sidebar
40
+
41
+ // Search
42
+ $search-active-color: $red-500;
43
+ $search-background-color: $grey-lt-000;
44
+
45
+ // Code
46
+ $code-background-color: $grey-lt-000;
47
+ $code-border-color: $grey-lt-300;
48
+
49
+ // Tables
50
+ $table-background-color: $grey-lt-000;
51
+ $border-color: $grey-lt-300;
52
+
53
+ // Buttons
54
+ $btn-outline-color: $red-500;
55
+
56
+ // Focus states
57
+ $focus-color: $red-400;
58
+
59
+ // Feedback states
60
+ $feedback-color: $grey-lt-300;
61
+
62
+ // Headings
63
+ $h1-color: $grey-dk-300;
64
+ $h2-color: $grey-dk-300;
65
+ $h3-color: $grey-dk-200;
66
+ $h4-color: $grey-dk-200;
67
+ $h5-color: $grey-dk-200;
68
+ $h6-color: $grey-dk-200;
69
+
70
+ // Body text
71
+ $body-text-color: $grey-dk-100;
72
+ $body-heading-color: $grey-dk-300;
@@ -0,0 +1,93 @@
1
+ // Self-hosted Inter Variable font
2
+ @font-face {
3
+ font-family: "Inter";
4
+ src: url("/assets/fonts/InterVariable.woff2") format("woff2-variations");
5
+ font-weight: 100 900;
6
+ font-style: normal;
7
+ font-display: swap;
8
+ font-named-instance: "Regular";
9
+ }
10
+
11
+ // Override font variables
12
+ $body-font-family:
13
+ "Inter",
14
+ -apple-system,
15
+ BlinkMacSystemFont,
16
+ "Segoe UI",
17
+ Helvetica,
18
+ Arial,
19
+ sans-serif;
20
+ $mono-font-family: "SF Mono", Monaco, Inconsolata, "Roboto Mono", monospace;
21
+
22
+ // Apply Inter to all text elements
23
+ body {
24
+ font-family: $body-font-family;
25
+ font-feature-settings:
26
+ "cv02", "cv03", "cv04", "cv11"; // Display-like features
27
+ }
28
+
29
+ // Ensure headings use Inter as well
30
+ h1,
31
+ h2,
32
+ h3,
33
+ h4,
34
+ h5,
35
+ h6 {
36
+ font-family: $body-font-family;
37
+ }
38
+
39
+ // Custom logo styling
40
+ .site-logo {
41
+ padding: 1rem;
42
+ border-bottom: 1px solid var(--border-color);
43
+
44
+ .site-logo-link {
45
+ display: flex;
46
+ align-items: center;
47
+ text-decoration: none;
48
+ color: inherit;
49
+
50
+ &:hover {
51
+ text-decoration: none;
52
+ }
53
+ }
54
+
55
+ .site-logo-image {
56
+ width: 32px;
57
+ height: 32px;
58
+ margin-right: 0.75rem;
59
+ border-radius: 6px;
60
+ }
61
+
62
+ .site-title {
63
+ font-weight: 600;
64
+ font-size: 1.1rem;
65
+ color: var(--body-heading-color);
66
+ }
67
+ }
68
+
69
+ // Note callout styling
70
+ .note {
71
+ padding: 1rem;
72
+ margin: 1rem 0;
73
+ background-color: #faf6f6;
74
+ border: 1px solid #f0e8e8;
75
+ border-left: 4px solid #d30001;
76
+ border-radius: 6px;
77
+ position: relative;
78
+
79
+ &::before {
80
+ content: "NOTE";
81
+ font-weight: 600;
82
+ font-size: 0.75rem;
83
+ color: #d30001;
84
+ text-transform: uppercase;
85
+ letter-spacing: 0.05em;
86
+ display: block;
87
+ margin-bottom: 0.5rem;
88
+ }
89
+
90
+ p:last-child {
91
+ margin-bottom: 0;
92
+ }
93
+ }
@@ -0,0 +1,353 @@
1
+ ---
2
+ layout: default
3
+ title: Architecture
4
+ nav_order: 4
5
+ published: false
6
+ ---
7
+
8
+ # Architecture
9
+
10
+ The AI Agents library is designed around key principles of immutability, thread safety, and separation of concerns. This architecture enables scalable multi-agent systems that can handle concurrent conversations while maintaining state consistency.
11
+
12
+ ## Core Architecture Principles
13
+
14
+ ### Immutability by Design
15
+ - **Agents are immutable** once created, preventing configuration drift
16
+ - **Context is deep-copied** for each execution to prevent cross-contamination
17
+ - **Registry is frozen** after initialization to prevent runtime modifications
18
+
19
+ ### Thread Safety
20
+ - **No shared mutable state** between concurrent executions
21
+ - **Context isolation** through deep copying and closure capture
22
+ - **Stateless tool design** with context injection via parameters
23
+
24
+ ### Separation of Concerns
25
+ - **Agent definition** (configuration) vs **Agent execution** (runtime)
26
+ - **Tool functionality** vs **Tool context** (execution state)
27
+ - **Conversation orchestration** vs **LLM communication**
28
+
29
+ ## Component Architecture
30
+
31
+ ### Two-Tier Execution Model
32
+
33
+ The library uses a two-tier architecture separating long-lived configuration from short-lived execution:
34
+
35
+ ```
36
+ ┌─────────────────┐ ┌─────────────────┐
37
+ │ AgentRunner │ │ Runner │
38
+ │ (Long-lived) │────▶│ (Per-request) │
39
+ │ │ │ │
40
+ │ • Agent Registry│ │ • Execution │
41
+ │ • Entry Point │ │ • Context Mgmt │
42
+ │ • Thread Safety │ │ • Handoff Logic │
43
+ └─────────────────┘ └─────────────────┘
44
+ ```
45
+
46
+ **AgentRunner** (Thread-Safe Manager):
47
+ - Created once, reused for multiple conversations
48
+ - Maintains immutable agent registry
49
+ - Determines conversation continuity
50
+ - Thread-safe for concurrent use
51
+
52
+ **Runner** (Execution Engine):
53
+ - Created per conversation turn
54
+ - Handles LLM communication and tool execution
55
+ - Manages context state during execution
56
+ - Stateless and garbage-collected after use
57
+
58
+ ### Context Management Architecture
59
+
60
+ Context flows through multiple abstraction layers:
61
+
62
+ ```
63
+ ┌─────────────────┐
64
+ │ User Context │ (Serializable across sessions)
65
+ │ │
66
+ ├─────────────────┤
67
+ │ RunContext │ (Execution isolation)
68
+ │ │
69
+ ├─────────────────┤
70
+ │ ToolContext │ (Tool-specific state)
71
+ │ │
72
+ └─────────────────┘
73
+ ```
74
+
75
+ **User Context**: Serializable hash for persistence
76
+ **RunContext**: Execution wrapper with usage tracking
77
+ **ToolContext**: Tool-specific view with retry metadata
78
+
79
+ ## Handoff Architecture
80
+
81
+ ### Tool-Based Handoff System
82
+
83
+ Handoffs are implemented as specialized tools rather than instruction-parsing:
84
+
85
+ ```
86
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
87
+ │ Triage Agent │ │ HandoffTool │ │ Billing Agent │
88
+ │ │ │ │ │ │
89
+ │ • Instructions │────▶│ • Target Agent │────▶│ • Instructions │
90
+ │ • Handoff List │ │ • Schema │ │ • Specialized │
91
+ │ • General Role │ │ • Execution │ │ • Tools │
92
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
93
+ ```
94
+
95
+ **Benefits of Tool-Based Handoffs**:
96
+ - **Reliable invocation**: LLMs consistently use tools when available
97
+ - **Clear semantics**: Tool schema explicitly defines handoff criteria
98
+ - **No text parsing**: Eliminates need to parse free-form responses
99
+ - **Provider agnostic**: Works consistently across OpenAI, Anthropic, etc.
100
+
101
+ ### First-Call-Wins Handoff Resolution
102
+
103
+ To prevent infinite handoff loops, the system implements first-call-wins semantics:
104
+
105
+ ```
106
+ LLM Response with Multiple Handoffs:
107
+ ┌─────────────────────────────────────┐
108
+ │ Call 1: handoff_to_support() │ ✓ Processed
109
+ │ Call 2: handoff_to_billing() │ ✗ Ignored
110
+ │ Call 3: handoff_to_support() │ ✗ Ignored
111
+ └─────────────────────────────────────┘
112
+ Result: Transfer to Support Agent only
113
+ ```
114
+
115
+ This mirrors OpenAI's SDK behavior and prevents conflicting handoff states.
116
+
117
+ ## Thread Safety Implementation
118
+
119
+ ### Context Isolation Pattern
120
+
121
+ Each execution receives an isolated context copy:
122
+
123
+ ```ruby
124
+ # Thread-safe execution flow
125
+ def run(input, context: {})
126
+ # 1. Deep copy context for isolation
127
+ context_copy = deep_copy_context(context)
128
+
129
+ # 2. Create execution wrapper
130
+ run_context = RunContext.new(context_copy)
131
+
132
+ # 3. Execute with isolated state
133
+ result = execute_with_context(run_context)
134
+
135
+ # 4. Return serializable result
136
+ return result
137
+ end
138
+ ```
139
+
140
+ ### Tool Wrapper Pattern
141
+
142
+ Tools remain stateless through the wrapper pattern:
143
+
144
+ ```ruby
145
+ # Tool Definition (Stateless)
146
+ class WeatherTool < Agents::Tool
147
+ def perform(tool_context, city:)
148
+ api_key = tool_context.context[:weather_api_key]
149
+ WeatherAPI.get(city, api_key)
150
+ end
151
+ end
152
+
153
+ # Runtime Wrapping (Context Injection)
154
+ wrapped_tool = ToolWrapper.new(weather_tool, context_wrapper)
155
+ ```
156
+
157
+ The wrapper captures context in its closure, injecting it during execution without modifying the tool instance.
158
+
159
+ ## Chat Architecture
160
+
161
+ ### Extended Chat System
162
+
163
+ The library extends RubyLLM::Chat with handoff detection:
164
+
165
+ ```
166
+ ┌─────────────────┐ ┌─────────────────┐
167
+ │ Agents::Chat │ │ RubyLLM::Chat │
168
+ │ (Extended) │────▶│ (Base) │
169
+ │ │ │ │
170
+ │ • Handoff Detect│ │ • LLM Comm │
171
+ │ • Tool Classify │ │ • Tool Execution│
172
+ │ • First-Call-Wins│ │ • Message Mgmt │
173
+ └─────────────────┘ └─────────────────┘
174
+ ```
175
+
176
+ **Handoff Detection Flow**:
177
+ 1. Classify tool calls into handoff vs regular tools
178
+ 2. Execute first handoff only (first-call-wins)
179
+ 3. Return HandoffResponse to signal agent switch
180
+ 4. Execute regular tools normally with continuation
181
+
182
+ ### Conversation Continuity
183
+
184
+ The system maintains conversation state through message attribution:
185
+
186
+ ```ruby
187
+ # Messages include agent attribution
188
+ {
189
+ role: :assistant,
190
+ content: "I can help with billing",
191
+ agent_name: "Billing" # Enables conversation continuity
192
+ }
193
+ ```
194
+
195
+ AgentRunner uses this attribution to determine conversation ownership when resuming.
196
+
197
+ ## Provider Abstraction
198
+
199
+ ### RubyLLM Integration
200
+
201
+ The library builds on RubyLLM for provider abstraction:
202
+
203
+ ```
204
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
205
+ │ AI Agents │ │ RubyLLM │ │ Providers │
206
+ │ │ │ │ │ │
207
+ │ • Multi-Agent │────▶│ • Unified API │────▶│ • OpenAI │
208
+ │ • Handoffs │ │ • Tool Calling │ │ • Anthropic │
209
+ │ • Context Mgmt │ │ • Streaming │ │ • Gemini │
210
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
211
+ ```
212
+
213
+ This abstraction allows switching between providers without changing agent logic.
214
+
215
+ ## State Management
216
+
217
+ ### Context Serialization
218
+
219
+ The entire conversation state is serializable:
220
+
221
+ ```ruby
222
+ # Serialize context for persistence
223
+ context_json = result.context.to_json
224
+
225
+ # Restore and continue conversation
226
+ restored_context = JSON.parse(context_json, symbolize_names: true)
227
+ next_result = runner.run("Continue conversation", context: restored_context)
228
+ ```
229
+
230
+ **Serialization includes**:
231
+ - Conversation history with agent attribution
232
+ - Current agent state
233
+ - Tool-specific state
234
+ - User-defined context data
235
+
236
+ ### State Persistence Patterns
237
+
238
+ The library supports multiple persistence patterns:
239
+
240
+ ```
241
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
242
+ │ In-Memory │ │ File-Based │ │ Database │
243
+ │ │ │ │ │ │
244
+ │ • Development │ │ • Simple Deploy │ │ • Production │
245
+ │ • Testing │ │ • Single Server │ │ • Multi-Server │
246
+ │ • Rapid Iteration│ │ • File Storage │ │ • ActiveRecord │
247
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
248
+ ```
249
+
250
+ All patterns use the same serialization format for consistency.
251
+
252
+ ## Performance Characteristics
253
+
254
+ ### Memory Management
255
+
256
+ - **Immutable objects** are shared safely across threads
257
+ - **Context copies** are garbage-collected after execution
258
+ - **Tool instances** are reused without state accumulation
259
+
260
+ ### Execution Efficiency
261
+
262
+ - **Minimal object creation** during execution
263
+ - **Direct LLM communication** without additional abstractions
264
+ - **Efficient handoff detection** through tool classification
265
+
266
+ ### Scalability
267
+
268
+ - **Thread-safe design** enables horizontal scaling
269
+ - **Stateless execution** supports load balancing
270
+ - **Serializable state** enables process migration
271
+
272
+ ## Error Handling Architecture
273
+
274
+ ### Graceful Degradation
275
+
276
+ The system handles errors at multiple levels:
277
+
278
+ ```
279
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
280
+ │ Agent Level │ │ Tool Level │ │ LLM Level │
281
+ │ │ │ │ │ │
282
+ │ • Handoff Fails │ │ • Tool Errors │ │ • API Errors │
283
+ │ • Max Turns │ │ • Retry Logic │ │ • Rate Limits │
284
+ │ • State Errors │ │ • Fallback │ │ • Timeouts │
285
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
286
+ ```
287
+
288
+ ### Error Recovery
289
+
290
+ - **Context preservation** even during errors
291
+ - **Partial execution results** for debugging
292
+ - **Conversation continuity** after error resolution
293
+
294
+ ## Extension Points
295
+
296
+ ### Custom Tools
297
+
298
+ The tool system is fully extensible:
299
+
300
+ ```ruby
301
+ class CustomTool < Agents::Tool
302
+ name "custom_action"
303
+ description "Perform custom business logic"
304
+ param :input, type: "string"
305
+
306
+ def perform(tool_context, input:)
307
+ # Access context for state
308
+ user_id = tool_context.context[:user_id]
309
+
310
+ # Perform custom logic
311
+ result = BusinessLogic.process(input, user_id)
312
+
313
+ # Update shared state
314
+ tool_context.state[:last_action] = result
315
+
316
+ result
317
+ end
318
+ end
319
+ ```
320
+
321
+ ### Custom Providers
322
+
323
+ While built on RubyLLM, the architecture supports custom providers:
324
+
325
+ ```ruby
326
+ # Custom provider integration
327
+ class CustomProvider < RubyLLM::Provider
328
+ def complete(messages, **options)
329
+ # Custom LLM integration
330
+ end
331
+ end
332
+
333
+ # Use with agents
334
+ agent = Agents::Agent.new(
335
+ name: "Assistant",
336
+ model: CustomProvider.new.model("custom-model")
337
+ )
338
+ ```
339
+
340
+ ### Debug Hooks
341
+
342
+ The system provides hooks for debugging and tracing:
343
+
344
+ ```ruby
345
+ # Enable debug logging
346
+ ENV["RUBYLLM_DEBUG"] = "true"
347
+
348
+ # Custom callbacks
349
+ chat.on(:new_message) { puts "Sending to LLM..." }
350
+ chat.on(:end_message) { |response| puts "Received: #{response.content}" }
351
+ ```
352
+
353
+ This architecture provides a robust foundation for building production-ready multi-agent systems while maintaining simplicity and developer productivity.