Task Intent Skill
Use this skill when implementing natural language understanding and intent detection for Todo chatbot conversations.
When to Use
- Parsing natural language commands for task operations
- Detecting user intent (add, list, update, delete, complete)
- Mapping intents to MCP tools
- Handling multi-step reasoning workflows
- Generating confirmation messages
- Managing ambiguous or error cases
Intent Classification
Primary Intents
| Intent |
Triggers |
MCP Tool |
Example |
| add_task |
add, create, new, remember, remind |
add_task |
"Add a task to buy groceries" |
| list_tasks |
show, list, view, what, display |
list_tasks |
"Show me all my tasks" |
| complete_task |
done, complete, finish, mark |
complete_task |
"Mark task 3 as complete" |
| delete_task |
delete, remove, cancel, drop |
delete_task |
"Delete the meeting task" |
| update_task |
change, update, edit, rename |
update_task |
"Change task 1 to 'Call mom tonight'" |
| add_tag |
tag, label, categorize |
add_task (with tags) |
"Add work tag to task 5" |
| list_by_tag |
tagged, labeled, category |
list_tasks (with tag filter) |
"Show me work tasks" |
| sort_tasks |
sort, order, arrange |
list_tasks (with sort param) |
"Show tasks by priority" |
Intent Detection Logic
# app/agents/intent_detector.py
import re
from typing import Tuple, Optional
class IntentDetector:
"""Detect user intent from natural language."""
# Intent patterns
ADD_PATTERNS = [
r'\b(add|create|new|remember|remind)\b',
r'\bneed to\b',
r'\bshould\b',
]
LIST_PATTERNS = [
r'\b(show|list|view|display|see)\b',
r'\bwhat.*tasks?\b',
r'\bmy tasks?\b',
r"what's (pending|completed|left)",
]
COMPLETE_PATTERNS = [
r'\b(done|complete|finish|mark|finished)\b',
r'\bmark.*complete\b',
r'\bmark.*done\b',
]
DELETE_PATTERNS = [
r'\b(delete|remove|cancel|drop|clear)\b',
r'\bget rid of\b',
]
UPDATE_PATTERNS = [
r'\b(change|update|edit|rename|modify)\b',
r'\bchange.*to\b',
]
TAG_PATTERNS = [
r'\b(tag|label|categorize)\b',
r'\btagged (as|with)\b',
r'\badd.*tag\b',
]
SORT_PATTERNS = [
r'\b(sort|order|arrange)\b',
r'\bby (priority|date|title|deadline)\b',
r'\bsorted by\b',
]
def detect(self, message: str) -> Tuple[str, dict]:
"""
Detect intent and extract parameters.
Returns: (intent_name, parameters)
"""
message_lower = message.lower()
# Check each intent pattern
if self._matches(message_lower, self.ADD_PATTERNS):
return self._extract_add_intent(message)
elif self._matches(message_lower, self.COMPLETE_PATTERNS):
return self._extract_complete_intent(message)
elif self._matches(message_lower, self.DELETE_PATTERNS):
return self._extract_delete_intent(message)
elif self._matches(message_lower, self.UPDATE_PATTERNS):
return self._extract_update_intent(message)
elif self._matches(message_lower, self.LIST_PATTERNS):
return self._extract_list_intent(message)
elif self._matches(message_lower, self.TAG_PATTERNS):
return self._extract_tag_intent(message)
elif self._matches(message_lower, self.SORT_PATTERNS):
return self._extract_sort_intent(message)
else:
# Default: treat as general query
return ("general", {"query": message})
def _matches(self, text: str, patterns: list) -> bool:
"""Check if text matches any pattern."""
return any(re.search(pattern, text, re.IGNORECASE) for pattern in patterns)
def _extract_add_intent(self, message: str) -> Tuple[str, dict]:
"""Extract task title and description for add intent."""
# Remove trigger words
clean = re.sub(
r'\b(add|create|new|remember|remind|task|to|a)\b',
'',
message,
flags=re.IGNORECASE
).strip()
# Split on common separators
parts = re.split(r'(?:with|:|–|—)', clean, maxsplit=1)
title = parts[0].strip()
description = parts[1].strip() if len(parts) > 1 else None
return ("add_task", {
"title": title,
"description": description
})
def _extract_complete_intent(self, message: str) -> Tuple[str, dict]:
"""Extract task_id for complete intent."""
# Look for task ID number
task_id_match = re.search(r'\btask\s*(\d+)\b', message, re.IGNORECASE)
if task_id_match:
return ("complete_task", {"task_id": int(task_id_match.group(1))})
# Look for standalone number
number_match = re.search(r'\b(\d+)\b', message)
if number_match:
return ("complete_task", {"task_id": int(number_match.group(1))})
# No ID found - need to ask or search by title
return ("complete_task", {"query": message})
def _extract_delete_intent(self, message: str) -> Tuple[str, dict]:
"""Extract task_id or query for delete intent."""
# Look for task ID
task_id_match = re.search(r'\btask\s*(\d+)\b', message, re.IGNORECASE)
if task_id_match:
return ("delete_task", {"task_id": int(task_id_match.group(1))})
# Extract task title/description for search
clean = re.sub(
r'\b(delete|remove|cancel|task|the)\b',
'',
message,
flags=re.IGNORECASE
).strip()
return ("delete_task", {"query": clean})
def _extract_update_intent(self, message: str) -> Tuple[str, dict]:
"""Extract task_id and new title for update intent."""
# Look for "task X to/change to Y" pattern
match = re.search(
r'\btask\s*(\d+).*(?:to|into|as)\s+["']?(.+?)["']?$',
message,
re.IGNORECASE
)
if match:
return ("update_task", {
"task_id": int(match.group(1)),
"new_title": match.group(2).strip()
})
# Fallback: needs clarification
return ("update_task", {"query": message})
def _extract_list_intent(self, message: str) -> Tuple[str, dict]:
"""Extract filter for list intent."""
message_lower = message.lower()
params = {"status": "all"}
# Status filter
if any(word in message_lower for word in ["pending", "incomplete", "todo"]):
params["status"] = "pending"
elif any(word in message_lower for word in ["completed", "done", "finished"]):
params["status"] = "completed"
# Priority filter
if "high priority" in message_lower or "urgent" in message_lower:
params["priority"] = "high"
elif "low priority" in message_lower:
params["priority"] = "low"
elif "medium priority" in message_lower:
params["priority"] = "medium"
# Tag/category filter
tag_match = re.search(r'\b(work|personal|home|shopping|urgent)\b', message_lower)
if tag_match:
params["tag_query"] = tag_match.group(1)
# Sort detection
if "by priority" in message_lower or "sorted by priority" in message_lower:
params["sort_by"] = "priority"
elif "by date" in message_lower or "by deadline" in message_lower:
params["sort_by"] = "due_date"
elif "by title" in message_lower or "alphabetically" in message_lower:
params["sort_by"] = "title"
return ("list_tasks", params)
def _extract_tag_intent(self, message: str) -> Tuple[str, dict]:
"""Extract tag operation from message."""
# Pattern: "Add work tag to task 5"
task_match = re.search(r'\btask\s*(\d+)\b', message, re.IGNORECASE)
tag_match = re.search(r'\b(work|personal|home|shopping|urgent)\b', message.lower())
if task_match and tag_match:
return ("add_tag", {
"task_id": int(task_match.group(1)),
"tag_name": tag_match.group(1)
})
# Pattern: "Tag this as work"
if tag_match:
return ("add_tag", {
"tag_name": tag_match.group(1),
"query": message
})
return ("add_tag", {"query": message})
def _extract_sort_intent(self, message: str) -> Tuple[str, dict]:
"""Extract sort preference from message."""
message_lower = message.lower()
if "priority" in message_lower:
return ("list_tasks", {"sort_by": "priority", "status": "all"})
elif "date" in message_lower or "deadline" in message_lower:
return ("list_tasks", {"sort_by": "due_date", "status": "all"})
elif "title" in message_lower or "alphabetically" in message_lower:
return ("list_tasks", {"sort_by": "title", "status": "all"})
elif "created" in message_lower or "newest" in message_lower:
return ("list_tasks", {"sort_by": "created_at", "status": "all"})
return ("list_tasks", {"sort_by": "priority", "status": "all"})
Multi-Step Reasoning
# app/agents/reasoning_engine.py
from typing import List, Dict
from app.agents.intent_detector import IntentDetector
from app.mcp.tools import MCPTools
class ReasoningEngine:
"""Handle multi-step reasoning workflows."""
def __init__(self, mcp_tools: MCPTools, user_id: str):
self.detector = IntentDetector()
self.mcp = mcp_tools
self.user_id = user_id
async def process(self, message: str) -> Dict:
"""
Process message through reasoning workflow.
Returns: {
"response": str,
"tool_calls": List[Dict]
}
"""
intent, params = self.detector.detect(message)
if intent == "add_task":
return await self._handle_add_task(params)
elif intent == "list_tasks":
return await self._handle_list_tasks(params)
elif intent == "complete_task":
return await self._handle_complete_task(params)
elif intent == "delete_task":
return await self._handle_delete_task(params)
elif intent == "update_task":
return await self._handle_update_task(params)
else:
return {
"response": "I can help you add, list, complete, update, or delete tasks. What would you like to do?",
"tool_calls": []
}
async def _handle_add_task(self, params: dict) -> dict:
"""Handle add task intent."""
result = await self.mcp.add_task(
user_id=self.user_id,
title=params["title"],
description=params.get("description")
)
return {
"response": f"✓ Created task: {params['title']}",
"tool_calls": [{"tool": "add_task", "params": params, "result": result}]
}
async def _handle_list_tasks(self, params: dict) -> dict:
"""Handle list tasks intent."""
result = await self.mcp.list_tasks(
user_id=self.user_id,
status=params.get("status", "all")
)
tasks = result.get("tasks", [])
if not tasks:
response = "You have no tasks."
else:
lines = [f"Your tasks ({params.get('status', 'all')}):"]
for task in tasks:
status = "✓" if task["completed"] else "○"
lines.append(f"{status} [{task['id']}] {task['title']}")
response = "\n".join(lines)
return {
"response": response,
"tool_calls": [{"tool": "list_tasks", "params": params, "result": result}]
}
async def _handle_complete_task(self, params: dict) -> dict:
"""Handle complete task intent."""
# Direct task ID provided
if "task_id" in params:
result = await self.mcp.complete_task(
user_id=self.user_id,
task_id=params["task_id"]
)
return {
"response": f"✓ Marked task {params['task_id']} as complete!",
"tool_calls": [{"tool": "complete_task", "params": params, "result": result}]
}
# Need to search by query
else:
# First list tasks to find match
list_result = await self.mcp.list_tasks(
user_id=self.user_id,
status="pending"
)
# Search for matching task
query = params.get("query", "").lower()
matching = [
t for t in list_result.get("tasks", [])
if query in t["title"].lower()
]
if not matching:
return {
"response": f"I couldn't find any task matching '{query}'.",
"tool_calls": []
}
if len(matching) > 1:
lines = ["Multiple tasks match. Which one?"]
for t in matching:
lines.append(f"[{t['id']}] {t['title']}")
return {
"response": "\n".join(lines),
"tool_calls": []
}
# Single match - complete it
task = matching[0]
result = await self.mcp.complete_task(
user_id=self.user_id,
task_id=task["id"]
)
return {
"response": f"✓ Marked '{task['title']}' as complete!",
"tool_calls": [
{"tool": "list_tasks", "params": {"status": "pending"}, "result": list_result},
{"tool": "complete_task", "params": {"task_id": task["id"]}, "result": result}
]
}
async def _handle_delete_task(self, params: dict) -> dict:
"""Handle delete task intent (multi-step like complete)."""
# Similar to complete_task logic
# ... (implementation follows same pattern)
pass
async def _handle_update_task(self, params: dict) -> dict:
"""Handle update task intent."""
# Similar to complete_task logic
# ... (implementation follows same pattern)
pass
Confirmation Messages
# app/agents/confirmations.py
CONFIRMATIONS = {
"add_task": "✓ Created task: {title}",
"complete_task": "✓ Marked task {task_id} as complete!",
"delete_task": "✓ Deleted task: {title}",
"update_task": "✓ Updated task {task_id}: {new_title}",
"list_tasks": "Showing {count} tasks",
}
ERROR_MESSAGES = {
"task_not_found": "I couldn't find task {task_id}.",
"no_tasks": "You have no tasks yet. Try adding one!",
"ambiguous": "Multiple tasks match '{query}'. Please be more specific or use task ID.",
"missing_title": "Please provide a task title.",
"invalid_input": "I didn't understand that. Try: 'Add a task to buy groceries'",
}
def format_confirmation(intent: str, **kwargs) -> str:
"""Format confirmation message."""
template = CONFIRMATIONS.get(intent, "Done!")
return template.format(**kwargs)
def format_error(error_type: str, **kwargs) -> str:
"""Format error message."""
template = ERROR_MESSAGES.get(error_type, "Something went wrong.")
return template.format(**kwargs)
Example Natural Language Commands
Basic Commands
| User Input |
Intent |
Parameters |
Response |
| "Add a task to buy groceries" |
add_task |
title="buy groceries" |
✓ Created task: buy groceries |
| "Show me all my tasks" |
list_tasks |
status="all" |
Your tasks (all): ... |
| "Mark task 3 as complete" |
complete_task |
task_id=3 |
✓ Marked task 3 as complete! |
| "Delete the meeting task" |
delete_task |
query="meeting" |
(multi-step: list → match → delete) |
| "What's pending?" |
list_tasks |
status="pending" |
Your tasks (pending): ... |
| "Change task 1 to Call mom" |
update_task |
task_id=1, new_title="Call mom" |
✓ Updated task 1: Call mom |
Intermediate Commands (Tags & Sort)
| User Input |
Intent |
Parameters |
Response |
| "Add work tag to task 5" |
add_tag |
task_id=5, tag_name="work" |
✓ Tagged task 5 as 'work' |
| "Show me work tasks" |
list_tasks |
tag_query="work" |
Your work tasks: ... |
| "Show high priority tasks" |
list_tasks |
priority="high" |
High priority tasks: ... |
| "Show tasks by priority" |
list_tasks |
sort_by="priority" |
Tasks sorted by priority: ... |
| "Sort tasks by deadline" |
list_tasks |
sort_by="due_date" |
Tasks sorted by deadline: ... |
| "Show pending work tasks" |
list_tasks |
status="pending", tag_query="work" |
Pending work tasks: ... |
Edge Case Handling
# Ambiguous input
User: "Complete the task"
Response: "Which task? Please specify task ID or title."
# No matches
User: "Delete xyz task"
Response: "I couldn't find any task matching 'xyz'."
# Multiple matches
User: "Complete call task"
Response: "Multiple tasks match. Which one?\n[1] Call mom\n[2] Call dentist"
# Invalid task ID
User: "Complete task 999"
Response: "I couldn't find task 999."
# Missing parameters
User: "Add a task"
Response: "What should the task be about?"
Best Practices
- Normalize Input: Lowercase, trim whitespace, handle punctuation
- Handle Synonyms: Map variations to same intent
- Multi-Step Search: List → filter → confirm before destructive actions
- Clear Confirmations: Use ✓ checkmarks and specific details
- Friendly Errors: Explain what went wrong and suggest next step
- Contextual Understanding: Use conversation history when available
- Graceful Fallback: If uncertain, ask clarifying questions
Integration Points
| Component |
Connection |
| ChatKit UI |
Sends user message |
| TodoChatAgent |
Invokes reasoning engine |
| MCP Tools |
Executes actual operations |
| FastAPI |
Returns structured response |
Production Standard: This skill ensures accurate intent detection, smooth multi-step workflows, and a friendly conversational experience with clear confirmations and error handling.
1---2name: task-intent-skill3description: AI reasoning skill for detecting user intents from natural language, mapping to MCP tools, handling multi-step workflows, and generating friendly confirmations for Todo task operations.4---5
6# Task Intent Skill
7
8Use this skill when implementing natural language understanding and intent detection for Todo chatbot conversations.
9
10## When to Use
11
12- Parsing natural language commands for task operations
13- Detecting user intent (add, list, update, delete, complete)
14- Mapping intents to MCP tools
15- Handling multi-step reasoning workflows
16- Generating confirmation messages
17- Managing ambiguous or error cases
18
19## Intent Classification
20
21### Primary Intents
22
23| Intent | Triggers | MCP Tool | Example |
24|--------|----------|----------|---------|
25| **add_task** | add, create, new, remember, remind | `add_task` | "Add a task to buy groceries" |
26| **list_tasks** | show, list, view, what, display | `list_tasks` | "Show me all my tasks" |
27| **complete_task** | done, complete, finish, mark | `complete_task` | "Mark task 3 as complete" |
28| **delete_task** | delete, remove, cancel, drop | `delete_task` | "Delete the meeting task" |
29| **update_task** | change, update, edit, rename | `update_task` | "Change task 1 to 'Call mom tonight'" |
30| **add_tag** | tag, label, categorize | `add_task` (with tags) | "Add work tag to task 5" |
31| **list_by_tag** | tagged, labeled, category | `list_tasks` (with tag filter) | "Show me work tasks" |
32| **sort_tasks** | sort, order, arrange | `list_tasks` (with sort param) | "Show tasks by priority" |
33
34## Intent Detection Logic
35
36```python
37# app/agents/intent_detector.py
38import re
39from typing import Tuple, Optional
40
41class IntentDetector:
42 """Detect user intent from natural language."""
43
44 # Intent patterns
45 ADD_PATTERNS = [
46 r'\b(add|create|new|remember|remind)\b',
47 r'\bneed to\b',
48 r'\bshould\b',
49 ]
50
51 LIST_PATTERNS = [
52 r'\b(show|list|view|display|see)\b',
53 r'\bwhat.*tasks?\b',
54 r'\bmy tasks?\b',
55 r"what's (pending|completed|left)",
56 ]
57
58 COMPLETE_PATTERNS = [
59 r'\b(done|complete|finish|mark|finished)\b',
60 r'\bmark.*complete\b',
61 r'\bmark.*done\b',
62 ]
63
64 DELETE_PATTERNS = [
65 r'\b(delete|remove|cancel|drop|clear)\b',
66 r'\bget rid of\b',
67 ]
68
69 UPDATE_PATTERNS = [
70 r'\b(change|update|edit|rename|modify)\b',
71 r'\bchange.*to\b',
72 ]
73
74 TAG_PATTERNS = [
75 r'\b(tag|label|categorize)\b',
76 r'\btagged (as|with)\b',
77 r'\badd.*tag\b',
78 ]
79
80 SORT_PATTERNS = [
81 r'\b(sort|order|arrange)\b',
82 r'\bby (priority|date|title|deadline)\b',
83 r'\bsorted by\b',
84 ]
85
86 def detect(self, message: str) -> Tuple[str, dict]:
87 """
88 Detect intent and extract parameters.
89
90 Returns: (intent_name, parameters)
91 """
92 message_lower = message.lower()
93
94 # Check each intent pattern
95 if self._matches(message_lower, self.ADD_PATTERNS):
96 return self._extract_add_intent(message)
97
98 elif self._matches(message_lower, self.COMPLETE_PATTERNS):
99 return self._extract_complete_intent(message)
100
101 elif self._matches(message_lower, self.DELETE_PATTERNS):
102 return self._extract_delete_intent(message)
103
104 elif self._matches(message_lower, self.UPDATE_PATTERNS):
105 return self._extract_update_intent(message)
106
107 elif self._matches(message_lower, self.LIST_PATTERNS):
108 return self._extract_list_intent(message)
109
110 elif self._matches(message_lower, self.TAG_PATTERNS):
111 return self._extract_tag_intent(message)
112
113 elif self._matches(message_lower, self.SORT_PATTERNS):
114 return self._extract_sort_intent(message)
115
116 else:
117 # Default: treat as general query
118 return ("general", {"query": message})
119
120 def _matches(self, text: str, patterns: list) -> bool:
121 """Check if text matches any pattern."""
122 return any(re.search(pattern, text, re.IGNORECASE) for pattern in patterns)
123
124 def _extract_add_intent(self, message: str) -> Tuple[str, dict]:
125 """Extract task title and description for add intent."""
126 # Remove trigger words
127 clean = re.sub(
128 r'\b(add|create|new|remember|remind|task|to|a)\b',
129 '',
130 message,
131 flags=re.IGNORECASE
132 ).strip()
133
134 # Split on common separators
135 parts = re.split(r'(?:with|:|–|—)', clean, maxsplit=1)
136 title = parts[0].strip()
137 description = parts[1].strip() if len(parts) > 1 else None
138
139 return ("add_task", {
140 "title": title,
141 "description": description
142 })
143
144 def _extract_complete_intent(self, message: str) -> Tuple[str, dict]:
145 """Extract task_id for complete intent."""
146 # Look for task ID number
147 task_id_match = re.search(r'\btask\s*(\d+)\b', message, re.IGNORECASE)
148 if task_id_match:
149 return ("complete_task", {"task_id": int(task_id_match.group(1))})
150
151 # Look for standalone number
152 number_match = re.search(r'\b(\d+)\b', message)
153 if number_match:
154 return ("complete_task", {"task_id": int(number_match.group(1))})
155
156 # No ID found - need to ask or search by title
157 return ("complete_task", {"query": message})
158
159 def _extract_delete_intent(self, message: str) -> Tuple[str, dict]:
160 """Extract task_id or query for delete intent."""
161 # Look for task ID
162 task_id_match = re.search(r'\btask\s*(\d+)\b', message, re.IGNORECASE)
163 if task_id_match:
164 return ("delete_task", {"task_id": int(task_id_match.group(1))})
165
166 # Extract task title/description for search
167 clean = re.sub(
168 r'\b(delete|remove|cancel|task|the)\b',
169 '',
170 message,
171 flags=re.IGNORECASE
172 ).strip()
173
174 return ("delete_task", {"query": clean})
175
176 def _extract_update_intent(self, message: str) -> Tuple[str, dict]:
177 """Extract task_id and new title for update intent."""
178 # Look for "task X to/change to Y" pattern
179 match = re.search(
180 r'\btask\s*(\d+).*(?:to|into|as)\s+["']?(.+?)["']?$',
181 message,
182 re.IGNORECASE
183 )
184 if match:
185 return ("update_task", {
186 "task_id": int(match.group(1)),
187 "new_title": match.group(2).strip()
188 })
189
190 # Fallback: needs clarification
191 return ("update_task", {"query": message})
192
193 def _extract_list_intent(self, message: str) -> Tuple[str, dict]:
194 """Extract filter for list intent."""
195 message_lower = message.lower()
196 params = {"status": "all"}
197
198 # Status filter
199 if any(word in message_lower for word in ["pending", "incomplete", "todo"]):
200 params["status"] = "pending"
201 elif any(word in message_lower for word in ["completed", "done", "finished"]):
202 params["status"] = "completed"
203
204 # Priority filter
205 if "high priority" in message_lower or "urgent" in message_lower:
206 params["priority"] = "high"
207 elif "low priority" in message_lower:
208 params["priority"] = "low"
209 elif "medium priority" in message_lower:
210 params["priority"] = "medium"
211
212 # Tag/category filter
213 tag_match = re.search(r'\b(work|personal|home|shopping|urgent)\b', message_lower)
214 if tag_match:
215 params["tag_query"] = tag_match.group(1)
216
217 # Sort detection
218 if "by priority" in message_lower or "sorted by priority" in message_lower:
219 params["sort_by"] = "priority"
220 elif "by date" in message_lower or "by deadline" in message_lower:
221 params["sort_by"] = "due_date"
222 elif "by title" in message_lower or "alphabetically" in message_lower:
223 params["sort_by"] = "title"
224
225 return ("list_tasks", params)
226
227 def _extract_tag_intent(self, message: str) -> Tuple[str, dict]:
228 """Extract tag operation from message."""
229 # Pattern: "Add work tag to task 5"
230 task_match = re.search(r'\btask\s*(\d+)\b', message, re.IGNORECASE)
231 tag_match = re.search(r'\b(work|personal|home|shopping|urgent)\b', message.lower())
232
233 if task_match and tag_match:
234 return ("add_tag", {
235 "task_id": int(task_match.group(1)),
236 "tag_name": tag_match.group(1)
237 })
238
239 # Pattern: "Tag this as work"
240 if tag_match:
241 return ("add_tag", {
242 "tag_name": tag_match.group(1),
243 "query": message
244 })
245
246 return ("add_tag", {"query": message})
247
248 def _extract_sort_intent(self, message: str) -> Tuple[str, dict]:
249 """Extract sort preference from message."""
250 message_lower = message.lower()
251
252 if "priority" in message_lower:
253 return ("list_tasks", {"sort_by": "priority", "status": "all"})
254 elif "date" in message_lower or "deadline" in message_lower:
255 return ("list_tasks", {"sort_by": "due_date", "status": "all"})
256 elif "title" in message_lower or "alphabetically" in message_lower:
257 return ("list_tasks", {"sort_by": "title", "status": "all"})
258 elif "created" in message_lower or "newest" in message_lower:
259 return ("list_tasks", {"sort_by": "created_at", "status": "all"})
260
261 return ("list_tasks", {"sort_by": "priority", "status": "all"})
262```
263
264## Multi-Step Reasoning
265
266```python
267# app/agents/reasoning_engine.py
268from typing import List, Dict
269from app.agents.intent_detector import IntentDetector
270from app.mcp.tools import MCPTools
271
272class ReasoningEngine:
273 """Handle multi-step reasoning workflows."""
274
275 def __init__(self, mcp_tools: MCPTools, user_id: str):
276 self.detector = IntentDetector()
277 self.mcp = mcp_tools
278 self.user_id = user_id
279
280 async def process(self, message: str) -> Dict:
281 """
282 Process message through reasoning workflow.
283
284 Returns: {
285 "response": str,
286 "tool_calls": List[Dict]
287 }
288 """
289 intent, params = self.detector.detect(message)
290
291 if intent == "add_task":
292 return await self._handle_add_task(params)
293
294 elif intent == "list_tasks":
295 return await self._handle_list_tasks(params)
296
297 elif intent == "complete_task":
298 return await self._handle_complete_task(params)
299
300 elif intent == "delete_task":
301 return await self._handle_delete_task(params)
302
303 elif intent == "update_task":
304 return await self._handle_update_task(params)
305
306 else:
307 return {
308 "response": "I can help you add, list, complete, update, or delete tasks. What would you like to do?",
309 "tool_calls": []
310 }
311
312 async def _handle_add_task(self, params: dict) -> dict:
313 """Handle add task intent."""
314 result = await self.mcp.add_task(
315 user_id=self.user_id,
316 title=params["title"],
317 description=params.get("description")
318 )
319
320 return {
321 "response": f"✓ Created task: {params['title']}",
322 "tool_calls": [{"tool": "add_task", "params": params, "result": result}]
323 }
324
325 async def _handle_list_tasks(self, params: dict) -> dict:
326 """Handle list tasks intent."""
327 result = await self.mcp.list_tasks(
328 user_id=self.user_id,
329 status=params.get("status", "all")
330 )
331
332 tasks = result.get("tasks", [])
333 if not tasks:
334 response = "You have no tasks."
335 else:
336 lines = [f"Your tasks ({params.get('status', 'all')}):"]
337 for task in tasks:
338 status = "✓" if task["completed"] else "○"
339 lines.append(f"{status} [{task['id']}] {task['title']}")
340 response = "\n".join(lines)
341
342 return {
343 "response": response,
344 "tool_calls": [{"tool": "list_tasks", "params": params, "result": result}]
345 }
346
347 async def _handle_complete_task(self, params: dict) -> dict:
348 """Handle complete task intent."""
349 # Direct task ID provided
350 if "task_id" in params:
351 result = await self.mcp.complete_task(
352 user_id=self.user_id,
353 task_id=params["task_id"]
354 )
355
356 return {
357 "response": f"✓ Marked task {params['task_id']} as complete!",
358 "tool_calls": [{"tool": "complete_task", "params": params, "result": result}]
359 }
360
361 # Need to search by query
362 else:
363 # First list tasks to find match
364 list_result = await self.mcp.list_tasks(
365 user_id=self.user_id,
366 status="pending"
367 )
368
369 # Search for matching task
370 query = params.get("query", "").lower()
371 matching = [
372 t for t in list_result.get("tasks", [])
373 if query in t["title"].lower()
374 ]
375
376 if not matching:
377 return {
378 "response": f"I couldn't find any task matching '{query}'.",
379 "tool_calls": []
380 }
381
382 if len(matching) > 1:
383 lines = ["Multiple tasks match. Which one?"]
384 for t in matching:
385 lines.append(f"[{t['id']}] {t['title']}")
386 return {
387 "response": "\n".join(lines),
388 "tool_calls": []
389 }
390
391 # Single match - complete it
392 task = matching[0]
393 result = await self.mcp.complete_task(
394 user_id=self.user_id,
395 task_id=task["id"]
396 )
397
398 return {
399 "response": f"✓ Marked '{task['title']}' as complete!",
400 "tool_calls": [
401 {"tool": "list_tasks", "params": {"status": "pending"}, "result": list_result},
402 {"tool": "complete_task", "params": {"task_id": task["id"]}, "result": result}
403 ]
404 }
405
406 async def _handle_delete_task(self, params: dict) -> dict:
407 """Handle delete task intent (multi-step like complete)."""
408 # Similar to complete_task logic
409 # ... (implementation follows same pattern)
410 pass
411
412 async def _handle_update_task(self, params: dict) -> dict:
413 """Handle update task intent."""
414 # Similar to complete_task logic
415 # ... (implementation follows same pattern)
416 pass
417```
418
419## Confirmation Messages
420
421```python
422# app/agents/confirmations.py
423
424CONFIRMATIONS = {
425 "add_task": "✓ Created task: {title}",
426 "complete_task": "✓ Marked task {task_id} as complete!",
427 "delete_task": "✓ Deleted task: {title}",
428 "update_task": "✓ Updated task {task_id}: {new_title}",
429 "list_tasks": "Showing {count} tasks",
430}
431
432ERROR_MESSAGES = {
433 "task_not_found": "I couldn't find task {task_id}.",
434 "no_tasks": "You have no tasks yet. Try adding one!",
435 "ambiguous": "Multiple tasks match '{query}'. Please be more specific or use task ID.",
436 "missing_title": "Please provide a task title.",
437 "invalid_input": "I didn't understand that. Try: 'Add a task to buy groceries'",
438}
439
440def format_confirmation(intent: str, **kwargs) -> str:
441 """Format confirmation message."""
442 template = CONFIRMATIONS.get(intent, "Done!")
443 return template.format(**kwargs)
444
445def format_error(error_type: str, **kwargs) -> str:
446 """Format error message."""
447 template = ERROR_MESSAGES.get(error_type, "Something went wrong.")
448 return template.format(**kwargs)
449```
450
451## Example Natural Language Commands
452
453### Basic Commands
454| User Input | Intent | Parameters | Response |
455|------------|--------|------------|----------|
456| "Add a task to buy groceries" | add_task | title="buy groceries" | ✓ Created task: buy groceries |
457| "Show me all my tasks" | list_tasks | status="all" | Your tasks (all): ... |
458| "Mark task 3 as complete" | complete_task | task_id=3 | ✓ Marked task 3 as complete! |
459| "Delete the meeting task" | delete_task | query="meeting" | (multi-step: list → match → delete) |
460| "What's pending?" | list_tasks | status="pending" | Your tasks (pending): ... |
461| "Change task 1 to Call mom" | update_task | task_id=1, new_title="Call mom" | ✓ Updated task 1: Call mom |
462
463### Intermediate Commands (Tags & Sort)
464| User Input | Intent | Parameters | Response |
465|------------|--------|------------|----------|
466| "Add work tag to task 5" | add_tag | task_id=5, tag_name="work" | ✓ Tagged task 5 as 'work' |
467| "Show me work tasks" | list_tasks | tag_query="work" | Your work tasks: ... |
468| "Show high priority tasks" | list_tasks | priority="high" | High priority tasks: ... |
469| "Show tasks by priority" | list_tasks | sort_by="priority" | Tasks sorted by priority: ... |
470| "Sort tasks by deadline" | list_tasks | sort_by="due_date" | Tasks sorted by deadline: ... |
471| "Show pending work tasks" | list_tasks | status="pending", tag_query="work" | Pending work tasks: ... |
472
473## Edge Case Handling
474
475```python
476# Ambiguous input
477User: "Complete the task"
478Response: "Which task? Please specify task ID or title."
479
480# No matches
481User: "Delete xyz task"
482Response: "I couldn't find any task matching 'xyz'."
483
484# Multiple matches
485User: "Complete call task"
486Response: "Multiple tasks match. Which one?\n[1] Call mom\n[2] Call dentist"
487
488# Invalid task ID
489User: "Complete task 999"
490Response: "I couldn't find task 999."
491
492# Missing parameters
493User: "Add a task"
494Response: "What should the task be about?"
495```
496
497## Best Practices
498
4991. **Normalize Input**: Lowercase, trim whitespace, handle punctuation
5002. **Handle Synonyms**: Map variations to same intent
5013. **Multi-Step Search**: List → filter → confirm before destructive actions
5024. **Clear Confirmations**: Use ✓ checkmarks and specific details
5035. **Friendly Errors**: Explain what went wrong and suggest next step
5046. **Contextual Understanding**: Use conversation history when available
5057. **Graceful Fallback**: If uncertain, ask clarifying questions
506
507## Integration Points
508
509| Component | Connection |
510|-----------|------------|
511| **ChatKit UI** | Sends user message |
512| **TodoChatAgent** | Invokes reasoning engine |
513| **MCP Tools** | Executes actual operations |
514| **FastAPI** | Returns structured response |
515
516---
517
518**Production Standard**: This skill ensures accurate intent detection, smooth multi-step workflows, and a friendly conversational experience with clear confirmations and error handling.