Commit Graph

7 Commits

Author SHA1 Message Date
f500210cd8 [fix] solution/backend: 카톡 5초 벽을 콜백으로 넘는다
실사용 첫날 "시설 편의에서 바비큐 이용 문구 빼줘" 가 타임아웃으로 끝났다.

★ 작은 표본으로 잰 수치를 상한 근거로 삼은 것이 틀렸다. 개발 중 잰 1.3~2.4초는
업종 필드 두 개짜리 장난감 프롬프트였고, 진짜 요청에는 필드 43개 + fact 수십 개가
실린다. "여유가 있다" 고 적어 둔 판단이 하루 만에 깨졌다.

- userRequest.callbackUrl 이 오면 {"useCallback": true} 로 즉답하고 백그라운드에서
  답을 만든 뒤 그 주소로 POST. 콜백 주소는 1분·1회라 재시도하지 않는다 —
  두 번째 POST 는 거절되고 사장님에게는 이미 "확인하고 있어요" 가 가 있다
- 콜백이 꺼져 있으면 예전처럼 동기, 상한만 4.0 → 4.5 (카카오가 5초에 끊는다)

★ 오픈빌더 스킬 설정에서 '콜백 사용' 을 켜야 열린다. 안 켜면 callbackUrl 이 안 와서
조용히 예전 경로로만 돈다.

test_kakao_webhook.py 24 passed(콜백 3건 추가)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 16:33:16 +09:00
1300652b09 [feat] solution/backend: 카톡 대화에 홈페이지 목록·가게 고르기
실제로 붙여 보니 빠진 것이 드러났다 — 연결은 됐는데 **어느 홈페이지를 다루는
대화인지** 말해 주지 않았다. 가게가 하나면 말없이 자동 선택돼 더 모호했다.

- 연결 직후 목록을 보여준다. 하나면 이름+발행 여부를, 여럿이면 바로가기 버튼으로
- 목록 줄에 발행 여부를 적는다 — 안 그러면 고친 것이 손님에게 보이는 줄 안다
- "목록"·"가게 바꿔줘" 로 언제든 돌아와 바꾼다. ★ 이 경로는 LLM 을 부르지 않는다:
  대화가 막혔을 때 처음 찾는 길이라 늘 통해야 하고, 목록에 돈을 쓸 이유가 없다
- 사업장 목록이 아니라 list_my_sites 를 쓴다 — 사장님이 알아야 하는 건
  "가게가 있다" 가 아니라 "발행돼 있나" 다(/sites 화면이 같은 이유로 그걸 쓴다)

test_kakao_webhook.py 21 passed(목록·전환 4건 추가).
전체 845 passed / 53 failed — 53 은 이번 변경 전과 동일

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 16:26:40 +09:00
d32df10cf7 [feat] solution/backend: 카카오 채널 웹훅 — 에이전트 4단계
런타임은 한 줄도 안 바뀌었다. 채널을 모르게 만들어 둔 것이 여기서 값을 했다 —
새로 생긴 것은 형식 변환(kakao_bot)과 대화 상태(channel)뿐이다.

★★ 오픈빌더는 서명을 주지 않는다. URL 만 알면 누구나 때릴 수 있고
userRequest.user.id 를 위조하면 그 사장님 행세를 한다 — 1단계의 신원 연결이
통째로 무의미해지는 자리다. 공유 시크릿(헤더 X-Agent-Secret, compare_digest)
+ 선택적 KAKAO_BOT_ID 대조로 막고, 시크릿이 없으면 엔드포인트가 404 다
(401 은 "여기 뭔가 있다" 를 알려 준다).

- router/v1/agent/kakao_bot: 카카오 형식을 아는 유일한 파일. 헤더·경로 두 경로
- services/agent/channel: 신원(★ 토큰을 발급하지 않는다) · 가게 고르기 · 확인
- 0022: owner_kakao_links 에 current_place_id · pending_*

빌더 화면과 다른 것 셋:
- 로그인 토큰이 없다 → 발화자 키로 사장님을 찾는다
- place_id 가 URL 에 없다 → 여럿이면 추측하지 않고 되묻는다. 임의로 첫 가게를
  고르면 사장님은 엉뚱한 가게를 고쳐 놓고도 모른다
- 확인을 되돌려 줄 프론트가 없다 → 서버가 pending 을 든다. ★ 3분 만료가 없으면
  한참 뒤의 "네" 한 마디에 묵은 발행이 돈다

5초 벽은 DEADLINE_SEC=4.0 으로 끊고, 어떤 실패도 200+안내다 —
메신저에서는 500 도 침묵으로 보인다.

밟은 것: execute_lambda 는 람다 반환값을 그대로 준다(CRUD 관례가 (ErrorType,값)).
우리 람다가 객체만 돌려주자 언패킹 TypeError 가 났고, 라우터가 예외를 삼켜
화면에는 안내 한 줄만 보였다 — 원인이 안 보이는 종류다.

