Files
vpd-permission-poc/docs/design/hmm-html-report-mcp/architecture.md
2026-08-10 18:02:49 +09:00

132 lines
7.3 KiB
Markdown

# 아키텍처와 입력 계약
## 책임 경계
| 구성요소 | 책임 | DB 접근 |
|---|---|---|
| `search_carrier_performance` | 기존 `HMM_CARRIER_FEDERATION_SEARCH`를 통한 Bearer/VPD 기반 Select AI Federation 조회 | 허용 |
| AI 웹 콘솔 | 도구 계약을 판별해 조회 결과를 정규화하고 렌더러를 후속 호출 | 없음 |
| `render_hmm_carrier_report` | DB CLOB 템플릿에 허용된 JSON을 매핑 | 템플릿만 읽음 |
| 브라우저 | 반환 HTML 표시·다운로드 | 없음 |
```text
HMM portal ──► /mcp (Bearer) ──► Backoffice MCP facade ──► DB Agent Tool
VPD applied rows
Console
report MCP tool call
HMM_REPORT_TEMPLATES
HTML 응답
```
리포트 MCP도 같은 Bearer 인증을 통과해야 하지만, 권한 판단은 조회 MCP에서 끝난다. 리포트
입력의 임의 사용자 ID를 신뢰하거나 DB를 재조회하지 않는다.
## 포털의 범용 순차 호출 규칙
포털은 `render_hmm_carrier_report`라는 이름을 조건문에 넣지 않는다. 발견한 MCP 도구가 아래
계약을 동시에 보이면 **이전 결과 입력형 렌더러**로 분류한다.
- 입력 스키마에 `reportJson`·`report_payload`처럼 리포트 JSON/payload를 받는 문자열이 있다.
- 설명에 HTML/리포트/렌더링 의도와 `조회 결과`, `후속 처리`, `already-authorized`처럼 선행 결과를
사용한다는 의도가 있다.
사용자가 리포트·보고서·대시보드·차트·HTML을 요청하고 이 렌더러가 발견되면, 기존 LLM 라우터는
렌더러를 제외한 데이터 도구 중 하나를 먼저 선택한다. 첫 응답의 `items`, `results`, `rows` 또는
`response.result` 안의 JSON 배열 문자열을 같은 방식으로 구조화하고, camelCase JSON으로 정규화해
두 번째 도구에 전달한다. 포털은 렌더러의 `html` 응답을 iframe으로 표시한다. 이 규칙은 특정
Tool 이름, 사용자 코드나 예상 행 수를 조건으로 사용하지 않는다.
## 변경 금지 경계
- `search_carrier_performance`의 DB target은 기존 `HMM_CARRIER_FEDERATION_SEARCH`다.
- 별도 호환성 시험 서버 `hmm-mcp.cloud-handson.com`은 이 운영 경로를 대신하지 않는다.
- HTML 추가를 이유로 조회 Agent Tool, Select AI profile, VPD 정책 또는 조회 package를 생성·교체하지 않는다.
- renderer는 선행 결과만 표현하며 누락 행을 조회하거나 고정 샘플로 채우지 않는다.
## `reportJson` 계약
최상위에는 `report` 객체와 `rows` 배열만 허용한다. `report`에는 `id`, `category`, `title`,
`generatedAt`, `requestedBy`, `question`, `answer`, `execution`, `evidence`, `limitation`
넣는다. 행에는 담당자·선사 식별자와 최신 KPI만 넣는다.
`report.title`은 사용자 질문 원문이 아니다. 포털이 모델에 다음 제목 계약을 지시해 만든 짧은
업무 제목이다.
- `HTML로 보여줘`, `리포트로 만들어줘`와 같은 출력 형식·행동 문구는 제거한다.
- 사용자·팀·업무 대상처럼 범위를 구분하는 식별자는 유지한다.
- 문장형 답변이 아니라 화면 머리글에 맞는 명사형 제목으로 만든다.
- 템플릿이 붙이는 고정 부제와 같은 문구를 반복하지 않는다.
제목 생성이 실패하면 전체 질문을 제목으로 사용하지 않고 짧은 일반 업무 제목으로 안전하게
대체한다. 이 규칙은 특정 사용자 코드나 조회 행 수를 조건으로 사용하지 않는다.
### 현재 선택 사용자와 대화 문맥
포털의 데모 사용자 선택값은 Bearer token 선택뿐 아니라 질문 해석과 제목 범위의 기준이다.
`내`, `나`, `우리` 같은 1인칭 표현은 현재 선택 사용자 ID를 기준으로 독립 질문으로 바꾼다.
대화 이력은 같은 `selected_user_id`로 저장된 turn만 불러오며, 제목 생성 모델에도 현재 선택 사용자
ID를 별도 입력으로 전달한다. 이전 사용자의 질문에 명시된 팀장·팀 범위가 현재 사용자의 제목으로
전파되어서는 안 된다.
```text
현재 사용자 E1001 + "E1001 팀장의 팀원별 ..." → E1001 팀 범위 제목 → VPD 결과 8건
현재 사용자 E1002 + "내 담당 선사 ..." → E1002 개인 범위 제목 → VPD 결과 2건
```
제목 문자열이나 예상 행 수를 사용자별로 하드코딩하지 않는다. 사용자 ID, 독립 질문, 실제 선행 조회
결과를 모델 입력으로 제공하고 공통 제목 지침으로 생성한다. `report.requestedBy`는 질문에서 추측하지
않고 현재 선택 사용자 ID를 사용한다.
## 포털 표시 계약
renderer 응답에 유효한 `html` 또는 `rendered_html`이 있으면 HTML artifact가 최종 표현물이다.
포털의 일반 답변 생성 지침은 HTML 태그, Markdown 표, 업무 행 전체를 다시 만들지 않고 제목과
조회 건수, 아래 리포트 확인 안내만 반환한다. 포털은 같은 조건을 출력 후에도 검사해 모델이
HTML을 반환하더라도 안전한 짧은 안내문으로 정규화한다.
## 조회 질문과 표현 요청 분리
사용자의 한 문장에는 데이터 요구와 표현 요구가 함께 있을 수 있다. 포털은 Agent instruction으로
두 의도를 분리한다.
```text
원문: E1001 팀 선사 KPI를 HTML로 보여줘
조회 단계: E1001 팀 선사 KPI를 보여줘
표현 단계: 조회된 구조화 행을 HTML 리포트로 렌더링
```
조회 단계 재작성은 직원·팀·기간·지표·필터를 모두 유지하고 `HTML`, `리포트`, `차트`, `대시보드`
같은 출력 형식과 생성 행동만 제거한다. 원문은 `report.question`에 보존한다. Select AI가 만든
`htmlRow`, HTML 태그 또는 Markdown 표는 구조화 업무 행으로 간주하지 않으며 renderer 입력으로
전달하지 않는다.
서버는 JSON 크기, 행 수, 문자열 길이, 숫자 형식을 제한하고, 템플릿에 주입할 JSON에서
`</script>`를 이스케이프한다. payload는 저장하지 않는다.
## 배포 도구 계약
`BACKOFFICE_MCP_TOOLS`에 다음과 같이 등록한다. 실제 환경 변수에는 비밀값을 넣지 않는다.
```json
{
"name": "render_hmm_carrier_report",
"label": "HMM 선사 실적 HTML 리포트",
"description": "VPD 적용 선사 실적 조회 결과를 HMM HTML 리포트로 표현합니다.",
"argumentName": "reportJson",
"argumentDescription": "정규화된 선사 실적 리포트 JSON입니다.",
"executionType": "AGENT_TOOL",
"targetName": "HMM_CARRIER_REPORT_RENDERER",
"targetParameterName": "P_REPORT_JSON"
}
```
`targetName`은 DBMS_CLOUD_AI_AGENT custom tool `HMM_CARRIER_REPORT_RENDERER`다.
템플릿에는 `const reportData = __REPORT_DATA__;` 자리만 둔다. DB 함수는 호출 시 전달받은 JSON을
이 자리에 삽입하고, 조회 Tool을 호출하거나 기본 업무 행을 보충하지 않는다.