fix: Make tools that set result_as_answer=True return their raw string output as the final answer #3367
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Add a
ToolAnswerResult
wrapper and plumbing so tools that set result_as_answer=True return their raw string output as the final answer (skipping pydantic/json conversion and schema parsing).Fix for Issue #3335: when a tool returns a plain string but sets result_as_answer=True (meaning the tool’s raw output should be used directly), the framework still attempted to convert/validate it into pydantic/json output. That caused incorrect parsing/validation and lost the intended raw-string answer. This PR prevents that conversion path for those cases.
##Key changes (files / behavior)
New type and behavior control
src/crewai/tools/tool_types.py
Adds ToolAnswerResult wrapper class (stores raw result and stringifies).
Agent / execution flow
src/crewai/agent.py
When iterating tools_results, if a tool result was marked result_as_answer the code now wraps the returned value with ToolAnswerResult before emitting AgentExecutionCompletedEvent.
src/crewai/task.py
Task._execute_core: when receiving agent result, capture raw_result from ToolAnswerResult so TaskOutput.raw holds the raw string.
Task._export_output: if result is a ToolAnswerResult, return (None, None) for pydantic/json outputs (skip conversion).
Tool usage
src/crewai/tools/tool_usage.py
Skip formatting when an available tool has result_as_answer True (so formatter/conversion paths are bypassed).
Tests
Added
tests/tools/test_tool_result_as_answer.py
with multiple unit tests validating:Behavioral impact
TaskOutput.raw will contain the exact string returned by the tool.
src/crewai/tools/tool_types.py
: file ends with "No newline at end of file" in the diff — consider adding trailing newline for style.src/crewai/agents/agent_adapters/langgraph/langgraph_adapter.py
: commented out import (ToolMessage) — confirm intentional.Ensure all callers that previously expected pydantic/json outputs handle the ToolAnswerResult case (Task._export_output now returns (None, None) — callers should expect that).
Run full test suite and CI to ensure no regressions; PR already introduces tests that validate the new behavior but double-check integration paths (delegation, agent events, and any consumers of TaskOutput.json_dict).
Note for reviewers
Suggested reviewers
Anyone responsible for tool execution and task output conversion (agents, tools, task serialization), and maintainers familiar with issue #3335.
Checklist
Confirm the goal: tools that set result_as_answer=True should return their raw string as the final answer, bypassing pydantic/json conversion and schema parsing.
Key files to review (changes & behavior)
src/crewai/tools/tool_types.py
Validate ToolAnswerResult implementation and str behavior.
src/crewai/agent.py
Confirm wrapping of tool result with ToolAnswerResult when result_as_answer True (lines added where AgentExecutionCompletedEvent emitted).
src/crewai/task.py
Verify Task._execute_core uses ToolAnswerResult to populate TaskOutput.raw and that Task._export_output returns (None, None) for ToolAnswerResult.
Ensure no callers assume pydantic/json are always present after this change.
src/crewai/agents/agent_adapters/langgraph/structured_output_converter.py
Confirm the narrower exception handling (catch ValueError) is correct for JSON decode/validate extraction.
Tests:
tests/tools/test_tool_result_as_answer.py
Review test coverage, edge cases, and mocking approach; ensure tests reflect intended behaviour and are not masking other issues.
Functional checks to perform locally
Run unit tests:
Manually exercise a task where:
Edge cases & defensive questions
Test & CI expectations
Suggested follow-ups (if approved)