fix(engine): put the user request last in the planner prompt

This commit is contained in:
Anthony Stirling
2026-09-02 10:12:45 +01:00
parent 2cf355c5cd
commit 4ec1bbe895
2 changed files with 15 additions and 31 deletions
+9 -26
View File
@@ -283,7 +283,7 @@ class PdfEditAgent:
allow_need_content=can_request_content, allow_need_content=can_request_content,
) )
return await agent.select( return await agent.select(
self._build_selection_prompt(request, supported_operations, unavailable_operations, repair_note) self._build_selection_prompt(request, supported_operations, repair_note)
) )
def _build_selection_agent( def _build_selection_agent(
@@ -330,7 +330,6 @@ class PdfEditAgent:
self, self,
request: PdfEditRequest, request: PdfEditRequest,
supported_operations: Iterable[ToolEndpoint], supported_operations: Iterable[ToolEndpoint],
unavailable_operations: Iterable[ToolEndpoint],
repair_note: str = "", repair_note: str = "",
) -> str: ) -> str:
repair_line = ( repair_line = (
@@ -343,35 +342,19 @@ class PdfEditAgent:
if repair_note if repair_note
else "" else ""
) )
unavailable_line = (
"Unavailable operations (exist but not currently usable): "
f"{self._get_operations_prompt(unavailable_operations)}\n"
if unavailable_operations
else ""
)
return ( return (
f"Conversation history:\n{format_conversation_history(request.conversation_history)}\n"
f"User request: {request.user_message}\n"
f"Files: {format_file_names(request.files)}\n"
f"Supported operations:\n{self._get_supported_operations_prompt(supported_operations)}\n" f"Supported operations:\n{self._get_supported_operations_prompt(supported_operations)}\n"
f"{unavailable_line}"
f"{repair_line}" f"{repair_line}"
f"Extracted page text:\n{format_page_text(request.page_text)}" f"Conversation history:\n{format_conversation_history(request.conversation_history)}\n"
f"Files: {format_file_names(request.files)}\n"
f"Extracted page text:\n{format_page_text(request.page_text)}\n"
f"User request: {request.user_message}"
) )
# Endpoints that exist on the server and are callable via the direct API or the manual UI, # Hidden from the AI planner only; still live for the manual UI, direct API and pipelines.
# but are never offered to the AI agent as a routing option. # AUTO_REDACT and REDACT pre-date the unified operation model and take a less expressive
# # schema, so AI redaction is channelled through REDACT_EXECUTE. Add an endpoint here when a
# Why: REDACT_EXECUTE is the preferred AI-driven redaction route. AUTO_REDACT and REDACT are # legacy one has a preferred replacement the AI should use exclusively.
# legacy endpoints that remain fully functional for human callers (the manual redact UI, direct
# API consumers, pipelines) but would produce a worse experience if the AI routed to them —
# they accept a simpler, less expressive schema and pre-date the unified operation model.
# Hiding them here channels all AI redaction traffic through REDACT_EXECUTE without disabling
# the legacy endpoints for anyone else.
#
# How to reuse: add an endpoint here whenever a legacy endpoint has a preferred replacement
# that the AI should use exclusively. The endpoint remains live on the server; only the AI
# planner is prevented from selecting it.
_AGENT_HIDDEN_ENDPOINTS: frozenset[ToolEndpoint] = frozenset({ToolEndpoint.AUTO_REDACT, ToolEndpoint.REDACT}) _AGENT_HIDDEN_ENDPOINTS: frozenset[ToolEndpoint] = frozenset({ToolEndpoint.AUTO_REDACT, ToolEndpoint.REDACT})
def _classify_operations(self, request: PdfEditRequest) -> tuple[list[ToolEndpoint], list[ToolEndpoint]]: def _classify_operations(self, request: PdfEditRequest) -> tuple[list[ToolEndpoint], list[ToolEndpoint]]:
+6 -5
View File
@@ -156,7 +156,6 @@ def test_repair_prompt_offers_reorder_or_telling_the_user(runtime: AppRuntime) -
prompt = agent._build_selection_prompt( prompt = agent._build_selection_prompt(
PdfEditRequest(user_message="anything", files=[]), PdfEditRequest(user_message="anything", files=[]),
list(OPERATIONS), list(OPERATIONS),
[],
"step 3 (SANITIZE_PDF) accepts PDF but the previous step produces IMAGE.", "step 3 (SANITIZE_PDF) accepts PDF but the previous step produces IMAGE.",
) )
assert "SANITIZE_PDF" in prompt assert "SANITIZE_PDF" in prompt
@@ -494,11 +493,13 @@ def test_pdf_edit_selection_prompt_includes_unavailable_operations(runtime: AppR
) )
supported, unavailable = agent._classify_operations(request) supported, unavailable = agent._classify_operations(request)
prompt = agent._build_selection_prompt(request, supported, unavailable) selection_agent = agent._build_selection_agent(supported, unavailable, allow_need_content=False)
system_prompt = "".join(selection_agent.agent._system_prompts)
prompt = agent._build_selection_prompt(request, supported)
assert "Unavailable operations" in prompt assert "NOT currently available" in system_prompt
assert "OCR_PDF" in prompt assert "OCR_PDF" in system_prompt
assert ToolEndpoint.OCR_PDF.value in prompt assert "OCR_PDF" not in prompt
@pytest.mark.anyio @pytest.mark.anyio