Skip to content

Commit 1ca72c0

Browse files
committed
feat(agent): structured task format and workflow improvements
- Replace free-form task description with structured XML format (<scan_task><targets><mode>) in StrixAgent for clearer LLM parsing - Replace verbose <inter_agent_message> with compact <agent_message> format to reduce token overhead in inter-agent communication - Add corrective message when agents respond with plain text instead of tool calls, enforcing tool-call-only behavior - Simplify thinking_blocks type annotation in AgentState - Add <agent_message> pattern to clean_content() for hidden XML cleanup
1 parent c9d2477 commit 1ca72c0

4 files changed

Lines changed: 48 additions & 40 deletions

File tree

strix/agents/StrixAgent/strix_agent.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,31 +57,45 @@ async def execute_scan(self, scan_config: dict[str, Any]) -> dict[str, Any]: #
5757
elif target_type == "ip_address":
5858
ip_addresses.append(details["target_ip"])
5959

60-
task_parts = []
60+
target_lines = []
6161

6262
if repositories:
63-
task_parts.append("\n\nRepositories:")
6463
for repo in repositories:
6564
if repo["workspace_path"]:
66-
task_parts.append(f"- {repo['url']} (available at: {repo['workspace_path']})")
65+
target_lines.append(f' <target type="repository">{repo["url"]} (code at: {repo["workspace_path"]})</target>')
6766
else:
68-
task_parts.append(f"- {repo['url']}")
67+
target_lines.append(f' <target type="repository">{repo["url"]}</target>')
6968

7069
if local_code:
71-
task_parts.append("\n\nLocal Codebases:")
72-
task_parts.extend(
73-
f"- {code['path']} (available at: {code['workspace_path']})" for code in local_code
74-
)
70+
for code in local_code:
71+
target_lines.append(f' <target type="local_code">{code["path"]} (code at: {code["workspace_path"]})</target>')
7572

7673
if urls:
77-
task_parts.append("\n\nURLs:")
78-
task_parts.extend(f"- {url}" for url in urls)
74+
for url in urls:
75+
target_lines.append(f' <target type="url">{url}</target>')
7976

8077
if ip_addresses:
81-
task_parts.append("\n\nIP Addresses:")
82-
task_parts.extend(f"- {ip}" for ip in ip_addresses)
83-
84-
task_description = " ".join(task_parts)
78+
for ip in ip_addresses:
79+
target_lines.append(f' <target type="ip">{ip}</target>')
80+
81+
targets_block = "\n".join(target_lines)
82+
83+
has_code = bool(repositories or local_code)
84+
has_urls = bool(urls or ip_addresses)
85+
if has_code and has_urls:
86+
mode = "COMBINED MODE (code + deployed target)"
87+
elif has_code:
88+
mode = "WHITE-BOX (source code provided)"
89+
else:
90+
mode = "BLACK-BOX (URL/domain targets)"
91+
92+
task_description = (
93+
f"<scan_task>\n"
94+
f"<targets>\n{targets_block}\n</targets>\n"
95+
f"<mode>{mode}</mode>\n"
96+
f"<action>Begin security assessment NOW. Your first tool call must be create_agent to spawn context-gathering subagents for the targets listed above. Do NOT call wait_for_message — the targets are already specified.</action>\n"
97+
f"</scan_task>"
98+
)
8599

86100
if user_instructions:
87101
task_description += f"\n\nSpecial instructions: {user_instructions}"

strix/agents/base_agent.py

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,17 @@ async def _process_iteration(self, tracer: Optional["Tracer"]) -> bool | None:
410410
if actions:
411411
return await self._execute_actions(actions, tracer)
412412

413+
corrective_message = (
414+
"You responded with plain text instead of a tool call. "
415+
"While the agent loop is running, EVERY response MUST be a tool call. "
416+
"Do NOT send plain text messages. Act via tools:\n"
417+
"- Use the think tool to reason through problems\n"
418+
"- Use create_agent to spawn subagents for testing\n"
419+
"- Use terminal_execute to run commands\n"
420+
"- Use wait_for_message ONLY when waiting for subagent results\n"
421+
"Review your task and take action now."
422+
)
423+
self.state.add_message("user", corrective_message)
413424
return None
414425

415426
async def _execute_actions(self, actions: list[Any], tracer: Optional["Tracer"]) -> bool:
@@ -484,33 +495,17 @@ def _check_agent_messages(self, state: AgentState) -> None: # noqa: PLR0912
484495
sender_name = "User"
485496
state.add_message("user", message.get("content", ""))
486497
else:
498+
sender_name = sender_id or "Unknown"
487499
if sender_id and sender_id in _agent_graph.get("nodes", {}):
488500
sender_name = _agent_graph["nodes"][sender_id]["name"]
489501

490-
message_content = f"""<inter_agent_message>
491-
<delivery_notice>
492-
<important>You have received a message from another agent. You should acknowledge
493-
this message and respond appropriately based on its content. However, DO NOT echo
494-
back or repeat the entire message structure in your response. Simply process the
495-
content and respond naturally as/if needed.</important>
496-
</delivery_notice>
497-
<sender>
498-
<agent_name>{sender_name}</agent_name>
499-
<agent_id>{sender_id}</agent_id>
500-
</sender>
501-
<message_metadata>
502-
<type>{message.get("message_type", "information")}</type>
503-
<priority>{message.get("priority", "normal")}</priority>
504-
<timestamp>{message.get("timestamp", "")}</timestamp>
505-
</message_metadata>
506-
<content>
502+
message_content = f"""<agent_message
503+
from="{sender_name}"
504+
id="{sender_id}"
505+
type="{message.get("message_type", "information")}"
506+
priority="{message.get("priority", "normal")}">
507507
{message.get("content", "")}
508-
</content>
509-
<delivery_info>
510-
<note>This message was delivered during your task execution.
511-
Please acknowledge and respond if needed.</note>
512-
</delivery_info>
513-
</inter_agent_message>"""
508+
</agent_message>"""
514509
state.add_message("user", message_content.strip())
515510

516511
message["read"] = True

strix/agents/state.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ def increment_iteration(self) -> None:
4444
self.iteration += 1
4545
self.last_updated = datetime.now(UTC).isoformat()
4646

47-
def add_message(
48-
self, role: str, content: Any, thinking_blocks: list[dict[str, Any]] | None = None
49-
) -> None:
47+
def add_message(self, role: str, content: Any, thinking_blocks: list | None = None) -> None:
5048
message = {"role": role, "content": content}
5149
if thinking_blocks:
5250
message["thinking_blocks"] = thinking_blocks

strix/llm/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ def clean_content(content: str) -> str:
150150

151151
hidden_xml_patterns = [
152152
r"<inter_agent_message>.*?</inter_agent_message>",
153+
r"<agent_message\b[^>]*>.*?</agent_message>",
153154
r"<agent_completion_report>.*?</agent_completion_report>",
154155
]
155156
for pattern in hidden_xml_patterns:

0 commit comments

Comments
 (0)