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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71f4852e0a5f1048dfeefa4b3ab678de189977940330f81425b60f687e66adc0
4
- data.tar.gz: 5661b109441687dbdba66208c71798e91bbd338e857619fcc3b634b4542662cb
3
+ metadata.gz: 9a2d71857e9614f2dad748a9510a5f29925cf070dd7a7dad59ac8a243eadbc28
4
+ data.tar.gz: e0f836ad647303fdd3482ca1f1d144bda34573d74ff8741487a4a0b550860f01
5
5
  SHA512:
6
- metadata.gz: 8070c4bc7fd3d9aa9477b2566d0e96f44166c8320b12cdf50017c9c19081ef539a7c20b369714afc0fb468f7c767fcb8f50ff52aae3491357bee69971c99a531
7
- data.tar.gz: 0d11ee7a275f5a240ceaf695ac3a3a1aea196aaa452f6fb38cb79ca8e411fd278b9fa526e3a48911ec856c96d84d2cc565ff5f7359771d3a2057ecbdf6b4f675
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: 2
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
- button: false
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
- **Provide analysis in this format:**
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
- puts result.output
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 # Lower temperature for consistent routing decisions
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
- puts "🤖 #{result.output || "[No output]"}"
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
- def initialize(name:, instructions: nil, model: "gpt-4.1-mini", tools: [], handoff_agents: [], temperature: 0.7)
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, **options)
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 # Pass context directly
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Agents
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
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.3.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