Commit Graph

9 Commits

Author SHA1 Message Date
민헌
36b818eda3 [feat] solution/backend: 대화로 섹션 옮기기·숨기기 확장 — 앞·한 칸·N번째·자리 바꾸기 · 여러 개 끄기 · 꺼진 목록
"맨 위 · 맨 아래 · X 다음으로" 만 되고 "X 앞으로" "한 칸 위로" "세 번째로" "자리 바꿔줘" 는
거절됐다. 발행본(HomePage.tsx)은 히어로를 늘 맨 위, SNS 를 늘 맨 아래에 그리는데 대화는
그 둘을 옮기고 "옮겼습니다" 라고 답했다 — 화면은 그대로였다.

- tools: move_section 에 where(맨 위·맨 아래·앞·뒤·위로·아래로·번째·바꾸기)·count —
  where 가 비면 예전 to 표기로 읽는다. 한 칸·N번째는 보이는 순서로 센다(꺼진 부분과 자리만
  바꾸는 헛이동 방지). 없는 순번·꺼진 부분의 칸 이동은 거절, 이미 그 자리면 Unchanged
- tools: PINNED(히어로 맨 위 · SNS 맨 아래) — 옮기기와 그 둘을 기준으로 한 앞·뒤·바꾸기를 막는다.
  히어로 다음은 맨 위, SNS 앞은 맨 아래로 읽는다
- tools: toggle_section 이 쉼표로 여럿을 받는다 — 하나라도 못 찾거나 잠겼으면 아무것도 안 바꾼다
- tools: list_sections 는 보이는 순서에 번호(= N번째 기준)를 붙이고 꺼진 것을 모은다, only=꺼진
- prompts/runtime: 스키마에 where·count·only, 섹션 줄에 [항상 맨 위]·[항상 맨 아래] —
  목록은 tools.PINNED 하나를 runtime 이 넘긴다(prompts 는 services 를 import 하지 않는다)
- docs/AGENT.md: 옮기기·숨기기 표와 근거. 동작하지 않던 예시("후기 빼줘") 교체 —
  이용 후기는 섹션 목록에 없고 발행본이 늘 그린다

테스트 21건 추가, 에이전트·카카오 174 passed. pyflakes 새 경고 없음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:49:20 +09:00
민헌
aaf97f2797 [fix] solution/backend: 대화 수정의 값 형식 검증 · 여러 요청의 못 한 것·남은 것 안내 — 조용히 틀리던 경로 정리
말로 고친 값이 형식 검증 없이 저장됐다 — "반려동물 이제 돼요" 가 "가능" 으로 들어가면
화면엔 "가능" 이 뜨는데 factBool 은 'true' 만 참으로 읽어 구조화 데이터가 거짓이 된다.
여러 요청을 한 번에 받을 때도 할 수 없는 것·멈춘 뒤의 것이 말없이 사라져 사장님은
전부 된 줄 알았다(message 는 actions 가 있으면 버려졌다).

- tools: set_fact 값을 스키마 형식으로 맞춘다(bool true/false · time HH:MM · number 숫자) —
  "3시" 처럼 오전·오후를 모르면 저장하지 않고 되묻는다. 알림 문구는 가능·불가로 말한다
- tools: enabled 를 모르면 끄지 않고 되묻는다 — "" 를 끄기로 읽어 "다시 보여줘" 가 섹션을 껐다
- tools: 대표 지정·사진 목록·프롬프트를 '나가는 사진' 기준으로 — 내린 사진을 대표로 지정하고
  "바꿨습니다" 라고 하던 것. 정확히 맞는 이름을 부분 일치보다 먼저 고른다
- tools: Unchanged 표시 — "이미 켜져 있어요" 에 재발행 안내·카톡 발행 대기가 붙던 것
- tools/runtime: 인자가 문자열·dict 가 아니어도 죽지 않는다(모델의 스키마 위반)
- runtime: 같은 대상은 마지막 하나로 합친다(Tool.target, 상한 세기 전). 멈춘 뒤 남은 요청과
  지어낸 도구를 코드가 만든 이름으로 알린다. 발행 확인 문구는 맨 끝에 선다
- prompts: 항목마다 형식을 싣고 skipped(할 수 없는 요청의 이름) 칸 추가 — 문장은 런타임이 만든다.
  다른 가게 이야기면 되묻는 규칙
- channel: 발화에 다른 내 가게 이름이 나오면 모델을 부르기 전에 고르게 한다. 기억한 가게가
  목록에 없으면 비우고 목록으로. 확인 경로의 AgentError 를 사장님 말로 옮긴다
- docs/AGENT.md: 값 형식 · 못 한 것·남은 것·겹친 것 · 나가는 사진 기준

