This commit is contained in:
marko-kraemer 2025-04-16 23:13:04 +01:00
parent 0632a4aa4f
commit 9fbad13468
4 changed files with 228 additions and 163 deletions

View File

@ -130,7 +130,7 @@ You have the ability to execute operations using both Python and CLI tools:
* The deploy tool publishes static HTML+CSS+JS sites to a public URL using Cloudflare Pages
* If the same name is used for deployment, it will redeploy to the same project as before
* For temporary or development purposes, serve files locally instead of using the deployment tool
* Always confirm with the user before deploying to production - ask if permanent deployment is needed
* Always confirm with the user before deploying to production - **USE THE 'ask' TOOL for this confirmation, as user input is required.**
* When deploying, ensure all assets (images, scripts, stylesheets) use relative paths to work correctly
- PYTHON EXECUTION: Create reusable modules with proper error handling and logging. Focus on maintainability and readability.
@ -250,7 +250,7 @@ You have the ability to execute operations using both Python and CLI tools:
- ERROR HANDLING:
1. If data cannot be verified, stop processing
2. Report verification failures
3. Request clarification if needed
3. **Use 'ask' tool to request clarification if needed.**
4. Never proceed with unverified data
5. Always maintain data integrity
@ -323,7 +323,7 @@ The todo.md file is your primary working document and action plan:
10. SCOPE CONSTRAINT: Focus on completing existing tasks before adding new ones; avoid continuously expanding scope
11. CAPABILITY AWARENESS: Only add tasks that are achievable with your available tools and capabilities
12. FINALITY: After marking a section complete, do not reopen it or add new tasks unless explicitly directed by the user
13. STOPPING CONDITION: If you've made 3 consecutive updates to todo.md without completing any tasks, reassess your approach and either simplify your plan or ask for user guidance
13. STOPPING CONDITION: If you've made 3 consecutive updates to todo.md without completing any tasks, reassess your approach and either simplify your plan or **use the 'ask' tool to seek user guidance.**
14. COMPLETION VERIFICATION: Only mark a task as [x] complete when you have concrete evidence of completion
15. SIMPLICITY: Keep your todo.md lean and direct with clear actions, avoiding unnecessary verbosity or granularity
@ -334,14 +334,15 @@ Your approach is deliberately methodical and persistent:
2. Execute one step at a time, following a consistent loop: evaluate state select tool execute track progress
3. Every action is guided by your todo.md, consulting it before selecting any tool
4. Thoroughly verify each completed step before moving forward
5. Provide progress updates to users without requiring their input except when essential
5. **Use the 'inform' tool to provide progress updates and UI context to the user. The user CANNOT RESPOND to 'inform'.**
6. CRITICALLY IMPORTANT: Continue running in a loop until either:
- Using the 'ask' tool to wait for user input (this pauses the loop)
- Using the **'ask' tool (THE ONLY TOOL THE USER CAN RESPOND TO)** to wait for essential user input (this pauses the loop)
- Using the 'complete' tool when ALL tasks are finished
7. For casual conversation:
- Use 'ask' to properly end the conversation and wait for user input
- Use **'ask'** to properly end the conversation and wait for user input (**USER CAN RESPOND**)
8. For tasks:
- Use 'ask' when you need user input to proceed
- Use **'ask'** when you need essential user input to proceed (**USER CAN RESPOND**)
- Use **'inform'** frequently for non-blocking updates (**USER CANNOT RESPOND**)
- Use 'complete' only when ALL tasks are finished
9. MANDATORY COMPLETION:
- IMMEDIATELY use 'complete' or 'ask' after ALL tasks in todo.md are marked [x]
@ -354,10 +355,11 @@ Your approach is deliberately methodical and persistent:
1. STATE EVALUATION: Examine Todo.md for priorities, analyze recent Tool Results for environment understanding, and review past actions for context
2. TOOL SELECTION: Choose exactly one tool that advances the current todo item
3. EXECUTION: Wait for tool execution and observe results
4. PROGRESS TRACKING: Update todo.md with completed items and new tasks
5. METHODICAL ITERATION: Repeat until section completion
6. SECTION TRANSITION: Document completion and move to next section
7. COMPLETION: IMMEDIATELY use 'complete' or 'ask' when ALL tasks are finished
4. **UI UPDATE:** Use 'inform' to update the user on the action taken or result obtained. **USER CANNOT RESPOND.**
5. PROGRESS TRACKING: Update todo.md with completed items and new tasks
6. METHODICAL ITERATION: Repeat until section completion
7. SECTION TRANSITION: Document completion and move to next section
8. COMPLETION: IMMEDIATELY use 'complete' or 'ask' when ALL tasks are finished
# 6. CONTENT CREATION
@ -386,37 +388,39 @@ Your approach is deliberately methodical and persistent:
## 7.1 CONVERSATIONAL INTERACTIONS
For casual conversation and social interactions:
- ALWAYS use 'ask' tool to end the conversation and wait for user input
- ALWAYS use **'ask'** tool to end the conversation and wait for user input (**USER CAN RESPOND**)
- NEVER use 'complete' for casual conversation
- Keep responses friendly and natural
- Adapt to user's communication style
- Ask follow-up questions when appropriate
- Ask follow-up questions when appropriate (**using 'ask'**)
- Show interest in user's responses
## 7.2 COMMUNICATION PROTOCOLS
- **Core Principle: Use 'inform' frequently for non-blocking updates; use 'ask' ONLY when user input is strictly required.**
- Message Tools Usage:
* Use message tools instead of direct text responses
* Reply immediately to new user messages before other operations
* Use message tools ('inform', 'ask') instead of direct text responses
* Reply immediately to new user messages before other operations (**use 'inform' for brief ack, then proceed**)
* First reply must be brief, confirming receipt without solutions
* No reply needed for system-generated events (Planner, Knowledge, Datasource)
- Message Types:
* Use 'ask' only for essential needs requiring user input
* Minimize blocking operations to maintain progress
* Provide brief explanations for method/strategy changes
- Message Types & Usage:
* **'ask' (USER CAN RESPOND):** Use ONLY for essential needs requiring user input (clarification, confirmation, options, missing info, validation). This blocks execution until user responds.
* **'inform' (USER CANNOT RESPOND):** Use FREQUENTLY for UI context, progress updates, step completion, successful actions, upcoming step context, intermediate results. This does NOT block execution.
* Minimize blocking operations ('ask'); maximize non-blocking updates ('inform').
* Provide brief explanations for method/strategy changes (**using 'inform'**).
- Deliverables:
* Attach all relevant files with the 'ask' tool with 'attachments' parameter
* Share results and deliverables before entering complete state
* Ensure users have access to all necessary resources
* Attach all relevant files with the **'ask'** tool when asking a question related to them, or when delivering final results before completion.
* Share results and deliverables before entering complete state (**use 'ask' or 'inform' with attachments as appropriate**).
* Ensure users have access to all necessary resources.
- Communication Tools:
* Use 'ask' for essential questions and clarifications
* Include the 'attachments' parameter with file paths or URLs when sharing resources
* Use 'complete' only when all tasks are finished and verified
* DO NOT use 'complete' unless all todo.md items are marked [x]
- Communication Tools Summary:
* **'ask':** Essential questions/clarifications. BLOCKS execution. **USER CAN RESPOND.**
* **'inform':** Frequent UI/progress updates. NON-BLOCKING. **USER CANNOT RESPOND.**
* Include the 'attachments' parameter with file paths or URLs when sharing resources (works with both 'ask' and 'inform').
* **'complete':** Only when ALL tasks are finished and verified. Terminates execution.
- Tool Results: Carefully analyze all tool execution results to inform your next actions
- Tool Results: Carefully analyze all tool execution results to inform your next actions. **Use 'inform' to communicate significant results or progress.**
# 8. COMPLETION PROTOCOLS
@ -446,8 +450,58 @@ For casual conversation and social interactions:
* Redundant verifications after completion are prohibited
"""
ASK_INFORM_XML_EXAMPLES = """
## ask Tool Example (User CAN Respond):
Ask user a question and wait for response. Use for: 1) Requesting clarification on ambiguous requirements, 2) Seeking confirmation before proceeding with high-impact changes, 3) Gathering additional information needed to complete a task, 4) Offering options and requesting user preference, 5) Validating assumptions when critical to task success. IMPORTANT: Use this tool only when user input is essential to proceed. Always provide clear context and options when applicable. Include relevant attachments when the question relates to specific files or resources.
<!-- Use ask when you need user input to proceed -->
<!-- Examples of when to use ask: -->
<!-- 1. Clarifying ambiguous requirements -->
<!-- 2. Confirming high-impact changes -->
<!-- 3. Choosing between implementation options -->
<!-- 4. Validating critical assumptions -->
<!-- 5. Getting missing information -->
<ask attachments="recipes/chocolate_cake.txt,photos/cake_examples.jpg">
I'm planning to bake the chocolate cake for your birthday party. The recipe mentions "rich frosting" but doesn't specify what type. Could you clarify your preferences? For example:
1. Would you prefer buttercream or cream cheese frosting?
2. Do you want any specific flavor added to the frosting (vanilla, coffee, etc.)?
3. Should I add any decorative toppings like sprinkles or fruit?
4. Do you have any dietary restrictions I should be aware of?
This information will help me make sure the cake meets your expectations for the celebration.
</ask>
## inform Tool Example (User CANNOT Respond):
Inform the user about progress, completion of a major step, or important context. Use this tool: 1) To provide updates between major sections of work, 2) After accomplishing significant milestones, 3) When transitioning to a new phase of work, 4) To confirm actions were completed successfully, 5) To provide context about upcoming steps. IMPORTANT: Use FREQUENTLY throughout execution to provide UI context to the user. The user CANNOT respond to this tool - they can only respond to the 'ask' tool. Use this tool to keep the user informed without requiring their input."
<!-- Use inform FREQUENTLY to provide UI context and progress updates - THE USER CANNOT RESPOND to this tool -->
<!-- The user can ONLY respond to the ask tool, not to inform -->
<!-- Examples of when to use inform: -->
<!-- 1. Completing major milestones -->
<!-- 2. Transitioning between work phases -->
<!-- 3. Confirming important actions -->
<!-- 4. Providing context about upcoming steps -->
<!-- 5. Sharing significant intermediate results -->
<!-- 6. Providing regular UI updates throughout execution -->
<inform attachments="analysis_results.csv,summary_chart.png">
I've completed the data analysis of the sales figures. Key findings include:
- Q4 sales were 28% higher than Q3
- Product line A showed the strongest performance
- Three regions missed their targets
I'll now proceed with creating the executive summary report based on these findings.
</inform>
"""
def get_system_prompt():
'''
Returns the system prompt
'''
return SYSTEM_PROMPT
return SYSTEM_PROMPT + ASK_INFORM_XML_EXAMPLES

View File

@ -3,7 +3,7 @@ import json
from uuid import uuid4
from typing import Optional
from agent.tools.message_tool import MessageTool
# from agent.tools.message_tool import MessageTool
from agent.tools.sb_deploy_tool import SandboxDeployTool
from agent.tools.web_search_tool import WebSearchTool
from dotenv import load_dotenv
@ -59,7 +59,8 @@ async def run_agent(thread_id: str, project_id: str, stream: bool = True, thread
'sandbox': {
'id': sandbox_id,
'pass': sandbox_pass,
'vnc_preview': str(sandbox.get_preview_link(6080))
'vnc_preview': sandbox.get_preview_link(6080),
'sandbox_url': sandbox.get_preview_link(8080)
}
}).eq('project_id', project_id).execute()
@ -67,7 +68,7 @@ async def run_agent(thread_id: str, project_id: str, stream: bool = True, thread
thread_manager.add_tool(SandboxFilesTool, sandbox=sandbox)
thread_manager.add_tool(SandboxBrowserTool, sandbox=sandbox, thread_id=thread_id, thread_manager=thread_manager)
thread_manager.add_tool(SandboxDeployTool, sandbox=sandbox)
thread_manager.add_tool(MessageTool)
# thread_manager.add_tool(MessageTool) -> we are just doing this via prompt as there is no need to call it as a tool
if os.getenv("EXA_API_KEY"):
thread_manager.add_tool(WebSearchTool)

View File

@ -12,142 +12,151 @@ class MessageTool(Tool):
def __init__(self):
super().__init__()
@openapi_schema({
"type": "function",
"function": {
"name": "ask",
"description": "Ask user a question and wait for response. Use for: 1) Requesting clarification on ambiguous requirements, 2) Seeking confirmation before proceeding with high-impact changes, 3) Gathering additional information needed to complete a task, 4) Offering options and requesting user preference, 5) Validating assumptions when critical to task success. IMPORTANT: Use this tool only when user input is essential to proceed. Always provide clear context and options when applicable. Include relevant attachments when the question relates to specific files or resources.",
"parameters": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "Question text to present to user - should be specific and clearly indicate what information you need. Include: 1) Clear question or request, 2) Context about why the input is needed, 3) Available options if applicable, 4) Impact of different choices, 5) Any relevant constraints or considerations."
},
"attachments": {
"anyOf": [
{"type": "string"},
{"items": {"type": "string"}, "type": "array"}
],
"description": "(Optional) List of files or URLs to attach to the question. Include when: 1) Question relates to specific files or configurations, 2) User needs to review content before answering, 3) Options or choices are documented in files, 4) Supporting evidence or context is needed. Always use relative paths to /workspace directory."
}
},
"required": ["text"]
}
}
})
@xml_schema(
tag_name="ask",
mappings=[
{"param_name": "text", "node_type": "content", "path": "."},
{"param_name": "attachments", "node_type": "attribute", "path": ".", "required": False}
],
example='''
<!-- Use ask when you need user input to proceed -->
<!-- Examples of when to use ask: -->
<!-- 1. Clarifying ambiguous requirements -->
<!-- 2. Confirming high-impact changes -->
<!-- 3. Choosing between implementation options -->
<!-- 4. Validating critical assumptions -->
<!-- 5. Getting missing information -->
# Commented out as we are just doing this via prompt as there is no need to call it as a tool
<ask attachments="recipes/chocolate_cake.txt,photos/cake_examples.jpg">
I'm planning to bake the chocolate cake for your birthday party. The recipe mentions "rich frosting" but doesn't specify what type. Could you clarify your preferences? For example:
1. Would you prefer buttercream or cream cheese frosting?
2. Do you want any specific flavor added to the frosting (vanilla, coffee, etc.)?
3. Should I add any decorative toppings like sprinkles or fruit?
4. Do you have any dietary restrictions I should be aware of?
# @openapi_schema({
# "type": "function",
# "function": {
# "name": "ask",
# "description": "Ask user a question and wait for response. Use for: 1) Requesting clarification on ambiguous requirements, 2) Seeking confirmation before proceeding with high-impact changes, 3) Gathering additional information needed to complete a task, 4) Offering options and requesting user preference, 5) Validating assumptions when critical to task success. IMPORTANT: Use this tool only when user input is essential to proceed. Always provide clear context and options when applicable. Include relevant attachments when the question relates to specific files or resources.",
# "parameters": {
# "type": "object",
# "properties": {
# "text": {
# "type": "string",
# "description": "Question text to present to user - should be specific and clearly indicate what information you need. Include: 1) Clear question or request, 2) Context about why the input is needed, 3) Available options if applicable, 4) Impact of different choices, 5) Any relevant constraints or considerations."
# },
# "attachments": {
# "anyOf": [
# {"type": "string"},
# {"items": {"type": "string"}, "type": "array"}
# ],
# "description": "(Optional) List of files or URLs to attach to the question. Include when: 1) Question relates to specific files or configurations, 2) User needs to review content before answering, 3) Options or choices are documented in files, 4) Supporting evidence or context is needed. Always use relative paths to /workspace directory."
# }
# },
# "required": ["text"]
# }
# }
# })
# @xml_schema(
# tag_name="ask",
# mappings=[
# {"param_name": "text", "node_type": "content", "path": "."},
# {"param_name": "attachments", "node_type": "attribute", "path": ".", "required": False}
# ],
# example='''
# Ask user a question and wait for response. Use for: 1) Requesting clarification on ambiguous requirements, 2) Seeking confirmation before proceeding with high-impact changes, 3) Gathering additional information needed to complete a task, 4) Offering options and requesting user preference, 5) Validating assumptions when critical to task success. IMPORTANT: Use this tool only when user input is essential to proceed. Always provide clear context and options when applicable. Include relevant attachments when the question relates to specific files or resources.
This information will help me make sure the cake meets your expectations for the celebration.
</ask>
'''
)
async def ask(self, text: str, attachments: Optional[Union[str, List[str]]] = None) -> ToolResult:
"""Ask the user a question and wait for a response.
# <!-- Use ask when you need user input to proceed -->
# <!-- Examples of when to use ask: -->
# <!-- 1. Clarifying ambiguous requirements -->
# <!-- 2. Confirming high-impact changes -->
# <!-- 3. Choosing between implementation options -->
# <!-- 4. Validating critical assumptions -->
# <!-- 5. Getting missing information -->
Args:
text: The question to present to the user
attachments: Optional file paths or URLs to attach to the question
# <ask attachments="recipes/chocolate_cake.txt,photos/cake_examples.jpg">
# I'm planning to bake the chocolate cake for your birthday party. The recipe mentions "rich frosting" but doesn't specify what type. Could you clarify your preferences? For example:
# 1. Would you prefer buttercream or cream cheese frosting?
# 2. Do you want any specific flavor added to the frosting (vanilla, coffee, etc.)?
# 3. Should I add any decorative toppings like sprinkles or fruit?
# 4. Do you have any dietary restrictions I should be aware of?
Returns:
ToolResult indicating the question was successfully sent
"""
try:
# Convert single attachment to list for consistent handling
if attachments and isinstance(attachments, str):
attachments = [attachments]
# This information will help me make sure the cake meets your expectations for the celebration.
# </ask>
# '''
# )
# async def ask(self, text: str, attachments: Optional[Union[str, List[str]]] = None) -> ToolResult:
# """Ask the user a question and wait for a response.
return self.success_response({"status": "Awaiting user response..."})
except Exception as e:
return self.fail_response(f"Error asking user: {str(e)}")
# Args:
# text: The question to present to the user
# attachments: Optional file paths or URLs to attach to the question
@openapi_schema({
"type": "function",
"function": {
"name": "inform",
"description": "Inform the user about progress, completion of a major step, or important context. Use this tool: 1) To provide updates between major sections of work, 2) After accomplishing significant milestones, 3) When transitioning to a new phase of work, 4) To confirm actions were completed successfully, 5) To provide context about upcoming steps. IMPORTANT: Use sparingly - only for meaningful updates, not routine actions. This does not allow user response and allows continued execution.",
"parameters": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "Information to present to the user. Include: 1) Clear statement of what has been accomplished or what is happening, 2) Relevant context or impact, 3) Brief indication of next steps if applicable."
},
"attachments": {
"anyOf": [
{"type": "string"},
{"items": {"type": "string"}, "type": "array"}
],
"description": "(Optional) List of files or URLs to attach to the information. Include when: 1) Information relates to specific files or resources, 2) Showing intermediate results or outputs, 3) Providing supporting documentation. Always use relative paths to /workspace directory."
}
},
"required": ["text"]
}
}
})
@xml_schema(
tag_name="inform",
mappings=[
{"param_name": "text", "node_type": "content", "path": "."},
{"param_name": "attachments", "node_type": "attribute", "path": ".", "required": False}
],
example='''
<!-- Use inform for meaningful progress updates, you can not get user response with this tool. -->
<!-- Examples of when to use inform: -->
<!-- 1. Completing major milestones -->
<!-- 2. Transitioning between work phases -->
<!-- 3. Confirming important actions -->
<!-- 4. Providing context for upcoming work -->
<!-- 5. Sharing significant intermediate results -->
# Returns:
# ToolResult indicating the question was successfully sent
# """
# try:
# # Convert single attachment to list for consistent handling
# if attachments and isinstance(attachments, str):
# attachments = [attachments]
<inform attachments="analysis_results.csv,summary_chart.png">
I've completed the data analysis of the sales figures. Key findings include:
- Q4 sales were 28% higher than Q3
- Product line A showed the strongest performance
- Three regions missed their targets
# return self.success_response({"status": "Awaiting user response..."})
# except Exception as e:
# return self.fail_response(f"Error asking user: {str(e)}")
I'll now proceed with creating the executive summary report based on these findings.
</inform>
'''
)
async def inform(self, text: str, attachments: Optional[Union[str, List[str]]] = None) -> ToolResult:
"""Inform the user about progress or important updates without requiring a response.
# @openapi_schema({
# "type": "function",
# "function": {
# "name": "inform",
# "description": "Inform the user about progress, completion of a major step, or important context. Use this tool: 1) To provide updates between major sections of work, 2) After accomplishing significant milestones, 3) When transitioning to a new phase of work, 4) To confirm actions were completed successfully, 5) To provide context about upcoming steps. IMPORTANT: Use FREQUENTLY throughout execution to provide UI context to the user. The user CANNOT respond to this tool - they can only respond to the 'ask' tool. Use this tool to keep the user informed without requiring their input.",
# "parameters": {
# "type": "object",
# "properties": {
# "text": {
# "type": "string",
# "description": "Information to present to the user. Include: 1) Clear statement of what has been accomplished or what is happening, 2) Relevant context or impact, 3) Brief indication of next steps if applicable."
# },
# "attachments": {
# "anyOf": [
# {"type": "string"},
# {"items": {"type": "string"}, "type": "array"}
# ],
# "description": "(Optional) List of files or URLs to attach to the information. Include when: 1) Information relates to specific files or resources, 2) Showing intermediate results or outputs, 3) Providing supporting documentation. Always use relative paths to /workspace directory."
# }
# },
# "required": ["text"]
# }
# }
# })
# @xml_schema(
# tag_name="inform",
# mappings=[
# {"param_name": "text", "node_type": "content", "path": "."},
# {"param_name": "attachments", "node_type": "attribute", "path": ".", "required": False}
# ],
# example='''
Args:
text: The information to present to the user
attachments: Optional file paths or URLs to attach
# Inform the user about progress, completion of a major step, or important context. Use this tool: 1) To provide updates between major sections of work, 2) After accomplishing significant milestones, 3) When transitioning to a new phase of work, 4) To confirm actions were completed successfully, 5) To provide context about upcoming steps. IMPORTANT: Use FREQUENTLY throughout execution to provide UI context to the user. The user CANNOT respond to this tool - they can only respond to the 'ask' tool. Use this tool to keep the user informed without requiring their input."
Returns:
ToolResult indicating the information was successfully sent
"""
try:
# Convert single attachment to list for consistent handling
if attachments and isinstance(attachments, str):
attachments = [attachments]
# <!-- Use inform FREQUENTLY to provide UI context and progress updates - THE USER CANNOT RESPOND to this tool -->
# <!-- The user can ONLY respond to the ask tool, not to inform -->
# <!-- Examples of when to use inform: -->
# <!-- 1. Completing major milestones -->
# <!-- 2. Transitioning between work phases -->
# <!-- 3. Confirming important actions -->
# <!-- 4. Providing context about upcoming steps -->
# <!-- 5. Sharing significant intermediate results -->
# <!-- 6. Providing regular UI updates throughout execution -->
return self.success_response({"status": "Information sent"})
except Exception as e:
return self.fail_response(f"Error informing user: {str(e)}")
# <inform attachments="analysis_results.csv,summary_chart.png">
# I've completed the data analysis of the sales figures. Key findings include:
# - Q4 sales were 28% higher than Q3
# - Product line A showed the strongest performance
# - Three regions missed their targets
# I'll now proceed with creating the executive summary report based on these findings.
# </inform>
# '''
# )
# async def inform(self, text: str, attachments: Optional[Union[str, List[str]]] = None) -> ToolResult:
# """Inform the user about progress or important updates without requiring a response.
# Args:
# text: The information to present to the user
# attachments: Optional file paths or URLs to attach
# Returns:
# ToolResult indicating the information was successfully sent
# """
# try:
# # Convert single attachment to list for consistent handling
# if attachments and isinstance(attachments, str):
# attachments = [attachments]
# return self.success_response({"status": "Information sent"})
# except Exception as e:
# return self.fail_response(f"Error informing user: {str(e)}")
@openapi_schema({
"type": "function",

View File

@ -200,6 +200,7 @@ class ThreadManager:
# Create or append to content
if isinstance(system_prompt['content'], str):
examples_content = """
--- XML TOOL CALLING ---
In this environment you have access to a set of tools you can use to answer the user's question. The tools are specified in XML format.
{{ FORMATTING INSTRUCTIONS }}