Skip to content

Commit 2fee0fc

Browse files
Merge pull request #1072 from microsoft/FIX_Akhileswar
fix: missing compliance and triage agents in response in content gen
2 parents 078449c + a6d6f85 commit 2fee0fc

3 files changed

Lines changed: 113 additions & 28 deletions

File tree

src/backend/orchestration/orchestration_manager.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,20 @@ async def init_orchestration(
143143

144144
manager_agent = Agent(manager_chat_client, name="MagenticManager")
145145

146+
# Collect participant agent names so the orchestrator plan prompt can
147+
# enforce mandatory inclusion of every team agent (e.g. TriageAgent,
148+
# ComplianceAgent) — otherwise the manager silently drops them.
149+
participant_agent_names = []
150+
for ag in agents:
151+
nm = getattr(ag, "agent_name", None) or getattr(ag, "name", None)
152+
if nm:
153+
participant_agent_names.append(nm)
154+
146155
# Get prompt customization kwargs
147-
prompt_kwargs = get_magentic_prompt_kwargs(has_user_responses=has_user_responses)
156+
prompt_kwargs = get_magentic_prompt_kwargs(
157+
has_user_responses=has_user_responses,
158+
participant_names=participant_agent_names,
159+
)
148160

149161
cls.logger.info(
150162
"Building MagenticBuilder for user '%s' with max_rounds=%d, "

src/backend/orchestration/plan_review_helpers.py

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,23 @@
3131
# Prompt kwargs builder
3232
# ---------------------------------------------------------------------------
3333

34-
def get_magentic_prompt_kwargs(*, has_user_responses: bool = False) -> dict:
34+
def get_magentic_prompt_kwargs(
35+
*,
36+
has_user_responses: bool = False,
37+
participant_names: Optional[list[str]] = None,
38+
) -> dict:
3539
"""Build the prompt-override kwargs dict for ``MagenticBuilder``.
3640
3741
Args:
3842
has_user_responses: Whether any agent has ``user_responses: true``,
3943
giving it access to the ``ask_user`` tool for user clarification.
4044
When True, prompts allow agents to gather info via their tools;
4145
when False, agents must use defaults only.
46+
participant_names: Names of the team's participant agents. When provided,
47+
the orchestrator plan prompt is augmented with a MANDATORY AGENTS
48+
clause requiring every one of these agents to appear as a plan step,
49+
so coordinator-like agents (e.g. TriageAgent) and final-validation
50+
agents (e.g. ComplianceAgent) are not silently dropped.
4251
4352
Returns:
4453
A dict suitable for unpacking into ``MagenticBuilder(**kwargs)``.
@@ -70,12 +79,26 @@ def get_magentic_prompt_kwargs(*, has_user_responses: bool = False) -> dict:
7079
- Ask EXACTLY 0 questions. Always proceed with sensible defaults.
7180
"""
7281

82+
mandatory_block = ""
83+
if participant_names:
84+
agent_lines = "\n".join(f"- {name}" for name in participant_names)
85+
mandatory_block = (
86+
"\n\nMANDATORY AGENTS (CRITICAL — NON-NEGOTIABLE):\n"
87+
"Every plan you generate MUST include EVERY ONE of the following agents,\n"
88+
"each as its own distinct step, each invoked at least once:\n"
89+
+ agent_lines
90+
+ "\nDo NOT omit any of these agents, even if a step seems optional,\n"
91+
"redundant, already covered by another agent, or something you "
92+
"(MagenticManager)\ncould do yourself. A plan missing any listed agent "
93+
"is INVALID — regenerate it\nuntil every listed agent appears as a step.\n"
94+
)
95+
7396
plan_append = """
7497
7598
PLAN RULES:
7699
- Steps are HIGH-LEVEL task assignments — one step per agent. Do NOT prescribe
77100
sub-tasks, parameters, or data retrieval. Agents discover their own processes.
78-
""" + clarification_policy + """
101+
""" + mandatory_block + clarification_policy + """
79102
OUTPUT FORMAT (CRITICAL — use EXACTLY this JSON structure, nothing else):
80103
```json
81104
[
@@ -86,11 +109,14 @@ def get_magentic_prompt_kwargs(*, has_user_responses: bool = False) -> dict:
86109
Use exact agent names from the team list above. Output ONLY the JSON array — no
87110
markdown fences, no commentary before or after.
88111
89-
IMPORTANT: There is NO UserInteractionAgent. Do NOT include any user-interaction
112+
""" + ("""IMPORTANT: There is NO UserInteractionAgent. Do NOT include any user-interaction
90113
agent in the plan. Domain agents gather user info themselves via their
91114
request_user_clarification tool — the framework pauses automatically when they
92115
call it and resumes when the user answers.
93-
116+
""" if has_user_responses else """IMPORTANT: There is NO UserInteractionAgent. Do NOT include any user-interaction
117+
agent in the plan. Agents apply sensible defaults for missing details and proceed
118+
without asking the user any questions.
119+
""") + """
94120
Example plan:
95121
[
96122
{{"agent": "HRHelperAgent", "action": "execute the onboarding process for the new employee"}},
@@ -143,12 +169,42 @@ def get_magentic_prompt_kwargs(*, has_user_responses: bool = False) -> dict:
143169
ORCHESTRATOR_TASK_LEDGER_FACTS_PROMPT + facts_append
144170
)
145171

146-
progress_append = """
172+
# Completion-enforcement progress-ledger rules. Applied ALWAYS (not only when
173+
# has_user_responses) so that EVERY plan-step agent — e.g. TriageAgent and
174+
# ComplianceAgent in the content_gen team, whose agents all have
175+
# user_responses=false — must actually run before the request can be marked
176+
# satisfied, and the orchestrator re-selects any uninvoked plan-step agent
177+
# instead of silently finishing early.
178+
progress_append = """
147179
148180
EXECUTION RULES:
149181
- When selecting next_speaker, prefer a work agent that has NOT yet been invoked.
150-
- MagenticManager MUST NOT generate answers, ask questions, or list missing info.
151-
It only routes tasks to the appropriate agent.
182+
- MagenticManager MUST NOT generate answers or fabricate content on behalf of
183+
work agents. It only routes tasks and compiles the final output.
184+
185+
COMPLETION CHECK (CRITICAL):
186+
Before setting is_request_satisfied to true, you MUST verify:
187+
1. Review the conversation history and list every agent that has actually produced
188+
a substantive response (meaningful output — calling tools and returning results
189+
where the agent has tools, or producing a substantive text response otherwise).
190+
2. Compare that list against the plan steps. If ANY plan-step agent has NOT been
191+
invoked and produced a substantive response, set is_request_satisfied to false
192+
and select the next uninvoked agent as next_speaker.
193+
3. is_request_satisfied = true ONLY when ALL plan-step agents have completed
194+
their work (produced a substantive response — tool results, or meaningful text
195+
output for agents that have no tools).
196+
- Each agent handles a DISTINCT domain. One agent's output does NOT satisfy
197+
another agent's step.
198+
- Do NOT re-invoke an agent that already completed its step successfully.
199+
- IGNORE agent-level completion language (e.g. "all steps are complete",
200+
"onboarding is done"). An individual agent only knows about its own domain.
201+
The workflow is NOT complete until every plan-step agent has been invoked."""
202+
203+
if has_user_responses:
204+
progress_append += """
205+
206+
USER-CLARIFICATION EXECUTION RULES:
207+
- MagenticManager MUST NOT ask questions or list missing info — it only routes.
152208
- There is NO UserInteractionAgent. Do NOT select it as next_speaker.
153209
- Domain agents that need user info will call their request_user_clarification
154210
tool. The framework handles the pause/resume automatically via
@@ -168,26 +224,11 @@ def get_magentic_prompt_kwargs(*, has_user_responses: bool = False) -> dict:
168224
STALL DETECTION OVERRIDE:
169225
- An agent calling request_user_clarification is NOT stalling. The framework
170226
pauses automatically. Set is_progress_being_made=true and is_in_loop=false.
171-
- Do NOT treat a framework pause as a stall or loop.
227+
- Do NOT treat a framework pause as a stall or loop."""
172228

173-
COMPLETION CHECK (CRITICAL):
174-
Before setting is_request_satisfied to true, you MUST verify:
175-
1. Review the conversation history and list every agent that has actually produced
176-
a substantive response (called tools and returned results).
177-
2. Compare that list against the plan steps. If ANY plan-step agent has NOT been
178-
invoked and produced a substantive response, set is_request_satisfied to false
179-
and select the next uninvoked agent as next_speaker.
180-
3. is_request_satisfied = true ONLY when ALL plan-step agents have completed
181-
their work successfully (called their tools, returned results).
182-
- Each agent handles a DISTINCT domain. One agent's output does NOT satisfy
183-
another agent's step.
184-
- Do NOT re-invoke an agent that already completed its step successfully.
185-
- IGNORE agent-level completion language (e.g. "all steps are complete",
186-
"onboarding is done"). An individual agent only knows about its own domain.
187-
The workflow is NOT complete until every plan-step agent has been invoked."""
188-
kwargs["progress_ledger_prompt"] = (
189-
ORCHESTRATOR_PROGRESS_LEDGER_PROMPT + progress_append
190-
)
229+
kwargs["progress_ledger_prompt"] = (
230+
ORCHESTRATOR_PROGRESS_LEDGER_PROMPT + progress_append
231+
)
191232

192233
return kwargs
193234

src/tests/backend/orchestration/test_plan_review_helpers.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,9 @@ def test_given_no_user_responses_when_called_then_returns_base_keys(self):
163163
assert "task_ledger_plan_update_prompt" in result
164164
assert "final_answer_prompt" in result
165165
assert "task_ledger_facts_prompt" not in result
166-
assert "progress_ledger_prompt" not in result
166+
# progress_ledger_prompt (completion enforcement) is now always present,
167+
# so plan-step agents must run even for teams without user_responses.
168+
assert "progress_ledger_prompt" in result
167169

168170
def test_given_user_responses_when_called_then_returns_extended_keys(self):
169171
# Act
@@ -198,6 +200,36 @@ def test_given_user_responses_when_called_then_progress_contains_execution_rules
198200
assert "EXECUTION RULES" in result["progress_ledger_prompt"]
199201
assert "COMPLETION CHECK" in result["progress_ledger_prompt"]
200202

203+
def test_given_no_user_responses_when_called_then_progress_still_enforces_completion(self):
204+
# Act
205+
result = get_magentic_prompt_kwargs(has_user_responses=False)
206+
207+
# Assert — completion enforcement applies even without user_responses
208+
assert "progress_ledger_prompt" in result
209+
assert "COMPLETION CHECK" in result["progress_ledger_prompt"]
210+
# User-clarification-only rules must NOT leak in for non-interactive teams
211+
assert "request_user_clarification" not in result["progress_ledger_prompt"]
212+
213+
def test_given_participant_names_when_called_then_plan_lists_mandatory_agents(self):
214+
# Act
215+
result = get_magentic_prompt_kwargs(
216+
has_user_responses=False,
217+
participant_names=["TriageAgent", "ComplianceAgent"],
218+
)
219+
220+
# Assert — every listed agent is required to appear in the plan
221+
plan_prompt = result["task_ledger_plan_prompt"]
222+
assert "MANDATORY AGENTS" in plan_prompt
223+
assert "- TriageAgent" in plan_prompt
224+
assert "- ComplianceAgent" in plan_prompt
225+
226+
def test_given_no_participant_names_when_called_then_no_mandatory_block(self):
227+
# Act
228+
result = get_magentic_prompt_kwargs(has_user_responses=False)
229+
230+
# Assert
231+
assert "MANDATORY AGENTS" not in result["task_ledger_plan_prompt"]
232+
201233
def test_given_no_user_responses_when_called_then_final_has_answer_rules(self):
202234
# Act
203235
result = get_magentic_prompt_kwargs(has_user_responses=False)

0 commit comments

Comments
 (0)