Spaces:
Runtime error
Runtime error
Commit Β·
ac8bca0
1
Parent(s): b7ebcf3
enhance iterative agentic loop and optimize configuration settings
Browse files
agent.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
| 2 |
FleetMind AI Agent
|
| 3 |
Autonomous fleet management agent using Gemini 2.0 Flash
|
| 4 |
Track 2: MCP in Action - Enterprise Category
|
|
|
|
|
|
|
| 5 |
"""
|
| 6 |
|
| 7 |
import json
|
|
@@ -44,6 +46,7 @@ class FleetMindAgent:
|
|
| 44 |
Uses Gemini 2.0 Flash for reasoning and MCP tools for execution
|
| 45 |
|
| 46 |
Advanced Features:
|
|
|
|
| 47 |
- Context Engineering: Smart conversation memory with summarization
|
| 48 |
- Multi-step Planning: Complex task breakdown and execution
|
| 49 |
- Reasoning Transparency: Detailed explanation of decision-making
|
|
@@ -60,12 +63,12 @@ class FleetMindAgent:
|
|
| 60 |
self.task_context: dict = {} # Current task context
|
| 61 |
self.max_history_length = 20 # Max messages before summarization
|
| 62 |
|
| 63 |
-
# Initialize Gemini
|
| 64 |
genai.configure(api_key=gemini_api_key)
|
| 65 |
self.model = genai.GenerativeModel(
|
| 66 |
model_name=Config.GEMINI_MODEL,
|
| 67 |
generation_config={
|
| 68 |
-
"temperature":
|
| 69 |
"top_p": 0.95,
|
| 70 |
"top_k": 40,
|
| 71 |
"max_output_tokens": 8192,
|
|
@@ -149,17 +152,17 @@ Provide a concise summary in 3-4 sentences."""
|
|
| 149 |
|
| 150 |
return "\n\n".join(schema_parts)
|
| 151 |
|
| 152 |
-
def
|
| 153 |
"""
|
| 154 |
-
Create
|
| 155 |
-
Includes
|
| 156 |
"""
|
| 157 |
# Get current date/time for context
|
| 158 |
now = datetime.now()
|
| 159 |
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
| 160 |
default_delivery_time = (now + timedelta(hours=2)).strftime("%Y-%m-%dT%H:%M:%S")
|
| 161 |
|
| 162 |
-
# Build context
|
| 163 |
context = f"""
|
| 164 |
Current Date/Time: {current_time}
|
| 165 |
Default Expected Delivery Time (if not specified): {default_delivery_time}
|
|
@@ -168,11 +171,11 @@ Connected to MCP Server: {self.mcp_client.is_connected}
|
|
| 168 |
Available Tools: {len(self.mcp_client.tools)}
|
| 169 |
"""
|
| 170 |
|
| 171 |
-
# Add conversation summary if available
|
| 172 |
if self.context_summary:
|
| 173 |
context += f"\n**Conversation Summary**: {self.context_summary}\n"
|
| 174 |
|
| 175 |
-
# Add learned user preferences
|
| 176 |
if self.user_preferences:
|
| 177 |
prefs_text = "\n**Learned User Preferences**:\n"
|
| 178 |
if self.user_preferences.get("prefers_urgent"):
|
|
@@ -181,8 +184,8 @@ Available Tools: {len(self.mcp_client.tools)}
|
|
| 181 |
prefs_text += "- User frequently handles fragile items\n"
|
| 182 |
context += prefs_text
|
| 183 |
|
| 184 |
-
# Recent conversation
|
| 185 |
-
recent_history = self.conversation_history[-
|
| 186 |
history_text = ""
|
| 187 |
if recent_history:
|
| 188 |
history_text = "\n\n**Recent Conversation**:\n"
|
|
@@ -194,39 +197,83 @@ Available Tools: {len(self.mcp_client.tools)}
|
|
| 194 |
# Tool schema
|
| 195 |
tools_schema = self._build_tools_schema()
|
| 196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
return f"""{AGENT_SYSTEM_PROMPT}
|
| 198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
## Context
|
| 200 |
{context}
|
| 201 |
{history_text}
|
| 202 |
|
| 203 |
## Available Tools Schema
|
| 204 |
{tools_schema}
|
|
|
|
| 205 |
|
| 206 |
-
## User Request
|
| 207 |
{user_message}
|
| 208 |
|
| 209 |
-
## Your
|
| 210 |
-
Respond with a JSON object containing:
|
| 211 |
-
{{
|
| 212 |
-
"reasoning": "Your step-by-step thinking process (be detailed and explain WHY you choose certain actions)",
|
| 213 |
-
"plan": [
|
| 214 |
-
{{
|
| 215 |
-
"step": 1,
|
| 216 |
-
"action": "Description of what you're doing",
|
| 217 |
-
"tool": "tool_name or null if no tool needed",
|
| 218 |
-
"arguments": {{}} // tool arguments if applicable
|
| 219 |
-
}}
|
| 220 |
-
],
|
| 221 |
-
"final_message": "Your response to the user after executing the plan"
|
| 222 |
-
}}
|
| 223 |
-
|
| 224 |
-
If no tools are needed (e.g., answering a question), set plan to an empty array.
|
| 225 |
-
IMPORTANT: Only include the JSON object in your response, no other text.
|
| 226 |
"""
|
| 227 |
|
| 228 |
-
def
|
| 229 |
-
"""Parse the AI's JSON response"""
|
| 230 |
# Try to extract JSON from the response
|
| 231 |
try:
|
| 232 |
# First try direct parse
|
|
@@ -250,21 +297,21 @@ IMPORTANT: Only include the JSON object in your response, no other text.
|
|
| 250 |
except json.JSONDecodeError:
|
| 251 |
pass
|
| 252 |
|
| 253 |
-
# Fallback:
|
| 254 |
return {
|
| 255 |
-
"
|
| 256 |
-
"
|
| 257 |
-
"
|
|
|
|
| 258 |
}
|
| 259 |
|
| 260 |
async def process_message(self, user_message: str) -> AgentResponse:
|
| 261 |
"""
|
| 262 |
-
Process a user message
|
| 263 |
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
- Maintains task context across messages
|
| 268 |
|
| 269 |
Args:
|
| 270 |
user_message: The user's natural language input
|
|
@@ -282,57 +329,99 @@ IMPORTANT: Only include the JSON object in your response, no other text.
|
|
| 282 |
"content": user_message
|
| 283 |
})
|
| 284 |
|
| 285 |
-
#
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
try:
|
| 289 |
-
response = self.model.generate_content(prompt)
|
| 290 |
-
ai_response_text = response.text
|
| 291 |
-
except Exception as e:
|
| 292 |
-
return AgentResponse(
|
| 293 |
-
message=f"Error generating AI response: {str(e)}",
|
| 294 |
-
success=False,
|
| 295 |
-
error=str(e)
|
| 296 |
-
)
|
| 297 |
-
|
| 298 |
-
# Parse the response
|
| 299 |
-
parsed = self._parse_ai_response(ai_response_text)
|
| 300 |
-
reasoning = parsed.get("reasoning", "")
|
| 301 |
-
plan = parsed.get("plan", [])
|
| 302 |
-
final_message = parsed.get("final_message", "")
|
| 303 |
-
|
| 304 |
-
# Execute the plan
|
| 305 |
steps: list[AgentStep] = []
|
| 306 |
tools_called: list[str] = []
|
|
|
|
|
|
|
| 307 |
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
action=step_plan.get("action", ""),
|
| 312 |
-
tool_name=step_plan.get("tool"),
|
| 313 |
-
tool_args=step_plan.get("arguments", {}),
|
| 314 |
-
reasoning=step_plan.get("reasoning", "")
|
| 315 |
-
)
|
| 316 |
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
step.tool_name,
|
| 321 |
-
step.tool_args
|
| 322 |
-
)
|
| 323 |
-
step.result = tool_result.result if tool_result.success else tool_result.error
|
| 324 |
-
tools_called.append(step.tool_name)
|
| 325 |
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
step.action += f" (FAILED: {tool_result.error})"
|
| 329 |
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
if steps:
|
| 334 |
final_message = await self._generate_final_response(
|
| 335 |
-
user_message, steps,
|
| 336 |
)
|
| 337 |
|
| 338 |
# Add assistant response to history
|
|
@@ -347,7 +436,7 @@ IMPORTANT: Only include the JSON object in your response, no other text.
|
|
| 347 |
return AgentResponse(
|
| 348 |
message=final_message,
|
| 349 |
steps=steps,
|
| 350 |
-
reasoning=
|
| 351 |
tools_called=tools_called,
|
| 352 |
success=True
|
| 353 |
)
|
|
|
|
| 2 |
FleetMind AI Agent
|
| 3 |
Autonomous fleet management agent using Gemini 2.0 Flash
|
| 4 |
Track 2: MCP in Action - Enterprise Category
|
| 5 |
+
|
| 6 |
+
FIXED: True iterative agentic loop - Gemini sees tool results and decides next action
|
| 7 |
"""
|
| 8 |
|
| 9 |
import json
|
|
|
|
| 46 |
Uses Gemini 2.0 Flash for reasoning and MCP tools for execution
|
| 47 |
|
| 48 |
Advanced Features:
|
| 49 |
+
- TRUE ITERATIVE AGENTIC LOOP: Model sees each tool result before deciding next action
|
| 50 |
- Context Engineering: Smart conversation memory with summarization
|
| 51 |
- Multi-step Planning: Complex task breakdown and execution
|
| 52 |
- Reasoning Transparency: Detailed explanation of decision-making
|
|
|
|
| 63 |
self.task_context: dict = {} # Current task context
|
| 64 |
self.max_history_length = 20 # Max messages before summarization
|
| 65 |
|
| 66 |
+
# Initialize Gemini - CRITICAL: temperature=1.0 for Gemini 2.0 reasoning
|
| 67 |
genai.configure(api_key=gemini_api_key)
|
| 68 |
self.model = genai.GenerativeModel(
|
| 69 |
model_name=Config.GEMINI_MODEL,
|
| 70 |
generation_config={
|
| 71 |
+
"temperature": 1.0, # IMPORTANT: Gemini 2.0 reasoning optimized for 1.0
|
| 72 |
"top_p": 0.95,
|
| 73 |
"top_k": 40,
|
| 74 |
"max_output_tokens": 8192,
|
|
|
|
| 152 |
|
| 153 |
return "\n\n".join(schema_parts)
|
| 154 |
|
| 155 |
+
def _create_iterative_prompt(self, user_message: str, execution_context: list[dict]) -> str:
|
| 156 |
"""
|
| 157 |
+
Create prompt for iterative agentic loop.
|
| 158 |
+
Includes previous tool results so model can make informed decisions.
|
| 159 |
"""
|
| 160 |
# Get current date/time for context
|
| 161 |
now = datetime.now()
|
| 162 |
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
| 163 |
default_delivery_time = (now + timedelta(hours=2)).strftime("%Y-%m-%dT%H:%M:%S")
|
| 164 |
|
| 165 |
+
# Build context
|
| 166 |
context = f"""
|
| 167 |
Current Date/Time: {current_time}
|
| 168 |
Default Expected Delivery Time (if not specified): {default_delivery_time}
|
|
|
|
| 171 |
Available Tools: {len(self.mcp_client.tools)}
|
| 172 |
"""
|
| 173 |
|
| 174 |
+
# Add conversation summary if available
|
| 175 |
if self.context_summary:
|
| 176 |
context += f"\n**Conversation Summary**: {self.context_summary}\n"
|
| 177 |
|
| 178 |
+
# Add learned user preferences
|
| 179 |
if self.user_preferences:
|
| 180 |
prefs_text = "\n**Learned User Preferences**:\n"
|
| 181 |
if self.user_preferences.get("prefers_urgent"):
|
|
|
|
| 184 |
prefs_text += "- User frequently handles fragile items\n"
|
| 185 |
context += prefs_text
|
| 186 |
|
| 187 |
+
# Recent conversation history
|
| 188 |
+
recent_history = self.conversation_history[-4:] if self.conversation_history else []
|
| 189 |
history_text = ""
|
| 190 |
if recent_history:
|
| 191 |
history_text = "\n\n**Recent Conversation**:\n"
|
|
|
|
| 197 |
# Tool schema
|
| 198 |
tools_schema = self._build_tools_schema()
|
| 199 |
|
| 200 |
+
# Build execution context string showing what's been done
|
| 201 |
+
execution_history = ""
|
| 202 |
+
if execution_context:
|
| 203 |
+
execution_history = "\n\n## EXECUTION HISTORY (What you've done so far)\n"
|
| 204 |
+
for step in execution_context:
|
| 205 |
+
step_num = step.get("step", "?")
|
| 206 |
+
tool_name = step.get("tool", "N/A")
|
| 207 |
+
args = step.get("arguments", {})
|
| 208 |
+
result = step.get("result", "N/A")
|
| 209 |
+
execution_history += f"""
|
| 210 |
+
### Step {step_num}: Called {tool_name}
|
| 211 |
+
Arguments: {json.dumps(args, indent=2)}
|
| 212 |
+
Result: {json.dumps(result, indent=2) if isinstance(result, dict) else result}
|
| 213 |
+
"""
|
| 214 |
+
|
| 215 |
return f"""{AGENT_SYSTEM_PROMPT}
|
| 216 |
|
| 217 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 218 |
+
β‘ CRITICAL: ITERATIVE AGENTIC REASONING
|
| 219 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 220 |
+
|
| 221 |
+
You are in an ITERATIVE LOOP. Each turn, you can call ONE tool or finish.
|
| 222 |
+
After each tool call, you will see the result and decide the NEXT step.
|
| 223 |
+
|
| 224 |
+
**WORKFLOW:**
|
| 225 |
+
1. Analyze what the user wants
|
| 226 |
+
2. Determine the NEXT SINGLE action needed
|
| 227 |
+
3. Either call a tool OR provide final response
|
| 228 |
+
|
| 229 |
+
**RESPONSE FORMAT (STRICT JSON):**
|
| 230 |
+
|
| 231 |
+
If you need to call a tool:
|
| 232 |
+
```json
|
| 233 |
+
{{
|
| 234 |
+
"thinking": "My reasoning about what to do next...",
|
| 235 |
+
"action": "call_tool",
|
| 236 |
+
"tool": "tool_name",
|
| 237 |
+
"arguments": {{}},
|
| 238 |
+
"status": "in_progress"
|
| 239 |
+
}}
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
If you're DONE (all steps complete):
|
| 243 |
+
```json
|
| 244 |
+
{{
|
| 245 |
+
"thinking": "Summarizing what was accomplished...",
|
| 246 |
+
"action": "respond",
|
| 247 |
+
"message": "Your final response to the user",
|
| 248 |
+
"status": "complete"
|
| 249 |
+
}}
|
| 250 |
+
```
|
| 251 |
+
|
| 252 |
+
**IMPORTANT RULES:**
|
| 253 |
+
1. Call ONE tool at a time - you'll see the result before deciding next step
|
| 254 |
+
2. USE ACTUAL DATA from previous tool results (coordinates, IDs, etc.)
|
| 255 |
+
3. Don't guess values - use what the tools returned
|
| 256 |
+
4. After geocoding, USE the lat/lng from the result in subsequent calls
|
| 257 |
+
5. After creating order, USE the order_id for assignment
|
| 258 |
+
|
| 259 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 260 |
+
|
| 261 |
## Context
|
| 262 |
{context}
|
| 263 |
{history_text}
|
| 264 |
|
| 265 |
## Available Tools Schema
|
| 266 |
{tools_schema}
|
| 267 |
+
{execution_history}
|
| 268 |
|
| 269 |
+
## Current User Request
|
| 270 |
{user_message}
|
| 271 |
|
| 272 |
+
## Your Next Action (JSON only, no other text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
"""
|
| 274 |
|
| 275 |
+
def _parse_iterative_response(self, response_text: str) -> dict:
|
| 276 |
+
"""Parse the AI's JSON response for iterative loop"""
|
| 277 |
# Try to extract JSON from the response
|
| 278 |
try:
|
| 279 |
# First try direct parse
|
|
|
|
| 297 |
except json.JSONDecodeError:
|
| 298 |
pass
|
| 299 |
|
| 300 |
+
# Fallback: treat as final response
|
| 301 |
return {
|
| 302 |
+
"thinking": "Direct response",
|
| 303 |
+
"action": "respond",
|
| 304 |
+
"message": response_text,
|
| 305 |
+
"status": "complete"
|
| 306 |
}
|
| 307 |
|
| 308 |
async def process_message(self, user_message: str) -> AgentResponse:
|
| 309 |
"""
|
| 310 |
+
Process a user message using TRUE ITERATIVE AGENTIC LOOP.
|
| 311 |
|
| 312 |
+
The model calls ONE tool at a time, sees the result, then decides
|
| 313 |
+
the next action. This allows proper use of tool results (like coordinates
|
| 314 |
+
from geocoding) in subsequent calls (like create_order).
|
|
|
|
| 315 |
|
| 316 |
Args:
|
| 317 |
user_message: The user's natural language input
|
|
|
|
| 329 |
"content": user_message
|
| 330 |
})
|
| 331 |
|
| 332 |
+
# Initialize execution tracking
|
| 333 |
+
execution_context: list[dict] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
steps: list[AgentStep] = []
|
| 335 |
tools_called: list[str] = []
|
| 336 |
+
all_reasoning: list[str] = []
|
| 337 |
+
final_message = ""
|
| 338 |
|
| 339 |
+
# ITERATIVE AGENTIC LOOP
|
| 340 |
+
max_iterations = self.max_tool_calls + 2 # Allow extra iterations for reasoning
|
| 341 |
+
iteration = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
|
| 343 |
+
while iteration < max_iterations:
|
| 344 |
+
iteration += 1
|
| 345 |
+
print(f"\nπ Agent Iteration {iteration}/{max_iterations}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
|
| 347 |
+
# Generate next action
|
| 348 |
+
prompt = self._create_iterative_prompt(user_message, execution_context)
|
|
|
|
| 349 |
|
| 350 |
+
try:
|
| 351 |
+
response = self.model.generate_content(prompt)
|
| 352 |
+
ai_response_text = response.text
|
| 353 |
+
print(f"π AI Response: {ai_response_text[:500]}...")
|
| 354 |
+
except Exception as e:
|
| 355 |
+
return AgentResponse(
|
| 356 |
+
message=f"Error generating AI response: {str(e)}",
|
| 357 |
+
success=False,
|
| 358 |
+
error=str(e)
|
| 359 |
+
)
|
| 360 |
|
| 361 |
+
# Parse the response
|
| 362 |
+
parsed = self._parse_iterative_response(ai_response_text)
|
| 363 |
+
thinking = parsed.get("thinking", "")
|
| 364 |
+
action = parsed.get("action", "respond")
|
| 365 |
+
status = parsed.get("status", "complete")
|
| 366 |
+
|
| 367 |
+
all_reasoning.append(f"Step {iteration}: {thinking}")
|
| 368 |
+
|
| 369 |
+
# Check if agent wants to call a tool
|
| 370 |
+
if action == "call_tool" and status != "complete":
|
| 371 |
+
tool_name = parsed.get("tool")
|
| 372 |
+
tool_args = parsed.get("arguments", {})
|
| 373 |
+
|
| 374 |
+
if not tool_name:
|
| 375 |
+
print("β οΈ No tool specified, treating as complete")
|
| 376 |
+
final_message = parsed.get("message", thinking)
|
| 377 |
+
break
|
| 378 |
+
|
| 379 |
+
print(f"π§ Calling tool: {tool_name}")
|
| 380 |
+
print(f" Args: {json.dumps(tool_args, indent=2)}")
|
| 381 |
+
|
| 382 |
+
# Execute the tool
|
| 383 |
+
tool_result = await self.mcp_client.call_tool(tool_name, tool_args)
|
| 384 |
+
|
| 385 |
+
result_data = tool_result.result if tool_result.success else {"error": tool_result.error}
|
| 386 |
+
print(f" Result: {json.dumps(result_data, indent=2) if isinstance(result_data, dict) else result_data}")
|
| 387 |
+
|
| 388 |
+
# Record the step
|
| 389 |
+
step = AgentStep(
|
| 390 |
+
step_number=len(steps) + 1,
|
| 391 |
+
action=thinking,
|
| 392 |
+
tool_name=tool_name,
|
| 393 |
+
tool_args=tool_args,
|
| 394 |
+
result=result_data,
|
| 395 |
+
reasoning=thinking
|
| 396 |
+
)
|
| 397 |
+
steps.append(step)
|
| 398 |
+
tools_called.append(tool_name)
|
| 399 |
+
|
| 400 |
+
# Add to execution context for next iteration
|
| 401 |
+
execution_context.append({
|
| 402 |
+
"step": len(execution_context) + 1,
|
| 403 |
+
"tool": tool_name,
|
| 404 |
+
"arguments": tool_args,
|
| 405 |
+
"result": result_data,
|
| 406 |
+
"success": tool_result.success
|
| 407 |
+
})
|
| 408 |
+
|
| 409 |
+
# Continue the loop - model will see this result next iteration
|
| 410 |
+
|
| 411 |
+
else:
|
| 412 |
+
# Agent is done - extract final message
|
| 413 |
+
final_message = parsed.get("message", thinking)
|
| 414 |
+
print(f"β
Agent complete: {final_message[:200]}...")
|
| 415 |
+
break
|
| 416 |
+
|
| 417 |
+
# If we hit max iterations without completing
|
| 418 |
+
if not final_message:
|
| 419 |
+
final_message = "I completed several operations. Please check the results above."
|
| 420 |
+
|
| 421 |
+
# Generate a nicely formatted final response if we have steps
|
| 422 |
if steps:
|
| 423 |
final_message = await self._generate_final_response(
|
| 424 |
+
user_message, steps, "\n".join(all_reasoning)
|
| 425 |
)
|
| 426 |
|
| 427 |
# Add assistant response to history
|
|
|
|
| 436 |
return AgentResponse(
|
| 437 |
message=final_message,
|
| 438 |
steps=steps,
|
| 439 |
+
reasoning="\n".join(all_reasoning),
|
| 440 |
tools_called=tools_called,
|
| 441 |
success=True
|
| 442 |
)
|
config.py
CHANGED
|
@@ -22,8 +22,8 @@ class Config:
|
|
| 22 |
GEMINI_MODEL: str = os.getenv("GEMINI_MODEL", "gemini-2.0-flash-exp")
|
| 23 |
|
| 24 |
# Agent Configuration
|
| 25 |
-
MAX_TOOL_CALLS_PER_TURN: int = int(os.getenv("MAX_TOOL_CALLS_PER_TURN", "
|
| 26 |
-
AGENT_TEMPERATURE: float = float(os.getenv("AGENT_TEMPERATURE", "
|
| 27 |
|
| 28 |
# UI Configuration
|
| 29 |
APP_TITLE: str = "FleetMind AI Agent"
|
|
@@ -189,53 +189,90 @@ TOOL_DESCRIPTIONS = """
|
|
| 189 |
"""
|
| 190 |
|
| 191 |
# System prompt for the AI agent
|
| 192 |
-
AGENT_SYSTEM_PROMPT = f"""You are FleetMind AI Agent, an
|
| 193 |
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
- Intelligent route planning with traffic and weather awareness
|
| 198 |
-
- AI-powered driver assignment optimization
|
| 199 |
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
2. **Plan Steps**: If the task requires multiple operations, plan the sequence of tool calls needed.
|
| 207 |
-
|
| 208 |
-
3. **Execute Tools**: Call the appropriate MCP tools with correct parameters.
|
| 209 |
-
|
| 210 |
-
4. **Explain Reasoning**: Always explain your reasoning process - what you're doing and why.
|
| 211 |
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
## Important Guidelines
|
| 215 |
-
|
| 216 |
-
- Always geocode addresses before creating orders (to get lat/lng coordinates)
|
| 217 |
-
- When creating orders, expected_delivery_time is MANDATORY (use ISO format: YYYY-MM-DDTHH:MM:SS)
|
| 218 |
-
- For intelligent assignment, explain the AI's reasoning and confidence score
|
| 219 |
-
- If a tool call fails, explain the error and suggest alternatives
|
| 220 |
-
- Be proactive - offer relevant follow-up actions
|
| 221 |
-
|
| 222 |
-
## Example Interactions
|
| 223 |
-
|
| 224 |
-
User: "Create an urgent order for John at 123 Main St, due by 5pm"
|
| 225 |
-
You should:
|
| 226 |
-
1. Geocode "123 Main St" to get coordinates
|
| 227 |
-
2. Create order with priority=urgent, expected_delivery_time set to 5pm today
|
| 228 |
-
3. Report success and offer to assign a driver
|
| 229 |
-
|
| 230 |
-
User: "Create a driver named Alex at Downtown SF with a van"
|
| 231 |
-
You should:
|
| 232 |
-
1. Geocode "Downtown SF" to get coordinates (lat/lng)
|
| 233 |
-
2. Create driver with: name="Alex", vehicle_type="van", current_address="Downtown SF", current_lat=<from geocode>, current_lng=<from geocode>
|
| 234 |
-
3. Report success with driver details
|
| 235 |
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
"""
|
|
|
|
| 22 |
GEMINI_MODEL: str = os.getenv("GEMINI_MODEL", "gemini-2.0-flash-exp")
|
| 23 |
|
| 24 |
# Agent Configuration
|
| 25 |
+
MAX_TOOL_CALLS_PER_TURN: int = int(os.getenv("MAX_TOOL_CALLS_PER_TURN", "10")) # Increased for multi-step workflows
|
| 26 |
+
AGENT_TEMPERATURE: float = float(os.getenv("AGENT_TEMPERATURE", "1.0")) # Gemini 2.0 optimized for 1.0
|
| 27 |
|
| 28 |
# UI Configuration
|
| 29 |
APP_TITLE: str = "FleetMind AI Agent"
|
|
|
|
| 189 |
"""
|
| 190 |
|
| 191 |
# System prompt for the AI agent
|
| 192 |
+
AGENT_SYSTEM_PROMPT = f"""You are FleetMind AI Agent, an AUTONOMOUS enterprise fleet management assistant.
|
| 193 |
|
| 194 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 195 |
+
π― YOUR CORE MISSION
|
| 196 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
| 197 |
|
| 198 |
+
You AUTONOMOUSLY manage delivery operations by executing multi-step workflows:
|
| 199 |
+
- Creating orders (geocode β create β assign)
|
| 200 |
+
- Managing drivers and assignments
|
| 201 |
+
- Intelligent route planning
|
| 202 |
+
- AI-powered driver optimization
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
+
{TOOL_DESCRIPTIONS}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 207 |
+
β‘ CRITICAL: MULTI-STEP EXECUTION RULES
|
| 208 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 209 |
+
|
| 210 |
+
**RULE 1: DEPENDENCIES MATTER**
|
| 211 |
+
Many tasks require SEQUENTIAL tool calls where later calls depend on earlier results:
|
| 212 |
+
- geocode_address β THEN create_order (using lat/lng from geocode)
|
| 213 |
+
- create_order β THEN intelligent_assign_order (using order_id)
|
| 214 |
+
- geocode_address β THEN create_driver (using lat/lng from geocode)
|
| 215 |
+
|
| 216 |
+
**RULE 2: USE ACTUAL DATA FROM TOOL RESULTS**
|
| 217 |
+
NEVER guess or fabricate values. ALWAYS use real data returned by tools:
|
| 218 |
+
- β
CORRECT: After geocode returns lat=37.7749, lng=-122.4194, use THOSE EXACT values
|
| 219 |
+
- β WRONG: Making up coordinates like lat=0, lng=0 or placeholders
|
| 220 |
+
|
| 221 |
+
**RULE 3: COMPLETE THE FULL WORKFLOW**
|
| 222 |
+
When user says "create order and assign driver":
|
| 223 |
+
1. First: geocode_address to get coordinates
|
| 224 |
+
2. Then: create_order using those coordinates β get order_id
|
| 225 |
+
3. Then: intelligent_assign_order using that order_id
|
| 226 |
+
4. Finally: Report complete results
|
| 227 |
+
|
| 228 |
+
**RULE 4: REQUIRED FIELDS FOR ORDERS**
|
| 229 |
+
- customer_name (string)
|
| 230 |
+
- delivery_address (string)
|
| 231 |
+
- delivery_lat (float) - FROM GEOCODING
|
| 232 |
+
- delivery_lng (float) - FROM GEOCODING
|
| 233 |
+
- expected_delivery_time (ISO 8601: YYYY-MM-DDTHH:MM:SS)
|
| 234 |
+
|
| 235 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 236 |
+
π EXAMPLE: COMPLETE ORDER CREATION WORKFLOW
|
| 237 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 238 |
+
|
| 239 |
+
User: "Create urgent order for Sarah at 456 Oak Ave SF, assign best driver"
|
| 240 |
+
|
| 241 |
+
**Step 1 - Geocode:**
|
| 242 |
+
Tool: geocode_address
|
| 243 |
+
Args: {{"address": "456 Oak Ave, San Francisco, CA"}}
|
| 244 |
+
Result: {{"latitude": 37.7749, "longitude": -122.4194, ...}}
|
| 245 |
+
|
| 246 |
+
**Step 2 - Create Order (using geocode results):**
|
| 247 |
+
Tool: create_order
|
| 248 |
+
Args: {{
|
| 249 |
+
"customer_name": "Sarah",
|
| 250 |
+
"delivery_address": "456 Oak Ave, San Francisco, CA",
|
| 251 |
+
"delivery_lat": 37.7749, β FROM STEP 1
|
| 252 |
+
"delivery_lng": -122.4194, β FROM STEP 1
|
| 253 |
+
"expected_delivery_time": "2024-01-15T17:00:00",
|
| 254 |
+
"priority": "urgent"
|
| 255 |
+
}}
|
| 256 |
+
Result: {{"order_id": "ORD-abc123", ...}}
|
| 257 |
+
|
| 258 |
+
**Step 3 - Assign Driver (using order_id):**
|
| 259 |
+
Tool: intelligent_assign_order
|
| 260 |
+
Args: {{"order_id": "ORD-abc123"}} β FROM STEP 2
|
| 261 |
+
Result: {{"assignment_id": "...", "driver": "...", ...}}
|
| 262 |
+
|
| 263 |
+
**Step 4 - Report to User:**
|
| 264 |
+
"Created urgent order ORD-abc123 for Sarah and assigned driver John (ETA 25 min)."
|
| 265 |
+
|
| 266 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 267 |
+
β οΈ IMPORTANT GUIDELINES
|
| 268 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 269 |
+
|
| 270 |
+
1. ALWAYS geocode addresses BEFORE creating orders/drivers
|
| 271 |
+
2. expected_delivery_time is MANDATORY (ISO 8601 format)
|
| 272 |
+
3. For intelligent assignment, include AI reasoning in response
|
| 273 |
+
4. If a tool fails, explain error and suggest alternatives
|
| 274 |
+
5. Be proactive - offer relevant follow-up actions
|
| 275 |
+
6. When listing data, format as readable tables
|
| 276 |
+
|
| 277 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 278 |
"""
|