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
@@ -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,42 @@
1
+ ---
2
+ layout: default
3
+ title: Callbacks
4
+ parent: Concepts
5
+ nav_order: 6
6
+ ---
7
+
8
+ # Real-time Callbacks
9
+
10
+ The AI Agents SDK provides real-time callbacks that allow you to monitor agent execution as it happens. This is particularly useful for building user interfaces that show live feedback about what agents are doing.
11
+
12
+ ## Available Callbacks
13
+
14
+ The SDK provides four types of callbacks that give you visibility into different stages of agent execution:
15
+
16
+ **Agent Thinking** - Triggered when an agent is about to make an LLM call. Useful for showing "thinking" indicators in UIs.
17
+
18
+ **Tool Start** - Called when an agent begins executing a tool. Shows which tool is being used and with what arguments.
19
+
20
+ **Tool Complete** - Triggered when a tool finishes execution. Provides the tool name and result for status updates.
21
+
22
+ **Agent Handoff** - Called when control transfers between agents. Shows the source agent, target agent, and handoff reason.
23
+
24
+ ## Basic Usage
25
+
26
+ Callbacks are registered on the AgentRunner using chainable methods:
27
+
28
+ ```ruby
29
+ runner = Agents::AgentRunner.with_agents(triage, support)
30
+ .on_agent_thinking { |agent, input| puts "#{agent} thinking..." }
31
+ .on_tool_start { |tool, args| puts "Using #{tool}" }
32
+ .on_tool_complete { |tool, result| puts "#{tool} completed" }
33
+ .on_agent_handoff { |from, to, reason| puts "#{from} → #{to}" }
34
+ ```
35
+
36
+ ## Integration Patterns
37
+
38
+ Callbacks work well with real-time web frameworks like Rails ActionCable, allowing you to stream agent status updates directly to browser clients. They're also useful for logging, metrics collection, and building debug interfaces.
39
+
40
+ ## Thread Safety
41
+
42
+ Callbacks execute synchronously in the same thread as agent execution. Exceptions in callbacks are caught and logged as warnings without interrupting agent operation. For heavy operations or external API calls, consider using background jobs triggered by the callbacks.
@@ -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
+ ```
@@ -0,0 +1,81 @@
1
+ ---
2
+ layout: default
3
+ title: Handoffs
4
+ parent: Concepts
5
+ nav_order: 4
6
+ ---
7
+
8
+ # Handoffs
9
+
10
+ **Handoffs** are a powerful feature of the Ruby Agents library that allow you to build sophisticated multi-agent systems. A handoff is the process of transferring a conversation from one agent to another, more specialized agent.
11
+
12
+ This is particularly useful when you have a general-purpose agent that can handle a wide range of queries, but you also have specialized agents that are better equipped to handle specific tasks. For example, you might have a triage agent that routes users to a billing agent or a technical support agent.
13
+
14
+ ## How Handoffs Work
15
+
16
+ Handoffs are implemented as a special type of tool called a `HandoffTool`. When you configure an agent with `handoff_agents`, the library automatically creates a `HandoffTool` for each of the specified agents.
17
+
18
+ Here's how the handoff process works:
19
+
20
+ 1. **The user sends a message:** The user sends a message that indicates they need help with a specific task (e.g., "I have a problem with my bill").
21
+ 2. **The LLM decides to hand off:** The current agent's language model determines that the query is best handled by another agent and decides to call the corresponding `HandoffTool`.
22
+ 3. **The `HandoffTool` signals the handoff:** The `HandoffTool` sets a `pending_handoff` flag in the `RunContext`, indicating which agent to hand off to.
23
+ 4. **The Runner switches agents:** The `Runner` detects the `pending_handoff` flag and switches the `current_agent` to the new agent.
24
+ 5. **The conversation continues:** The conversation continues with the new agent, which now has access to the full conversation history.
25
+
26
+ ### Loop Prevention
27
+
28
+ To prevent infinite handoff loops, the library automatically processes only the first handoff tool call in any LLM response. If multiple handoff tools are called in a single response, only the first one is executed and subsequent calls are ignored. This prevents conflicting handoff states and ensures clean agent transitions.
29
+
30
+ ## Why Use Tools for Handoffs?
31
+
32
+ Using tools for handoffs has several advantages over simply instructing the LLM to hand off the conversation:
33
+
34
+ * **Reliability:** LLMs are very good at using tools when they are available. By representing handoffs as tools, we can be more confident that the LLM will use them when appropriate.
35
+ * **Clarity:** The tool's schema clearly defines when each handoff is suitable, making it easier for the LLM to make the right decision.
36
+ * **Simplicity:** We don't need to parse free-text responses from the LLM to determine if a handoff is needed.
37
+ * **Consistency:** This approach works consistently across different LLM providers.
38
+
39
+ ## Example
40
+
41
+ ```ruby
42
+ # Create the specialized agents
43
+ billing_agent = Agents::Agent.new(name: "Billing", instructions: "Handle billing and payment issues.")
44
+ support_agent = Agents::Agent.new(name: "Support", instructions: "Provide technical support.")
45
+
46
+ # Create the triage agent with handoff agents
47
+ triage_agent = Agents::Agent.new(
48
+ name: "Triage",
49
+ instructions: "You are a triage agent. Your job is to route users to the correct department.",
50
+ handoff_agents: [billing_agent, support_agent]
51
+ )
52
+
53
+ # Run the triage agent
54
+ result = Agents::Runner.run(triage_agent, "I have a problem with my bill.")
55
+
56
+ # The runner will automatically hand off to the billing agent
57
+ ```
58
+
59
+ In this example, the `triage_agent` will automatically hand off the conversation to the `billing_agent` when the user asks a question about their bill. This allows you to create a seamless user experience where the user is always talking to the most qualified agent for their needs.
60
+
61
+ ## Troubleshooting Handoffs
62
+
63
+ ### Infinite Handoff Loops
64
+
65
+ **Problem:** Agents keep handing off to each other in an endless loop.
66
+
67
+ **Common Causes:**
68
+ - Agent instructions that conflict with each other
69
+ - Agents configured to hand off for overlapping scenarios
70
+ - Poor instruction clarity about when to hand off vs. when to handle directly
71
+
72
+ **Solutions:**
73
+ 1. **Review agent instructions:** Ensure each agent has a clear, distinct responsibility
74
+ 2. **Use hub-and-spoke pattern:** Have specialized agents only hand off back to a central triage agent
75
+ 3. **Add specific scenarios:** Include examples in instructions of when to handle vs. hand off
76
+ 4. **Enable debug logging:** Use `ENV["RUBYLLM_DEBUG"] = "true"` to see handoff decisions
77
+
78
+
79
+ ### Multiple Handoffs in One Response
80
+
81
+ The library automatically handles cases where an LLM tries to call multiple handoff tools in a single response. Only the first handoff will be processed, and subsequent calls will be ignored. This is normal behavior and prevents conflicting handoff states.
@@ -0,0 +1,87 @@
1
+ ---
2
+ layout: default
3
+ title: Runner
4
+ parent: Concepts
5
+ nav_order: 5
6
+ ---
7
+
8
+ # AgentRunner
9
+
10
+ The **AgentRunner** is the thread-safe execution manager that provides the main API for multi-agent conversations. It separates agent registry management from execution, enabling safe concurrent use across multiple threads while maintaining conversation continuity.
11
+
12
+ ## Two-Tier Architecture
13
+
14
+ The library uses a two-tier design separating long-lived configuration from short-lived execution:
15
+
16
+ ### AgentRunner (Thread-Safe Manager)
17
+ - Created once at application startup
18
+ - Maintains immutable agent registry
19
+ - Determines conversation continuity from history
20
+ - Thread-safe for concurrent conversations
21
+
22
+ ### Runner (Internal Execution Engine)
23
+ - Created per conversation turn
24
+ - Handles LLM communication and tool execution
25
+ - Manages context state during execution
26
+ - Stateless and garbage-collected after use
27
+
28
+ ## Conversation Flow
29
+
30
+ Each conversation follows this flow:
31
+
32
+ 1. **Agent Selection**: AgentRunner determines current agent from conversation history
33
+ 2. **Context Isolation**: Creates deep copy of context for thread safety
34
+ 3. **LLM Communication**: Sends message with context to language model
35
+ 4. **Tool Execution**: Executes any requested tools through RubyLLM
36
+ 5. **Handoff Detection**: Checks for agent handoffs and switches if needed
37
+ 6. **State Persistence**: Updates context with conversation state
38
+
39
+ ## Thread Safety
40
+
41
+ The AgentRunner ensures thread safety through several key mechanisms:
42
+
43
+ * **Immutable Registry**: Agent registry is frozen after initialization, preventing runtime modifications
44
+ * **Context Isolation**: Each execution receives a deep copy of context to prevent cross-contamination
45
+ * **Stateless Execution**: Internal Runner instances store no execution-specific state
46
+ * **Tool Wrapping**: ToolWrapper injects context through parameters, keeping tools stateless
47
+
48
+ ## Usage Pattern
49
+
50
+ Create an AgentRunner once and reuse it for multiple conversations:
51
+
52
+ ```ruby
53
+ # Create agents
54
+ triage_agent = Agents::Agent.new(
55
+ name: "Triage",
56
+ instructions: "Route users to appropriate specialists"
57
+ )
58
+ billing_agent = Agents::Agent.new(
59
+ name: "Billing",
60
+ instructions: "Handle billing inquiries"
61
+ )
62
+
63
+ # Register handoffs
64
+ triage_agent.register_handoffs(billing_agent)
65
+
66
+ # Create runner once (thread-safe)
67
+ runner = Agents::Runner.with_agents(triage_agent, billing_agent)
68
+
69
+ # Use from multiple threads safely
70
+ result1 = runner.run("I have a billing question")
71
+ result2 = runner.run("Follow up", context: result1.context)
72
+ ```
73
+
74
+ ## Conversation Continuity
75
+
76
+ The AgentRunner automatically maintains conversation continuity by analyzing message history to determine which agent should handle each turn:
77
+
78
+ ```ruby
79
+ # First message -> Uses triage agent (default entry point)
80
+ result1 = runner.run("I need help with my bill")
81
+
82
+ # Triage hands off to billing agent
83
+ # Next message -> AgentRunner detects billing agent should continue
84
+ result2 = runner.run("What payment methods do you accept?", context: result1.context)
85
+ ```
86
+
87
+ The `run` method returns a `RunResult` with output, conversation history, usage metrics, and updated context.
@@ -0,0 +1,62 @@
1
+ ---
2
+ layout: default
3
+ title: Tools
4
+ parent: Concepts
5
+ nav_order: 2
6
+ ---
7
+
8
+ # Tools
9
+
10
+ **Tools** are the components that allow agents to interact with the outside world. They are the primary way to extend an agent's capabilities beyond what the language model can do on its own. A tool can be anything from a simple calculator to a complex integration with an external API.
11
+
12
+ In Ruby Agents, tools are designed to be thread-safe and stateless. This is a critical design principle that ensures the stability and reliability of your agent system, especially in concurrent environments.
13
+
14
+ ## Thread-Safe Design
15
+
16
+ The key to the thread-safe design of tools is that they do not store any execution-specific state in their instance variables. All the data a tool needs to perform its action is passed to it through the `perform` method, which receives a `ToolContext` object.
17
+
18
+ ### The `ToolContext`
19
+
20
+ The `ToolContext` provides access to the current execution context, including:
21
+
22
+ * **Shared context data:** A hash of data that is shared across all tools and agents in a given run.
23
+ * **Usage tracking:** An object that tracks token usage for the current run.
24
+ * **Retry count:** The number of times the current tool execution has been retried.
25
+
26
+ By passing all the necessary data through the `ToolContext`, we ensure that tool instances can be safely shared across multiple threads without the risk of data corruption.
27
+
28
+ ## Creating a Tool
29
+
30
+ You can create a tool in two ways:
31
+
32
+ 1. **Creating a Tool Class:** For more complex tools, you can create a class that inherits from `Agents::Tool` and implements the `perform` method.
33
+
34
+ ```ruby
35
+ class WeatherTool < Agents::Tool
36
+ name "get_weather"
37
+ description "Get the current weather for a location."
38
+ param :location, type: "string", desc: "The city and state, e.g., San Francisco, CA"
39
+
40
+ def perform(tool_context, location:)
41
+ # Access the API key from the shared context
42
+ api_key = tool_context.context[:weather_api_key]
43
+
44
+ # Call the weather API and return the result
45
+ WeatherApi.get(location, api_key)
46
+ end
47
+ end
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.
data/docs/concepts.md ADDED
@@ -0,0 +1,22 @@
1
+ ---
2
+ layout: default
3
+ title: Concepts
4
+ nav_order: 2
5
+ has_children: true
6
+ ---
7
+
8
+ # Concepts
9
+
10
+ This section covers the core concepts of the AI Agents library. Understanding these concepts is essential for building robust and scalable AI agent systems.
11
+
12
+ ## Overview
13
+
14
+ The AI Agents library is built around several key concepts that work together to provide a powerful framework for multi-agent AI workflows:
15
+
16
+ - **[Agents](concepts/agents.html)** - Immutable, thread-safe AI assistants with specific roles and capabilities
17
+ - **[AgentRunner](concepts/runner.html)** - Thread-safe execution manager for multi-agent conversations
18
+ - **[Context](concepts/context.html)** - Serializable state management that persists across agent interactions
19
+ - **[Handoffs](concepts/handoffs.html)** - Tool-based mechanism for seamless agent transitions
20
+ - **[Tools](concepts/tools.html)** - Stateless extensions for external system integration
21
+ - **[Callbacks](concepts/callbacks.html)** - Real-time notifications for agent thinking, tool execution, and handoffs
22
+ - **[AgentTool](concepts/agent-tool.html)** - Agent-to-agent collaboration without conversation handoffs