ai-agents 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +1 -0
- data/docs/_config.yml +8 -6
- data/docs/assets/images/ai-agents.png +0 -0
- data/docs/assets/images/favicon.png +0 -0
- data/docs/guides/structured-output.md +76 -0
- data/docs/guides.md +1 -0
- data/examples/collaborative-copilot/agents/analysis_agent.rb +117 -8
- data/examples/collaborative-copilot/interactive.rb +48 -1
- data/examples/isp-support/agents_factory.rb +36 -1
- data/examples/isp-support/interactive.rb +18 -1
- data/lib/agents/agent.rb +25 -3
- data/lib/agents/chat.rb +5 -1
- data/lib/agents/runner.rb +2 -1
- data/lib/agents/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a2d71857e9614f2dad748a9510a5f29925cf070dd7a7dad59ac8a243eadbc28
|
4
|
+
data.tar.gz: e0f836ad647303fdd3482ca1f1d144bda34573d74ff8741487a4a0b550860f01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2728cc0f9e4b377c438ecfd73db1ff0e72e3399f8c5602c330b78fcfb53f081bbd80e44bb3036549f0b906b8df09da5ecff371af3b895f7b0e98645e74809967
|
7
|
+
data.tar.gz: c22baa1ce3c5aef5e1a2d17e208eb040f49edb46f4d275946e93fab506dcb3cc40eb1dfc9638f62beadec19a61421fe27d9fe079268b0b867b7c787a2c31cb19
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.4.0] - 2025-08-01
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- **Structured Output Support**: Agents can now enforce JSON schema validation for responses
|
12
|
+
- Added `response_schema` parameter to `Agent#initialize` supporting both JSON Schema objects and `RubyLLM::Schema` classes
|
13
|
+
- Automatic response validation ensures agents return data in predictable formats
|
14
|
+
- Full support for complex nested schemas with objects, arrays, and validation constraints
|
15
|
+
- Multi-agent systems can use different response schemas per agent
|
16
|
+
- Comprehensive documentation with examples for data extraction and API integrations
|
17
|
+
- Updated to RubyLLM 1.5.0 with enhanced structured output capabilities
|
18
|
+
- New structured output documentation guide with practical examples
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- Enhanced `Chat` class to pass through `response_schema` configuration to underlying RubyLLM provider
|
22
|
+
- Improved agent cloning to preserve and override response schemas
|
23
|
+
|
8
24
|
## [0.3.0] - 2025-07-22
|
9
25
|
|
10
26
|
### Added
|
data/README.md
CHANGED
@@ -18,6 +18,7 @@ A delightful provider agnostic Ruby SDK for building multi-agent AI workflows wi
|
|
18
18
|
- **🤖 Multi-Agent Orchestration**: Create specialized AI agents that work together
|
19
19
|
- **🔄 Seamless Handoffs**: Transparent agent-to-agent transfers (users never know!)
|
20
20
|
- **🛠️ Tool Integration**: Agents can use custom tools and functions
|
21
|
+
- **📊 Structured Output**: JSON schema-validated responses for reliable data extraction
|
21
22
|
- **💾 Shared Context**: State management across agent interactions
|
22
23
|
- **🔌 Provider Agnostic**: Works with OpenAI, Anthropic, and other LLM providers
|
23
24
|
|
data/docs/_config.yml
CHANGED
@@ -2,6 +2,8 @@ title: AI Agents
|
|
2
2
|
description: A Ruby SDK for building multi-agent AI workflows
|
3
3
|
baseurl: ""
|
4
4
|
url: ""
|
5
|
+
logo: "/assets/images/ai-agents.png"
|
6
|
+
favicon_ico: "/assets/images/favicon.png"
|
5
7
|
|
6
8
|
# Theme
|
7
9
|
theme: just-the-docs
|
@@ -12,19 +14,19 @@ color_scheme: ruby
|
|
12
14
|
# Search
|
13
15
|
search_enabled: true
|
14
16
|
search:
|
15
|
-
heading_level:
|
16
|
-
previews: 3
|
17
|
-
preview_words_before: 5
|
18
|
-
preview_words_after: 10
|
19
|
-
tokenizer_separator: /[\s/]+/
|
17
|
+
heading_level: 3
|
20
18
|
rel_url: true
|
21
|
-
|
19
|
+
focus_shortcut_key: "k"
|
22
20
|
|
23
21
|
# Navigation
|
24
22
|
nav_sort: case_insensitive
|
25
23
|
nav_external_links:
|
26
24
|
- title: GitHub
|
27
25
|
url: https://github.com/chatwoot/ai-agents
|
26
|
+
- title: RubyLLM
|
27
|
+
url: https://rubyllm.com
|
28
|
+
- title: Chatwoot
|
29
|
+
url: https://chatwoot.com/
|
28
30
|
|
29
31
|
# Footer
|
30
32
|
footer_content: "Copyright © 2025 Chatwoot Inc."
|
Binary file
|
Binary file
|
@@ -0,0 +1,76 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
title: Structured Output
|
4
|
+
parent: Guides
|
5
|
+
nav_order: 5
|
6
|
+
---
|
7
|
+
|
8
|
+
# Structured Output
|
9
|
+
|
10
|
+
Structured output ensures AI agents return responses in a predictable JSON format that conforms to a specified schema. This is useful for building reliable integrations, data extraction workflows, and applications that need consistent response formats.
|
11
|
+
|
12
|
+
## Basic Usage
|
13
|
+
|
14
|
+
Add a `response_schema` when creating an agent to enforce structured responses:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# JSON Schema approach
|
18
|
+
extraction_agent = Agents::Agent.new(
|
19
|
+
name: "DataExtractor",
|
20
|
+
instructions: "Extract key information from user messages",
|
21
|
+
response_schema: {
|
22
|
+
type: 'object',
|
23
|
+
properties: {
|
24
|
+
entities: { type: 'array', items: { type: 'string' } },
|
25
|
+
sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
|
26
|
+
summary: { type: 'string' }
|
27
|
+
},
|
28
|
+
required: ['entities', 'sentiment'],
|
29
|
+
additionalProperties: false
|
30
|
+
}
|
31
|
+
)
|
32
|
+
|
33
|
+
runner = Agents::AgentRunner.with_agents(extraction_agent)
|
34
|
+
result = runner.run("I love the new product features, especially the API and dashboard!")
|
35
|
+
|
36
|
+
# Response will be valid JSON matching the schema:
|
37
|
+
# {
|
38
|
+
# "entities": ["API", "dashboard"],
|
39
|
+
# "sentiment": "positive",
|
40
|
+
# "summary": "User expresses enthusiasm for new product features"
|
41
|
+
# }
|
42
|
+
```
|
43
|
+
|
44
|
+
## RubyLLM::Schema (Recommended)
|
45
|
+
|
46
|
+
For more complex schemas, use `RubyLLM::Schema` which provides a cleaner Ruby DSL:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class ContactSchema < RubyLLM::Schema
|
50
|
+
string :name, description: "Full name of the person"
|
51
|
+
string :email, description: "Email address"
|
52
|
+
string :phone, description: "Phone number", required: false
|
53
|
+
string :company, description: "Company name", required: false
|
54
|
+
array :interests, description: "List of interests or topics mentioned" do
|
55
|
+
string description: "Individual interest or topic"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
contact_agent = Agents::Agent.new(
|
60
|
+
name: "ContactExtractor",
|
61
|
+
instructions: "Extract contact information from business communications",
|
62
|
+
response_schema: ContactSchema
|
63
|
+
)
|
64
|
+
|
65
|
+
runner = Agents::AgentRunner.with_agents(contact_agent)
|
66
|
+
result = runner.run("Hi, I'm Sarah Johnson from TechCorp. You can reach me at sarah@techcorp.com or 555-0123. I'm interested in AI and automation solutions.")
|
67
|
+
|
68
|
+
# Returns structured contact data:
|
69
|
+
# {
|
70
|
+
# "name": "Sarah Johnson",
|
71
|
+
# "email": "sarah@techcorp.com",
|
72
|
+
# "phone": "555-0123",
|
73
|
+
# "company": "TechCorp",
|
74
|
+
# "interests": ["AI", "automation solutions"]
|
75
|
+
# }
|
76
|
+
```
|
data/docs/guides.md
CHANGED
@@ -16,3 +16,4 @@ Practical guides for building real-world applications with the AI Agents library
|
|
16
16
|
- **[Agent-as-Tool Pattern](guides/agent-as-tool-pattern.html)** - Enable agent collaboration behind the scenes without conversation handoffs
|
17
17
|
- **[Rails Integration](guides/rails-integration.html)** - Integrating agents with Ruby on Rails applications and ActiveRecord persistence
|
18
18
|
- **[State Persistence](guides/state-persistence.html)** - Managing conversation state and context across sessions and processes
|
19
|
+
- **[Structured Output](guides/structured-output.html)** - Enforcing JSON schema validation for reliable agent responses
|
@@ -11,7 +11,8 @@ module Copilot
|
|
11
11
|
model: "gpt-4o-mini",
|
12
12
|
tools: [
|
13
13
|
GetConversationTool.new
|
14
|
-
]
|
14
|
+
],
|
15
|
+
response_schema: analysis_response_schema
|
15
16
|
)
|
16
17
|
end
|
17
18
|
|
@@ -34,15 +35,123 @@ module Copilot
|
|
34
35
|
3. Assess how well the conversation is progressing
|
35
36
|
4. Identify any escalation risks or satisfaction issues
|
36
37
|
|
37
|
-
|
38
|
-
- **Conversation Health**: Overall assessment of how the conversation is going
|
39
|
-
- **Customer Sentiment**: Current emotional state and any changes over time
|
40
|
-
- **Communication Quality**: How well the agent is handling the situation
|
41
|
-
- **Risk Assessment**: Any signs of escalation or dissatisfaction
|
42
|
-
- **Tone Recommendations**: Suggested communication approach and tone
|
43
|
-
|
38
|
+
Your response MUST be in the required JSON format with all the specified fields.
|
44
39
|
Focus on practical communication advice that will improve the interaction.
|
45
40
|
INSTRUCTIONS
|
46
41
|
end
|
42
|
+
|
43
|
+
def self.analysis_response_schema
|
44
|
+
{
|
45
|
+
type: "object",
|
46
|
+
properties: {
|
47
|
+
conversation_health: {
|
48
|
+
type: "object",
|
49
|
+
properties: {
|
50
|
+
status: {
|
51
|
+
type: "string",
|
52
|
+
enum: %w[excellent good fair concerning critical],
|
53
|
+
description: "Overall health status of the conversation"
|
54
|
+
},
|
55
|
+
summary: {
|
56
|
+
type: "string",
|
57
|
+
description: "Brief assessment of how the conversation is progressing"
|
58
|
+
}
|
59
|
+
},
|
60
|
+
required: %w[status summary]
|
61
|
+
},
|
62
|
+
customer_sentiment: {
|
63
|
+
type: "object",
|
64
|
+
properties: {
|
65
|
+
current: {
|
66
|
+
type: "string",
|
67
|
+
enum: %w[very_positive positive neutral negative very_negative],
|
68
|
+
description: "Current emotional state"
|
69
|
+
},
|
70
|
+
trajectory: {
|
71
|
+
type: "string",
|
72
|
+
enum: %w[improving stable declining],
|
73
|
+
description: "How sentiment is changing over time"
|
74
|
+
},
|
75
|
+
key_emotions: {
|
76
|
+
type: "array",
|
77
|
+
items: { type: "string" },
|
78
|
+
description: "Dominant emotions detected (e.g., frustrated, satisfied, confused)"
|
79
|
+
}
|
80
|
+
},
|
81
|
+
required: %w[current trajectory key_emotions]
|
82
|
+
},
|
83
|
+
communication_quality: {
|
84
|
+
type: "object",
|
85
|
+
properties: {
|
86
|
+
score: {
|
87
|
+
type: "integer",
|
88
|
+
minimum: 1,
|
89
|
+
maximum: 10,
|
90
|
+
description: "Overall communication quality score"
|
91
|
+
},
|
92
|
+
strengths: {
|
93
|
+
type: "array",
|
94
|
+
items: { type: "string" },
|
95
|
+
description: "What the agent is doing well"
|
96
|
+
},
|
97
|
+
areas_for_improvement: {
|
98
|
+
type: "array",
|
99
|
+
items: { type: "string" },
|
100
|
+
description: "Areas that could be improved"
|
101
|
+
}
|
102
|
+
},
|
103
|
+
required: %w[score strengths areas_for_improvement]
|
104
|
+
},
|
105
|
+
risk_assessment: {
|
106
|
+
type: "object",
|
107
|
+
properties: {
|
108
|
+
escalation_risk: {
|
109
|
+
type: "string",
|
110
|
+
enum: %w[none low medium high],
|
111
|
+
description: "Risk of conversation escalating"
|
112
|
+
},
|
113
|
+
churn_risk: {
|
114
|
+
type: "string",
|
115
|
+
enum: %w[none low medium high],
|
116
|
+
description: "Risk of customer churning"
|
117
|
+
},
|
118
|
+
warning_signs: {
|
119
|
+
type: "array",
|
120
|
+
items: { type: "string" },
|
121
|
+
description: "Specific warning signs detected"
|
122
|
+
}
|
123
|
+
},
|
124
|
+
required: %w[escalation_risk churn_risk warning_signs]
|
125
|
+
},
|
126
|
+
tone_recommendations: {
|
127
|
+
type: "object",
|
128
|
+
properties: {
|
129
|
+
recommended_tone: {
|
130
|
+
type: "string",
|
131
|
+
description: "Suggested overall tone (e.g., empathetic, professional, friendly)"
|
132
|
+
},
|
133
|
+
key_phrases: {
|
134
|
+
type: "array",
|
135
|
+
items: { type: "string" },
|
136
|
+
description: "Suggested phrases to use"
|
137
|
+
},
|
138
|
+
avoid_phrases: {
|
139
|
+
type: "array",
|
140
|
+
items: { type: "string" },
|
141
|
+
description: "Phrases or approaches to avoid"
|
142
|
+
},
|
143
|
+
next_steps: {
|
144
|
+
type: "string",
|
145
|
+
description: "Recommended immediate next action"
|
146
|
+
}
|
147
|
+
},
|
148
|
+
required: %w[recommended_tone key_phrases avoid_phrases next_steps]
|
149
|
+
}
|
150
|
+
},
|
151
|
+
required: %w[conversation_health customer_sentiment communication_quality risk_assessment
|
152
|
+
tone_recommendations],
|
153
|
+
additionalProperties: false
|
154
|
+
}
|
155
|
+
end
|
47
156
|
end
|
48
157
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "json"
|
4
5
|
require_relative "../../lib/agents"
|
5
6
|
require_relative "agents/copilot_orchestrator"
|
6
7
|
|
@@ -80,7 +81,53 @@ loop do
|
|
80
81
|
# Update context with the returned context from Runner
|
81
82
|
context = result.context if result.respond_to?(:context) && result.context
|
82
83
|
|
83
|
-
|
84
|
+
output = result.output
|
85
|
+
|
86
|
+
# Check if the output might be structured JSON (e.g., from Analysis Agent)
|
87
|
+
if output&.strip&.start_with?("{") && context[:current_agent] == "Analysis Agent"
|
88
|
+
begin
|
89
|
+
analysis = JSON.parse(output)
|
90
|
+
|
91
|
+
# Display structured analysis in a readable format
|
92
|
+
puts "\n📊 Conversation Analysis:"
|
93
|
+
puts "━" * 60
|
94
|
+
|
95
|
+
# Conversation Health
|
96
|
+
health = analysis["conversation_health"]
|
97
|
+
puts "🏥 Health: #{health["status"].upcase} - #{health["summary"]}"
|
98
|
+
|
99
|
+
# Customer Sentiment
|
100
|
+
sentiment = analysis["customer_sentiment"]
|
101
|
+
puts "😊 Sentiment: #{sentiment["current"]} (#{sentiment["trajectory"]})"
|
102
|
+
puts " Emotions: #{sentiment["key_emotions"].join(", ")}"
|
103
|
+
|
104
|
+
# Communication Quality
|
105
|
+
quality = analysis["communication_quality"]
|
106
|
+
puts "💬 Communication: #{quality["score"]}/10"
|
107
|
+
puts " ✅ Strengths: #{quality["strengths"].join(", ")}" if quality["strengths"].any?
|
108
|
+
puts " ⚠️ Improve: #{quality["areas_for_improvement"].join(", ")}" if quality["areas_for_improvement"].any?
|
109
|
+
|
110
|
+
# Risk Assessment
|
111
|
+
risk = analysis["risk_assessment"]
|
112
|
+
puts "⚠️ Risks: Escalation=#{risk["escalation_risk"]}, Churn=#{risk["churn_risk"]}"
|
113
|
+
puts " Warning signs: #{risk["warning_signs"].join(", ")}" if risk["warning_signs"].any?
|
114
|
+
|
115
|
+
# Tone Recommendations
|
116
|
+
tone = analysis["tone_recommendations"]
|
117
|
+
puts "\n💡 Recommendations:"
|
118
|
+
puts " Tone: #{tone["recommended_tone"]}"
|
119
|
+
puts " Next: #{tone["next_steps"]}"
|
120
|
+
puts " Use: #{tone["key_phrases"].join("; ")}" if tone["key_phrases"].any?
|
121
|
+
puts " Avoid: #{tone["avoid_phrases"].join("; ")}" if tone["avoid_phrases"].any?
|
122
|
+
|
123
|
+
puts "━" * 60
|
124
|
+
rescue JSON::ParserError
|
125
|
+
# Fall back to regular output if not valid JSON
|
126
|
+
puts output
|
127
|
+
end
|
128
|
+
else
|
129
|
+
puts output
|
130
|
+
end
|
84
131
|
rescue StandardError => e
|
85
132
|
puts "Error: #{e.message}"
|
86
133
|
end
|
@@ -45,7 +45,8 @@ module ISPSupport
|
|
45
45
|
instructions: triage_instructions,
|
46
46
|
model: "gpt-4.1-mini",
|
47
47
|
tools: [],
|
48
|
-
temperature: 0.3
|
48
|
+
temperature: 0.3, # Lower temperature for consistent routing decisions
|
49
|
+
response_schema: triage_response_schema
|
49
50
|
)
|
50
51
|
end
|
51
52
|
|
@@ -88,9 +89,43 @@ module ISPSupport
|
|
88
89
|
- If unclear, ask one clarifying question before routing
|
89
90
|
|
90
91
|
Keep responses brief and professional. Use handoff tools to transfer to specialists.
|
92
|
+
|
93
|
+
Your response MUST be in the required JSON format with greeting, intent_category, needs_clarification, clarifying_question, and recommended_agent fields.
|
91
94
|
INSTRUCTIONS
|
92
95
|
end
|
93
96
|
|
97
|
+
def triage_response_schema
|
98
|
+
{
|
99
|
+
type: "object",
|
100
|
+
properties: {
|
101
|
+
greeting: {
|
102
|
+
type: "string",
|
103
|
+
description: "A brief, friendly greeting acknowledging the customer's inquiry"
|
104
|
+
},
|
105
|
+
intent_category: {
|
106
|
+
type: "string",
|
107
|
+
enum: %w[sales support unclear],
|
108
|
+
description: "The detected category of the customer's intent"
|
109
|
+
},
|
110
|
+
needs_clarification: {
|
111
|
+
type: "boolean",
|
112
|
+
description: "Whether the intent is unclear and needs clarification"
|
113
|
+
},
|
114
|
+
clarifying_question: {
|
115
|
+
type: ["string", "null"],
|
116
|
+
description: "A question to ask if the intent is unclear (null if clear)"
|
117
|
+
},
|
118
|
+
recommended_agent: {
|
119
|
+
type: ["string", "null"],
|
120
|
+
enum: ["Sales Agent", "Support Agent", null],
|
121
|
+
description: "The recommended specialist agent to route to (null if unclear)"
|
122
|
+
}
|
123
|
+
},
|
124
|
+
required: %w[greeting intent_category needs_clarification],
|
125
|
+
additionalProperties: false
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
94
129
|
def sales_instructions
|
95
130
|
<<~INSTRUCTIONS
|
96
131
|
You are the Sales Agent for an ISP. You handle new customer acquisition, service upgrades,
|
@@ -55,7 +55,24 @@ class ISPSupportDemo
|
|
55
55
|
|
56
56
|
# Clear status and show response
|
57
57
|
clear_status_line
|
58
|
-
|
58
|
+
|
59
|
+
# Handle structured output from triage agent
|
60
|
+
output = result.output || "[No output]"
|
61
|
+
if @context[:current_agent] == "Triage Agent" && output.start_with?("{")
|
62
|
+
begin
|
63
|
+
structured = JSON.parse(output)
|
64
|
+
# Display the greeting from structured response
|
65
|
+
puts "🤖 #{structured["greeting"]}"
|
66
|
+
if structured["intent_category"]
|
67
|
+
puts " [Intent: #{structured["intent_category"]}, Routing to: #{structured["recommended_agent"] || "TBD"}]"
|
68
|
+
end
|
69
|
+
rescue JSON::ParserError
|
70
|
+
# Fall back to regular output if not valid JSON
|
71
|
+
puts "🤖 #{output}"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
puts "🤖 #{output}"
|
75
|
+
end
|
59
76
|
|
60
77
|
puts
|
61
78
|
end
|
data/lib/agents/agent.rb
CHANGED
@@ -13,6 +13,23 @@
|
|
13
13
|
# tools: [calculator_tool, weather_tool]
|
14
14
|
# )
|
15
15
|
#
|
16
|
+
# @example Creating an agent with structured output
|
17
|
+
# agent = Agents::Agent.new(
|
18
|
+
# name: "DataExtractor",
|
19
|
+
# instructions: "Extract structured information from user input",
|
20
|
+
# model: "gpt-4o",
|
21
|
+
# response_schema: {
|
22
|
+
# type: 'object',
|
23
|
+
# properties: {
|
24
|
+
# entities: { type: 'array', items: { type: 'string' } },
|
25
|
+
# sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
|
26
|
+
# summary: { type: 'string' }
|
27
|
+
# },
|
28
|
+
# required: ['entities', 'sentiment'],
|
29
|
+
# additionalProperties: false
|
30
|
+
# }
|
31
|
+
# )
|
32
|
+
#
|
16
33
|
# @example Creating an agent with dynamic state-aware instructions
|
17
34
|
# agent = Agents::Agent.new(
|
18
35
|
# name: "Support Agent",
|
@@ -33,7 +50,7 @@
|
|
33
50
|
# )
|
34
51
|
module Agents
|
35
52
|
class Agent
|
36
|
-
attr_reader :name, :instructions, :model, :tools, :handoff_agents, :temperature
|
53
|
+
attr_reader :name, :instructions, :model, :tools, :handoff_agents, :temperature, :response_schema
|
37
54
|
|
38
55
|
# Initialize a new Agent instance
|
39
56
|
#
|
@@ -43,13 +60,16 @@ module Agents
|
|
43
60
|
# @param tools [Array<Agents::Tool>] Array of tool instances the agent can use
|
44
61
|
# @param handoff_agents [Array<Agents::Agent>] Array of agents this agent can hand off to
|
45
62
|
# @param temperature [Float] Controls randomness in responses (0.0 = deterministic, 1.0 = very random, default: 0.7)
|
46
|
-
|
63
|
+
# @param response_schema [Hash, nil] JSON schema for structured output responses
|
64
|
+
def initialize(name:, instructions: nil, model: "gpt-4.1-mini", tools: [], handoff_agents: [], temperature: 0.7,
|
65
|
+
response_schema: nil)
|
47
66
|
@name = name
|
48
67
|
@instructions = instructions
|
49
68
|
@model = model
|
50
69
|
@tools = tools.dup
|
51
70
|
@handoff_agents = []
|
52
71
|
@temperature = temperature
|
72
|
+
@response_schema = response_schema
|
53
73
|
|
54
74
|
# Mutex for thread-safe handoff registration
|
55
75
|
# While agents are typically configured at startup, we want to ensure
|
@@ -134,6 +154,7 @@ module Agents
|
|
134
154
|
# @option changes [Array<Agents::Tool>] :tools New tools array (replaces all tools)
|
135
155
|
# @option changes [Array<Agents::Agent>] :handoff_agents New handoff agents
|
136
156
|
# @option changes [Float] :temperature Temperature for LLM responses (0.0-1.0)
|
157
|
+
# @option changes [Hash, nil] :response_schema JSON schema for structured output
|
137
158
|
# @return [Agents::Agent] A new frozen agent instance with the specified changes
|
138
159
|
def clone(**changes)
|
139
160
|
self.class.new(
|
@@ -142,7 +163,8 @@ module Agents
|
|
142
163
|
model: changes.fetch(:model, @model),
|
143
164
|
tools: changes.fetch(:tools, @tools.dup),
|
144
165
|
handoff_agents: changes.fetch(:handoff_agents, @handoff_agents),
|
145
|
-
temperature: changes.fetch(:temperature, @temperature)
|
166
|
+
temperature: changes.fetch(:temperature, @temperature),
|
167
|
+
response_schema: changes.fetch(:response_schema, @response_schema)
|
146
168
|
)
|
147
169
|
end
|
148
170
|
|
data/lib/agents/chat.rb
CHANGED
@@ -26,7 +26,8 @@ module Agents
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def initialize(model: nil, handoff_tools: [], context_wrapper: nil, temperature: nil,
|
29
|
+
def initialize(model: nil, handoff_tools: [], context_wrapper: nil, temperature: nil, response_schema: nil,
|
30
|
+
**options)
|
30
31
|
super(model: model, **options)
|
31
32
|
@handoff_tools = handoff_tools
|
32
33
|
@context_wrapper = context_wrapper
|
@@ -34,6 +35,9 @@ module Agents
|
|
34
35
|
# Set temperature if provided (RubyLLM::Chat sets this via accessor)
|
35
36
|
@temperature = temperature if temperature
|
36
37
|
|
38
|
+
# Set response schema if provided
|
39
|
+
with_schema(response_schema) if response_schema
|
40
|
+
|
37
41
|
# Register handoff tools with RubyLLM for schema generation
|
38
42
|
@handoff_tools.each { |tool| with_tool(tool) }
|
39
43
|
end
|
data/lib/agents/runner.rb
CHANGED
@@ -248,7 +248,8 @@ module Agents
|
|
248
248
|
model: agent.model,
|
249
249
|
temperature: agent.temperature,
|
250
250
|
handoff_tools: handoff_tools, # Direct tools, no wrapper
|
251
|
-
context_wrapper: context_wrapper
|
251
|
+
context_wrapper: context_wrapper, # Pass context directly
|
252
|
+
response_schema: agent.response_schema # Pass structured output schema
|
252
253
|
)
|
253
254
|
|
254
255
|
chat.with_instructions(system_prompt) if system_prompt
|
data/lib/agents/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ai-agents
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shivam Mishra
|
@@ -46,6 +46,8 @@ files:
|
|
46
46
|
- docs/_sass/custom/custom.scss
|
47
47
|
- docs/architecture.md
|
48
48
|
- docs/assets/fonts/InterVariable.woff2
|
49
|
+
- docs/assets/images/ai-agents.png
|
50
|
+
- docs/assets/images/favicon.png
|
49
51
|
- docs/concepts.md
|
50
52
|
- docs/concepts/agent-tool.md
|
51
53
|
- docs/concepts/agents.md
|
@@ -59,6 +61,7 @@ files:
|
|
59
61
|
- docs/guides/multi-agent-systems.md
|
60
62
|
- docs/guides/rails-integration.md
|
61
63
|
- docs/guides/state-persistence.md
|
64
|
+
- docs/guides/structured-output.md
|
62
65
|
- docs/index.md
|
63
66
|
- examples/README.md
|
64
67
|
- examples/collaborative-copilot/README.md
|