fix(tools): stop rewriting the authored tool description at construction#6508
fix(tools): stop rewriting the authored tool description at construction#6508joaomdmoura wants to merge 2 commits into
Conversation
BaseTool.model_post_init silently replaced the public description field
with the LLM-facing composite ("Tool Name: ...\nTool Arguments: ...\n
Tool Description: <authored>"), breaking equality assertions on authored
text and hiding the extra prompt tokens from token-careful authors.
The authored description now survives construction as written. The
composite is composed on demand via a new formatted_description property
on BaseTool and CrewStructuredTool (shared format_description_for_llm
helper), and every prompt path that relied on the baked-in composite —
render_text_description_and_args, ToolUsage._render, and tool-usage
error messages — now renders through it, so the text the LLM sees is
unchanged.
The helper strips any pre-existing composite block before composing, so
tools deserialized from old checkpoints and adapters that still bake the
composite into the field (e.g. the crewai-tools MCP adapter) don't get
double-wrapped. BaseTool._generate_description remains as a no-op hook
because subclasses override it and model_post_init still calls it.
Fixes EPD-179.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughChangesTool Description Formatting
Sequence Diagram(s)sequenceDiagram
participant ToolRenderer
participant BaseTool
participant DescriptionFormatter
ToolRenderer->>BaseTool: request formatted_description
BaseTool->>DescriptionFormatter: pass name, args_schema, description
DescriptionFormatter-->>BaseTool: return composed LLM description
BaseTool-->>ToolRenderer: render formatted description
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4362a45. Configure here.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/crewai/src/crewai/tools/structured_tool.py`:
- Around line 139-140: Update the idempotency logic in the structured tool
description rendering to strip the prefix only when the description starts with
the complete composite header sequence: “Tool Name: … Tool Arguments: … Tool
Description:”. Replace the naive substring check in the affected rendering
method with an anchored shape check that preserves authored prose containing
“Tool Description:” while still removing legacy pre-composed or double-wrapped
headers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c83021b8-bf32-4d77-9fd2-2bdd220fdb82
📒 Files selected for processing (5)
lib/crewai/src/crewai/tools/base_tool.pylib/crewai/src/crewai/tools/structured_tool.pylib/crewai/src/crewai/tools/tool_usage.pylib/crewai/src/crewai/utilities/agent_utils.pylib/crewai/tests/tools/test_base_tool.py
- Anchor the pre-baked-composite check to the actual three-line block shape instead of a naive substring match, so authored prose that merely mentions "Tool Description:" is never truncated (CodeRabbit / Bugbot review finding). Shared as strip_composite_description_prefix() and reused by the function- calling schema builder, which had the same naive split. - Make render_text_description_and_args tolerate duck-typed tools without a real formatted_description string (fixes CI: step-executor tests pass Mock tools whose auto-created attribute is not a str). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Summary
Fixes EPD-179:
BaseTool.model_post_initsilently replaced the authoreddescriptionwith the LLM-facing composite (Tool Name: …\nTool Arguments: {…}\nTool Description: <authored>). Undocumented and surprising: equality assertions on authored text break, and the extra schema tokens are invisible to token-careful authors. The framework itself already treated this as a liability — the native function-calling path strips the prefix back out (agent_utils.py).Fix
The authored
descriptionnow survives construction as written; the composite is composed on demand where prompts are built:format_description_for_llm(name, args_schema, description)instructured_tool.py, exposed as aformatted_descriptionproperty on bothBaseToolandCrewStructuredTool. Always fresh — it tracks later edits toname/args_schema/description.render_text_description_and_args(ReAct system prompt),ToolUsage._render(function-calling selector), and tool-usage error messages. The text the LLM sees is byte-identical to before.BaseTool._generate_description()remains as a documented no-op hook:model_post_initstill calls it because subclasses override it (crewai-tools has ~25self._generate_description()call sites following "mutate description, then refresh" — those calls are now harmless and their authored text is preserved too).Side effects that are improvements: consumers that passed the raw
descriptionto places where the composite was redundant (native function-calling schemas, LangGraph/OpenAI-agents adapters, evaluation metrics listing, the knowledge-tools prompt block that used to renderTool description:\nTool Name: …) now naturally get the clean authored text.Follow-up candidates (intentionally not in this PR): remove the vestigial
description_updatedfield, and drop the crewai-tools MCP adapter's own_generate_descriptionoverride so MCP tools'descriptionfield also stays authored.Testing
NOT REPRODUCED: description preserved as authored, with the identical composite available atformatted_description.TestAuthoredDescriptionPreservedregression class: authored text preserved, composite content/ordering, on-demand recomposition after edits, no double-wrap for pre-baked composites, and prompt rendering unchanged for bothBaseToolandCrewStructuredTool.test_tool_usage_render(asserts the composite in the rendered prompt) passes untouched.tests/tools/(138),tests/agents/test_agent.py+test_lite_agent.py(126), tool-relatedtest_crew.py(11), experimental tool-metrics tests (8), ruff, and mypy.🤖 Generated with Claude Code
Note
Medium Risk
Touches core agent prompt/tool rendering and OpenAI schema description handling; behavior is intended to be prompt-identical but any missed call site still reading raw
descriptionfor LLM prompts could regress.Overview
Tool
descriptionis no longer mutated at construction.BaseTool._generate_description()is a documented no-op; authored text stays ondescription, and the LLM-facing block (Tool Name/Tool Arguments/Tool Description) is built on demand viaformat_description_for_llmand the newformatted_descriptionproperty onBaseToolandCrewStructuredTool.Prompt and schema paths are wired to the split. ReAct rendering (
render_text_description_and_args), function-calling tool selection (ToolUsage._render), and tool error messages useformatted_descriptionso prompt text to the model should match the old composite. Native OpenAI schemas usestrip_composite_description_prefixso checkpoints/adapters that still store a baked composite do not leak intofunction.description.Backward compatibility for legacy composite strings.
strip_composite_description_prefixonly strips descriptions that match the full three-line composite shape (so prose mentioningTool Description:is not truncated).format_description_for_llmis idempotent and avoids double-wrapping pre-baked descriptions.Tests were updated for preserved authored descriptions and a new
TestAuthoredDescriptionPreservedregression suite was added.Reviewed by Cursor Bugbot for commit afce744. Bugbot is set up for automated code reviews on this repo. Configure here.