Skip to content

feat(ai): Single-turn Router → Supervisor 패턴 전환 (#51) - #63

Merged
crolvlee merged 20 commits into
developfrom
feat/51-supervisor-pattern
Jun 24, 2026
Merged

feat(ai): Single-turn Router → Supervisor 패턴 전환 (#51)#63
crolvlee merged 20 commits into
developfrom
feat/51-supervisor-pattern

Conversation

@crolvlee

@crolvlee crolvlee commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

변경 내용

1. 핵심 구조 전환

  • AgentState에서 Intent 열거형 제거 → next_worker, workers_called 필드로 교체
  • classify_intent 노드 삭제, supervisor 순환 그래프로 교체
  • LLMClientdecide_next_worker 추가 (SUPERVISOR_PROMPT 기반 LLM 판단)
  • builder.py: supervisor → 워커 → supervisor 루프 구조로 재설계
image

2. 기능 추가

  • GENERAL_CHAT 워커 추가 (인사·잡담·서비스 소개 처리)
  • supervisor 호출 횟수·workers_called 터미널 로깅

3. API 응답 변경

  • intent: stringworkersCalled: string[] (복수 워커 반환 가능)

4. 버그 수정

  • supabase_client.py: SET LOCAL statement_timeout 파라미터 바인딩 오류 수정
  • supabase_client.py: IVFFlat probes=1 기본값으로 벡터 검색 결과 0개 반환 문제 수정 (probes=100 설정)

5. 성능 측정

  • eval set 38케이스 기준 baseline / supervisor 비교 스크립트 추가

연결 이슈

closes #51

테스트

  • 단위 테스트 통과 (test_supervisor.py, test_agent_chat.py)
  • 로컬 동작 확인
  • API 응답 형식 확인 (docs/08_API_SPEC.md)

eval 측정 결과 (38케이스 기준)

지표 Before After
단순 의도 정확도 22/22 (100%) 22/22 (100%)
복합 의도 완전 처리율 0/16 (0%) 12/16 (75%)
복합 의도 평균 재현율 40.6% 94.8%
평균 응답 지연 1,543ms 7,001ms

리뷰 포인트

  • 무한루프 방지: workers_called에 이미 있는 워커를 LLM이 다시 선택하면 FINISH로 강제 처리 (decide_next_worker 내 guard 로직)
  • API 응답 형식 변경: intent(string) → workersCalled(array) — Spring Boot 연동 시 프론트엔드 파싱 확인 필요
  • IVFFlat probes: 현재 lists=100, probes=100으로 전체 탐색. 데이터가 충분히 쌓이면 probes 값을 낮춰 성능 최적화 필요

Summary by CodeRabbit

  • New Features
    • Agent chat now uses a supervisor-based flow that can invoke multiple workers for complex requests.
    • API responses now include workersCalled to show which steps were executed.
  • Bug Fixes
    • Improved resilience when worker routing data is missing or incomplete (returns an empty/default worker list rather than failing).
    • Refined routing logic for general chat, legal, property, price, and safety workflows.
  • Documentation
    • Updated architecture and API spec to reflect the supervisor pattern and workersCalled.
  • Tests
    • Added/updated evaluation scripts and revised routing tests for the new multi-worker contract.

crolvlee and others added 13 commits June 24, 2026 11:36
- Intent StrEnum 및 intent 필드 삭제
- next_worker, workers_called 필드 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SUPERVISOR_PROMPT 및 decide_next_worker() 메서드 추가
- supervisor 노드 생성 (workers_called 누적, FINISH 시 미포함)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- classify_intent.py 삭제
- builder.py: START → supervisor → [worker] → supervisor 순환 구조로 재작성
- FINISH 신호 시 generate_answer로 이동

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AgentChatResponse: intent 필드 → workersCalled(list[str])로 교체
- routes.py: result["intent"] → result.get("workers_called", [])
- generate_answer(): Intent 열거형 비교 → workers_called in 검사로 교체

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- test_classify_intent.py 삭제
- test_supervisor.py 신규 생성 (decide_next_worker, supervisor 노드 단위 테스트 7개)
- test_agent_chat.py 재작성: route_as() → supervisor mock 방식, body["intent"] → body["workersCalled"]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
salmanhae Ready Ready Preview, Comment Jun 24, 2026 12:43pm

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e19ae35-f1bd-43a6-ac78-1dd0d7e8c404

📥 Commits

Reviewing files that changed from the base of the PR and between 3d9d3fe and 9dfdc3b.

📒 Files selected for processing (9)
  • backend-ai/app/clients/llm_client.py
  • backend-ai/tests/eval/baseline_result.json
  • backend-ai/tests/eval/run_baseline.py
  • backend-ai/tests/test_agent_chat.py
  • backend-ai/tests/test_supervisor.py
  • docs/08_API_SPEC.md
  • phases/supervisor-pattern/phase2-supervisor-node.md
  • phases/supervisor-pattern/phase3-builder-refactor.md
  • phases/supervisor-pattern/phase4-api-and-answer.md
✅ Files skipped from review due to trivial changes (2)
  • docs/08_API_SPEC.md
  • backend-ai/tests/eval/baseline_result.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • backend-ai/app/clients/llm_client.py
  • phases/supervisor-pattern/phase2-supervisor-node.md
  • phases/supervisor-pattern/phase3-builder-refactor.md
  • phases/supervisor-pattern/phase4-api-and-answer.md
  • backend-ai/tests/test_agent_chat.py

📝 Walkthrough

Walkthrough

The PR replaces intent-based routing with a supervisor loop that selects workers through next_worker and workers_called. It updates the graph, API response, tests, evaluation assets, and documentation to use the new worker-based flow.

Changes

Supervisor Pattern Migration

Layer / File(s) Summary
State and supervisor selection
backend-ai/app/graph/state.py, backend-ai/app/clients/llm_client.py, backend-ai/app/graph/nodes/supervisor.py
AgentState drops intent and adds worker-orchestration fields. LLMClient adds SUPERVISOR_PROMPT and decide_next_worker(). The new supervisor() node reads workers_called, calls the client, and returns updated state.
Graph loop and worker nodes
backend-ai/app/graph/builder.py, backend-ai/app/graph/nodes/general_chat.py, backend-ai/app/graph/nodes/classify_intent.py
The graph starts at supervisor, routes by next_worker, adds general_chat, and sends each worker back to supervisor. The intent-classification node is removed.
API response contract
backend-ai/app/api/schemas.py, backend-ai/app/api/routes.py
AgentChatResponse replaces intent with workers_called/workersCalled, and the route handler now returns workersCalled from the graph result.
Answer generation and tests
backend-ai/app/clients/llm_client.py, backend-ai/tests/test_supervisor.py, backend-ai/tests/test_agent_chat.py, backend-ai/tests/test_classify_intent.py
generate_answer() branches on workers_called and adds live general-chat handling. Tests switch to worker-string routing, add supervisor coverage, and remove the intent-classification test module.
Evaluation artifacts and docs
backend-ai/tests/eval/*, docs/*, phases/supervisor-pattern/*
Adds the evaluation dataset, baseline/supervisor scripts, result JSON artifacts, and updates architecture, ADR, API spec, and phase documents for the supervisor pattern and workersCalled contract.

pgvector Query Optimization

Layer / File(s) Summary
SupabaseVectorClient session settings
backend-ai/app/clients/supabase_client.py
The legal similarity search now sets statement_timeout inline and enables ivfflat.probes before running the pgvector query.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ssafy-salman/salmanhae#23: Shares the LLMClient.generate_answer() legal-answer path in backend-ai/app/clients/llm_client.py.
  • ssafy-salman/salmanhae#37: Also changes the /internal/agent/chat response contract in backend-ai/app/api/schemas.py and backend-ai/app/api/routes.py.
  • ssafy-salman/salmanhae#42: Replaces intent-based routing with worker-based supervisor routing in the same LangGraph area.

Suggested labels

ai-generated

Poem

🐇 Hop, hop, the graph found a new way,
Workers take turns and FINISH ends the play.
workersCalled jingles like carrots in a row,
Through the supervisor loop, wee rabbits go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the supervisor-pattern routing refactor and matches the main change.
Description check ✅ Passed The description covers the required sections and includes the routing changes, issue link, tests, and review notes.
Linked Issues check ✅ Passed The supervisor flow, state schema, builder, tests, and API updates align with the linked issue objectives.
Out of Scope Changes check ✅ Passed The docs, eval scripts, and Supabase fix are all consistent with the stated PR objectives and not clearly unrelated.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/51-supervisor-pattern

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
docs/08_API_SPEC.md

markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)
Error: Unable to use configuration file '/coderabbit-0.markdownlint-cli2.jsonc'; ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at throwForConfigurationFile (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:48:9)
at readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:169:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
[cause]: Error: ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:141:17)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/coderabbit-0.markdownlint-cli2.jsonc'
}
}

phases/supervisor-pattern/phase3-builder-refactor.md

markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)
Error: Unable to use configuration file '/coderabbit-2.markdownlint-cli2.jsonc'; ENOENT: no such file or directory, open '/coderabbit-2.markdownlint-cli2.jsonc'
at throwForConfigurationFile (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:48:9)
at readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:169:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
[cause]: Error: ENOENT: no such file or directory, open '/coderabbit-2.markdownlint-cli2.jsonc'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:141:17)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/coderabbit-2.markdownlint-cli2.jsonc'
}
}

phases/supervisor-pattern/phase2-supervisor-node.md

markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)
Error: Unable to use configuration file '/coderabbit-1.markdownlint-cli2.jsonc'; ENOENT: no such file or directory, open '/coderabbit-1.markdownlint-cli2.jsonc'
at throwForConfigurationFile (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:48:9)
at readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:169:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
[cause]: Error: ENOENT: no such file or directory, open '/coderabbit-1.markdownlint-cli2.jsonc'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:141:17)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/coderabbit-1.markdownlint-cli2.jsonc'
}
}

  • 1 others

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (1)
phases/supervisor-pattern/phase5-tests.md (1)

178-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add GENERAL_CHAT to the test migration matrix.

Update the replacement list to include route_as(monkeypatch, "GENERAL_CHAT") and corresponding workersCalled assertion guidance for parity with the current worker set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@phases/supervisor-pattern/phase5-tests.md` around lines 178 - 183, The test
migration matrix in phase5-tests.md is missing the GENERAL_CHAT case, so update
the replacement list used for route_as and workersCalled parity to include
route_as(monkeypatch, "GENERAL_CHAT") alongside the existing Intent-to-string
migrations. Make sure the guidance references the same test migration matrix
section and keeps the worker assertion mapping aligned with the current worker
set so GENERAL_CHAT is covered consistently with PROPERTY_SEARCH, LEGAL_CONSULT,
PRICE_ANALYSIS, and SAFETY_ANALYSIS.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend-ai/app/clients/llm_client.py`:
- Around line 176-200: generate_answer() is returning on the first worker match,
which discards multi-worker results and breaks the expected supervisor
aggregation behavior. Update the logic in llm_client.py inside generate_answer()
to collect and combine outputs from all relevant workers in workers_called
instead of short-circuiting on the first match, preserving both GENERAL_CHAT and
domain-specific responses when present. Use the existing helper methods
_generate_live_general_chat_answer, _generate_live_legal_answer,
_generate_live_analysis_answer, generate_legal_answer, and AnalysisAnswerService
to build each part, then merge them into a single final response.
- Around line 99-127: The decide_next_worker flow is turning every
transport/JSON/validation failure into FINISH, which prematurely routes the
graph to generate_answer even when no worker has run yet. Update
decide_next_worker in llm_client.py so only explicit invalid next_worker values
or repeated workers_called are mapped to FINISH, while first-call
request/parsing failures are handled by a non-terminal fallback path that keeps
routing available for another worker or retry. Keep the validation around
parsed["next_worker"] and workers_called, but avoid swallowing initial
httpx.HTTPError/json parsing errors into FINISH.

In `@backend-ai/app/clients/supabase_client.py`:
- Line 80: The timeout-setting SQL in SupabaseClient now uses a single literal
SQL string via execute, so the stale tests still expecting a parameterized call
must be updated. Adjust the FakeCursor.execute signatures in
test_legal_retriever and test_legal_ingestion_upsert to accept the new
one-argument form, and change their assertions to match the literal SET LOCAL
statement_timeout SQL emitted by the client. Ensure the expectations line up
with the behavior in SupabaseClient's statement_timeout handling.

In `@backend-ai/tests/eval/baseline_result.json`:
- Around line 8-14: The eval baseline artifact is stale and no longer matches
the current dataset split used by eval_set.py, so update baseline_result.json to
reflect the regenerated evaluation results. Recompute the baseline against the
current eval set, then replace the summary fields (including single_intent_total
and complex_total) and any affected case expectations so baseline-vs-supervisor
comparisons stay consistent.

In `@backend-ai/tests/eval/run_baseline.py`:
- Around line 148-155: The token average calculations in run_baseline.py are
using the wrong denominator, which can skew the reported values when some
results have missing or zero token counts. Update avg_prompt_tokens and
avg_completion_tokens to divide by each metric’s own count of non-zero entries
rather than len(all_tokens), and keep the existing zero-safe fallback behavior
in the aggregation logic around results and all_tokens.
- Around line 31-33: The baseline script still imports legacy symbols removed by
the supervisor migration, so fix the imports in run_baseline.py to use the new
supervisor-based types instead of RouteDecision and Intent. Update the code that
references these symbols to the current equivalents used by the migrated
graph/state modules, and ensure any eval logic continues to work without relying
on deleted app.graph.* definitions.

In `@backend-ai/tests/test_agent_chat.py`:
- Line 65: The single-worker tests are only checking that the expected worker
appears in workersCalled, which allows extra unintended workers to slip through.
Update the assertions in the affected test cases in test_agent_chat to verify
the exact workersCalled value for each single-worker scenario instead of using
inclusion checks, so the tests fail when over-routing occurs.

In `@backend-ai/tests/test_supervisor.py`:
- Around line 19-46: The `LLMClient.decide_next_worker` tests are mocking the
method under test, so they never exercise the real routing logic. Update the
tests in `test_supervisor.py` to patch only the external LLM dependency used by
`LLMClient.decide_next_worker`, then assert the actual method handles JSON
parsing, the valid-worker allowlist, repeated-worker fallback, and
failure-to-`FINISH` behavior. Use the existing `LLMClient` symbol and the
related tests around `test_decide_next_worker_*` to locate the cases and rewrite
them so they validate real behavior instead of patched return values.

In `@phases/supervisor-pattern/phase2-supervisor-node.md`:
- Line 79: The decide_next_worker allowlist is missing GENERAL_CHAT, causing the
phase2 supervisor contract to conflict with the supported worker set. Update the
valid set in the supervisor pattern doc’s decide_next_worker section to include
GENERAL_CHAT alongside the existing worker names so the documented routing
options match the current contract.

In `@phases/supervisor-pattern/phase3-builder-refactor.md`:
- Around line 13-15: The phase instructions are missing the GENERAL_CHAT route,
so update the supervisor graph documentation to include GENERAL_CHAT alongside
the existing worker mappings and loop-back edges. In the phase3 builder refactor
doc, adjust the checklist items and any related route/edge descriptions so
`GENERAL_CHAT` is explicitly covered in the same supervisor-to-worker and
worker-to-supervisor flow as the other nodes, ensuring the documented graph
matches the current supervisor behavior.

In `@phases/supervisor-pattern/phase4-api-and-answer.md`:
- Around line 52-78: The generate_answer dispatch logic is missing the
GENERAL_CHAT branch, so the phase documentation does not match the current
worker set. Update the generate_answer flow to include a GENERAL_CHAT condition
alongside the existing LEGAL_CONSULT, PRICE_ANALYSIS, SAFETY_ANALYSIS, and
PROPERTY_SEARCH handling, and make sure the fallback/default response still
applies only when no worker-specific branch matches.

---

Nitpick comments:
In `@phases/supervisor-pattern/phase5-tests.md`:
- Around line 178-183: The test migration matrix in phase5-tests.md is missing
the GENERAL_CHAT case, so update the replacement list used for route_as and
workersCalled parity to include route_as(monkeypatch, "GENERAL_CHAT") alongside
the existing Intent-to-string migrations. Make sure the guidance references the
same test migration matrix section and keeps the worker assertion mapping
aligned with the current worker set so GENERAL_CHAT is covered consistently with
PROPERTY_SEARCH, LEGAL_CONSULT, PRICE_ANALYSIS, and SAFETY_ANALYSIS.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 40143b00-ee91-43d2-81c9-4dc6f35a2106

📥 Commits

Reviewing files that changed from the base of the PR and between 123df64 and 3d9d3fe.

📒 Files selected for processing (26)
  • backend-ai/app/api/routes.py
  • backend-ai/app/api/schemas.py
  • backend-ai/app/clients/llm_client.py
  • backend-ai/app/clients/supabase_client.py
  • backend-ai/app/graph/builder.py
  • backend-ai/app/graph/nodes/classify_intent.py
  • backend-ai/app/graph/nodes/general_chat.py
  • backend-ai/app/graph/nodes/supervisor.py
  • backend-ai/app/graph/state.py
  • backend-ai/tests/eval/__init__.py
  • backend-ai/tests/eval/baseline_result.json
  • backend-ai/tests/eval/eval_set.py
  • backend-ai/tests/eval/run_baseline.py
  • backend-ai/tests/eval/run_supervisor.py
  • backend-ai/tests/eval/supervisor_result.json
  • backend-ai/tests/test_agent_chat.py
  • backend-ai/tests/test_classify_intent.py
  • backend-ai/tests/test_supervisor.py
  • docs/02_ARCHITECTURE.md
  • docs/03_ADR.md
  • docs/08_API_SPEC.md
  • phases/supervisor-pattern/phase1-state-schema.md
  • phases/supervisor-pattern/phase2-supervisor-node.md
  • phases/supervisor-pattern/phase3-builder-refactor.md
  • phases/supervisor-pattern/phase4-api-and-answer.md
  • phases/supervisor-pattern/phase5-tests.md
💤 Files with no reviewable changes (2)
  • backend-ai/app/graph/nodes/classify_intent.py
  • backend-ai/tests/test_classify_intent.py

Comment thread backend-ai/app/clients/llm_client.py
Comment thread backend-ai/app/clients/llm_client.py
"set local statement_timeout = %s",
(self.statement_timeout_ms,),
)
cursor.execute(f"SET LOCAL statement_timeout = {int(self.statement_timeout_ms)}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd test_legal_retriever.py backend-ai/tests --exec sed -n '120,185p' {}
fd supabase_client.py backend-ai/app/clients --exec sed -n '74,90p' {}

Repository: ssafy-salman/salmanhae

Length of output: 3102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for all execute() test doubles and related expectations in backend-ai tests
rg -n "def execute\(self, sql|execute\(sql, params\)|SET LOCAL statement_timeout|statement_timeout" backend-ai/tests

# Show the full affected test file for context around the existing assertion
sed -n '1,260p' backend-ai/tests/test_legal_retriever.py

# Show the current client implementation around the timeout calls
sed -n '70,95p' backend-ai/app/clients/supabase_client.py

Repository: ssafy-salman/salmanhae

Length of output: 10386


Update the timeout test expectations. backend-ai/tests/test_legal_retriever.py still mocks FakeCursor.execute(self, sql, params) and asserts the old parameterized SET LOCAL statement_timeout call; it needs to accept the new one-argument SQL call and assert the literal timeout statement instead. The same stale expectation also appears in backend-ai/tests/test_legal_ingestion_upsert.py.

🧰 Tools
🪛 OpenGrep (1.23.0)

[ERROR] 80-80: SQL query built via f-string passed to execute()/executemany(). Use parameterized queries with placeholders instead.

(coderabbit.sql-injection.python-fstring-execute)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend-ai/app/clients/supabase_client.py` at line 80, The timeout-setting
SQL in SupabaseClient now uses a single literal SQL string via execute, so the
stale tests still expecting a parameterized call must be updated. Adjust the
FakeCursor.execute signatures in test_legal_retriever and
test_legal_ingestion_upsert to accept the new one-argument form, and change
their assertions to match the literal SET LOCAL statement_timeout SQL emitted by
the client. Ensure the expectations line up with the behavior in
SupabaseClient's statement_timeout handling.

Comment thread backend-ai/tests/eval/baseline_result.json Outdated
Comment thread backend-ai/tests/eval/run_baseline.py Outdated
Comment thread backend-ai/tests/test_agent_chat.py Outdated
Comment thread backend-ai/tests/test_supervisor.py Outdated
Comment thread phases/supervisor-pattern/phase2-supervisor-node.md Outdated
Comment thread phases/supervisor-pattern/phase3-builder-refactor.md Outdated
Comment thread phases/supervisor-pattern/phase4-api-and-answer.md
crolvlee and others added 7 commits June 24, 2026 17:57
workers_called가 비어있는 첫 번째 supervisor 호출에서 LLM 장애가 발생하면
FINISH를 반환해 빈 응답으로 generate_answer에 도달하는 문제를 수정.
첫 호출 실패 시 PROPERTY_SEARCH로 라우팅해 최소한의 응답을 보장한다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
첫 번째 매칭 워커에서 return해 나머지 워커 결과가 버려지는 문제 수정.
parts 리스트로 전체 워커 결과를 수집한 뒤 합쳐서 반환하도록 변경.
PRICE_ANALYSIS + SAFETY_ANALYSIS는 analysis_cards를 공유하므로
_generate_live_analysis_answer 한 번 호출로 처리.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
supervisor 전환으로 삭제된 RouteDecision, Intent 임포트 제거.
_classify_with_usage를 raw JSON 파싱 방식으로 교체해 ImportError 해소.
prompt/completion 토큰 평균을 각자 non-zero 건수로 나누도록 수정.
baseline_result.json의 eval_set 불일치(21/17 → 22/16) 및 case[21] 수정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
자기 자신을 mock하던 decide_next_worker 테스트 3개를 http_post 주입 방식으로
교체해 JSON 파싱·valid 체크·중복 워커 guard 실제 동작을 검증.
첫 호출 실패 → PROPERTY_SEARCH, 이후 실패 → FINISH 동작도 테스트 추가.
test_agent_chat.py 단일 워커 assertion을 in 포함 체크에서 == 정확 일치로 변경해
over-routing 감지 가능하도록 수정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
phase2: valid set에 GENERAL_CHAT 추가, 첫 호출 실패 시 PROPERTY_SEARCH 폴백 반영.
phase3: 라우팅 매핑 및 루프백 엣지에 general_chat 추가.
phase4: generate_answer 스니펫에 GENERAL_CHAT 브랜치 및 복합 의도 parts 수집 방식 반영.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
첫 호출 실패 시 FINISH가 아닌 PROPERTY_SEARCH를 반환하는
실제 구현에 맞게 체크리스트 항목 수정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@crolvlee
crolvlee merged commit b0666a8 into develop Jun 24, 2026
3 checks passed
@HOKAGO-MEMORIES
HOKAGO-MEMORIES deleted the feat/51-supervisor-pattern branch June 25, 2026 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT][F-2][F-3][F-4] supervisor 패턴으로 의도 분류 라우팅 전환

1 participant