mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-12 22:12:37 +00:00
Refactor PlannerObserver and StepExecutor to Utilize I18N for Prompts
This update enhances the PlannerObserver and StepExecutor classes by integrating the I18N utility for managing prompts and messages. The system and user prompts are now retrieved from the I18N module, allowing for better localization and maintainability. Additionally, the code has been cleaned up to remove hardcoded strings, improving readability and consistency across the planning and execution processes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""PlannerObserver: Observation phase after each step execution.
|
||||
|
||||
Implements the "Observe" phase from PLAN-AND-ACT (Section 3.3). After every
|
||||
Implements the "Observe" phase. After every
|
||||
step execution, the Planner analyzes what happened, what new information was
|
||||
learned, and whether the remaining plan is still valid.
|
||||
|
||||
@@ -19,6 +19,7 @@ from crewai.events.types.observation_events import (
|
||||
StepObservationFailedEvent,
|
||||
StepObservationStartedEvent,
|
||||
)
|
||||
from crewai.utilities.i18n import I18N, get_i18n
|
||||
from crewai.utilities.llm_utils import create_llm
|
||||
from crewai.utilities.planning_types import StepObservation, TodoItem
|
||||
from crewai.utilities.types import LLMMessage
|
||||
@@ -53,6 +54,7 @@ class PlannerObserver:
|
||||
self.agent = agent
|
||||
self.task = task
|
||||
self.llm = self._resolve_llm()
|
||||
self._i18n: I18N = get_i18n()
|
||||
|
||||
def _resolve_llm(self) -> Any:
|
||||
"""Resolve which LLM to use for observation/planning.
|
||||
@@ -238,18 +240,7 @@ class PlannerObserver:
|
||||
task_desc = self.task.description or ""
|
||||
task_goal = self.task.expected_output or ""
|
||||
|
||||
system_prompt = (
|
||||
"You are a Planning Agent observing execution progress. "
|
||||
"After each step completes, you analyze what happened and decide "
|
||||
"whether the remaining plan is still valid.\n\n"
|
||||
"Reason step-by-step about:\n"
|
||||
"1. What new information was learned from this step's result\n"
|
||||
"2. Whether the remaining steps still make sense given this new information\n"
|
||||
"3. What refinements, if any, are needed for upcoming steps\n"
|
||||
"4. Whether the overall goal has already been achieved\n\n"
|
||||
"Be conservative about triggering full replans — only do so when the "
|
||||
"remaining plan is fundamentally wrong, not just suboptimal."
|
||||
)
|
||||
system_prompt = self._i18n.retrieve("planning", "observation_system_prompt")
|
||||
|
||||
# Build context of what's been done
|
||||
completed_summary = ""
|
||||
@@ -276,15 +267,14 @@ class PlannerObserver:
|
||||
remaining_lines
|
||||
)
|
||||
|
||||
user_prompt = (
|
||||
f"## Original task\n{task_desc}\n\n"
|
||||
f"## Expected output\n{task_goal}\n"
|
||||
f"{completed_summary}\n"
|
||||
f"\n## Just completed step {completed_step.step_number}\n"
|
||||
f"Description: {completed_step.description}\n"
|
||||
f"Result: {result}\n"
|
||||
f"{remaining_summary}\n\n"
|
||||
"Analyze this step's result and provide your observation."
|
||||
user_prompt = self._i18n.retrieve("planning", "observation_user_prompt").format(
|
||||
task_description=task_desc,
|
||||
task_goal=task_goal,
|
||||
completed_summary=completed_summary,
|
||||
step_number=completed_step.step_number,
|
||||
step_description=completed_step.description,
|
||||
step_result=result,
|
||||
remaining_summary=remaining_summary,
|
||||
)
|
||||
|
||||
return [
|
||||
@@ -298,24 +288,17 @@ class PlannerObserver:
|
||||
remaining_todos: list[TodoItem],
|
||||
) -> list[LLMMessage]:
|
||||
"""Build messages for the refinement LLM call."""
|
||||
system_prompt = (
|
||||
"You are refining upcoming plan steps based on new information. "
|
||||
"Update the step descriptions to be more specific and actionable "
|
||||
"given what was learned. Keep the same step numbers.\n\n"
|
||||
"Respond with one line per step in the format:\n"
|
||||
"Step N: <refined description>"
|
||||
)
|
||||
system_prompt = self._i18n.retrieve("planning", "refinement_system_prompt")
|
||||
|
||||
refinements = "\n".join(observation.suggested_refinements or [])
|
||||
todo_lines = "\n".join(
|
||||
f"Step {t.step_number}: {t.description}" for t in remaining_todos
|
||||
)
|
||||
|
||||
user_prompt = (
|
||||
f"## New information learned\n{observation.key_information_learned}\n\n"
|
||||
f"## Suggested refinements\n{refinements}\n\n"
|
||||
f"## Current pending steps\n{todo_lines}\n\n"
|
||||
"Update the step descriptions to incorporate the new information."
|
||||
user_prompt = self._i18n.retrieve("planning", "refinement_user_prompt").format(
|
||||
key_information_learned=observation.key_information_learned,
|
||||
refinements=refinements,
|
||||
todo_lines=todo_lines,
|
||||
)
|
||||
|
||||
return [
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
|
||||
Implements a bounded ReAct loop scoped to ONE todo item. The tool execution
|
||||
machinery (native function calling, text-parsed tools, caching, hooks) lives
|
||||
here — moved from AgentExecutor so the outer Plan-and-Execute loop stays clean.
|
||||
|
||||
Based on PLAN-AND-ACT (Section 3.2): The Executor translates high-level plan
|
||||
steps into concrete environment actions.
|
||||
here — moved from AgentExecutor so the outer loop stays clean.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -199,36 +196,49 @@ class StepExecutor:
|
||||
tools_section = ""
|
||||
if self.tools and not self._use_native_tools:
|
||||
tool_names = ", ".join(sanitize_tool_name(t.name) for t in self.tools)
|
||||
tools_section = f"\n\nAvailable tools: {tool_names}"
|
||||
tools_section += "\n\nTo use a tool, respond with:\nThought: <your reasoning>\nAction: <tool_name>\nAction Input: <input>"
|
||||
tools_section += "\n\nWhen you have the final answer, respond with:\nThought: <your reasoning>\nFinal Answer: <your answer>"
|
||||
tools_section = self._i18n.retrieve(
|
||||
"planning", "step_executor_tools_section"
|
||||
).format(tool_names=tool_names)
|
||||
|
||||
return f"""You are {role}. {backstory}
|
||||
|
||||
Your goal: {goal}
|
||||
|
||||
You are executing a specific step in a multi-step plan. Focus ONLY on completing
|
||||
the current step. Do not plan ahead or worry about future steps.
|
||||
|
||||
Before acting, briefly reason about what you need to do and which approach
|
||||
or tool would be most helpful for this specific step.{tools_section}"""
|
||||
return self._i18n.retrieve("planning", "step_executor_system_prompt").format(
|
||||
role=role,
|
||||
backstory=backstory,
|
||||
goal=goal,
|
||||
tools_section=tools_section,
|
||||
)
|
||||
|
||||
def _build_user_prompt(self, todo: TodoItem, context: StepExecutionContext) -> str:
|
||||
"""Build the user prompt for this specific step."""
|
||||
parts: list[str] = []
|
||||
|
||||
parts.append(f"## Current Step\n{todo.description}")
|
||||
parts.append(
|
||||
self._i18n.retrieve("planning", "step_executor_user_prompt").format(
|
||||
step_description=todo.description,
|
||||
)
|
||||
)
|
||||
|
||||
if todo.tool_to_use:
|
||||
parts.append(f"\nSuggested tool: {todo.tool_to_use}")
|
||||
parts.append(
|
||||
self._i18n.retrieve("planning", "step_executor_suggested_tool").format(
|
||||
tool_to_use=todo.tool_to_use,
|
||||
)
|
||||
)
|
||||
|
||||
# Include dependency results (final results only, no traces)
|
||||
if context.dependency_results:
|
||||
parts.append("\n## Context from previous steps:")
|
||||
parts.append(
|
||||
self._i18n.retrieve("planning", "step_executor_context_header")
|
||||
)
|
||||
for step_num, result in sorted(context.dependency_results.items()):
|
||||
parts.append(f"Step {step_num} result: {result}")
|
||||
parts.append(
|
||||
self._i18n.retrieve(
|
||||
"planning", "step_executor_context_entry"
|
||||
).format(step_number=step_num, result=result)
|
||||
)
|
||||
|
||||
parts.append("\nComplete this step and provide your result.")
|
||||
parts.append(
|
||||
self._i18n.retrieve("planning", "step_executor_complete_step")
|
||||
)
|
||||
|
||||
return "\n".join(parts)
|
||||
|
||||
@@ -625,12 +635,13 @@ or tool would be most helpful for this specific step.{tools_section}"""
|
||||
|
||||
def _force_final_answer(self, messages: list[LLMMessage]) -> str:
|
||||
"""Force the LLM to provide a final answer when max iterations reached."""
|
||||
force_prompt = (
|
||||
"You have used the maximum number of tool calls for this step. "
|
||||
"Based on the information gathered so far, provide your final answer now."
|
||||
force_prompt = self._i18n.retrieve(
|
||||
"planning", "step_executor_force_final_answer"
|
||||
)
|
||||
if not self._use_native_tools:
|
||||
force_prompt += "\n\nFinal Answer: "
|
||||
force_prompt += self._i18n.retrieve(
|
||||
"planning", "step_executor_force_final_answer_suffix"
|
||||
)
|
||||
|
||||
messages.append(format_message_for_llm(force_prompt, role="user"))
|
||||
|
||||
@@ -650,7 +661,7 @@ or tool would be most helpful for this specific step.{tools_section}"""
|
||||
except Exception: # noqa: S110
|
||||
pass
|
||||
|
||||
return "Step could not be completed within the iteration limit."
|
||||
return self._i18n.retrieve("planning", "step_could_not_complete")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal: Native tool support
|
||||
|
||||
@@ -66,6 +66,20 @@
|
||||
"planning": {
|
||||
"system_prompt": "You are a strategic planning assistant. Create minimal, effective execution plans. Prefer fewer steps over more.",
|
||||
"create_plan_prompt": "Create a focused execution plan for the following task:\n\n## Task\n{description}\n\n## Expected Output\n{expected_output}\n\n## Available Tools\n{tools}\n\n## Planning Principles\nFocus on WHAT needs to be accomplished, not HOW. Group related actions into logical units. Fewer steps = better. Most tasks need 3-6 steps. Hard limit: {max_steps} steps.\n\n## Step Types (only these are valid):\n1. **Tool Step**: Uses a tool to gather information or take action\n2. **Output Step**: Synthesizes prior results into the final deliverable (usually the last step)\n\n## Rules:\n- Each step must either USE A TOOL or PRODUCE THE FINAL OUTPUT\n- Combine related tool calls: \"Research A, B, and C\" = ONE step, not three\n- Combine all synthesis into ONE final output step\n- NO standalone \"thinking\" steps (review, verify, confirm, refine, analyze) - these happen naturally between steps\n\nFor each step: State the action, specify the tool (if any), and note dependencies.\n\nAfter your plan, state READY or NOT READY.",
|
||||
"refine_plan_prompt": "Your previous plan:\n{current_plan}\n\nYou indicated you weren't ready. Refine your plan to address the specific gap.\n\nKeep the plan minimal - only add steps that directly address the issue.\n\nConclude with READY or NOT READY as before."
|
||||
"refine_plan_prompt": "Your previous plan:\n{current_plan}\n\nYou indicated you weren't ready. Refine your plan to address the specific gap.\n\nKeep the plan minimal - only add steps that directly address the issue.\n\nConclude with READY or NOT READY as before.",
|
||||
"observation_system_prompt": "You are a Planning Agent observing execution progress. After each step completes, you analyze what happened and decide whether the remaining plan is still valid.\n\nReason step-by-step about:\n1. What new information was learned from this step's result\n2. Whether the remaining steps still make sense given this new information\n3. What refinements, if any, are needed for upcoming steps\n4. Whether the overall goal has already been achieved\n\nBe conservative about triggering full replans — only do so when the remaining plan is fundamentally wrong, not just suboptimal.",
|
||||
"observation_user_prompt": "## Original task\n{task_description}\n\n## Expected output\n{task_goal}\n{completed_summary}\n\n## Just completed step {step_number}\nDescription: {step_description}\nResult: {step_result}\n{remaining_summary}\n\nAnalyze this step's result and provide your observation.",
|
||||
"refinement_system_prompt": "You are refining upcoming plan steps based on new information. Update the step descriptions to be more specific and actionable given what was learned. Keep the same step numbers.\n\nRespond with one line per step in the format:\nStep N: <refined description>",
|
||||
"refinement_user_prompt": "## New information learned\n{key_information_learned}\n\n## Suggested refinements\n{refinements}\n\n## Current pending steps\n{todo_lines}\n\nUpdate the step descriptions to incorporate the new information.",
|
||||
"step_executor_system_prompt": "You are {role}. {backstory}\n\nYour goal: {goal}\n\nYou are executing a specific step in a multi-step plan. Focus ONLY on completing the current step. Do not plan ahead or worry about future steps.\n\nBefore acting, briefly reason about what you need to do and which approach or tool would be most helpful for this specific step.{tools_section}",
|
||||
"step_executor_tools_section": "\n\nAvailable tools: {tool_names}\n\nTo use a tool, respond with:\nThought: <your reasoning>\nAction: <tool_name>\nAction Input: <input>\n\nWhen you have the final answer, respond with:\nThought: <your reasoning>\nFinal Answer: <your answer>",
|
||||
"step_executor_user_prompt": "## Current Step\n{step_description}",
|
||||
"step_executor_suggested_tool": "\nSuggested tool: {tool_to_use}",
|
||||
"step_executor_context_header": "\n## Context from previous steps:",
|
||||
"step_executor_context_entry": "Step {step_number} result: {result}",
|
||||
"step_executor_complete_step": "\nComplete this step and provide your result.",
|
||||
"step_executor_force_final_answer": "You have used the maximum number of tool calls for this step. Based on the information gathered so far, provide your final answer now.",
|
||||
"step_executor_force_final_answer_suffix": "\n\nFinal Answer: ",
|
||||
"step_could_not_complete": "Step could not be completed within the iteration limit."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,12 @@ class I18N(BaseModel):
|
||||
def retrieve(
|
||||
self,
|
||||
kind: Literal[
|
||||
"slices", "errors", "tools", "reasoning", "hierarchical_manager_agent"
|
||||
"slices",
|
||||
"errors",
|
||||
"tools",
|
||||
"reasoning",
|
||||
"planning",
|
||||
"hierarchical_manager_agent",
|
||||
],
|
||||
key: str,
|
||||
) -> str:
|
||||
|
||||
Reference in New Issue
Block a user