feat: introduce clarifying questions tool for interactive user engagement
- Added a new `clarify_tool` to enable the agent to ask structured multiple-choice or open-ended questions to users. - Implemented callback functionality for user interaction, allowing the platform to handle UI presentation. - Updated the CLI and agent to support clarify questions, including timeout handling and response management. - Enhanced toolset definitions and requirements to include the clarify tool, ensuring availability across platforms.
This commit is contained in:
parent
997f793af1
commit
9350e26e68
6 changed files with 386 additions and 2 deletions
|
|
@ -142,6 +142,13 @@ from .todo_tool import (
|
|||
TodoStore,
|
||||
)
|
||||
|
||||
# Clarifying questions tool (interactive Q&A with the user)
|
||||
from .clarify_tool import (
|
||||
clarify_tool,
|
||||
check_clarify_requirements,
|
||||
CLARIFY_SCHEMA,
|
||||
)
|
||||
|
||||
# File tools have no external requirements - they use the terminal backend
|
||||
def check_file_requirements():
|
||||
"""File tools only require terminal backend to be available."""
|
||||
|
|
@ -239,5 +246,9 @@ __all__ = [
|
|||
'check_todo_requirements',
|
||||
'TODO_SCHEMA',
|
||||
'TodoStore',
|
||||
# Clarifying questions tool
|
||||
'clarify_tool',
|
||||
'check_clarify_requirements',
|
||||
'CLARIFY_SCHEMA',
|
||||
]
|
||||
|
||||
|
|
|
|||
125
tools/clarify_tool.py
Normal file
125
tools/clarify_tool.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Clarify Tool Module - Interactive Clarifying Questions
|
||||
|
||||
Allows the agent to present structured multiple-choice questions or open-ended
|
||||
prompts to the user. In CLI mode, choices are navigable with arrow keys. On
|
||||
messaging platforms, choices are rendered as a numbered list.
|
||||
|
||||
The actual user-interaction logic lives in the platform layer (cli.py for CLI,
|
||||
gateway/run.py for messaging). This module defines the schema, validation, and
|
||||
a thin dispatcher that delegates to a platform-provided callback.
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Dict, Any, List, Optional, Callable
|
||||
|
||||
|
||||
# Maximum number of predefined choices the agent can offer.
|
||||
# A 5th "Other (type your answer)" option is always appended by the UI.
|
||||
MAX_CHOICES = 4
|
||||
|
||||
|
||||
def clarify_tool(
|
||||
question: str,
|
||||
choices: Optional[List[str]] = None,
|
||||
callback: Optional[Callable] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Ask the user a question, optionally with multiple-choice options.
|
||||
|
||||
Args:
|
||||
question: The question text to present.
|
||||
choices: Up to 4 predefined answer choices. When omitted the
|
||||
question is purely open-ended.
|
||||
callback: Platform-provided function that handles the actual UI
|
||||
interaction. Signature: callback(question, choices) -> str.
|
||||
Injected by the agent runner (cli.py / gateway).
|
||||
|
||||
Returns:
|
||||
JSON string with the user's response.
|
||||
"""
|
||||
if not question or not question.strip():
|
||||
return json.dumps({"error": "Question text is required."}, ensure_ascii=False)
|
||||
|
||||
question = question.strip()
|
||||
|
||||
# Validate and trim choices
|
||||
if choices is not None:
|
||||
if not isinstance(choices, list):
|
||||
return json.dumps({"error": "choices must be a list of strings."}, ensure_ascii=False)
|
||||
choices = [str(c).strip() for c in choices if str(c).strip()]
|
||||
if len(choices) > MAX_CHOICES:
|
||||
choices = choices[:MAX_CHOICES]
|
||||
if not choices:
|
||||
choices = None # empty list → open-ended
|
||||
|
||||
if callback is None:
|
||||
return json.dumps(
|
||||
{"error": "Clarify tool is not available in this execution context."},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
try:
|
||||
user_response = callback(question, choices)
|
||||
except Exception as exc:
|
||||
return json.dumps(
|
||||
{"error": f"Failed to get user input: {exc}"},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
return json.dumps({
|
||||
"question": question,
|
||||
"choices_offered": choices,
|
||||
"user_response": str(user_response).strip(),
|
||||
}, ensure_ascii=False)
|
||||
|
||||
|
||||
def check_clarify_requirements() -> bool:
|
||||
"""Clarify tool has no external requirements -- always available."""
|
||||
return True
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# OpenAI Function-Calling Schema
|
||||
# =============================================================================
|
||||
|
||||
CLARIFY_SCHEMA = {
|
||||
"name": "clarify",
|
||||
"description": (
|
||||
"Ask the user a question when you need clarification, feedback, or a "
|
||||
"decision before proceeding. Supports two modes:\n\n"
|
||||
"1. **Multiple choice** — provide up to 4 choices. The user picks one "
|
||||
"or types their own answer via a 5th 'Other' option.\n"
|
||||
"2. **Open-ended** — omit choices entirely. The user types a free-form "
|
||||
"response.\n\n"
|
||||
"Use this tool when:\n"
|
||||
"- The task is ambiguous and you need the user to choose an approach\n"
|
||||
"- You want post-task feedback ('How did that work out?')\n"
|
||||
"- You want to offer to save a skill or update memory\n"
|
||||
"- A decision has meaningful trade-offs the user should weigh in on\n\n"
|
||||
"Do NOT use this tool for simple yes/no confirmation of dangerous "
|
||||
"commands (the terminal tool handles that). Prefer making a reasonable "
|
||||
"default choice yourself when the decision is low-stakes."
|
||||
),
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {
|
||||
"type": "string",
|
||||
"description": "The question to present to the user.",
|
||||
},
|
||||
"choices": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"maxItems": MAX_CHOICES,
|
||||
"description": (
|
||||
"Up to 4 answer choices. Omit this parameter entirely to "
|
||||
"ask an open-ended question. When provided, the UI "
|
||||
"automatically appends an 'Other (type your answer)' option."
|
||||
),
|
||||
},
|
||||
},
|
||||
"required": ["question"],
|
||||
},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue