Skip to content

Commit eb10518

Browse files
Implement citation suppression in agent instructions and tests
1 parent dd67f30 commit eb10518

4 files changed

Lines changed: 48 additions & 0 deletions

File tree

src/backend/agents/agent_factory.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ class UnsupportedModelError(Exception):
5555
6. Do NOT re-ask anything already answered in the conversation history.
5656
"""
5757

58+
_KNOWLEDGE_BASE_NO_CITATIONS_PROMPT = """
59+
60+
RESPONSE CITATION POLICY (CRITICAL):
61+
- Do not include any citation markers, source-reference tokens, attribution
62+
markers, or footnotes in your response.
63+
"""
64+
5865

5966
class AgentFactory:
6067
"""Create and manage teams of agents from JSON configuration.
@@ -158,6 +165,9 @@ async def create_agent_from_config(
158165
# Build agent instructions from system_message + optional interaction rules
159166
instructions = getattr(agent_obj, "system_message", "")
160167

168+
if kb_config:
169+
instructions += _KNOWLEDGE_BASE_NO_CITATIONS_PROMPT
170+
161171
# Universal user-interaction rules for agents that have
162172
# user_responses=true — tells them to call request_user_clarification.
163173
if user_responses:

src/backend/orchestration/plan_review_helpers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ def get_magentic_prompt_kwargs(
180180
recommend, or guess any specific team, do NOT claim any action was performed, and
181181
do NOT attempt to answer the out-of-scope request itself.
182182
- Compile ONLY from messages agents actually produced. Quote verbatim where appropriate.
183+
- Do not include any citation markers, source-reference tokens, attribution
184+
markers, or footnotes in your response.
183185
- Do NOT fabricate URLs, results, or content that no agent produced.
184186
- If a required agent step did not run, state it plainly — do not pretend it did.
185187
- If an agent produced an image (a markdown image ![alt](url) or an image URL such as one

src/tests/backend/agents/test_agent_factory.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
# --- agents sub-modules (short absolute imports in factory code)
4747
mock_agent_template_cls = Mock()
4848
mock_mcp_config_cls = Mock()
49+
mock_knowledge_base_config_cls = Mock()
4950

5051
sys.modules.setdefault("agents", Mock()) # parent package stub
5152
_mock_agent_template_mod = Mock()
@@ -57,6 +58,7 @@
5758
_mock_mcp_config_mod = Mock()
5859
_mock_mcp_config_mod.MCPConfig = mock_mcp_config_cls
5960
_mock_mcp_config_mod.VectorStoreConfig = mock_vector_store_config_cls
61+
_mock_mcp_config_mod.KnowledgeBaseConfig = mock_knowledge_base_config_cls
6062
sys.modules["config.mcp_config"] = _mock_mcp_config_mod
6163

6264
# Now import the module under test (full backend.* path as per project convention)
@@ -76,6 +78,8 @@ def _agent_obj(**overrides) -> SimpleNamespace:
7678
coding_tools=False,
7779
use_toolbox=False,
7880
use_file_search=False,
81+
use_knowledge_base=False,
82+
knowledge_base_name=None,
7983
user_responses=False,
8084
vector_store_name=None,
8185
)
@@ -113,6 +117,7 @@ def setup_method(self):
113117
self.memory_store = Mock()
114118
mock_agent_template_cls.reset_mock()
115119
mock_mcp_config_cls.reset_mock()
120+
mock_knowledge_base_config_cls.reset_mock()
116121
mock_vector_store_config_cls.reset_mock()
117122

118123
@pytest.mark.asyncio
@@ -164,6 +169,31 @@ async def test_user_responses_false_no_mcp_config(self):
164169

165170
mock_mcp_config_cls.from_env.assert_not_called()
166171

172+
@pytest.mark.asyncio
173+
async def test_knowledge_base_agent_appends_no_citations_prompt(self):
174+
"""KB-backed agents receive citation cleanup instructions."""
175+
kb_instance = Mock()
176+
mock_knowledge_base_config_cls.from_env.return_value = kb_instance
177+
agent_instance = Mock()
178+
agent_instance.open = AsyncMock()
179+
mock_agent_template_cls.return_value = agent_instance
180+
181+
await self.factory.create_agent_from_config(
182+
"user123",
183+
_agent_obj(
184+
use_knowledge_base=True,
185+
knowledge_base_name="test-kb",
186+
system_message="Use retrieved facts.",
187+
),
188+
self.team_config,
189+
self.memory_store,
190+
)
191+
192+
mock_knowledge_base_config_cls.from_env.assert_called_once_with("test-kb")
193+
instructions = mock_agent_template_cls.call_args[1]["agent_instructions"]
194+
assert "RESPONSE CITATION POLICY" in instructions
195+
assert "Do not include any citation markers" in instructions
196+
167197
@pytest.mark.asyncio
168198
async def test_use_toolbox_takes_priority_over_user_responses(self):
169199
"""use_toolbox=True takes priority; MCPConfig uses the toolbox_filter, not 'user_responses'."""

src/tests/backend/orchestration/test_plan_review_helpers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ def test_given_no_user_responses_when_called_then_final_has_answer_rules(self):
237237
# Assert
238238
assert "FINAL ANSWER RULES" in result["final_answer_prompt"]
239239

240+
def test_given_any_team_when_called_then_final_suppresses_citations(self):
241+
result = get_magentic_prompt_kwargs(has_user_responses=False)
242+
243+
final_prompt = result["final_answer_prompt"]
244+
assert "Do not include any citation markers" in final_prompt
245+
240246
def test_given_default_when_called_then_user_responses_is_false(self):
241247
# Act
242248
result = get_magentic_prompt_kwargs()

0 commit comments

Comments
 (0)