테스트 48건 추가, 에이전트·카카오 153 passed. 전체 932 passed / 47 failed —
실패 47건은 변경 전과 동일(gemini·openai 키 미설정, search_console KeyError, weather_notes)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:11:00 +09:00
c4aa1794e2 [feat] solution/backend: 한 발화에 여러 가지 — 응답을 actions 배열로
사장님은 "체크인 3시로 바꾸고 후기도 빼줘" 처럼 한 번에 시킨다.

- prompts/agent: 응답 스키마를 actions 배열로
- runtime: 시킨 순서대로 실행. MAX_ACTIONS=5 — 무한정이면 "다 지워줘" 한 마디에
  연쇄 실행된다
- ★ publish(SEMI)가 섞이면 그 앞까지만 하고 확인을 받는다. 확인이 필요한 행위를
  다른 일에 묻어 실행하면 확인의 의미가 없다
- ★ 중간에 실패해도 앞의 것을 되돌리지 않는다(사장님 결정). 되돌리는 것도 시키지
  않은 변경이다 — 대신 무엇이 됐고 무엇이 안 됐는지 그대로 말한다
- Tool.republish 플래그로 재발행 안내를 런타임이 한 번만 붙인다. 도구 문장에
  박아 두면 셋을 고쳤을 때 같은 말이 세 번 나왔다

실모델 4/4 정확히 쪼갬(1.8~2.8초).
test_agent_runtime·test_kakao_webhook 63 passed.
전체 878 passed / 53 failed — 53 은 기존과 동일. npm run lint 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 17:00:30 +09:00
47bde7388e [fix] solution/backend: 새 도구의 인자가 모델에 닿지 않던 것 — 응답 스키마 칸 부족
실배포 확인에서 잡았다(킹서버, 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) <noreply@anthropic.com>
2026-09-28 15:33:55 +09:00
af6e3799eb [feat] solution/backend: 에이전트가 사진을 내리고 대표를 지정한다
사진 쪽은 MediaService 에 list_media 하나뿐이었다 — 쓰기 경로가 아예 없었고
빌더 화면에서도 보기만 됐다. 서비스·라우터부터 열고 도구를 붙였다.

- crud: set_sort_order / service: hide_media · set_primary
- POST .../media/{id}/hide · /primary — ★ 에이전트 전용 뒷문을 만들지 않는다.
  그러면 빌더 화면이 그 기능을 못 쓰고 나중에 붙일 때 로직이 두 벌이 된다
- 도구 셋: list_photos(READ) · hide_photo · set_primary_photo(REVERSIBLE)

★ 대표 사진에 별도 칸을 두지 않았다. primary_media 가 '첫 장' 을 쓰고 목록이
ORDER BY sort_order 라, 지정은 sort_order 를 가장 작게 내리는 일이다 —
칸을 따로 두면 규칙이 둘이 되어 검색 결과의 그림과 화면 첫 장이 갈린다.

★ 내려도 지우지 않는다(REJECTED). origin_url·source_type 이 남아야 재게시
권리(DECISIONS 1-2) 결론이 났을 때 되짚을 수 있다.

★★ 업로드·교체는 만들지 않았다 — 미결 사항을 코드가 먼저 푸는 자리다.
테스트가 레지스트리에 upload·replace 가 없는지 실제로 검사한다.

test_agent_runtime 32 passed(사진 6건 추가).
전체 871 passed / 53 failed — 53 은 이번 변경 전과 동일. npm run lint 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 14:16:08 +09:00
ea9ae1ac2a [feat] solution/backend: 에이전트가 페이지 구성을 바꾼다 — 섹션 on/off·순서
"문구 변경밖에 안 된다" 는 지적에서 시작했다. 페이지 구성은 sites.theme.sections
배열 하나이고 배열 순서가 곧 발행본의 순서라, 그 JSON 을 만지는 도구 셋을 붙였다.

- list_sections(READ) · toggle_section(REVERSIBLE) · move_section(REVERSIBLE)
- 목록은 site_payload._sections 를 그대로 쓴다 — 발행본이 쓰는 그 함수다.
  표를 따로 만들면 에디터·발행본·대화 셋이 갈라지고 "껐는데 나온다" 가 된다
- 잠긴 섹션은 못 끈다. _sections 가 어차피 켜서 내보내므로 끌 수 있게 두면
  화면만 거짓말한다
- 이름이 둘 이상 걸리면 고르지 않는다 — 추측으로 고르면 발행하고 나서야 안다
- sections 만 갈아끼운다 — theme 을 통째로 쓰면 고른 색·서체가 말없이 사라진다

★ 템플릿·색은 넣지 않았다. templatesFor() 가 색·look·기본 섹션·배리에이션을 함께
계산해서, 백엔드가 template_id 만 바꾸면 "레이아웃은 새것, 색은 옛것" 이 된다 —
레지스트리를 공유 단일 출처로 옮기는 작업이 먼저다(docs/AGENT.md).

