Skip to content

fix(agents): substitute prompt placeholders in a single pass#6444

Open
ritsth wants to merge 1 commit into
crewAIInc:mainfrom
ritsth:fix/format-prompt-placeholder-clobber
Open

fix(agents): substitute prompt placeholders in a single pass#6444
ritsth wants to merge 1 commit into
crewAIInc:mainfrom
ritsth:fix/format-prompt-placeholder-clobber

Conversation

@ritsth

@ritsth ritsth commented Jul 3, 2026

Copy link
Copy Markdown

Description

AgentExecutor._format_prompt (lib/crewai/src/crewai/experimental/agent_executor.py) substituted the template placeholders sequentially:

prompt = prompt.replace("{input}", inputs["input"])
prompt = prompt.replace("{tool_names}", inputs["tool_names"])
return prompt.replace("{tools}", inputs["tools"])

Because {input} is substituted first, any value inserted earlier that itself contains a later placeholder token gets clobbered by the subsequent replace. So when a task's input legitimately mentions the literal {tools} or {tool_names} (for example an agent working on prompt-engineering or template docs), those tokens in the user's text are overwritten with the tool list/names before reaching the LLM:

inputs = {"input": "explain the {tools} placeholder", "tool_names": "search", "tools": "search: web search"}
_format_prompt("Task: {input}", inputs)
# -> "Task: explain the search: web search placeholder"   (user text corrupted)

This switches to a single re.sub pass that matches only the three known tokens, so substituted values are not re-scanned and user text is preserved. Behavior is unchanged for templates that don't contain this collision, and other {...} content in the template is left untouched exactly as before.

The now-deprecated CrewAgentExecutor has an identical copy of this method; I left it untouched since it is slated for removal, but happy to mirror the fix there if you'd prefer.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Testing

Extended test_agent_executor.py::TestAgentExecutor with test_format_prompt_preserves_placeholder_tokens_in_values. It fails on main (the token is clobbered) and passes with this change; the existing test_format_prompt still passes, and the full tests/agents/test_agent_executor.py file is green (92 passed). ruff check, ruff format --check, and mypy on the changed files are clean.

This bug was found and the patch drafted with AI assistance (Claude Code); I reviewed the change, ran the reproduction above, and validated the tests before and after the fix.

AgentExecutor._format_prompt replaced {input}, {tool_names} and {tools}
sequentially, so a value substituted earlier that itself contained a later
placeholder token was clobbered. Task input mentioning the literal {tools} or
{tool_names} therefore had those tokens overwritten with the tool list/names.

Substitute all placeholders in one re.sub pass so inserted values are not
re-scanned. Adds a regression test.
@ritsth

ritsth commented Jul 3, 2026

Copy link
Copy Markdown
Author

Per CONTRIBUTING, flagging that this PR was drafted with AI assistance (Claude Code) and should carry the llm-generated label. I don't have label permissions as an external contributor, so could a maintainer please apply it? The change itself was human-reviewed and validated with the reproduction and tests described above.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b7c5d4ec-921a-4130-9f43-332f415ae678

📥 Commits

Reviewing files that changed from the base of the PR and between 2b90117 and d1f168a.

📒 Files selected for processing (2)
  • lib/crewai/src/crewai/experimental/agent_executor.py
  • lib/crewai/tests/agents/test_agent_executor.py

📝 Walkthrough

Walkthrough

The AgentExecutor._format_prompt method now uses a single regex-based re.sub pass to substitute {input}, {tool_names}, and {tools} placeholders, replacing sequential str.replace calls that could clobber placeholder-like tokens within substituted values. A regression test was added to verify this behavior.

Changes

Prompt Placeholder Fix

Layer / File(s) Summary
Single-pass regex substitution
lib/crewai/src/crewai/experimental/agent_executor.py
Adds import re and replaces sequential str.replace calls in _format_prompt with a single-pass re.sub using a match-to-inputs[...] mapping, preventing later substitutions from altering placeholder-like tokens introduced by earlier ones.
Regression test
lib/crewai/tests/agents/test_agent_executor.py
Adds test_format_prompt_preserves_placeholder_tokens_in_values, asserting literal {tools} and {tool_names} tokens inside input values remain unchanged after formatting.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: single-pass substitution for prompt placeholders.
Description check ✅ Passed The description accurately explains the bug, the fix, and the added regression test.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant