Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions agentic_ai/agents/agent_framework/multi_agent/reflection_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ async def _run_agent_streaming(

def _is_approved(self, review: str) -> bool:
"""Check if the reviewer approved the response."""
return "APPROVE" in review.upper()
return review.strip().upper() == "APPROVE"

async def chat_async(self, prompt: str) -> str:
"""Run the reflection workflow: Primary → Reviewer → Refine (if needed)."""
Expand Down Expand Up @@ -325,16 +325,16 @@ async def chat_async(self, prompt: str) -> str:
f"Provide only the improved response, no meta-commentary."
)
response = await self._run_agent(self._primary_agent, refine_prompt, "primary_agent")
# Re-review if not last attempt
if attempt < self._max_refinements - 1:
review_prompt = (
f"Review this refined response:\n\n"
f"**Question:** {prompt}\n\n"
f"**Response:** {response}"
)
review = await self._run_agent(self._reviewer, review_prompt, "reviewer_agent")
logger.info(f"[Reflection] Re-review: approved={self._is_approved(review)}")

# Every candidate response must pass through the reviewer, including
# the final refinement allowed by the configured attempt budget.
review_prompt = (
f"Review this refined response:\n\n"
f"**Question:** {prompt}\n\n"
f"**Response:** {response}"
)
review = await self._run_agent(self._reviewer, review_prompt, "reviewer_agent")
logger.info(f"[Reflection] Re-review: approved={self._is_approved(review)}")

# Complete
await self._broadcast("result", "✅ Reflection Complete\n\nFinal response delivered with quality assurance!")
Expand Down
38 changes: 36 additions & 2 deletions tests/test_agent_framework_1_2_1_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,12 +547,46 @@ def test_reflection_agent_init(self):
assert agent._max_refinements == 2

def test_reflection_approval_detection(self):
"""Reviewer approval detection works."""
"""Only the reviewer's exact approval token is accepted."""
from agents.agent_framework.multi_agent.reflection_agent import Agent
agent = Agent(state_store={}, session_id="test")
assert agent._is_approved("APPROVE - looks good")
assert agent._is_approved("APPROVE")
assert agent._is_approved(" approve\n")
assert not agent._is_approved("APPROVE - looks good")
assert not agent._is_approved("I cannot APPROVE this response")
assert not agent._is_approved("Needs improvement on point 3")

def test_final_refinement_is_reviewed(self):
"""The reviewer must inspect the final candidate before it is returned."""
from agents.agent_framework.multi_agent.reflection_agent import Agent

agent = Agent(state_store={}, session_id="test", max_refinements=2)
agent._initialized = True
agent._primary_agent = MagicMock()
agent._reviewer = MagicMock()
agent._session = MagicMock()
agent._session.to_dict.return_value = {}
agent._run_agent = AsyncMock(side_effect=[
"DRAFT v1: ok",
"Reject. Missing context.",
"DRAFT v2: contains a credit card number",
"Reject. Redact the credit card number.",
"DRAFT v3: contains an SSN",
"Reject. Redact the SSN.",
])

response = asyncio.run(agent.chat_async("What's my account status?"))

assert response == "DRAFT v3: contains an SSN"
assert agent._run_agent.await_count == 6
reviewer_calls = [
call
for call in agent._run_agent.await_args_list
if call.args[2] == "reviewer_agent"
]
assert len(reviewer_calls) == 3
assert response in reviewer_calls[-1].args[1]

def test_reflection_agent_uses_1_2_1_chat_client_kwargs(self):
"""The reflection agent must use 1.2.x kwarg names (model, azure_endpoint).

Expand Down
Loading