# ๐Ÿ Python Tool Standards (The "Robust-Tool" Protocol) **Audience:** AI Agents & Developers. **Objective:** Create tools that are **Self-Documenting, Crash-Resistant, and Agent-Friendly**. > [!IMPORTANT] > **The Golden Rule of Tooling:** > "An agent cannot see your code. It can only see your Docstring and your Type Hints. If those are bad, the agent is blind." ## 1. ๐Ÿ“ Anatomy of a Perfect Tool Every Python tool used by our agents must follow this structure (compatible with CrewAI/LangChain): ### A. The `@tool` Decorator Pattern For simple functions, use the decorator. ```python from crewai_tools import tool @tool("Name of the Tool") def my_tool_function(arg1: str, arg2: int = 10) -> str: """ A CLEAR, descriptive summary of what this tool does. Args: arg1: Explain exactly what this string should be (e.g., "The absolute path to the file"). arg2: Explain the integer (e.g., "Number of retries, default 10"). Returns: A string describing the outcome, NOT just raw data. """ # Logic... return "Successfully processed..." ``` ### B. The Class Pattern (Recommended for Complex Logic) For stateful or complex tools, inherit from `BaseTool`. ```python from crewai_tools import BaseTool from pydantic import BaseModel, Field class MyToolInput(BaseModel): """Input schema for MyTool.""" file_path: str = Field(..., description="Absolute path to the target file.") mode: str = Field(..., description="Operation mode: 'read' or 'write'.") class MyTool(BaseTool): name: str = "My Complex Tool" description: str = ( "Detailed description of when to use this tool and what it accomplishes. " "Mention side effects like file modification." ) args_schema: type[BaseModel] = MyToolInput def _run(self, file_path: str, mode: str) -> str: # Implementation... return "Result..." ``` ## 2. ๐Ÿ›ก๏ธ The "Crash-Proof" Mandate (Error Handling) Agents get confused by stack traces. Tools **MUST catch exceptions** and return a meaningful error message string. **โŒ BAD (Agent sees stack trace and panics):** ```python def read_file(path): with open(path) as f: # FileNotFoundError crashes the agent loop return f.read() ``` **โœ… GOOD (Agent sees context and fixes itself):** ```python def read_file(path: str) -> str: try: with open(path, 'r', encoding='utf-8') as f: return f.read() except FileNotFoundError: return f"Error: The file '{path}' was not found. Please check existing files." except PermissionError: return f"Error: Permission denied for '{path}'." except Exception as e: return f"Error: Unexpected failure reading '{path}': {str(e)}" ``` ## 3. ๐Ÿง  Type Hinting & Pydantic (No `Any`) - **Strict Typing:** ALL arguments and return values MUST have type hints (`str`, `int`, `List[str]`). - **No `Any`:** Avoid `Any` or `dict` without structure. Agents need to know *exactly* what to provide. - **Docstrings:** Google Style or Sphinx Style. The Agent uses this text to decide *how* to call the tool. ## 4. ๐Ÿ“ฆ Return Values: "Narrative Data" Agents understand natural language better than raw JSON. - **Prefer:** "Successfully deleted 3 files: a.txt, b.txt, c.txt." - **Avoid:** `{"status": "ok", "count": 3}` (Unless the agent is explicitly programmed to parse JSON). - **Large Outputs:** Truncate large outputs (e.g., "File content (first 500 chars): ..."). Do not flood the context window. ## 5. ๐Ÿงช Testing Expectation Every new tool usually comes with: 1. **`if __name__ == "__main__":` block:** Allowing immediate manual verification of the script. 2. **No user input (`input()`):** Tools must run autonomously. No interactive prompts.