diff --git a/dynamiq/nodes/agents/agent.py b/dynamiq/nodes/agents/agent.py
index 8c646bbaa..0f61cd045 100644
--- a/dynamiq/nodes/agents/agent.py
+++ b/dynamiq/nodes/agents/agent.py
@@ -1576,6 +1576,14 @@ def _init_prompt_blocks(self):
has_sub_agent_tools=any(isinstance(t, SubAgentTool) for t in self.tools),
)
+ # Append user-provided instructions to operational_instructions block
+ if self.instructions:
+ existing = self.system_prompt_manager._prompt_blocks.get("operational_instructions", "")
+ self.system_prompt_manager.set_block(
+ "operational_instructions",
+ f"{existing}\n\n{self.instructions}" if existing else self.instructions,
+ )
+
# Only auto-wrap the entire role in a raw block if the user did not
# provide explicit raw/endraw markers. This allows roles to mix
# literal sections (via raw) with Jinja variables like {{ input }}
diff --git a/dynamiq/nodes/agents/base.py b/dynamiq/nodes/agents/base.py
index d20bb9b8e..ae20e1c64 100644
--- a/dynamiq/nodes/agents/base.py
+++ b/dynamiq/nodes/agents/base.py
@@ -236,6 +236,10 @@ class Agent(Node):
Can be used to provide additional context or instructions to the agent.
Accepts Jinja templates to provide additional parameters.""",
)
+ instructions: str | None = Field(
+ default=None,
+ description="Additional operational instructions appended to the operational instructions block.",
+ )
description: str | None = Field(default=None, description="Short human-readable description of the agent.")
_mcp_servers: list[MCPServer] = PrivateAttr(default_factory=list)
_excluded_tool_ids: set[str] = PrivateAttr(default_factory=set)
diff --git a/dynamiq/nodes/agents/prompts/manager.py b/dynamiq/nodes/agents/prompts/manager.py
index 4db84eb47..1f4a59279 100644
--- a/dynamiq/nodes/agents/prompts/manager.py
+++ b/dynamiq/nodes/agents/prompts/manager.py
@@ -14,7 +14,7 @@
REACT_BLOCK_INSTRUCTIONS_STRUCTURED_OUTPUT,
REACT_BLOCK_OUTPUT_FORMAT,
REACT_BLOCK_TOOLS,
- REACT_BLOCK_TOOLS_NO_FORMATS,
+ REACT_BLOCK_TOOLS_BRIEF,
REACT_BLOCK_XML_INSTRUCTIONS_NO_TOOLS,
REACT_BLOCK_XML_INSTRUCTIONS_SINGLE,
REACT_MAX_LOOPS_PROMPT,
@@ -321,7 +321,7 @@ def get_model_specific_prompts(
)
if has_tools:
prompt_blocks["tools"] = get_prompt_constant(
- model_name, "REACT_BLOCK_TOOLS_NO_FORMATS", REACT_BLOCK_TOOLS_NO_FORMATS
+ model_name, "REACT_BLOCK_TOOLS_BRIEF", REACT_BLOCK_TOOLS_BRIEF
)
case InferenceMode.STRUCTURED_OUTPUT:
@@ -341,7 +341,7 @@ def get_model_specific_prompts(
logger.debug(f"Using model-specific REACT_BLOCK_XML_INSTRUCTIONS_SINGLE for '{model_name}'")
prompt_blocks["instructions"] = xml_instructions_no_tools if not has_tools else instructions_xml
- # Build secondary_instructions from enabled features
+ # Build operational_instructions from enabled features
secondary_parts = []
if parallel_tool_calls_enabled:
secondary_parts.append(REACT_BLOCK_MULTI_TOOL_PLANNING)
@@ -354,16 +354,15 @@ def get_model_specific_prompts(
secondary_parts.append(CONTEXT_MANAGER_INSTRUCTIONS)
if todo_management_enabled:
secondary_parts.append(TODO_TOOLS_INSTRUCTIONS)
- if sandbox_base_path:
- secondary_parts.append(
- SANDBOX_INSTRUCTIONS_TEMPLATE.format(
- base_path=sandbox_base_path,
- )
- )
if has_sub_agent_tools:
secondary_parts.append(SUB_AGENT_INSTRUCTIONS)
if secondary_parts:
- prompt_blocks["secondary_instructions"] = "\n\n".join(secondary_parts)
+ prompt_blocks["operational_instructions"] = "\n\n".join(secondary_parts)
+
+ if sandbox_base_path:
+ prompt_blocks["environment"] = SANDBOX_INSTRUCTIONS_TEMPLATE.format(
+ base_path=sandbox_base_path,
+ )
return prompt_blocks, agent_template
diff --git a/dynamiq/nodes/agents/prompts/react/instructions.py b/dynamiq/nodes/agents/prompts/react/instructions.py
index 905f6d340..db162cf2b 100644
--- a/dynamiq/nodes/agents/prompts/react/instructions.py
+++ b/dynamiq/nodes/agents/prompts/react/instructions.py
@@ -33,12 +33,12 @@
- Explicitly link key statements to specific findings from the referenced materials to strengthen credibility and transparency.
- Make sure to adhere to AGENT PERSONA & STYLE & ADDITIONAL BEHAVIORAL GUIDELINES.
-SINGLE ACTION PER TURN:
+## Single Action Per Turn
- Execute exactly ONE action per response, then wait for its Observation before continuing
- Do NOT chain multiple Action/Action Input pairs in the same response
- After receiving an Observation, decide the next single action based on the result
-FILE HANDLING:
+## File Handling
- Tools may generate or process files (images, CSVs, PDFs, etc.)
- If you want to return files, include an "Output Files:" line before "Answer:" listing file paths (comma-separated). This line is optional — omit it if there are no files to return.
""" # noqa: E501
@@ -82,7 +82,7 @@
[Optional: comma-separated absolute file paths to return]
-CRITICAL XML FORMAT RULES:
+## Critical XML Format Rules
- ALWAYS include tags with detailed reasoning
- Start the text immediately after each opening tag; do not add leading newlines or indentation inside the tags
- Write thoughts in the first person (e.g., "I will...", "I should...")
@@ -105,19 +105,19 @@
- Explicitly link key statements to specific findings from the referenced materials to strengthen credibility and transparency.
- Make sure to adhere to AGENT PERSONA & STYLE & ADDITIONAL BEHAVIORAL GUIDELINES.
-SINGLE ACTION PER TURN:
+## Single Action Per Turn
- Execute exactly ONE / pair per response, then wait for its Observation before continuing
- Do NOT include multiple action blocks or answer blocks in the same response
- After receiving an Observation, decide the next single action based on the result
-JSON FORMATTING REQUIREMENTS:
+## JSON Formatting Requirements
- Put JSON on single line within tags
- Use double quotes for all strings
- Escape newlines as \\n, quotes as \\"
- NO multi-line JSON formatting
- For tools that accept code (e.g. python), the code must be one JSON string with \\n for line breaks, not literal newlines
-FILE HANDLING:
+## File Handling
- Tools may generate or process files (images, CSVs, PDFs, reports, etc.)
- If you want to return files, include an tag after (but still inside