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
5 changes: 5 additions & 0 deletions apps/backend/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ class Settings(BaseSettings):
WRITING_TEAM_MAX_DISPATCHES: int = 5
DATA_SCIENCE_TEAM_MAX_DISPATCHES: int = 5
CODING_TEAM_MAX_DISPATCHES: int = 6
# Vision team has a single worker (vision_analyst) — one good pass plus
# at most one reviewer-driven retry should be enough. The previous absence
# of this ceiling let reviewer↔analyst loop indefinitely on OCR-heavy
# critiques (see plans/vision-team-reviewer-loop-fix.md).
VISION_TEAM_MAX_DISPATCHES: int = 3
MAIN_AGENT_MODEL: str = "gpt-5.4-nano"
THREAD_TITLE_MODEL: str = "gpt-5.4-mini"
THREAD_SUGGESTIONS_MODEL: str = "gpt-5.4-mini"
Expand Down
25 changes: 25 additions & 0 deletions apps/backend/tests/test_routing_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
CODING_TEAM_SUPERVISOR_PROMPT,
DATA_SCIENCE_TEAM_SUPERVISOR_PROMPT,
RESEARCH_TEAM_SUPERVISOR_PROMPT,
REVIEWER_PROMPT,
SYSTEM_SUPERVISOR_PROMPT,
TEAM_SUPERVISOR_PROMPT,
)
Expand Down Expand Up @@ -57,3 +58,27 @@ def test_dedicated_team_prompts_pin_first_workers() -> None:
assert "Start with `search`" in RESEARCH_TEAM_SUPERVISOR_PROMPT.template
assert "Start with `data_engineer`" in DATA_SCIENCE_TEAM_SUPERVISOR_PROMPT.template
assert "Start with `codebase_explorer`" in CODING_TEAM_SUPERVISOR_PROMPT.template


def test_reviewer_prompt_contains_vision_stopping_rules() -> None:
"""Reviewer must have explicit stopping rules for the Vision Team.

Without these rules the reviewer kept rejecting vision_analyst answers
that flagged OCR-impossible regions as "확인 불가" and looped indefinitely
(see plans/vision-team-reviewer-loop-fix.md). The rules here pin the four
escape hatches so prompt drift can't silently bring the loop back.
"""
prompt = REVIEWER_PROMPT.template

assert "# VISION TEAM — STOPPING RULES" in prompt
# Escape hatch 1: respect explicit "unreadable" markers
assert "확인 불가" in prompt
assert "판독 불가" in prompt
# Escape hatch 2: don't demand OCR unless user asked
assert "OCR-grade transcription" in prompt
# Escape hatch 3: same critique twice is a loop, not progress
assert "Repeating the same critique is a loop" in prompt
# Escape hatch 4: chart type + qualitative insight is enough
assert "chart TYPE" in prompt
# Hard ceiling: two attempts max
assert "after TWO vision_analyst attempts" in prompt
7 changes: 7 additions & 0 deletions apps/backend/tests/test_team_subgraphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from workflow.teams.research import ResearchTeamBuilder, get_research_graph
from workflow.teams.coding import CodingTeamBuilder, get_coding_graph
from workflow.teams.writing import get_writing_graph
from workflow.teams.vision import get_vision_graph
from prompt_kit.prompts import (
CODEBASE_EXPLORER_PROMPT,
CODING_TEAM_SUPERVISOR_PROMPT,
Expand Down Expand Up @@ -131,6 +132,12 @@ def test_team_modules_use_add_worker_without_blocking_wrappers():
13,
get_coding_graph,
),
(
"workflow.teams.vision.VisionTeamBuilder.build",
"workflow.teams.vision.settings.VISION_TEAM_MAX_DISPATCHES",
4,
get_vision_graph,
),
],
)
def test_team_graphs_use_configured_dispatch_limits(
Expand Down
4 changes: 3 additions & 1 deletion apps/backend/workflow/teams/vision.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from agent_core.builder import TeamBuilder
from agent_tools.vision import get_image_metadata, resize_image
from core.config import settings
from prompt_kit.prompts import VISION_ANALYST_PROMPT


Expand All @@ -14,5 +15,6 @@ def register_nodes(self):

def get_vision_graph(llm):
return VisionTeamBuilder(llm, "VisionTeam", ["vision_analyst"]).build(
with_validator=True
with_validator=True,
max_team_dispatches=settings.VISION_TEAM_MAX_DISPATCHES,
)
9 changes: 8 additions & 1 deletion packages/prompt-kit/src/prompt_kit/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,17 @@ class PromptTemplate(BaseModel):
- Do NOT request another data_engineer pass once the engineer brief is in the conversation. Re-inspecting the same file is wasted dispatch.
- Do NOT fail the response solely because the analyst did not re-narrate every calculation step the engineer already covered.

# VISION TEAM — STOPPING RULES (when {team_name} mentions Vision)
- vision_analyst can only "see" what the model's native vision actually resolves in the attached image. If the analyst explicitly marked dense/blurry regions as "확인 불가" / "판독 불가" / "unreadable", treat that as a legitimate, complete answer — DO NOT fail the response demanding sharper OCR.
- The user did NOT ask for OCR-grade transcription unless they used words like "그대로", "한 글자도 빠짐없이", "verbatim", "exact text". For ordinary "describe / interpret / 정리해줘 / 해석해줘" requests, a structured visual summary plus best-effort partial text is sufficient — mark VALID.
- If you previously gave the same critique (e.g. "text not transcribed", "labels unreadable") and the analyst's second answer is materially similar, mark VALID this round. Repeating the same critique is a loop, not progress.
- Charts in raster screenshots often have sub-pixel-sized labels. If the analyst correctly identifies chart TYPE (bar / line / pie / scatter) and PROVIDES qualitative insights based on visible relative magnitudes, treat the response as satisfying a "chart interpretation" request even when exact axis tick numbers are unreadable.
- Hard ceiling: after TWO vision_analyst attempts on the same image, mark VALID regardless. The head supervisor will synthesize from what was gathered.

Provide a detailed 'critique' and specific 'feedback' for the worker to follow.
Approve (is_valid=True) when the response materially satisfies the user's request and has no meaningful factual or formatting issues.
""",
version="1.2",
version="1.3",
)

DOC_WRITER_PROMPT = PromptTemplate(
Expand Down
Loading