mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-27 04:20:09 +00:00
- Fix import sorting (I001) in all packages - Fix bare except (E722) in lsp_tool.py - Fix duplicate set item (B033) in refactor_tool.py
179 lines
6.6 KiB
Python
179 lines
6.6 KiB
Python
"""Prompt generation utilities for agent tool.
|
|
|
|
This module provides functions for generating effective prompts for sub-agents,
|
|
including filtering tools based on permissions and formatting system instructions.
|
|
"""
|
|
|
|
import os
|
|
from typing import Any
|
|
|
|
from hanzo_tools.core import BaseTool, PermissionManager
|
|
|
|
|
|
def get_allowed_agent_tools(
|
|
tools: list[BaseTool],
|
|
permission_manager: PermissionManager,
|
|
) -> list[BaseTool]:
|
|
"""Filter tools available to the agent based on permissions.
|
|
|
|
Args:
|
|
tools: List of available tools
|
|
permission_manager: Permission manager for checking tool access
|
|
|
|
Returns:
|
|
Filtered list of tools available to the agent
|
|
"""
|
|
# Get all tools except for the agent tool itself (avoid recursion)
|
|
filtered_tools = [tool for tool in tools if tool.name != "agent"]
|
|
|
|
return filtered_tools
|
|
|
|
|
|
def get_system_prompt(
|
|
tools: list[BaseTool],
|
|
permission_manager: PermissionManager,
|
|
) -> str:
|
|
"""Generate system prompt for the sub-agent.
|
|
|
|
Args:
|
|
tools: List of available tools
|
|
permission_manager: Permission manager for checking tool access
|
|
|
|
Returns:
|
|
System prompt for the sub-agent
|
|
"""
|
|
# Get filtered tools
|
|
filtered_tools = get_allowed_agent_tools(tools, permission_manager)
|
|
|
|
# Extract tool names for display
|
|
tool_names = ", ".join(f"`{tool.name}`" for tool in filtered_tools)
|
|
|
|
# Base system prompt - agents always have edit tools
|
|
system_prompt = f"""You are a Claude sub-agent with access to these tools: {tool_names}.
|
|
|
|
CAPABILITIES:
|
|
1. You have FULL read and write access - you can create, edit, and modify files
|
|
2. You can ask clarifying questions if needed - your response goes to the coordinating agent
|
|
3. You work as part of a team of specialized agents
|
|
4. Other agents may be available via MCP tools (look for tools named after agents)
|
|
5. When relevant, share file names and code snippets
|
|
6. Any file paths you return MUST be absolute. DO NOT use relative paths.
|
|
7. You can only work with the absolute paths provided in your task prompt.
|
|
|
|
CLARIFICATION:
|
|
- You can request clarification ONCE per task using the request_clarification tool
|
|
- Use this when instructions are ambiguous or you need additional context
|
|
- Types: AMBIGUOUS_INSTRUCTION, MISSING_CONTEXT, MULTIPLE_OPTIONS, CONFIRMATION_NEEDED, ADDITIONAL_INFO
|
|
- The main loop will provide automated guidance based on context
|
|
|
|
CRITICAL REVIEW (Devil's Advocate):
|
|
- Use the critic tool to get harsh, challenging feedback that attacks assumptions
|
|
- The critic will find flaws and push for improvements aggressively
|
|
- You can request up to 2 critic reviews per task
|
|
- Review types: CODE_QUALITY, CORRECTNESS, PERFORMANCE, SECURITY, COMPLETENESS, BEST_PRACTICES, GENERAL
|
|
- Use when you need someone to find what's wrong with your approach
|
|
|
|
BALANCED REVIEW:
|
|
- Use the review tool for constructive, balanced code review
|
|
- Provides objective assessment without predetermined bias
|
|
- You can request up to 3 reviews per task
|
|
- Focus areas: GENERAL, FUNCTIONALITY, READABILITY, MAINTAINABILITY, TESTING, DOCUMENTATION, ARCHITECTURE
|
|
- Use for regular code review and feedback
|
|
|
|
CREATIVE GUIDANCE:
|
|
- Use the iching tool when you need creative problem-solving approaches
|
|
- Combines ancient I Ching wisdom with Hanzo engineering principles
|
|
- Provides unique perspectives and actionable guidance
|
|
- Use when stuck, need fresh ideas, or want philosophical alignment
|
|
|
|
EDITING GUIDELINES:
|
|
- ALWAYS read the file first before attempting any edits
|
|
- For edit tool: The old_string must match EXACTLY including all whitespace, tabs, and newlines
|
|
- When copying text from read output, be careful with line numbers and indentation
|
|
- If an edit fails due to whitespace mismatch, try reading the specific lines again
|
|
- Prefer multi_edit when making multiple changes to the same file
|
|
- Test your edits by verifying the exact string exists in the file first
|
|
|
|
COLLABORATION:
|
|
- If you see MCP tools named after other agents, you can communicate with them
|
|
- Use agent MCP tools to delegate specialized tasks or get expert opinions
|
|
- Share context when communicating with other agents
|
|
|
|
RESPONSE FORMAT:
|
|
- Begin with a summary of what you did or found
|
|
- If you have questions, ask them clearly
|
|
- Include details of any edits performed
|
|
- Report any errors encountered
|
|
- End with clear conclusions or next steps
|
|
"""
|
|
|
|
return system_prompt
|
|
|
|
|
|
def get_default_model(model_override: str | None = None) -> str:
|
|
"""Get the default model for agent execution.
|
|
|
|
Args:
|
|
model_override: Optional model override string in LiteLLM format (e.g., "openai/gpt-4o")
|
|
|
|
Returns:
|
|
Model identifier string with provider prefix
|
|
"""
|
|
# Use model override if provided
|
|
if model_override:
|
|
# If in testing mode and using a test model, return as-is
|
|
if model_override.startswith("test-model") or "TEST_MODE" in os.environ:
|
|
return model_override
|
|
|
|
# If the model already has a provider prefix, return as-is
|
|
if "/" in model_override:
|
|
return model_override
|
|
|
|
# Otherwise, add the default provider prefix
|
|
provider = os.environ.get("AGENT_PROVIDER", "openai")
|
|
return f"{provider}/{model_override}"
|
|
|
|
# Fall back to environment variables
|
|
# Default to Sonnet for cost efficiency
|
|
model = os.environ.get("AGENT_MODEL", "claude-3-5-sonnet-20241022")
|
|
|
|
# Special cases for tests
|
|
if model.startswith("test-model") or "TEST_MODE" in os.environ and model == "claude-3-5-sonnet-20241022":
|
|
return model
|
|
|
|
provider = os.environ.get("AGENT_PROVIDER", "anthropic")
|
|
|
|
# Only add provider prefix if it's not already in the model name
|
|
if "/" not in model and provider != "anthropic":
|
|
return f"{provider}/{model}"
|
|
elif "/" not in model and provider == "anthropic":
|
|
return f"anthropic/{model}"
|
|
elif "/" not in model:
|
|
return f"openai/{model}"
|
|
else:
|
|
# Model already has a provider prefix
|
|
return model
|
|
|
|
|
|
def get_model_parameters(max_tokens: int | None = None) -> dict[str, Any]:
|
|
"""Get model parameters from environment variables.
|
|
|
|
Args:
|
|
max_tokens: Optional maximum tokens parameter override
|
|
|
|
Returns:
|
|
Dictionary of model parameters
|
|
"""
|
|
params = {
|
|
"temperature": float(os.environ.get("AGENT_TEMPERATURE", "0.7")),
|
|
"timeout": int(os.environ.get("AGENT_API_TIMEOUT", "60")),
|
|
}
|
|
|
|
# Add max_tokens if provided or if set in environment variable
|
|
if max_tokens is not None:
|
|
params["max_tokens"] = max_tokens
|
|
elif os.environ.get("AGENT_MAX_TOKENS"):
|
|
params["max_tokens"] = int(os.environ.get("AGENT_MAX_TOKENS", "1000"))
|
|
|
|
return params
|