Skip to content

통합 탐색·답변 온보딩 — 한 곳에서 켜는 체크리스트 ​

목적: "모든 포털 상단 검색이 같은 검색·같은 답을 준다"를 배포에 켜는 데 필요한 등록·env·확인을 한 곳에 모은다. 관련 문서: docs/delegation-token-contract.md(위임 발급 계약) · ExploreGateway/docs/adapter-onboarding.md(어댑터 계약 §3-2) · ModelGateway/docs/PURPOSE_REGISTRY.md(rag.answer 목적).

0. 역할 (누가 무엇을 소유하는가) ​

서비스역할지켜야 할 것
PortalConsole (원장)사용자·조직·등급, 포털 등록, 서비스/위임 토큰 발급소비자 목록과 탐색 카탈로그의 정본
ExploreGateway검색·답변 오케스트레이션(답을 만들지 않는다)카탈로그를 읽고, 각 포털 어댑터를 서비스 토큰으로 호출
RagService (rag-center)유일한 answer provider + 문서 색인게이트웨이 서비스 토큰 + 사용자 위임 토큰으로 호출됨, RLS 직접 강제
소비 포털 (PC·AC·DL·GC)자기 색인 검색 + 상단 검색 UI답변은 게이트웨이에 위임(createExploreGateway), 검색은 자기 색인
ModelGateway모델 접근 단일 창구RagService 가 x-purpose: rag.answer 로 경유

1. PortalConsole (원장) ​

1-1. 위임 소비자 등록 — PORTAL_CONSOLE_TOKEN_CONSUMERS_JSON ​

json
[
  { "clientId": "process-center", "clientSecret": "<PC 시크릿>", "audience": "ui-center-theme",
    "scopes": ["theme:catalog:read", "theme:policy:read", "theme:policy:write"],
    "delegationAudiences": ["rag-center", "explore-gateway", "directory-user"],
    "delegationScopes": ["rag:read", "rag:sync", "rag:admin", "search:read"] },
  { "clientId": "agent-center",    "clientSecret": "<AC 시크릿>", "audience": "ui-center-theme", "scopes": [],
    "delegationAudiences": ["explore-gateway"], "delegationScopes": ["search:read"] },
  { "clientId": "document-lib",    "clientSecret": "<DL 시크릿>", "audience": "ui-center-theme", "scopes": [],
    "delegationAudiences": ["explore-gateway"], "delegationScopes": ["search:read"] },
  { "clientId": "guide-center",    "clientSecret": "<GC 시크릿>", "audience": "ui-center-theme", "scopes": [],
    "delegationAudiences": ["explore-gateway"], "delegationScopes": ["search:read"] }
]
  • 각 clientSecret 은 그 포털 서버 env 와 같은 값이다: PROCESS_CENTER_SERVICE_CLIENT_SECRET · AGENT_CENTER_SERVICE_CLIENT_SECRET · DOCUMENT_LIBRARY_SERVICE_CLIENT_SECRET · GUIDE_CENTER_SERVICE_CLIENT_SECRET.
  • delegationAudiences·delegationScopes 밖의 요청은 거절된다 — 위임은 좁히기만 한다.
  • 개발 스택 값은 ProcessCenter/docker-compose.dev.yml 의 portal-console 서비스에 있다(4개 소비자 등록 포함).

1-2. 탐색 카탈로그 — PORTAL_EXPLORE_CATALOG_JSON ​

json
{
  "process-center":   { "capabilities": ["search"],            "endpoint": "/api/explore/v1/search", "timeoutMs": 1500 },
  "agent-center":     { "capabilities": ["search"],            "endpoint": "/api/explore/v1/search", "timeoutMs": 1500 },
  "document-library": { "capabilities": ["search"],            "endpoint": "/api/explore/v1/search", "timeoutMs": 1500 },
  "guide-center":     { "capabilities": ["search"],            "endpoint": "/api/explore/v1/search", "timeoutMs": 1500 },
  "rag-center":       { "capabilities": ["search", "answer"],  "endpoint": "/api/explore/v1/search", "answerEndpoint": "/api/explore/v1/ask", "timeoutMs": 8000 }
}
  • answer 능력은 rag-center 하나만 갖는다 — provider 가 둘이면 질문마다 답이 달라진다.
  • rag-center 의 search 어댑터는 문서 결과를 kind=document 로 돌려줘 상단 검색 결과에 합류한다.

