From 47bde7388e69dd3e64422492a246e740325566b2 Mon Sep 17 00:00:00 2001 From: hbyang Date: Mon, 28 Sep 2026 15:33:55 +0900 Subject: [PATCH] =?UTF-8?q?[fix]=20solution/backend:=20=EC=83=88=20?= =?UTF-8?q?=EB=8F=84=EA=B5=AC=EC=9D=98=20=EC=9D=B8=EC=9E=90=EA=B0=80=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=EC=97=90=20=EB=8B=BF=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8D=98=20=EA=B2=83=20=E2=80=94=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=B9=B8=20=EB=B6=80=EC=A1=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 실배포 확인에서 잡았다(킹서버, 2026-09-28). 도구 선택은 6/6 정확했는데 인자가 엉뚱하게 왔다: move_section 기대 {name,to} → 실제 {key,value} hide_photo 기대 {name} → 실제 {key} set_primary_photo 기대 {name} → 실제 {key} 응답 스키마의 args 가 {key,value,keyword} 로 고정돼 있어 모델이 name·to·enabled 를 넣을 자리가 없었다. 이대로면 새 도구 다섯이 전부 "못 찾았어요" 로 끝난다. - prompts/agent: args 에 name·to·enabled 추가(strict 라 required 도 같이) - ★ test_도구가_선언한_인자는_응답_스키마에_있다 추가 — 다른 테스트는 _choose 를 monkeypatch 해서 이 층을 건너뛴다. 그래서 단위 테스트가 전부 초록인데도 실제로는 안 됐다. 소스로 대조해 같은 일이 다시 나지 않게 한다 test_agent_runtime 33 passed Co-Authored-By: Claude Opus 5 (1M context) --- solution/backend/services/prompts/agent.py | 10 +++++++++- solution/backend/tests/test_agent_runtime.py | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/solution/backend/services/prompts/agent.py b/solution/backend/services/prompts/agent.py index 5db2d56..364c349 100644 --- a/solution/backend/services/prompts/agent.py +++ b/solution/backend/services/prompts/agent.py @@ -20,14 +20,22 @@ RESPONSE_SCHEMA = { "tool": {"type": "string"}, # ★ strict 모드는 모든 프로퍼티를 required 로 만든다(llm/openai._to_strict_schema). # 그래서 안 쓰는 인자는 빈 문자열로 온다 — 도구는 "" 를 '없음' 으로 읽는다. + # ★ 여기 없는 이름은 모델이 채울 자리가 없다 — 도구가 아무리 선언해도 빈손으로 온다. + # 실측(2026-09-28, 킹서버): move_section 이 {name,to} 를 받는데 스키마에 그 칸이 없어 + # {key,value} 로 왔고, 도구는 "어느 부분인지 못 찾았어요" 로 끝났다. 도구 선택은 + # 6/6 정확했는데도 그랬다 — 단위 테스트는 _choose 를 대신해서 이 층을 건너뛴다. + # 그래서 `test_도구가_선언한_인자는_응답_스키마에_있다` 가 소스로 대조한다. "args": { "type": "object", "properties": { "key": {"type": "string"}, "value": {"type": "string"}, "keyword": {"type": "string"}, + "name": {"type": "string"}, + "to": {"type": "string"}, + "enabled": {"type": "string"}, }, - "required": ["key", "value", "keyword"], + "required": ["key", "value", "keyword", "name", "to", "enabled"], }, # 도구를 못 고른 경우에만 쓴다(되묻기·안내). "message": {"type": "string"}, diff --git a/solution/backend/tests/test_agent_runtime.py b/solution/backend/tests/test_agent_runtime.py index d9302ec..597fbac 100644 --- a/solution/backend/tests/test_agent_runtime.py +++ b/solution/backend/tests/test_agent_runtime.py @@ -463,3 +463,17 @@ async def test_업로드_도구는_만들지_않았다(): assert tools.REGISTRY["list_photos"].grade == ToolGrade.READ assert tools.REGISTRY["hide_photo"].grade == ToolGrade.REVERSIBLE assert tools.REGISTRY["set_primary_photo"].grade == ToolGrade.REVERSIBLE + + +def test_도구가_선언한_인자는_응답_스키마에_있다(): + """★ 스키마에 없는 이름은 모델이 채울 자리가 없다 — 도구가 선언해도 빈손으로 온다. + + 실측(2026-09-28, 킹서버): move_section 이 {name,to} 를 받는데 스키마에 그 칸이 없어 + {key,value} 로 왔고 도구는 "못 찾았어요" 로 끝났다. **도구 선택은 6/6 정확했는데도** 그랬다 — + 다른 테스트는 `_choose` 를 monkeypatch 해서 이 층을 통째로 건너뛰므로 아무도 못 잡는다.""" + from services.prompts import agent as prompt + + allowed = set(prompt.RESPONSE_SCHEMA["properties"]["args"]["properties"]) + for name, tool in tools.REGISTRY.items(): + missing = set(tool.args) - allowed + assert not missing, f"{name} 이 선언한 인자가 응답 스키마에 없다: {missing}"