signalwire-agents 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- signalwire_agents/__init__.py +1 -1
- signalwire_agents/agent_server.py +1 -1
- signalwire_agents/core/__init__.py +29 -0
- signalwire_agents/core/agent_base.py +2541 -0
- signalwire_agents/core/function_result.py +123 -0
- signalwire_agents/core/pom_builder.py +204 -0
- signalwire_agents/core/security/__init__.py +9 -0
- signalwire_agents/core/security/session_manager.py +179 -0
- signalwire_agents/core/state/__init__.py +17 -0
- signalwire_agents/core/state/file_state_manager.py +219 -0
- signalwire_agents/core/state/state_manager.py +101 -0
- signalwire_agents/core/swaig_function.py +172 -0
- signalwire_agents/core/swml_builder.py +214 -0
- signalwire_agents/core/swml_handler.py +227 -0
- signalwire_agents/core/swml_renderer.py +368 -0
- signalwire_agents/core/swml_service.py +1057 -0
- signalwire_agents/prefabs/__init__.py +26 -0
- signalwire_agents/prefabs/concierge.py +267 -0
- signalwire_agents/prefabs/faq_bot.py +305 -0
- signalwire_agents/prefabs/info_gatherer.py +263 -0
- signalwire_agents/prefabs/receptionist.py +294 -0
- signalwire_agents/prefabs/survey.py +378 -0
- signalwire_agents/utils/__init__.py +9 -0
- signalwire_agents/utils/pom_utils.py +9 -0
- signalwire_agents/utils/schema_utils.py +357 -0
- signalwire_agents/utils/token_generators.py +9 -0
- signalwire_agents/utils/validators.py +9 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/METADATA +1 -1
- signalwire_agents-0.1.2.dist-info/RECORD +34 -0
- signalwire_agents-0.1.1.dist-info/RECORD +0 -9
- {signalwire_agents-0.1.1.data → signalwire_agents-0.1.2.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/WHEEL +0 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.1.dist-info → signalwire_agents-0.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""
|
11
|
+
Prefab agents with specific functionality that can be used out-of-the-box
|
12
|
+
"""
|
13
|
+
|
14
|
+
from signalwire_agents.prefabs.info_gatherer import InfoGathererAgent
|
15
|
+
from signalwire_agents.prefabs.faq_bot import FAQBotAgent
|
16
|
+
from signalwire_agents.prefabs.concierge import ConciergeAgent
|
17
|
+
from signalwire_agents.prefabs.survey import SurveyAgent
|
18
|
+
from signalwire_agents.prefabs.receptionist import ReceptionistAgent
|
19
|
+
|
20
|
+
__all__ = [
|
21
|
+
"InfoGathererAgent",
|
22
|
+
"FAQBotAgent",
|
23
|
+
"ConciergeAgent",
|
24
|
+
"SurveyAgent",
|
25
|
+
"ReceptionistAgent"
|
26
|
+
]
|
@@ -0,0 +1,267 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""
|
11
|
+
ConciergeAgent - Prefab agent for providing virtual concierge services
|
12
|
+
"""
|
13
|
+
|
14
|
+
from typing import List, Dict, Any, Optional, Union
|
15
|
+
import json
|
16
|
+
import os
|
17
|
+
from datetime import datetime
|
18
|
+
|
19
|
+
from signalwire_agents.core.agent_base import AgentBase
|
20
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
21
|
+
|
22
|
+
|
23
|
+
class ConciergeAgent(AgentBase):
|
24
|
+
"""
|
25
|
+
A prefab agent designed to act as a virtual concierge, providing information
|
26
|
+
and services to users.
|
27
|
+
|
28
|
+
This agent will:
|
29
|
+
1. Welcome users and explain available services
|
30
|
+
2. Answer questions about amenities, hours, and directions
|
31
|
+
3. Help with bookings and reservations
|
32
|
+
4. Provide personalized recommendations
|
33
|
+
|
34
|
+
Example:
|
35
|
+
agent = ConciergeAgent(
|
36
|
+
venue_name="Grand Hotel",
|
37
|
+
services=["room service", "spa bookings", "restaurant reservations"],
|
38
|
+
amenities={
|
39
|
+
"pool": {"hours": "7 AM - 10 PM", "location": "2nd Floor"},
|
40
|
+
"gym": {"hours": "24 hours", "location": "3rd Floor"}
|
41
|
+
}
|
42
|
+
)
|
43
|
+
"""
|
44
|
+
|
45
|
+
def __init__(
|
46
|
+
self,
|
47
|
+
venue_name: str,
|
48
|
+
services: List[str],
|
49
|
+
amenities: Dict[str, Dict[str, str]],
|
50
|
+
hours_of_operation: Optional[Dict[str, str]] = None,
|
51
|
+
special_instructions: Optional[List[str]] = None,
|
52
|
+
welcome_message: Optional[str] = None,
|
53
|
+
**kwargs
|
54
|
+
):
|
55
|
+
"""
|
56
|
+
Initialize a concierge agent
|
57
|
+
|
58
|
+
Args:
|
59
|
+
venue_name: Name of the venue or business
|
60
|
+
services: List of services offered
|
61
|
+
amenities: Dictionary of amenities with details
|
62
|
+
hours_of_operation: Optional dictionary of operating hours
|
63
|
+
special_instructions: Optional list of special instructions
|
64
|
+
welcome_message: Optional custom welcome message
|
65
|
+
**kwargs: Additional arguments for AgentBase
|
66
|
+
"""
|
67
|
+
# Initialize the base agent
|
68
|
+
super().__init__(
|
69
|
+
name="concierge",
|
70
|
+
route="/concierge",
|
71
|
+
use_pom=True,
|
72
|
+
**kwargs
|
73
|
+
)
|
74
|
+
|
75
|
+
# Store configuration
|
76
|
+
self.venue_name = venue_name
|
77
|
+
self.services = services
|
78
|
+
self.amenities = amenities
|
79
|
+
self.hours_of_operation = hours_of_operation or {"default": "9 AM - 5 PM"}
|
80
|
+
self.special_instructions = special_instructions or []
|
81
|
+
|
82
|
+
# Set up the agent's configuration
|
83
|
+
self._setup_concierge_agent(welcome_message)
|
84
|
+
|
85
|
+
def _setup_concierge_agent(self, welcome_message: Optional[str] = None):
|
86
|
+
"""Configure the concierge agent with appropriate settings"""
|
87
|
+
# Basic personality and instructions
|
88
|
+
self.prompt_add_section("Personality",
|
89
|
+
body=f"You are a professional and helpful virtual concierge for {self.venue_name}."
|
90
|
+
)
|
91
|
+
|
92
|
+
self.prompt_add_section("Goal",
|
93
|
+
body="Provide exceptional service by helping users with information, recommendations, and booking assistance."
|
94
|
+
)
|
95
|
+
|
96
|
+
# Build detailed instructions
|
97
|
+
instructions = [
|
98
|
+
"Be warm and welcoming but professional at all times.",
|
99
|
+
"Provide accurate information about amenities, services, and operating hours.",
|
100
|
+
"Offer to help with reservations and bookings when appropriate.",
|
101
|
+
"Answer questions concisely with specific, relevant details."
|
102
|
+
]
|
103
|
+
|
104
|
+
# Add any special instructions
|
105
|
+
instructions.extend(self.special_instructions)
|
106
|
+
|
107
|
+
self.prompt_add_section("Instructions", bullets=instructions)
|
108
|
+
|
109
|
+
# Services section
|
110
|
+
services_list = ", ".join(self.services)
|
111
|
+
self.prompt_add_section("Available Services",
|
112
|
+
body=f"The following services are available: {services_list}"
|
113
|
+
)
|
114
|
+
|
115
|
+
# Amenities section with details
|
116
|
+
amenities_subsections = []
|
117
|
+
for name, details in self.amenities.items():
|
118
|
+
subsection = {
|
119
|
+
"title": name.title(),
|
120
|
+
"body": "\n".join([f"{k.title()}: {v}" for k, v in details.items()])
|
121
|
+
}
|
122
|
+
amenities_subsections.append(subsection)
|
123
|
+
|
124
|
+
self.prompt_add_section("Amenities",
|
125
|
+
body="Information about available amenities:",
|
126
|
+
subsections=amenities_subsections
|
127
|
+
)
|
128
|
+
|
129
|
+
# Hours of operation
|
130
|
+
hours_list = "\n".join([f"{k.title()}: {v}" for k, v in self.hours_of_operation.items()])
|
131
|
+
self.prompt_add_section("Hours of Operation", body=hours_list)
|
132
|
+
|
133
|
+
# Set up the post-prompt for summary
|
134
|
+
self.set_post_prompt("""
|
135
|
+
Return a JSON summary of this interaction:
|
136
|
+
{
|
137
|
+
"topic": "MAIN_TOPIC",
|
138
|
+
"service_requested": "SPECIFIC_SERVICE_REQUESTED_OR_null",
|
139
|
+
"questions_answered": ["QUESTION_1", "QUESTION_2"],
|
140
|
+
"follow_up_needed": true/false
|
141
|
+
}
|
142
|
+
""")
|
143
|
+
|
144
|
+
# Configure hints for better understanding
|
145
|
+
self.add_hints([
|
146
|
+
self.venue_name,
|
147
|
+
*self.services,
|
148
|
+
*self.amenities.keys()
|
149
|
+
])
|
150
|
+
|
151
|
+
# Set up parameters
|
152
|
+
self.set_params({
|
153
|
+
"wait_for_user": False,
|
154
|
+
"end_of_speech_timeout": 1000,
|
155
|
+
"ai_volume": 5,
|
156
|
+
"local_tz": "America/New_York"
|
157
|
+
})
|
158
|
+
|
159
|
+
# Add global data
|
160
|
+
self.set_global_data({
|
161
|
+
"venue_name": self.venue_name,
|
162
|
+
"services": self.services,
|
163
|
+
"amenities": self.amenities,
|
164
|
+
"hours": self.hours_of_operation
|
165
|
+
})
|
166
|
+
|
167
|
+
# Configure native functions
|
168
|
+
self.set_native_functions(["check_time"])
|
169
|
+
|
170
|
+
# Set custom welcome message if provided
|
171
|
+
if welcome_message:
|
172
|
+
self.set_params({
|
173
|
+
"static_greeting": welcome_message,
|
174
|
+
"static_greeting_no_barge": True
|
175
|
+
})
|
176
|
+
|
177
|
+
# Register tool methods
|
178
|
+
# These methods are already decorated with @AgentBase.tool in the class definition
|
179
|
+
|
180
|
+
@AgentBase.tool(
|
181
|
+
name="check_availability",
|
182
|
+
description="Check availability for a service on a specific date and time",
|
183
|
+
parameters={
|
184
|
+
"service": {
|
185
|
+
"type": "string",
|
186
|
+
"description": "The service to check (e.g., spa, restaurant)"
|
187
|
+
},
|
188
|
+
"date": {
|
189
|
+
"type": "string",
|
190
|
+
"description": "The date to check (YYYY-MM-DD format)"
|
191
|
+
},
|
192
|
+
"time": {
|
193
|
+
"type": "string",
|
194
|
+
"description": "The time to check (HH:MM format, 24-hour)"
|
195
|
+
}
|
196
|
+
}
|
197
|
+
)
|
198
|
+
def check_availability(self, args, raw_data):
|
199
|
+
"""
|
200
|
+
Check availability for a service on a specific date and time
|
201
|
+
|
202
|
+
This is a simulated function that would typically connect to a real booking system.
|
203
|
+
In this example, it returns a mock availability response.
|
204
|
+
"""
|
205
|
+
service = args.get("service", "").lower()
|
206
|
+
date = args.get("date", "")
|
207
|
+
time = args.get("time", "")
|
208
|
+
|
209
|
+
# Simple availability simulation - in a real application, this would
|
210
|
+
# connect to your actual booking system
|
211
|
+
if service in self.services:
|
212
|
+
# Generate a simple availability response
|
213
|
+
return SwaigFunctionResult(
|
214
|
+
f"Yes, {service} is available on {date} at {time}. Would you like to make a reservation?"
|
215
|
+
)
|
216
|
+
else:
|
217
|
+
return SwaigFunctionResult(
|
218
|
+
f"I'm sorry, we don't offer {service} at {self.venue_name}. "
|
219
|
+
f"Our available services are: {', '.join(self.services)}."
|
220
|
+
)
|
221
|
+
|
222
|
+
@AgentBase.tool(
|
223
|
+
name="get_directions",
|
224
|
+
description="Get directions to a specific location or amenity",
|
225
|
+
parameters={
|
226
|
+
"location": {
|
227
|
+
"type": "string",
|
228
|
+
"description": "The location or amenity to get directions to"
|
229
|
+
}
|
230
|
+
}
|
231
|
+
)
|
232
|
+
def get_directions(self, args, raw_data):
|
233
|
+
"""Provide directions to a specific location or amenity"""
|
234
|
+
location = args.get("location", "").lower()
|
235
|
+
|
236
|
+
# Check if the location is an amenity
|
237
|
+
if location in self.amenities and "location" in self.amenities[location]:
|
238
|
+
amenity_location = self.amenities[location]["location"]
|
239
|
+
return SwaigFunctionResult(
|
240
|
+
f"The {location} is located at {amenity_location}. "
|
241
|
+
f"From the main entrance, follow the signs to {amenity_location}."
|
242
|
+
)
|
243
|
+
else:
|
244
|
+
return SwaigFunctionResult(
|
245
|
+
f"I don't have specific directions to {location}. "
|
246
|
+
f"You can ask our staff at the front desk for assistance."
|
247
|
+
)
|
248
|
+
|
249
|
+
def on_summary(self, summary, raw_data=None):
|
250
|
+
"""
|
251
|
+
Process the interaction summary
|
252
|
+
|
253
|
+
Args:
|
254
|
+
summary: Summary data from the conversation
|
255
|
+
raw_data: The complete raw POST data from the request
|
256
|
+
"""
|
257
|
+
if summary:
|
258
|
+
try:
|
259
|
+
# For structured summary
|
260
|
+
if isinstance(summary, dict):
|
261
|
+
print(f"Concierge interaction summary: {json.dumps(summary, indent=2)}")
|
262
|
+
else:
|
263
|
+
print(f"Concierge interaction summary: {summary}")
|
264
|
+
|
265
|
+
# Subclasses can override this to log or process the interaction
|
266
|
+
except Exception as e:
|
267
|
+
print(f"Error processing summary: {str(e)}")
|
@@ -0,0 +1,305 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""
|
11
|
+
FAQBotAgent - Prefab agent for answering frequently asked questions
|
12
|
+
"""
|
13
|
+
|
14
|
+
from typing import List, Dict, Any, Optional, Union
|
15
|
+
import json
|
16
|
+
import os
|
17
|
+
|
18
|
+
from signalwire_agents.core.agent_base import AgentBase
|
19
|
+
from signalwire_agents.core.function_result import SwaigFunctionResult
|
20
|
+
|
21
|
+
|
22
|
+
class FAQBotAgent(AgentBase):
|
23
|
+
"""
|
24
|
+
A prefab agent designed to answer frequently asked questions based on
|
25
|
+
a provided list of question/answer pairs.
|
26
|
+
|
27
|
+
This agent will:
|
28
|
+
1. Match user questions against the FAQ database
|
29
|
+
2. Provide the most relevant answer
|
30
|
+
3. Suggest other relevant questions when appropriate
|
31
|
+
|
32
|
+
Example:
|
33
|
+
agent = FAQBotAgent(
|
34
|
+
faqs=[
|
35
|
+
{
|
36
|
+
"question": "What is SignalWire?",
|
37
|
+
"answer": "SignalWire is a developer-friendly cloud communications platform."
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"question": "How much does it cost?",
|
41
|
+
"answer": "SignalWire offers pay-as-you-go pricing with no monthly fees."
|
42
|
+
}
|
43
|
+
]
|
44
|
+
)
|
45
|
+
"""
|
46
|
+
|
47
|
+
def __init__(
|
48
|
+
self,
|
49
|
+
faqs: List[Dict[str, str]],
|
50
|
+
suggest_related: bool = True,
|
51
|
+
persona: Optional[str] = None,
|
52
|
+
name: str = "faq_bot",
|
53
|
+
route: str = "/faq",
|
54
|
+
**kwargs
|
55
|
+
):
|
56
|
+
"""
|
57
|
+
Initialize an FAQ bot agent
|
58
|
+
|
59
|
+
Args:
|
60
|
+
faqs: List of FAQ items, each with:
|
61
|
+
- question: The question text
|
62
|
+
- answer: The answer text
|
63
|
+
- categories: Optional list of category tags
|
64
|
+
suggest_related: Whether to suggest related questions
|
65
|
+
persona: Optional custom personality description
|
66
|
+
name: Agent name for the route
|
67
|
+
route: HTTP route for this agent
|
68
|
+
**kwargs: Additional arguments for AgentBase
|
69
|
+
"""
|
70
|
+
# Initialize the base agent
|
71
|
+
super().__init__(
|
72
|
+
name=name,
|
73
|
+
route=route,
|
74
|
+
use_pom=True,
|
75
|
+
**kwargs
|
76
|
+
)
|
77
|
+
|
78
|
+
self.faqs = faqs
|
79
|
+
self.suggest_related = suggest_related
|
80
|
+
self.persona = persona or "You are a helpful FAQ bot that provides accurate answers to common questions."
|
81
|
+
|
82
|
+
# Build the prompt
|
83
|
+
self._build_faq_bot_prompt()
|
84
|
+
|
85
|
+
# Set up the post-prompt template
|
86
|
+
self._setup_post_prompt()
|
87
|
+
|
88
|
+
# Configure additional agent settings
|
89
|
+
self._configure_agent_settings()
|
90
|
+
|
91
|
+
def _build_faq_bot_prompt(self):
|
92
|
+
"""Build the agent prompt for the FAQ bot"""
|
93
|
+
# Set up the personality
|
94
|
+
self.prompt_add_section(
|
95
|
+
"Personality",
|
96
|
+
body=self.persona
|
97
|
+
)
|
98
|
+
|
99
|
+
# Set up the goal
|
100
|
+
self.prompt_add_section(
|
101
|
+
"Goal",
|
102
|
+
body="Answer user questions by matching them to the most similar FAQ in your database."
|
103
|
+
)
|
104
|
+
|
105
|
+
# Set up the instructions
|
106
|
+
instructions = [
|
107
|
+
"Compare user questions to your FAQ database and find the best match.",
|
108
|
+
"Provide the answer from the FAQ database for the matching question.",
|
109
|
+
"If no close match exists, politely say you don't have that information.",
|
110
|
+
"Be concise and factual in your responses."
|
111
|
+
]
|
112
|
+
|
113
|
+
# Add instruction about suggesting related questions if enabled
|
114
|
+
if self.suggest_related:
|
115
|
+
instructions.append(
|
116
|
+
"When appropriate, suggest other related questions from the FAQ database that might be helpful."
|
117
|
+
)
|
118
|
+
|
119
|
+
self.prompt_add_section(
|
120
|
+
"Instructions",
|
121
|
+
bullets=instructions
|
122
|
+
)
|
123
|
+
|
124
|
+
# Add FAQ Database section with subsections for each FAQ
|
125
|
+
faq_subsections = []
|
126
|
+
for faq in self.faqs:
|
127
|
+
question = faq.get("question", "")
|
128
|
+
answer = faq.get("answer", "")
|
129
|
+
categories = faq.get("categories", [])
|
130
|
+
|
131
|
+
# Skip invalid entries
|
132
|
+
if not question or not answer:
|
133
|
+
continue
|
134
|
+
|
135
|
+
# Build the body text with answer and optional categories
|
136
|
+
body_text = answer
|
137
|
+
if categories:
|
138
|
+
category_str = "Categories: " + ", ".join(categories)
|
139
|
+
body_text = f"{body_text}\n\n{category_str}"
|
140
|
+
|
141
|
+
faq_subsections.append({
|
142
|
+
"title": question,
|
143
|
+
"body": body_text
|
144
|
+
})
|
145
|
+
|
146
|
+
# Add the FAQ Database section with all FAQ subsections
|
147
|
+
self.prompt_add_section(
|
148
|
+
"FAQ Database",
|
149
|
+
body="Here is your database of frequently asked questions and answers:",
|
150
|
+
subsections=faq_subsections
|
151
|
+
)
|
152
|
+
|
153
|
+
# Add section about suggesting related questions if enabled
|
154
|
+
if self.suggest_related:
|
155
|
+
self.prompt_add_section(
|
156
|
+
"Related Questions",
|
157
|
+
body="When appropriate, suggest other related questions from the FAQ database that might be helpful."
|
158
|
+
)
|
159
|
+
|
160
|
+
def _setup_post_prompt(self):
|
161
|
+
"""Set up the post-prompt for summary"""
|
162
|
+
post_prompt = """
|
163
|
+
Return a JSON summary of this interaction:
|
164
|
+
{
|
165
|
+
"question": "MAIN_QUESTION_ASKED",
|
166
|
+
"matched_faq": "MATCHED_FAQ_QUESTION_OR_null",
|
167
|
+
"answered_successfully": true/false,
|
168
|
+
"suggested_related": []
|
169
|
+
}
|
170
|
+
"""
|
171
|
+
self.set_post_prompt(post_prompt)
|
172
|
+
|
173
|
+
def _configure_agent_settings(self):
|
174
|
+
"""Configure additional agent settings"""
|
175
|
+
# Add hints for better recognition of FAQ-related terms
|
176
|
+
hints = []
|
177
|
+
|
178
|
+
# Add questions and categories as hints
|
179
|
+
for faq in self.faqs:
|
180
|
+
# Extract key terms from questions
|
181
|
+
question = faq.get("question", "")
|
182
|
+
if question:
|
183
|
+
# Split question into words and add words with 4+ characters
|
184
|
+
words = [word.strip(".,?!") for word in question.split() if len(word.strip(".,?!")) >= 4]
|
185
|
+
hints.extend(words)
|
186
|
+
|
187
|
+
# Add categories as hints
|
188
|
+
categories = faq.get("categories", [])
|
189
|
+
hints.extend(categories)
|
190
|
+
|
191
|
+
# Remove duplicates and add as hints
|
192
|
+
unique_hints = list(set(hints))
|
193
|
+
if unique_hints:
|
194
|
+
self.add_hints(unique_hints)
|
195
|
+
|
196
|
+
# Set AI behavior parameters
|
197
|
+
self.set_params({
|
198
|
+
"wait_for_user": False,
|
199
|
+
"end_of_speech_timeout": 1000,
|
200
|
+
"ai_volume": 5
|
201
|
+
})
|
202
|
+
|
203
|
+
# Add global data
|
204
|
+
self.set_global_data({
|
205
|
+
"faq_count": len(self.faqs),
|
206
|
+
"categories": list(set(
|
207
|
+
category
|
208
|
+
for faq in self.faqs
|
209
|
+
for category in faq.get("categories", [])
|
210
|
+
))
|
211
|
+
})
|
212
|
+
|
213
|
+
# Configure native functions
|
214
|
+
self.set_native_functions(["check_time"])
|
215
|
+
|
216
|
+
@AgentBase.tool(
|
217
|
+
name="search_faqs",
|
218
|
+
description="Search for FAQs matching a specific query or category",
|
219
|
+
parameters={
|
220
|
+
"query": {
|
221
|
+
"type": "string",
|
222
|
+
"description": "The search query"
|
223
|
+
},
|
224
|
+
"category": {
|
225
|
+
"type": "string",
|
226
|
+
"description": "Optional category to filter by"
|
227
|
+
}
|
228
|
+
}
|
229
|
+
)
|
230
|
+
def search_faqs(self, args, raw_data):
|
231
|
+
"""
|
232
|
+
Search for FAQs matching a specific query or category
|
233
|
+
|
234
|
+
This function helps find relevant FAQs based on a search query or category.
|
235
|
+
It returns matching FAQs in order of relevance.
|
236
|
+
"""
|
237
|
+
query = args.get("query", "").lower()
|
238
|
+
category = args.get("category", "").lower()
|
239
|
+
|
240
|
+
# Simple search logic (in a real implementation, you would use more
|
241
|
+
# sophisticated search algorithms such as vector embeddings)
|
242
|
+
results = []
|
243
|
+
|
244
|
+
for faq in self.faqs:
|
245
|
+
question = faq.get("question", "").lower()
|
246
|
+
categories = [c.lower() for c in faq.get("categories", [])]
|
247
|
+
match_score = 0
|
248
|
+
|
249
|
+
# Match on query
|
250
|
+
if query and query in question:
|
251
|
+
# Simple substring matching - higher score for exact matches
|
252
|
+
if query == question:
|
253
|
+
match_score += 100
|
254
|
+
else:
|
255
|
+
match_score += 50
|
256
|
+
|
257
|
+
# Boost score for matches at the beginning of the question
|
258
|
+
if question.startswith(query):
|
259
|
+
match_score += 25
|
260
|
+
|
261
|
+
# Match on category
|
262
|
+
if category and category in categories:
|
263
|
+
match_score += 30
|
264
|
+
|
265
|
+
# Only include results with a positive match score
|
266
|
+
if match_score > 0:
|
267
|
+
results.append({
|
268
|
+
"question": faq.get("question"),
|
269
|
+
"score": match_score
|
270
|
+
})
|
271
|
+
|
272
|
+
# Sort results by score (highest first)
|
273
|
+
results.sort(key=lambda x: x["score"], reverse=True)
|
274
|
+
|
275
|
+
# Limit to top 3 results
|
276
|
+
top_results = results[:3]
|
277
|
+
|
278
|
+
if top_results:
|
279
|
+
result_text = "Here are the most relevant FAQs:\n\n"
|
280
|
+
for i, result in enumerate(top_results, 1):
|
281
|
+
result_text += f"{i}. {result['question']}\n"
|
282
|
+
|
283
|
+
return SwaigFunctionResult(result_text)
|
284
|
+
else:
|
285
|
+
return SwaigFunctionResult("No matching FAQs found.")
|
286
|
+
|
287
|
+
def on_summary(self, summary, raw_data=None):
|
288
|
+
"""
|
289
|
+
Process the interaction summary
|
290
|
+
|
291
|
+
Args:
|
292
|
+
summary: Summary data from the conversation
|
293
|
+
raw_data: The complete raw POST data from the request
|
294
|
+
"""
|
295
|
+
if summary:
|
296
|
+
try:
|
297
|
+
# For structured summary
|
298
|
+
if isinstance(summary, dict):
|
299
|
+
print(f"FAQ interaction summary: {json.dumps(summary, indent=2)}")
|
300
|
+
else:
|
301
|
+
print(f"FAQ interaction summary: {summary}")
|
302
|
+
|
303
|
+
# Subclasses can override this to log or save the interaction
|
304
|
+
except Exception as e:
|
305
|
+
print(f"Error processing summary: {str(e)}")
|