1-3. 포털 등록 — portal_configurations ​

  • 소비 포털 4개 + rag-center + explore-gateway 를 등록한다. 각 포털의 baseUrl 은 게이트웨이가 실제로 호출하는 주소이므로 HTTPS(개발은 localhost 허용)로 열려 있어야 한다.
  • explore-gateway 는 UI 가 없으므로 applicationType=PLATFORM_SERVICE 로 등록한다(카탈로그 portals[] 에 나타나지 않게).
  • 게이트웨이가 컨트롤플레인을 읽으려면 directory:read scope 의 포털 API 키가 필요하다(아래 CONTROL_PLANE_API_KEY).

2. ExploreGateway (오케스트레이터) ​

env값
CONTROL_PLANE_ORIGINPortalConsole 오리진
CONTROL_PLANE_API_KEYdirectory:read 포털 API 키
PORTAL_CONSOLE_JWT_SECRETPortalConsole 과 같은 서명키 — 위임 토큰 검증
PORTAL_CONSOLE_JWT_AUDIENCEexplore-gateway (포털이 요청하는 audience 와 같아야 한다)
PORTAL_SERVICE_TOKENS_JSON아래
PORTAL_DEFAULT_TIMEOUT_MS기본 어댑터 타임아웃(선택)
json
{ "process-center": "<PC 어댑터 토큰>", "agent-center": "<AC 어댑터 토큰>",
  "document-library": "<DL 어댑터 토큰>", "guide-center": "<GC 어댑터 토큰>",
  "rag-center": "<RagService 어댑터 토큰>" }
  • 각 값은 그 포털의 EXPLORE_SERVICE_TOKEN / RagService 의 RAG_EXPLORE_GATEWAY_API_KEY 와 같은 값이다.
  • 위임 audience 교정: 게이트웨이가 받는 토큰은 포털이 재위임한 것이라 aud=explore-gateway 다(directory-user 가 아니다).

3. RagService (rag-center) ​

env값
RAG_EXPLORE_GATEWAY_API_KEY게이트웨이 PORTAL_SERVICE_TOKENS_JSON["rag-center"] 와 같은 값. 비우면 어댑터 경로가 503(정보 노출 방지)
PORTAL_CONSOLE_JWT_AUDIENCErag-center,explore-gateway — 게이트웨이가 재위임한 토큰도 받아야 한다
PORTAL_CONSOLE_JWT_SIGNING_KEY · PORTAL_CONSOLE_JWT_ISSUERPortalConsole 과 동일
MODEL_GATEWAY_URL · MODEL_GATEWAY_SERVICE_TOKEN설정하면 로컬 vLLM 대신 게이트웨이 경유(x-purpose: rag.answer). ModelGateway 에 rag.answer 모델 매핑 필요

ModelGateway rag.answer 매핑 등록(실측 절차) ​

매핑이 없으면 게이트웨이는 defaultModel 로 폴백한다 — 답변은 오지만 의도한 모델 고정이 아니고, 목적별 사용량·쿼터 집계가 rag.answer 로 잡히지 않는다. 저장된 설정(DB model_gateway_settings)이 env(MODEL_GATEWAY_PURPOSE_MODELS)보다 우선하므로, 이미 떠 있는 게이트웨이는 관리 API 로 저장한다.

bash
# 1) 매핑 저장 — PUT 은 실행 설정 '전체'를 교체한다. 기존 키(예: process.version-summary)를 함께 넣지 않으면 지워진다.
curl -X PUT -H "Authorization: Bearer $MODEL_GATEWAY_ADMIN_TOKEN" -H 'content-type: application/json' \
  -d '{"defaultModel":"deepseek-flash","allowedModels":["deepseek-flash","deepseek-v4-pro"],
       "purposeModels":{"rag.answer":"deepseek-flash","process.version-summary":"deepseek-flash"}}' \
  https://model-gateway.rlab.monster/admin/api/settings

