mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-06 07:29:24 +00:00
Use custom format_output_for_agent override for tool output
When a `BaseTool` subclass overrides `format_output_for_agent`, route the agent-facing text through it instead of the default JSON/`str()` serialization. The structured tool wrapper now delegates to the original tool via `_original_tool`, so a tool can present Markdown or any custom representation to the agent while `tool.run(...)` still returns the raw Python value.
This commit is contained in:
@@ -211,6 +211,23 @@ raw_result = tool.run(query="CrewAI")
|
||||
print(raw_result.score)
|
||||
```
|
||||
|
||||
To send a custom representation to the agent, such as Markdown, override `format_output_for_agent` on your `BaseTool` subclass. This does not change direct execution: `tool.run(...)` still returns the raw Python value.
|
||||
|
||||
```python Code
|
||||
class SearchTool(BaseTool):
|
||||
name: str = "Search"
|
||||
description: str = "Searches for a query and returns the top match score."
|
||||
|
||||
def _run(self, query: str) -> SearchResult:
|
||||
return SearchResult(query=query, score=0.97)
|
||||
|
||||
def format_output_for_agent(self, raw_result: object) -> str:
|
||||
result = SearchResult.model_validate(raw_result)
|
||||
return f"### Search result\n\n- Query: `{result.query}`\n- Score: {result.score}"
|
||||
```
|
||||
|
||||
If you do not override `format_output_for_agent`, CrewAI uses the default typed-output behavior: Pydantic outputs become JSON for the agent, and untyped outputs use `str(raw_result)`.
|
||||
|
||||
If validation or serialization fails during agent execution, CrewAI emits a runtime warning and falls back to `str(raw_result)` for the agent-facing text. Direct tool calls still receive the raw result.
|
||||
|
||||
## Asynchronous Tool Support
|
||||
|
||||
@@ -142,6 +142,27 @@ class GeolocateTool(BaseTool):
|
||||
return {"latitude": 40.7128, "longitude": -74.0060}
|
||||
```
|
||||
|
||||
If agents should receive a custom text format instead of JSON, override `format_output_for_agent` on your `BaseTool` subclass. This is useful when the best agent-facing representation is Markdown, a terse summary, or another format derived from the same raw result.
|
||||
|
||||
```python
|
||||
class GeolocateTool(BaseTool):
|
||||
name: str = "Geolocate"
|
||||
description: str = "Converts a street address into latitude/longitude coordinates."
|
||||
|
||||
def _run(self, address: str) -> GeolocateResult:
|
||||
return GeolocateResult(latitude=40.7128, longitude=-74.0060)
|
||||
|
||||
def format_output_for_agent(self, raw_result: object) -> str:
|
||||
result = GeolocateResult.model_validate(raw_result)
|
||||
return (
|
||||
f"### Coordinates\n\n"
|
||||
f"- Latitude: `{result.latitude}`\n"
|
||||
f"- Longitude: `{result.longitude}`"
|
||||
)
|
||||
```
|
||||
|
||||
The override only controls the text sent to the agent. Direct users of your package still receive the raw value from `tool.run(...)`.
|
||||
|
||||
### Optional: Environment Variables
|
||||
|
||||
If your tool requires API keys or other configuration, declare them with `env_vars` so users know what to set:
|
||||
|
||||
@@ -147,6 +147,50 @@ def product_lookup(sku: str) -> dict[str, object]:
|
||||
}
|
||||
```
|
||||
|
||||
#### Customize the Text Sent to the Agent
|
||||
|
||||
By default, typed tool outputs are sent to the agent as JSON. If your agent should receive Markdown, XML, or a compact human-readable summary instead, subclass `BaseTool` and override `format_output_for_agent`.
|
||||
|
||||
This only changes the agent-facing text. Direct calls to `tool.run(...)` still return the raw Python value from `_run`.
|
||||
|
||||
```python Code
|
||||
from crewai.tools import BaseTool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class ProductLookupResult(BaseModel):
|
||||
sku: str = Field(description="The product SKU.")
|
||||
name: str = Field(description="The product name.")
|
||||
in_stock: bool = Field(description="Whether the product is available.")
|
||||
|
||||
class ProductLookupTool(BaseTool):
|
||||
name: str = "Product Lookup"
|
||||
description: str = "Look up product availability by SKU."
|
||||
|
||||
def _run(self, sku: str) -> ProductLookupResult:
|
||||
return ProductLookupResult(
|
||||
sku=sku,
|
||||
name="CrewAI Enterprise License",
|
||||
in_stock=True,
|
||||
)
|
||||
|
||||
def format_output_for_agent(self, raw_result: object) -> str:
|
||||
result = ProductLookupResult.model_validate(raw_result)
|
||||
status = "in stock" if result.in_stock else "out of stock"
|
||||
return (
|
||||
f"### {result.name}\n\n"
|
||||
f"- SKU: `{result.sku}`\n"
|
||||
f"- Status: **{status}**"
|
||||
)
|
||||
|
||||
tool = ProductLookupTool()
|
||||
result = tool.run(sku="CREW-ENT")
|
||||
|
||||
# Direct Python calls receive the raw Pydantic object.
|
||||
print(result.name)
|
||||
```
|
||||
|
||||
When an agent calls `ProductLookupTool`, it receives the Markdown returned by `format_output_for_agent`. When you do not override this method, CrewAI uses the default behavior: validate typed outputs and serialize them to JSON, or use `str(raw_result)` for untyped outputs.
|
||||
|
||||
Use typed outputs for tool results that have stable fields, nested data, lists, IDs, status values, scores, or any structure the agent should interpret precisely. Plain strings are still fine for simple prose results.
|
||||
|
||||
### Defining a Cache Function for the Tool
|
||||
|
||||
Reference in New Issue
Block a user