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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -106
  3. data/docs/Gemfile +14 -0
  4. data/docs/Gemfile.lock +183 -0
  5. data/docs/_config.yml +53 -0
  6. data/docs/_sass/color_schemes/ruby.scss +72 -0
  7. data/docs/_sass/custom/custom.scss +93 -0
  8. data/docs/architecture.md +353 -0
  9. data/docs/assets/fonts/InterVariable.woff2 +0 -0
  10. data/docs/concepts/agent-tool.md +166 -0
  11. data/docs/concepts/agents.md +43 -0
  12. data/docs/concepts/context.md +110 -0
  13. data/docs/concepts/handoffs.md +81 -0
  14. data/docs/concepts/runner.md +87 -0
  15. data/docs/concepts/tools.md +62 -0
  16. data/docs/concepts.md +21 -0
  17. data/docs/guides/agent-as-tool-pattern.md +242 -0
  18. data/docs/guides/multi-agent-systems.md +261 -0
  19. data/docs/guides/rails-integration.md +440 -0
  20. data/docs/guides/state-persistence.md +451 -0
  21. data/docs/guides.md +18 -0
  22. data/docs/index.md +95 -0
  23. data/examples/collaborative-copilot/README.md +169 -0
  24. data/examples/collaborative-copilot/agents/analysis_agent.rb +48 -0
  25. data/examples/collaborative-copilot/agents/answer_suggestion_agent.rb +50 -0
  26. data/examples/collaborative-copilot/agents/copilot_orchestrator.rb +85 -0
  27. data/examples/collaborative-copilot/agents/integrations_agent.rb +58 -0
  28. data/examples/collaborative-copilot/agents/research_agent.rb +52 -0
  29. data/examples/collaborative-copilot/data/contacts.json +47 -0
  30. data/examples/collaborative-copilot/data/conversations.json +170 -0
  31. data/examples/collaborative-copilot/data/knowledge_base.json +58 -0
  32. data/examples/collaborative-copilot/data/linear_issues.json +83 -0
  33. data/examples/collaborative-copilot/data/stripe_billing.json +71 -0
  34. data/examples/collaborative-copilot/interactive.rb +90 -0
  35. data/examples/collaborative-copilot/tools/create_linear_ticket_tool.rb +58 -0
  36. data/examples/collaborative-copilot/tools/get_article_tool.rb +41 -0
  37. data/examples/collaborative-copilot/tools/get_contact_tool.rb +51 -0
  38. data/examples/collaborative-copilot/tools/get_conversation_tool.rb +53 -0
  39. data/examples/collaborative-copilot/tools/get_stripe_billing_tool.rb +44 -0
  40. data/examples/collaborative-copilot/tools/search_contacts_tool.rb +57 -0
  41. data/examples/collaborative-copilot/tools/search_conversations_tool.rb +54 -0
  42. data/examples/collaborative-copilot/tools/search_knowledge_base_tool.rb +55 -0
  43. data/examples/collaborative-copilot/tools/search_linear_issues_tool.rb +60 -0
  44. data/examples/isp-support/agents_factory.rb +57 -1
  45. data/examples/isp-support/tools/create_lead_tool.rb +16 -2
  46. data/examples/isp-support/tools/crm_lookup_tool.rb +13 -1
  47. data/lib/agents/agent.rb +52 -6
  48. data/lib/agents/agent_tool.rb +113 -0
  49. data/lib/agents/handoff.rb +8 -34
  50. data/lib/agents/tool_context.rb +36 -0
  51. data/lib/agents/version.rb +1 -1
  52. data/lib/agents.rb +1 -0
  53. metadata +44 -2