# 2) 확인 — purposeModels 에 rag.answer 가 보여야 한다(게이트웨이는 서비스 토큰으로 응답).
curl -H "Authorization: Bearer $MODEL_GATEWAY_SERVICE_TOKEN" https://model-gateway.rlab.monster/v1/models

# 3) 게이트웨이 단독 점검(선택) — 200 과 모델 이름이 돌아오면 업스트림(DeepSeek)까지 연결된 것이다.
curl -H "Authorization: Bearer $MODEL_GATEWAY_SERVICE_TOKEN" -H 'content-type: application/json' \
  -H 'x-purpose: rag.answer' -H 'x-portal-id: rag-center' \
  -d '{"model":"auto","messages":[{"role":"user","content":"한 문장으로 인사하세요"}]}' \
  https://model-gateway.rlab.monster/v1/chat/completions

PortalConsole 화면(모델 게이트웨이 → 실행 설정 → purpose 매핑)에서도 같은 값을 관리할 수 있고, 제공자 키는 콘솔에서 암호화 저장한다(MODEL_GATEWAY_CONFIG_MASTER_KEY). 매핑·토큰이 준비되면 §5 의 /api/explore/v1/ask 점검이 근거 없음 폴백이 아닌 실제 답변 + citations 를 돌려준다.

4. 소비 포털 ​

포털필요한 env위임 주체
ProcessCenterEXPLORE_GATEWAY_URL, EXPLORE_GATEWAY_AUDIENCE, EXPLORE_GATEWAY_API_KEY(어댑터), PROCESS_CENTER_SERVICE_CLIENT_SECRETopenId(directory:<id>)
AgentCenterEXPLORE_GATEWAY_URL, EXPLORE_GATEWAY_AUDIENCE, EXPLORE_SERVICE_TOKEN(어댑터), AGENT_CENTER_SERVICE_CLIENT_SECRET세션 subjectId
DocumentLibrary위와 동일 + DOCUMENT_LIBRARY_SERVICE_CLIENT_SECRET · DIRECTORY_SERVICE_URL로컬 원장의 directoryUserId
GuideCenter동일 + GUIDE_CENTER_SERVICE_CLIENT_SECRET · DIRECTORY_BASE_URL세션 directoryUserId
  • 미설정이면 질문 버튼을 켜지 않는다(capabilities.answer=false) — 눌러도 답이 없는 버튼을 만들지 않는다.
  • document-lib·guide-center 는 directory 모드에서만 답변이 켜진다(자체 인증 배포에는 위임할 Directory 주체가 없다).

5. 확인 (순서대로) ​

bash
# 1) 원장이 소비자를 알고 있는가 — 잘못된 시크릿은 401
curl -sS -X POST "$PORTAL_CONSOLE/api/internal/delegation-token" -H 'content-type: application/json' \
  -d '{"client_id":"rag-center","client_secret":"<시크릿>","subject":"<directoryUserId>","audience":"rag-center","scope":"rag:read"}'

# 2) 게이트웨이가 카탈로그를 읽는가 (어댑터 상태가 보인다)
curl -sS "$EXPLORE_GATEWAY/api/explore/v1/health"

# 3) 검색 — 사용자 위임 토큰이 필요하다
curl -sS -X POST "$EXPLORE_GATEWAY/api/explore/v1/search" -H "authorization: Bearer <위임토큰>" \
  -H 'content-type: application/json' -d '{"query":"결재","limit":10}'

# 4) 답변 — providerId 가 rag-center 인지 확인
curl -sS -X POST "$EXPLORE_GATEWAY/api/explore/v1/ask" -H "authorization: Bearer <위임토큰>" \
  -H 'content-type: application/json' -d '{"question":"결재선은 어떻게 되나요?"}'

6. 흔한 실패와 원인 ​

