mirror of
https://github.com/pocketpaw/pocketpaw.git
synced 2026-05-22 17:55:03 +00:00
A tool that returned a large blob used to drop the raw blob straight into the agent's context window with nothing capping it. A long pytest run, a build log, a big HTTP response body, verbose command stdout -- the whole thing went in. That wasted tokens and buried the lines the agent needed. Add output_budget.cap_tool_output. Output within the cap is returned unchanged. An oversized blob gets a deterministic head+tail slice with an elision marker. A recognized structured format (pytest run, ruff or flake8 lint output) gets a salient-lines extract instead, keeping the failures and the summary line and dropping the PASSED noise. Wire it at two boundaries: BaseTool._success/_error, and ToolRegistry.execute plus the tool_bridge wrappers. Two boundaries because shell and run_python return strings directly and never touch _success -- the registry is the universal chokepoint that still catches them. The transform is deterministic and idempotent, so a result already capped by _success passes through the registry unchanged. The cap defaults to 12000 chars and is configurable through the new tool_output_char_cap setting. Closes #1160
113 lines
3.6 KiB
Plaintext
113 lines
3.6 KiB
Plaintext
---
|
|
title: "Custom Tools: Build Your Own PocketPaw Extensions"
|
|
description: "Build custom tools for PocketPaw using the ToolProtocol interface. Define tool schemas compatible with both Anthropic and OpenAI APIs, register them in the ToolRegistry, and use them immediately."
|
|
section: Tools
|
|
ogType: article
|
|
keywords:
|
|
["custom tools", "tool protocol", "tool development", "plugin", "extensible"]
|
|
tags: ["tools", "development"]
|
|
---
|
|
|
|
# Custom Tools: Build Your Own PocketPaw Extensions
|
|
|
|
PocketPaw's tool system is extensible. You can create custom tools by implementing the `ToolProtocol`.
|
|
|
|
## ToolProtocol Interface
|
|
|
|
```python
|
|
from pocketpaw.tools.registry import ToolProtocol, ToolDefinition
|
|
|
|
class MyCustomTool:
|
|
@property
|
|
def name(self) -> str:
|
|
return "my_tool"
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
return "Does something useful"
|
|
|
|
@property
|
|
def definition(self) -> ToolDefinition:
|
|
return ToolDefinition(
|
|
name=self.name,
|
|
description=self.description,
|
|
input_schema={
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {
|
|
"type": "string",
|
|
"description": "The input query"
|
|
}
|
|
},
|
|
"required": ["query"]
|
|
}
|
|
)
|
|
|
|
async def execute(self, **kwargs) -> str:
|
|
query = kwargs.get("query", "")
|
|
# Your tool logic here
|
|
return f"Result for: {query}"
|
|
```
|
|
|
|
## Registering Custom Tools
|
|
|
|
Register your tool with the `ToolRegistry`:
|
|
|
|
```python
|
|
from pocketpaw.tools.registry import ToolRegistry
|
|
|
|
registry = ToolRegistry()
|
|
registry.register(MyCustomTool())
|
|
```
|
|
|
|
## Schema Export
|
|
|
|
`ToolDefinition` supports exporting to both Anthropic and OpenAI formats:
|
|
|
|
```python
|
|
tool = MyCustomTool()
|
|
|
|
# Anthropic format
|
|
anthropic_schema = tool.definition.to_anthropic()
|
|
|
|
# OpenAI format
|
|
openai_schema = tool.definition.to_openai()
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Keep tools focused** - Each tool should do one thing well
|
|
2. **Validate inputs** - Check required parameters before execution
|
|
3. **Return strings** - Tool results are always strings
|
|
4. **Handle errors** - Return error messages instead of raising exceptions
|
|
5. **Be async** - The `execute` method must be async
|
|
6. **Document inputs** - Provide clear descriptions in the schema so the LLM knows how to use the tool
|
|
|
|
## Output Size
|
|
|
|
You do not need to truncate large output yourself. Every tool result passes
|
|
through an [output budget](/concepts/tool-system#output-budget) before it
|
|
reaches the agent: oversized blobs are shortened to a head + tail slice, or a
|
|
salient-lines extract for recognised test and lint formats. This runs at
|
|
`BaseTool._success` / `_error` and again at `ToolRegistry.execute`, so it
|
|
applies whether your tool returns through `_success` or returns a string
|
|
directly. The cap defaults to 12,000 characters (`tool_output_char_cap`).
|
|
|
|
Return the full, accurate result from `execute` and let the budget handle
|
|
size. Avoid ad hoc per-tool slicing — it loses the salient-lines treatment
|
|
and is harder to tune than one central cap.
|
|
|
|
## Related
|
|
|
|
<CardGroup>
|
|
<Card title="Tool Policy" icon="lucide:shield-check" href="/tools/tool-policy">
|
|
Control which tools are available with profiles and allow/deny lists.
|
|
</Card>
|
|
<Card title="Skill Generator" icon="lucide:wand-2" href="/tools/skill-generator">
|
|
Create persistent skill definitions that extend agent capabilities.
|
|
</Card>
|
|
<Card title="Tools Overview" icon="lucide:wrench" href="/tools">
|
|
Browse all 50+ built-in tools available in PocketPaw.
|
|
</Card>
|
|
</CardGroup>
|