@@ -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.
@@ -0,0 +1,166 @@
1
+ ---
2
+ layout: default
3
+ title: AgentTool
4
+ parent: Concepts
5
+ nav_order: 6
6
+ ---
7
+
8
+ # AgentTool
9
+
10
+ The `AgentTool` class enables **agent-to-agent collaboration** by wrapping agents as callable tools. This pattern allows specialized agents to work behind the scenes to help each other without conversation handoffs.
11
+
12
+ ## Key Concept
13
+
14
+ Unlike handoffs where control transfers between agents with full conversation context, AgentTool creates **constrained execution environments** where wrapped agents:
15
+
16
+ - Cannot perform handoffs (empty registry)
17
+ - Have limited turn counts to prevent infinite loops
18
+ - Only receive shared state, not conversation history
19
+ - Always return control to the calling agent
20
+
21
+ ## Architecture
22
+
23
+ ```ruby
24
+ # Specialized agents as tools
25
+ research_agent = Agent.new(
26
+ name: "Research Agent",
27
+ instructions: "Research topics and extract key information"
28
+ )
29
+
30
+ analysis_agent = Agent.new(
31
+ name: "Analysis Agent",
32
+ instructions: "Analyze data and provide insights"
33
+ )
34
+
35
+ # Main orchestrator uses other agents as tools
36
+ orchestrator = Agent.new(
37
+ name: "Orchestrator",
38
+ instructions: "Coordinate research and analysis",
39
+ tools: [
40
+ research_agent.as_tool(
41
+ name: "research_topic",
42
+ description: "Research a specific topic"
43
+ ),
44
+ analysis_agent.as_tool(
45
+ name: "analyze_data",
46
+ description: "Analyze research findings"
47
+ )
48
+ ]
49
+ )
50
+ ```
51
+
52
+ ## Implementation Details
53
+
54
+ ### Constraints
55
+
56
+ AgentTool implements several safety constraints:
57
+
58
+ 1. **No Handoffs**: Wrapped agents receive an empty registry, preventing handoff calls
59
+ 2. **Limited Turns**: Maximum 3 turns to prevent infinite loops
60
+ 3. **Context Isolation**: Only shared state is passed, not conversation history
61
+ 4. **Return Control**: Always returns to the calling agent
62
+
63
+ ### Context Isolation
64
+
65
+ ```ruby
66
+ # Parent context (full conversation state)
67
+ parent_context = {
68
+ state: { user_id: 123, session: "abc" },
69
+ conversation_history: [...],
70
+ current_agent: "MainAgent",
71
+ turn_count: 5
72
+ }
73
+
74
+ # Isolated context (only state)
75
+ isolated_context = {
76
+ state: { user_id: 123, session: "abc" }
77
+ }
78
+ ```
79
+
80
+ ### Error Handling
81
+
82
+ AgentTool provides robust error handling:
83
+
84
+ - Execution failures return descriptive error messages
85
+ - Runtime exceptions are caught and reported
86
+ - Missing output is handled gracefully
87
+
88
+ ## Usage Patterns
89
+
90
+ ### Customer Support Copilot
91
+
92
+ ```ruby
93
+ # Specialized agents for different domains
94
+ conversation_analyzer = Agent.new(
95
+ name: "ConversationAnalyzer",
96
+ instructions: "Extract order IDs and customer intent"
97
+ )
98
+
99
+ shopify_agent = Agent.new(
100
+ name: "ShopifyAgent",
101
+ instructions: "Perform Shopify operations",
102
+ tools: [shopify_refund_tool, shopify_lookup_tool]
103
+ )
104
+
105
+ # Main copilot coordinates specialized agents
106
+ copilot = Agent.new(
107
+ name: "SupportCopilot",
108
+ instructions: "Help support agents with customer requests",
109
+ tools: [
110
+ conversation_analyzer.as_tool(
111
+ name: "analyze_conversation",
112
+ description: "Extract key information from conversation"
113
+ ),
114
+ shopify_agent.as_tool(
115
+ name: "shopify_action",
116
+ description: "Perform Shopify operations"
117
+ )
118
+ ]
119
+ )
120
+ ```
121
+
122
+ ## Output Transformation
123
+
124
+ AgentTool supports custom output extractors for transforming results:
125
+
126
+ ```ruby
127
+ # Custom output extraction
128
+ summarizer = Agent.new(
129
+ name: "Summarizer",
130
+ instructions: "Summarize long content"
131
+ )
132
+
133
+ summarizer_tool = summarizer.as_tool(
134
+ name: "summarize",
135
+ description: "Create a summary",
136
+ output_extractor: ->(result) {
137
+ "SUMMARY: #{result.output&.first(200)}..."
138
+ }
139
+ )
140
+ ```
141
+
142
+ ## Best Practices
143
+
144
+ 1. **Keep it Simple**: Use AgentTool for focused, single-purpose tasks
145
+ 2. **Avoid Deep Nesting**: Don't create agents that use agents that use agents
146
+ 3. **State Management**: Only share necessary state between agents
147
+ 4. **Error Handling**: Always handle potential execution failures
148
+ 5. **Performance**: Consider the overhead of multiple agent calls
149
+
150
+ ## Comparison with Handoffs
151
+
152
+ | Aspect | AgentTool | Handoff |
153
+ |--------|-----------|---------|
154
+ | **Purpose** | Behind-the-scenes collaboration | User-facing conversation transfer |
155
+ | **Context** | Isolated (state only) | Full conversation history |
156
+ | **Control** | Returns to caller | Transfers to target |
157
+ | **Constraints** | Limited turns, no handoffs | Full agent capabilities |
158
+ | **Use Case** | Internal processing | Conversation routing |
159
+
160
+ ## See Also
161
+
162
+ <!-- - [Agent-as-Tool Pattern Guide](../guides/agent-as-tool-pattern.html)
163
+ - [Multi-Agent Systems](../guides/multi-agent-systems.html) -->
164
+
165
+ - [Tools Concept](tools.html)
166
+ - [Handoffs Concept](handoffs.html)
@@ -0,0 +1,43 @@
1
+ ---
2
+ layout: default
3
+ title: Agents
4
+ parent: Concepts
5
+ nav_order: 1
6
+ ---
7
+
8
+ # Agents
9
+
10
+ An **Agent** is the fundamental building block of the library. It represents an AI assistant with a specific set of capabilities, defined by its instructions, tools, and the underlying language model it uses.
11
+
12
+ Agents are immutable and thread-safe by design. Once created, their configuration cannot be changed, ensuring safe sharing across multiple threads without race conditions. Agents can have dynamic instructions using Proc objects that receive runtime context.
13
+
14
+ {: .note }
15
+ This project is in early development. While thread safety is a core design goal and the architecture is built around it, complete thread safety is not yet guaranteed. We're actively working toward this goal.
16
+
17
+ ### Key Attributes of an Agent
18
+
19
+ * **`name`**: A unique name for the agent, used for identification and in handoffs.
20
+ * **`instructions`**: The system prompt that guides the agent's behavior. This can be a static string or a `Proc` that dynamically generates instructions based on the current context.
21
+ * **`model`**: The language model the agent will use (e.g., `"gpt-4.1-mini"`).
22
+ * **`tools`**: An array of `Agents::Tool` instances that the agent can use to perform actions.
23
+ * **`handoff_agents`**: An array of other agents that this agent can hand off conversations to.
24
+
25
+ ### Example
26
+
27
+ ```ruby
28
+ # Create a simple agent
29
+ assistant_agent = Agents::Agent.new(
30
+ name: "Assistant",
31
+ instructions: "You are a helpful assistant.",
32
+ model: "gpt-4.1-mini",
33
+ tools: [CalculatorTool.new]
34
+ )
35
+
36
+ # Create a specialized agent by cloning the base agent
37
+ specialized_agent = assistant_agent.clone(
38
+ instructions: "You are a specialized assistant for financial calculations.",
39
+ tools: assistant_agent.tools + [FinancialDataTool.new]
40
+ )
41
+ ```
42
+
43
+ In this example, we create a base `assistant_agent` and then create a `specialized_agent` by cloning it and adding a new tool. This approach allows for easy composition and reuse of agent configurations.
@@ -0,0 +1,110 @@
1
+ ---
2
+ layout: default
3
+ title: Context
4
+ parent: Concepts
5
+ nav_order: 3
6
+ ---
7
+
8
+ # Context
9
+
10
+ **Context** is the serializable state management system that preserves information across agent interactions, tool executions, and handoffs. Context enables conversation continuity and cross-session persistence through a simple hash-based structure.
11
+
12
+ ## Context Architecture
13
+
14
+ Context flows through multiple abstraction layers:
15
+
16
+ ### User Context (Serializable)
17
+ The main context hash that persists across sessions:
18
+ ```ruby
19
+ context = {
20
+ user_id: 123,
21
+ conversation_history: [...],
22
+ current_agent_name: "Billing",
23
+ state: { customer_tier: "premium" } # Tools use this nested hash for persistent data
24
+ }
25
+ ```
26
+
27
+ ### RunContext (Execution Wrapper)
28
+ Wraps user context with execution-specific features:
29
+ - **Context Hash**: Shared data accessible to all tools and agents
30
+ - **Thread Safety**: Deep copying ensures execution isolation
31
+
32
+ ### ToolContext (Tool-Specific View)
33
+ Provides tools with controlled access to execution state:
34
+ - **Context Access**: Read/write access to shared context hash
35
+ - **State Management**: Dedicated space for persistent tool state
36
+
37
+ ## The `ToolContext`
38
+
39
+ The `ToolContext` is a wrapper around the `RunContext` that is passed to each tool when it is executed. It provides the tool with controlled access to the execution state, including the shared context hash.
40
+
41
+ By passing the context through the `ToolContext`, we ensure that tools can remain stateless and thread-safe, as they do not need to store any execution-specific state in their instance variables.
42
+
43
+ ## Context Serialization
44
+
45
+ Context is fully serializable for persistence across process boundaries:
46
+
47
+ ```ruby
48
+ # Run conversation
49
+ result = runner.run("Hello, I'm John")
50
+
51
+ # Serialize for storage
52
+ context_json = result.context.to_json
53
+ # Store in database, file, session, etc.
54
+
55
+ # Later: restore and continue
56
+ restored_context = JSON.parse(context_json, symbolize_names: true)
57
+ next_result = runner.run("What's my name?", context: restored_context)
58
+ # => "Your name is John"
59
+ ```
60
+
61
+ ## Conversation Continuity
62
+
63
+ The AgentRunner automatically manages conversation continuity through context:
64
+
65
+ ```ruby
66
+ # Create runner
67
+ runner = Agents::Runner.with_agents(triage_agent, billing_agent)
68
+
69
+ # First interaction
70
+ result1 = runner.run("I need billing help")
71
+ # Triage agent hands off to billing agent
72
+ # Context includes: current_agent_name: "Billing"
73
+
74
+ # Continue conversation
75
+ result2 = runner.run("What payment methods do you accept?", context: result1.context)
76
+ # AgentRunner detects billing agent should continue based on context
77
+ ```
78
+
79
+ ## State Management
80
+
81
+ Tools can use the context for persistent state:
82
+
83
+ ```ruby
84
+ class CustomerLookupTool < Agents::Tool
85
+ def perform(tool_context, customer_id:)
86
+ customer = Customer.find(customer_id)
87
+
88
+ # Store in shared state for other tools
89
+ tool_context.state[:customer_id] = customer_id
90
+ tool_context.state[:customer_name] = customer.name
91
+ tool_context.state[:account_type] = customer.account_type
92
+
93
+ "Found customer: #{customer.name}"
94
+ end
95
+ end
96
+
97
+ class BillingTool < Agents::Tool
98
+ def perform(tool_context)
99
+ # Access state from previous tool
100
+ customer_id = tool_context.state[:customer_id]
101
+ account_type = tool_context.state[:account_type]
102
+
103
+ return "No customer found" unless customer_id
104
+
105
+ # Use customer info for billing operations
106
+ billing_info = get_billing_info(customer_id, account_type)
107
+ billing_info.to_s
108
+ end
109
+ end
110
+ ```