ai-agents 0.1.1 → 0.1.2
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/examples/isp-support/agents_factory.rb +57 -1
- data/examples/isp-support/tools/create_lead_tool.rb +16 -2
- data/examples/isp-support/tools/crm_lookup_tool.rb +13 -1
- data/lib/agents/agent.rb +18 -6
- data/lib/agents/tool_context.rb +36 -0
- data/lib/agents/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 004f33c8652e5b8c4d8be7bd84e959c55f6926411d3cac8bd3b3d029b7b21690
|
4
|
+
data.tar.gz: 6592c590cd91809ee47865a25adcf734370695bd4c697b1926aabc95c6e2d574
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0baadd9d0771c8e2bdd345a0a8e00bc792c6a607ec78b8796a6718e4695dc5e92b2fdc2ec11b4384c77ad409af4660be5288b020c6404750a2db5a66d1cf094c
|
7
|
+
data.tar.gz: de6edbb743e8b01634aaa12c656bc7cfa618de8086aa4e4e6add08683e8383b611856b4b9fa452c2e2e2bbd3efa2a74edefcc17af8188d2605babfc5067ecaf2
|
@@ -51,7 +51,7 @@ module ISPSupport
|
|
51
51
|
def create_sales_agent
|
52
52
|
Agents::Agent.new(
|
53
53
|
name: "Sales Agent",
|
54
|
-
instructions:
|
54
|
+
instructions: sales_instructions_with_state,
|
55
55
|
model: "gpt-4.1-mini",
|
56
56
|
tools: [ISPSupport::CreateLeadTool.new, ISPSupport::CreateCheckoutTool.new]
|
57
57
|
)
|
@@ -113,6 +113,62 @@ module ISPSupport
|
|
113
113
|
INSTRUCTIONS
|
114
114
|
end
|
115
115
|
|
116
|
+
def sales_instructions_with_state
|
117
|
+
lambda { |context|
|
118
|
+
state = context.context[:state] || {}
|
119
|
+
|
120
|
+
base_instructions = <<~INSTRUCTIONS
|
121
|
+
You are the Sales Agent for an ISP. You handle new customer acquisition, service upgrades,
|
122
|
+
and plan changes.
|
123
|
+
|
124
|
+
**Your tools:**
|
125
|
+
- `create_lead`: Create sales leads with customer information
|
126
|
+
- `create_checkout`: Generate secure checkout links for purchases
|
127
|
+
- Handoff tools: Route back to triage when needed
|
128
|
+
|
129
|
+
**When to hand off:**
|
130
|
+
- Pure technical support questions → Triage Agent for re-routing
|
131
|
+
- Customer needs to speak with human agent → Triage Agent for re-routing
|
132
|
+
INSTRUCTIONS
|
133
|
+
|
134
|
+
# Add customer context if available from previous agent interactions
|
135
|
+
if state[:customer_name] && state[:customer_id]
|
136
|
+
base_instructions += <<~CONTEXT
|
137
|
+
|
138
|
+
**Customer Context Available:**
|
139
|
+
- Customer Name: #{state[:customer_name]}
|
140
|
+
- Customer ID: #{state[:customer_id]}
|
141
|
+
- Email: #{state[:customer_email]}
|
142
|
+
- Phone: #{state[:customer_phone]}
|
143
|
+
- Address: #{state[:customer_address]}
|
144
|
+
#{state[:current_plan] ? "- Current Plan: #{state[:current_plan]}" : ""}
|
145
|
+
#{state[:account_status] ? "- Account Status: #{state[:account_status]}" : ""}
|
146
|
+
#{state[:monthly_usage] ? "- Monthly Usage: #{state[:monthly_usage]}GB" : ""}
|
147
|
+
|
148
|
+
**IMPORTANT:**#{" "}
|
149
|
+
- Use this existing customer information when creating leads or providing service
|
150
|
+
- Do NOT ask for name, email, phone, or address - you already have these details
|
151
|
+
- For new connections, use the existing customer details and only ask for the desired plan
|
152
|
+
- Provide personalized recommendations based on their current information
|
153
|
+
CONTEXT
|
154
|
+
end
|
155
|
+
|
156
|
+
base_instructions += <<~FINAL_INSTRUCTIONS
|
157
|
+
|
158
|
+
**Instructions:**
|
159
|
+
- Be enthusiastic but not pushy
|
160
|
+
- Gather required info: name, email, desired plan for leads
|
161
|
+
- For account verification, ask customer for their account details directly
|
162
|
+
- For existing customers wanting upgrades, collect account info and proceed
|
163
|
+
- Create checkout links for confirmed purchases
|
164
|
+
- Always explain next steps after creating leads or checkout links
|
165
|
+
- Handle billing questions yourself - don't hand off for account verification
|
166
|
+
FINAL_INSTRUCTIONS
|
167
|
+
|
168
|
+
base_instructions
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
116
172
|
def support_instructions
|
117
173
|
<<~INSTRUCTIONS
|
118
174
|
You are the Support Agent for an ISP. You handle technical support, troubleshooting,
|
@@ -8,8 +8,22 @@ module ISPSupport
|
|
8
8
|
param :email, type: "string", desc: "Customer's email address"
|
9
9
|
param :desired_plan, type: "string", desc: "Plan the customer is interested in"
|
10
10
|
|
11
|
-
def perform(
|
12
|
-
|
11
|
+
def perform(tool_context, name:, email:, desired_plan:)
|
12
|
+
# Store lead information in state for follow-up
|
13
|
+
tool_context.state[:lead_name] = name
|
14
|
+
tool_context.state[:lead_email] = email
|
15
|
+
tool_context.state[:desired_plan] = desired_plan
|
16
|
+
tool_context.state[:lead_created_at] = Time.now.iso8601
|
17
|
+
|
18
|
+
# Check if we have existing customer info from CRM lookup
|
19
|
+
if tool_context.state[:customer_id]
|
20
|
+
existing_customer = tool_context.state[:customer_name]
|
21
|
+
"Lead created for existing customer #{existing_customer} (#{email}) " \
|
22
|
+
"interested in upgrading to #{desired_plan} plan. Sales team will contact within 24 hours."
|
23
|
+
else
|
24
|
+
"Lead created for #{name} (#{email}) interested in #{desired_plan} plan. " \
|
25
|
+
"Sales team will contact within 24 hours."
|
26
|
+
end
|
13
27
|
end
|
14
28
|
end
|
15
29
|
end
|
@@ -8,7 +8,7 @@ module ISPSupport
|
|
8
8
|
description "Look up customer account information by account ID"
|
9
9
|
param :account_id, type: "string", desc: "Customer account ID (e.g., CUST001)"
|
10
10
|
|
11
|
-
def perform(
|
11
|
+
def perform(tool_context, account_id:)
|
12
12
|
data_file = File.join(__dir__, "../data/customers.json")
|
13
13
|
return "Customer database unavailable" unless File.exist?(data_file)
|
14
14
|
|
@@ -18,6 +18,18 @@ module ISPSupport
|
|
18
18
|
|
19
19
|
return "Customer not found" unless customer
|
20
20
|
|
21
|
+
# Store customer information in shared state for other tools/agents
|
22
|
+
tool_context.state[:customer_id] = account_id.upcase
|
23
|
+
tool_context.state[:customer_name] = customer["name"]
|
24
|
+
tool_context.state[:customer_email] = customer["email"]
|
25
|
+
tool_context.state[:customer_phone] = customer["phone"]
|
26
|
+
tool_context.state[:customer_address] = customer["address"]
|
27
|
+
tool_context.state[:current_plan] = customer["plan"]["name"]
|
28
|
+
tool_context.state[:account_status] = customer["account_status"]
|
29
|
+
tool_context.state[:plan_price] = customer["plan"]["price"]
|
30
|
+
tool_context.state[:next_bill_date] = customer["billing"]["next_bill_date"]
|
31
|
+
tool_context.state[:account_balance] = customer["billing"]["balance"]
|
32
|
+
|
21
33
|
# Return the entire customer data as JSON for the agent to process
|
22
34
|
customer.to_json
|
23
35
|
rescue StandardError
|
data/lib/agents/agent.rb
CHANGED
@@ -13,11 +13,16 @@
|
|
13
13
|
# tools: [calculator_tool, weather_tool]
|
14
14
|
# )
|
15
15
|
#
|
16
|
-
# @example Creating an agent with dynamic instructions
|
16
|
+
# @example Creating an agent with dynamic state-aware instructions
|
17
17
|
# agent = Agents::Agent.new(
|
18
18
|
# name: "Support Agent",
|
19
19
|
# instructions: ->(context) {
|
20
|
-
#
|
20
|
+
# state = context.context[:state] || {}
|
21
|
+
# base = "You are a support agent."
|
22
|
+
# if state[:customer_name]
|
23
|
+
# base += " Customer: #{state[:customer_name]} (#{state[:customer_id]})"
|
24
|
+
# end
|
25
|
+
# base
|
21
26
|
# }
|
22
27
|
# )
|
23
28
|
#
|
@@ -147,18 +152,25 @@ module Agents
|
|
147
152
|
# instructions: "You are a helpful support agent"
|
148
153
|
# )
|
149
154
|
#
|
150
|
-
# @example Dynamic instructions
|
155
|
+
# @example Dynamic instructions with state awareness
|
151
156
|
# agent = Agent.new(
|
152
|
-
# name: "
|
157
|
+
# name: "Sales Agent",
|
153
158
|
# instructions: ->(context) {
|
154
|
-
#
|
155
|
-
# "You are
|
159
|
+
# state = context.context[:state] || {}
|
160
|
+
# base = "You are a sales agent."
|
161
|
+
# if state[:customer_name] && state[:current_plan]
|
162
|
+
# base += " Customer: #{state[:customer_name]} on #{state[:current_plan]} plan."
|
163
|
+
# end
|
164
|
+
# base
|
156
165
|
# }
|
157
166
|
# )
|
158
167
|
#
|
159
168
|
# @param context [Agents::RunContext] The current execution context containing runtime data
|
160
169
|
# @return [String, nil] The system prompt string or nil if no instructions are set
|
161
170
|
def get_system_prompt(context)
|
171
|
+
# TODO: Add string interpolation support for instructions
|
172
|
+
# Allow instructions like "You are helping %{customer_name}" that automatically
|
173
|
+
# get state values injected from context[:state] using Ruby's % formatting
|
162
174
|
case instructions
|
163
175
|
when String
|
164
176
|
instructions
|
data/lib/agents/tool_context.rb
CHANGED
@@ -94,5 +94,41 @@ module Agents
|
|
94
94
|
def usage
|
95
95
|
@run_context.usage
|
96
96
|
end
|
97
|
+
|
98
|
+
# Convenient access to the shared state hash within the context.
|
99
|
+
# This provides tools with a dedicated space to store and retrieve
|
100
|
+
# state that persists across agent interactions within a conversation.
|
101
|
+
#
|
102
|
+
# State is automatically initialized as an empty hash if it doesn't exist.
|
103
|
+
# All state modifications are automatically included in context serialization,
|
104
|
+
# making it persist across process boundaries (e.g., Rails with ActiveRecord).
|
105
|
+
#
|
106
|
+
# @return [Hash] The shared state hash
|
107
|
+
# @example Tool storing customer information in state
|
108
|
+
# def perform(tool_context, customer_id:)
|
109
|
+
# customer = Customer.find(customer_id)
|
110
|
+
#
|
111
|
+
# # Store in shared state for other tools/agents to access
|
112
|
+
# tool_context.state[:customer_id] = customer_id
|
113
|
+
# tool_context.state[:customer_name] = customer.name
|
114
|
+
# tool_context.state[:plan_type] = customer.plan
|
115
|
+
#
|
116
|
+
# "Found customer #{customer.name}"
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# @example Tool reading from shared state
|
120
|
+
# def perform(tool_context)
|
121
|
+
# customer_id = tool_context.state[:customer_id]
|
122
|
+
# plan_type = tool_context.state[:plan_type]
|
123
|
+
#
|
124
|
+
# if customer_id && plan_type
|
125
|
+
# "Current plan: #{plan_type} for customer #{customer_id}"
|
126
|
+
# else
|
127
|
+
# "No customer information available"
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
def state
|
131
|
+
context[:state] ||= {}
|
132
|
+
end
|
97
133
|
end
|
98
134
|
end
|
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.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shivam Mishra
|
@@ -75,7 +75,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
75
|
requirements:
|
76
76
|
- - ">="
|
77
77
|
- !ruby/object:Gem::Version
|
78
|
-
version: 3.
|
78
|
+
version: 3.2.0
|
79
79
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
81
|
- - ">="
|