증상원인
답변 버튼이 안 보인다포털 capabilities.answer=false — EXPLORE_GATEWAY_URL 미설정 또는 위임 주체 없음(자체 인증)
401 explore_gateway_auth_required게이트웨이 PORTAL_SERVICE_TOKENS_JSON 과 포털 EXPLORE_SERVICE_TOKEN 값 불일치
위임 발급 invalid_client소비자 목록의 clientSecret 과 포털 시크릿 불일치
scope_not_allowed/audience_not_alloweddelegationAudiences·delegationScopes 에 그 값이 없다(위임은 좁히기만 한다)
답변이 비어 있다(partial: true)게이트웨이가 provider 를 못 찾았다 — 카탈로그의 rag-center 와 등록 상태를 확인
RagService 어댑터가 503RAG_EXPLORE_GATEWAY_API_KEY 미설정(경로를 닫는다)
검색에 그 포털 문서가 안 나온다그 포털의 search 어댑터가 카탈로그에 없거나 서비스 토큰이 다르다
빌드가 file:vendor/... 를 못 찾는다vendor tarball 이 커밋되지 않았다 — git add -f vendor/<pkg>.tgz(.gitignore 의 *.tgz 규칙)

7. 배포 순서 (의존성 순) ​

각 단계는 앞 단계가 준비된 뒤 배포한다. 소비 포털을 먼저 켜면 게이트웨이가 카탈로그를 못 읽어 검색이 조용히 빈다.

#서비스배포 전 확인배포 후 확인
1PortalConsole§1-1 소비자 JSON, §1-2 카탈로그 JSON, §1-3 등록(포털 4·rag-center·explore-gateway=PLATFORM_SERVICE)포털 API 키로 카탈로그가 조회되는가(directory:read)
2RagService (rag-center)§3 env 전부 + ModelGateway 의 rag.answer 모델 매핑POST /api/explore/v1/search 가 503 이 아닌가(서비스 토큰 유효), 답변이 오는가
3ExploreGateway§2 env 전부(CONTROL_PLANE_*·PORTAL_SERVICE_TOKENS_JSON·PORTAL_CONSOLE_JWT_AUDIENCE=explore-gateway)/api/explore/v1/health 의 adapters 에 등록한 포털이 보이는가
4소비 포털 (PC → AC → DL → GC)§4 env(포털별)상단 검색에서 ① 자기 색인 결과 ② 질문 버튼 ③ 답변 카드(출처 포함)가 순서대로 보이는가 — 한 곳씩 확인하고 다음으로
5공통 확인§5 curl 4단계providerId 가 rag-center 인가 · partial:false 인가

롤백 ​

  • 포털 단위: 그 포털의 EXPLORE_GATEWAY_URL 을 비우고 재배포 → 질문 버튼이 사라지고 자기 색인 검색만 남는다(장애 확산 차단).
  • 게이트웨이 단위: 카탈로그에서 문제 포털을 빼면 그 포털만 결과에서 빠진다(unavailablePortals 로 드러난다).
  • provider 단위: rag-center 를 카탈로그에서 빼면 답변이 멈추고 검색만 동작한다 — 답변을 되살릴 대안 provider 는 없다(정본 하나 원칙).
  • 코드 단위: 각 리포는 git revert <커밋> 후 재배포. vendor tarball 변경은 되돌릴 때도 git add -f 가 필요하다.

배포 게이트 (이 저장소들이 쓰는 방식) ​

  • 로컬 게이트가 기본: pnpm check && pnpm build && pnpm test — 원격 CI 는 실행 횟수만큼 비용이 든다.
  • 신규 클론 검증(배포 이미지와 같은 경로): git clone <repo> /tmp/x && cd /tmp/x && pnpm install --frozen-lockfile && pnpm build — vendor tarball 누락은 여기서만 드러난다.
  • 원격 검증 1회(수동): gh workflow run verify.yml --repo hohyun94/<repo> --ref main
    • DocumentLibrary · GuideCenter 의 verify.yml 은 PR 전용이라 수동 실행이 없다 — 그 두 리포는 PR 을 열거나 위 신규 클론 검증으로 대신한다.