24장. 서비스 통합과 실행
왜 서비스로 만들어야 하는가 — 고객은 터미널을 쓰지 않는다
한 줄 요약
Supervisor와 전문 에이전트, 승인 게이트까지 구현했지만, 지금까지는 터미널에서만 시스템을 사용했습니다. 마지막 장에서는 고객용 채팅 화면과 상담원용 승인 화면을 서버에 연결해, 브라우저에서 상담과 승인 업무를 진행할 수 있도록 만듭니다. 이어서 표준 질문 12개로 전체 상담 흐름이 의도한 대로 동작하는지 확인합니다.
1. 지금 가진 것과 없는 것
| 가진 것 | 만든 곳 |
|---|---|
| 본인 확인과 마스킹이 들어간 도구 | 8장 haru_tools.py |
| 정책 문서 검색 저장소 | 13장 chroma_db/ |
| 배송지 변경·교환 접수·환불 승인 요청을 하는 처리 도구 | 21장 haru_actions.py |
| 전문 에이전트 셋 (주문조회, 정책안내, 요청처리) | 21장 haru_agents.py |
| 이들을 지휘하는 Supervisor | 22장 haru_supervisor.py |
| 사람의 승인을 거치는 흐름 | 23장 lesson23_hitl.py |
없는 것은 입구입니다. 그것도 둘입니다.
| 없는 입구 | 지금은 어떻게 하고 있나 |
|---|---|
| 고객이 문의하는 곳 | 파이썬 파일을 열어 질문을 고쳐 적고 python lesson22_supervisor.py를 실행한다 |
| 상담원이 승인하는 곳 | 23장에서는 코드가 Command(resume="approve")를 넣어 상담원 역할을 대신했다 |
2. 브라우저와 우리 프로그램은 따로 돈다
웹 서비스의 기본 구조부터 정리합니다.
| 이름 | 무엇인가 | 우리 프로젝트에서 |
|---|---|---|
| 프론트엔드(frontend) | 사용자가 보는 화면. 브라우저에서 돈다 | app/frontend/index.html 채팅 화면, app/frontend/admin.html 상담원 화면 (둘 다 제공됨) |
| 백엔드(backend) | 요청을 받아 처리하는 프로그램. 서버에서 돈다 | app/main.py (이번 장에서 만든다) |
| HTTP | 둘이 주고받는 약속된 형식 | "이 주소로 이 내용을 보낸다 → 응답을 받는다" |
| API | 백엔드가 열어 둔 주소들의 목록과 사용법 | /api/chat에 문의를 보내면 답이 온다. /api/approvals에서 승인 대기 목록을 받는다 |
브라우저와 우리 파이썬 프로그램은 서로 다른 프로그램입니다. 브라우저는 파이썬 함수를 직접 부를 수 없습니다. 그래서 파이썬 쪽에서 "이 주소로 요청이 오면 이 함수를 실행한다" 고 문을 열어 두어야 합니다. 그 문을 여는 프로그램이 서버입니다.
화면은 이 과정에서 만들지 않습니다. 화면을 만드는 것은 다른 분야의 일이라, 완성된 파일 둘을 app/frontend/ 폴더에 제공했습니다. 우리가 만드는 것은 그 두 화면이 말을 걸 상대입니다.
| 제공된 화면 | 누가 쓰나 | 하는 일 |
|---|---|---|
index.html |
고객 | 문의를 보내고, 위임 과정과 답변을 본다 |
admin.html |
상담원 | 승인 대기 요청을 승인·반려하고, 처리 내역과 넘겨받은 문의를 본다 |
3. 승인 게이트는 화면의 버튼이 된다
23장에서 사람의 결정은 코드 안의 "approve"라는 글자였습니다. 서비스에서는 그 자리에 상담원 화면의 승인 버튼이 들어갑니다.
| 23장 | 24장 | |
|---|---|---|
| 승인 요청을 등록하는 것 | 그래프의 검토 노드가 request_refund를 부른다 |
요청처리 에이전트가 request_refund를 부른다 |
| 사람이 보는 곳 | 터미널에 출력한 요청서 | 상담원 화면의 "승인 대기" 구역 |
| 사람의 결정이 들어오는 길 | Command(resume="approve") |
승인 버튼 → POST /api/approvals/{처리번호} |
| 결정을 기록하는 것 | decide() |
같은 decide() |
| 기다리는 동안 상태가 있는 곳 | 체크포인터 + actions.json |
actions.json (승인대기) |
요청을 등록하는 쪽과 결정을 기록하는 쪽이 같은 처리 기록 파일을 봅니다. 그래서 고객이 채팅으로 주문 취소를 요청하면 상담원 화면에 곧 한 줄이 뜨고, 상담원이 승인하면 고객이 다시 물었을 때 "승인이 끝났다"는 답이 나갑니다.
4. 서버로 만들면 새로 생기는 문제
터미널에서는 없던 문제가 세 가지 생깁니다. 이번 장의 개념 절 셋이 각각 하나씩 다룹니다.
| 문제 | 터미널에서는 | 서버에서는 | 다루는 곳 |
|---|---|---|---|
| 입력이 밖에서 온다 | 우리가 코드에 적은 질문 | 누가 무엇을 보낼지 모른다 | FastAPI와 입력 검증 |
| 여러 사람이 동시에 쓴다 | 대화 하나 | 브라우저마다 대화가 따로 있어야 한다 | 세션과 대화 이력 |
| 답이 늦게 온다 | [위임 →] 줄이 찍히니 기다릴 만하다 |
빈 화면 10초는 멈춘 서비스로 보인다 | SSE 스트리밍 |
세 번째를 조금 더 봅니다. 22장에서 복합 문의 하나는 전문 에이전트를 여러 번 거치느라 10초 가까이 걸렸습니다. 터미널에서는 [위임 → 주문조회] 줄이 하나씩 찍혀 "일하고 있구나"를 알 수 있었습니다. 화면에서도 지금 무엇을 하고 있는지를 보여 줘야 합니다.
5. 이걸 모르면 무엇을 판단하지 못하는가
- 화면에 답이 안 나올 때 화면 문제인지, 서버 문제인지, 에이전트 문제인지 가려내지 못합니다.
- 서버를 껐다 켜면 대화가 사라지는 이유, 새로고침하면 "그거"를 못 알아듣는 이유를 설명하지 못합니다.
- 고객 화면과 상담원 화면이 무엇을 함께 보고 있기에 서로의 일이 이어지는지 설명하지 못합니다.
- "완성했다"는 말의 기준이 없습니다. 몇 번 써 보고 잘 되는 것 같다는 것은 기준이 아닙니다.
마지막 항목이 이 장의 끝입니다. 1장에서 정한 표준 질문 12개가 완성의 기준입니다. 서버를 띄운 뒤 열두 개를 전부 넣어 보고, 기대한 동작과 실제 동작을 표로 맞춰 봅니다.
6. 이번 장에서 만드는 것
| 파일 | 위치 | 내용 |
|---|---|---|
main.py |
app 폴더 안 |
두 화면을 내려 주고, 문의를 받아 Supervisor를 실행하고, 상담원의 승인·반려를 받는 서버 |
만드는 파일은 이것 하나입니다. 화면 둘(index.html, admin.html)은 제공된 것을 그대로 씁니다.
지금까지 만든 파일은 모두
haru-market폴더 맨 위에 있습니다.main.py만app폴더 안에 만듭니다. 이 과정에서 유일한 예외입니다.
핵심 정리
- 시스템은 완성됐지만 고객이 문의할 입구와 상담원이 승인할 입구가 없습니다.
- 브라우저(프론트엔드)와 우리 프로그램(백엔드)은 따로 돌고, HTTP로 대화합니다.
- 23장의 승인 게이트는 웹에서 상담원 화면의 승인 버튼이 되고, 버튼은 같은
decide()를 부릅니다. - 서버로 만들면 밖에서 오는 입력, 여러 사람의 대화, 늦게 오는 답 세 가지를 다뤄야 합니다.
- 완성의 기준은 느낌이 아니라 표준 질문 12개입니다.
- 이번 장의 산출물은
app/main.py하나이고,app폴더 안에 만듭니다. 화면 둘은 제공됩니다.