test_kakao_webhook.py 17 passed. 전체 841 passed / 53 failed(이전과 동일).
npm run lint 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 15:13:33 +09:00
5b54daac10 [chore] solution/backend: 에이전트 대화창 다시 염 — AGENT_CHAT_ENABLED 기본 1
카카오톡 채널의 통신사 인증이 끝나 어제 걸어 둔 보류를 푼다. 코드는 어제도 오늘도
그대로고 값만 바꿨다 — 닫고 여는 일이 커밋을 되짚는 일이 되면 안 된다는 어제
판단이 하루 만에 값을 쳤다.

- config/agent_config: 기본값 0 → 1
- ★ 켜도 LLM 키가 없으면 안 열린다(runtime.is_configured 가 스위치와 키를 둘 다
  본다). 키 없는 환경에서 켜 둔 채 잊어도 "눌러도 안 되는 입구" 가 안 생긴다
- 스위치 테스트를 새 기본값에 맞춰 갱신 — 키가 없을 때도 안 열리는 것을 함께 검사

카카오 연결 카드는 아직 감춰져 있다(KAKAO_CHANNEL_PUBLIC_ID 미설정). 채우면 코드는
발급되지만 소비할 웹훅(4단계)이 없어 연결이 완성되지 않는다.

test_agent_runtime·test_kakao_link 34 passed. npm run lint 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 14:17:48 +09:00
a553e41197 [chore] solution/backend,frontend: 에이전트 화면 보류 — 설정으로 닫고 코드는 남긴다
카카오톡 채널 개설이 법인폰 본인인증에 걸려 보류됐다. 채널이 없으면 대화창은
사장님에게 **어디에도 닿지 않는 입구**이고, 열려 있으면 "되는 기능" 으로 오해한다.

- config/agent_config: AGENT_CHAT_ENABLED 신설(기본 0)
- runtime.is_configured(): 스위치와 LLM 키를 둘 다 본다 — 화면을 우회해 API 를
  직접 불러도 AGENT_NOT_CONFIGURED 다
- AgentChatDock · KakaoChannelCard: 조건 미충족이면 통째로 감춘다(return null).
  연결 카드는 connection_enabled 가 기준이라 설정만 채우면 그대로 다시 나타난다
- ★ 코드를 지우지 않았다 — 되돌릴 때 커밋을 되짚지 않고 값 둘만 채우면 된다

★ Threads 카드와 판단이 갈린 것이 맞다. 저쪽은 사장님이 곧 쓸 수 있는 기능이라
자리를 두고 버튼만 죽였고, 이쪽은 언제 열릴지 말해 줄 수 없다.

test_agent_runtime(스위치 2건 추가)·test_kakao_link 34 passed. npm run lint 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 11:15:03 +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
16b17bc91c [feat] solution/backend,frontend: 카카오톡 채널 신원 연결 — 에이전트 1단계
카카오 채널이 주는 발화자 식별자는 **채널 단위 익명 키**라 우리 user_id 와 관계가 없다.
다른 엔드포인트는 전부 place_crud.get_place(s, owner_user_id, place_id) 로 소유자 범위를
지키는데 채널 발화에는 그 owner_user_id 를 줄 근거가 없다 — 매핑이 없으면 채널
진입점만 소유자 범위 밖에 놓이고, 채널에 말을 건 아무나가 남의 가게를 고친다.

- postgres-init: owner_kakao_links(0021 + init.sql). 부분 유니크 셋 중
  uq_kakao_link_channel_key(한 카카오 계정 = 한 사장님)가 없으면 "어느 가게
  이야기냐" 가 대화가 아니라 DB 에서 갈라진다
- services/kakao_link_service: 일회성은 코드 값이 아니라 WHERE status='PENDING'
  CAS 한 문장이 보장한다. 실패는 전부 같은 에러 — 없는 코드·만료·시도초과를
  구분해 답하면 6자리의 유효성을 밖에서 탐색할 수 있다
- 코드는 sha256 만 저장. 손으로 치는 짧은 값이라 평문이면 DB 를 읽는 쪽이 곧
  연결 권한이다. 글자에서 0·O·1·I·L 제외 — 잘못 읽으면 원인이 화면에 안 보인다
- router/v1/agent/kakao: 셋 다 no-store·no-referrer·noindex.
  ★ 소비(redeem) 엔드포인트는 일부러 없다 — 웹훅 서명 검증 전에 공개 소비 경로를
  열면 누구나 6자리를 대입해 남의 계정에 자기 카톡을 붙인다
- config/agent_config: social_config 와 일부러 가름. SNS 게재는 되돌릴 수 없는
  대외 발화, 에이전트는 자기 사이트를 고치는 창구 — 승인 강도가 다르다
- frontend/features/agent: /sites 의 Threads 카드 옆. 연결은 사람 단위라 같은 자리다
- docs/AGENT.md 신설, CLAUDE.md 색인·함정, DEVLOG

test_kakao_link.py 15 passed. 전체 780 passed / 50 failed —
그 50건은 HEAD 에서도 동일(워크트리 대조), 기존 이슈로 이번 변경과 무관.
npm run lint 통과

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