test_agent_runtime 26 passed(구성 7건 추가).
전체 864 passed / 53 failed — 53 은 이번 변경 전과 동일. npm run lint 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 11:50:38 +09:00
bbcdf78c62 [fix] solution/backend: 카톡 응답이 5초 벽에 계속 걸리던 것 — 프롬프트 축소
실사용에서 모든 발화가 4.5초 상한에 걸려 "확인하는 데 시간이 조금 걸리네요" 만
반복됐다(킹서버 로그: 4.60s · 4.51s · 4.51s).

- 이미 연결된 사람이 코드를 또 보내면 LLM 을 부르지 않는다. 실제로 그랬고,
  6자리가 그냥 발화로 넘어가 유료 호출 + 대기만 쌓였다
- 사이트 상태를 프롬프트에서 뺀다. 그 한 줄 때문에 매 턴 사이트 조회 + 슬러그
  계산이 돌았고, 정작 필요할 때는 get_site_status 도구를 부르면 된다
- fact 는 key:value 만, 상한 30개. label 은 항목 목록에 이미 있어 두 번 보내면
  프롬프트만 커지고 모델이 얻는 것이 없다
- 항목 목록도 JSON 대신 `key: 이름` 줄로

★ 근본 해결은 콜백이다(f500210). 오픈빌더에서 '콜백 사용' 이 꺼져 있으면
callbackUrl 이 안 와서 조용히 동기 경로로만 돈다 — 지금 로그가 그 상태다.

test_kakao_webhook·test_agent_runtime 43 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 16:52:24 +09:00
59df616a7b [fix] solution/backend: 응답 스키마 대문자 타입 — openai 에서만 400 으로 터지던 것
OpenAI strict 모드는 'STRING' 을 거부한다:
  Invalid schema for response_format: 'STRING' is not valid under any of
  the given schemas
Gemini 는 대소문자를 둘 다 받아서, 대문자로 써 두면 **공급자를 openai 로 바꾸는
순간에만** 터진다. LLM_PROVIDER 기본값이 openai 인데 두 파일만 대문자로 남아
있었다 — 대화창은 첫 발화부터 502 였고 SNS 초안도 같은 이유로 못 돌았다.

- prompts/agent: 소문자로. args 는 strict 가 전 프로퍼티를 required 로 만드므로
  안 쓰는 인자가 빈 문자열로 온다 — 도구는 "" 를 '없음' 으로 읽는다
- prompts/social: 같은 수정. 나머지 프롬프트는 원래 소문자였다

실측 확인: "체크인 시간 3시로 바꿔줘" → set_fact(check_in_time, 15:00)
test_agent_runtime·test_social 34 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 17:29:29 +09:00
b1a34ba58d [feat] solution/backend,frontend: 에이전트 도구·런타임·빌더 채팅창 — 2단계
런타임이 채널을 모르므로 채널·챗봇 심사 없이 에이전트 전체를 빌더 화면에서
검증할 수 있다. 웹훅 핸들러 안에 짜면 빌더에서 같은 걸 못 쓰고, 심사가 끝나야
무엇 하나 확인되지 않는다 — 카톡은 나중에 붙는 두 번째 입구다.

- services/agent/tools.py: 도구 넷 + 등급 셋(READ·REVERSIBLE·SEMI).
  ★ 도구는 반드시 services/* 를 통과한다 — crud 를 직접 부르면 스키마 검증·
  출처 필수·정정본 보호가 아무 증상 없이 사라진다. 테스트가 소스로 검사한다
- services/agent/runtime.py: 발화 → 도구 선택(LLM 1콜) → 실행 → 응답
- services/prompts/agent.py: LLM 네 겹 규약대로 프롬프트만 여기
- router/v1/agent/chat.py + features/agent/AgentChatDock.tsx(/sites 우하단)

모델에게 맡기지 않은 셋:
- 등급 — 응답 스키마에 칸 자체가 없다. 모델이 정하면 프롬프트에 끼어든 한 줄이
  확인 절차를 건너뛴다
- 결과 문구 — 도구가 만든다. 모델이 쓰면 하지 않은 일을 했다고 말할 수 있고
  사장님에게는 그 말이 사실로 보인다
- key — set_fact 의 key 는 업종 스키마가 최종 판정이다

확인(SEMI)은 실행하지 않고 되묻는다. 돌아온 confirm 값을 믿지 않고 도구는
레지스트리에서 다시 찾고 인자는 도구가 다시 검증한다 — 확인 절차가 검증을
건너뛰는 구멍이 되면 안 된다.

값을 고치면 재발행 안내를 함께 낸다 — fact 는 바뀌어도 사이트는 안 바뀐다.

test_agent_runtime.py 17 passed(LLM 은 monkeypatch, 실제 모델 호출 없음).
전체 796 passed / 50 failed — 그 50건은 HEAD 에서도 동일한 기존 이슈.
npm run lint 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 16:18:00 +09:00