API reference

Cortex developer docs

Cortex는 헤드리스 공간 인텔리전스 플랫폼입니다. 이 문서는 외부 채널이 실제로 호출하는 두 개의 표면 — Ingest API와 Conversation API — 을 실제 코드 계약 기준으로 설명합니다.

개요

Cortex는 두 개의 서브시스템으로 구성됩니다. Core는 장소 지식의 인텔리전스 경계로, TypeDB 지식 그래프와 그 유일한 writer인 백그라운드 core-worker, 원문을 내구성 있게 적재하는 Inbox 스테이징 레이어, 그리고 미등록 용어를 수집·성장시키는 Dictionary로 이루어집니다. Venue Manager(VM)는 Core 앞단의 에이전트형 API 서비스로, 이 문서가 다루는 Ingest API와 Conversation API를 외부에 노출합니다. VM은 TypeDB에 직접 접근하지 않고 항상 Core를 HTTP로 경유합니다.

핵심 철학은 grounding-abstain입니다. Core에 실제로 저장된 사실에만 근거해 답하며, 근거가 없으면 추정하거나 지어내지 않고 정직하게 모른다고 답합니다(0% 사실오류 지향). Conversation API의 abstain 필드가 이 신호를 그대로 드러냅니다.

Base URL: https://cortex-api.shareit.kr — Ingest/Conversation API는 /vm 접두사 아래 마운트됩니다(예: POST /vm/ingest).

인증

모든 요청은 채널별 api-key로 인증합니다. 키는 Cortex 운영팀이 admin 콘솔(/admin/keys)에서 발급하며, 평문 키는 발급 응답에서 한 번만 노출됩니다(show-once) — 이후에는 접두사(prefix)만 조회할 수 있습니다. 발급받은 키는 매 요청 Authorization: Bearer <key> 헤더로 전달합니다.

curl -H "Authorization: Bearer $CORTEX_API_KEY" ...

발급 시 키에는 요청 빈도 한도(rate_limit_per_min, 기본 60/분)와 일일 쿼터(daily_quota, 기본 무제한)가 함께 기록됩니다. 현재 이 한도는 Conversation API(/vm/chat)에만 적용되며, 초과 시 429를 반환합니다. Ingest API는 아직 이 한도를 강제하지 않습니다.

Bearer 헤더가 없거나, 키가 존재하지 않거나 폐기(revoked)된 경우 두 API 모두 401을 반환합니다.

// 헤더 누락
{"detail": "missing_bearer"}

// 키 무효/폐기
{"detail": "invalid_api_key"}

Ingest API

호스트나 정보 담당자가 제출한 비정형 장소 정보를 받아 정제(sanitize) → 보강 평가(enrichment) → 게이트 판정 순으로 처리합니다. 텍스트가 충분하면 Core Inbox로 즉시 전달하고, 갭이 있으면 구체적인 질문 목록으로 되묻습니다. 마크다운 문서를 통째로 제출해도 되며, 질문은 실질적인 갭에 대해서만 최대 10개까지 생성됩니다 — 사전에 없는 용어(unknown terms) 그 자체는 질문을 만들지 않습니다.

POST /vm/ingest — 원문 텍스트 제출

필드타입필수설명
raw_textstring제출할 원문 텍스트
scope_type"venue" | "venue_group"대상 스코프 종류
scope_idstring | null아니오대상 장소/그룹 id. 생략하면 신규 공간으로 Core에 전달
channelstring아니오출처 채널 식별자 (기본값 "api")
curl -X POST https://cortex-api.shareit.kr/vm/ingest \
  -H "Authorization: Bearer $CORTEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "raw_text": "스탠딩 100명까지 수용 가능하고 천장 높이는 4미터입니다.",
    "scope_type": "venue",
    "scope_id": "01KT41HB68EQ1YS4QEKBPZBX8H",
    "channel": "api"
  }'

충분한 텍스트 → 201, Core Inbox로 forward 완료:

// 201 Created
{"ingest_id": "I0123456789", "status": "forwarded", "inbox_id": "01KT7NQ1CV67B1GBCE429K3EKP"}

정보가 모호하거나 부족 → 202, 답변이 필요한 질문 목록과 함께 세션이 열립니다:

// 202 Accepted
{
  "ingest_id": "I0123456789",
  "status": "needs_clarification",
  "questions": [
    {"id": "q1", "question": "최대 수용 인원이 몇 명인가요?", "why": "수용 인원 정보가 확인되지 않았습니다."}
  ]
}

차단된 입력(주입 패턴, 길이 초과 등) → 422, 어떤 데이터도 저장되지 않습니다:

// 422 Unprocessable Entity
{"error": "BLOCKED", "reason": "injection"}

POST /vm/ingest/{ingest_id}/answers — 보강 질의 답변

needs_clarification 세션에 답변을 제출합니다. 질문 세트는 최초 제출 시점에 고정되며, 답변은 question_id로 매칭됩니다. 아직 답변되지 않은 질문이 남아 있으면 다시 202로 잔여 질문을 반환하고, 모두 해소되면 원문과 답변을 합쳐 Core Inbox로 전달하며 201을 반환합니다.

curl -X POST https://cortex-api.shareit.kr/vm/ingest/I0123456789/answers \
  -H "Authorization: Bearer $CORTEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"answers": [{"question_id": "q1", "answer": "최대 100명입니다."}]}'
// 잔여 질문이 남은 경우 — 202
{"ingest_id": "I0123456789", "status": "needs_clarification", "questions": [/* 남은 질문 */]}

// 모두 해소된 경우 — 201
{"ingest_id": "I0123456789", "status": "forwarded", "inbox_id": "01KT7NQ1CV67B1GBCE429K3EKP"}

POST /vm/ingest/file — 문서 업로드

PDF/Word/Excel/PowerPoint 문서를 업로드하면 텍스트로 변환한 뒤 위와 동일한 파이프라인을 통과합니다.multipart/form-data로 전송합니다. JSON 본문과 multipart 본문은 하나의 경로에 공존할 수 없어, 원문 텍스트 제출과는 별도 경로(/vm/ingest/file)를 사용합니다.

curl -X POST https://cortex-api.shareit.kr/vm/ingest/file \
  -H "Authorization: Bearer $CORTEX_API_KEY" \
  -F "file=@venue-info.pdf;type=application/pdf" \
  -F "scope_type=venue" \
  -F "scope_id=01KT41HB68EQ1YS4QEKBPZBX8H"

응답은 원문 텍스트 제출과 동일한 형태(201 forwarded 또는 202 needs_clarification)입니다.

GET /vm/ingest/{ingest_id} — 세션 조회

제출한 ingest 세션의 현재 상태를 조회합니다. 존재하지 않는 id는 404를 반환합니다.

curl https://cortex-api.shareit.kr/vm/ingest/I0123456789 \
  -H "Authorization: Bearer $CORTEX_API_KEY"
// forwarded 상태 — Core 처리 결과를 best-effort 로 함께 반환
{
  "ingest_id": "I0123456789",
  "status": "forwarded",
  "inbox_id": "01KT7NQ1CV67B1GBCE429K3EKP",
  "core_status": "projected",          // Core 최종 상태 (조회 실패 시 생략)
  "venue_ids": ["V9X3K2M4Q1R7T5W8A6B0C"]  // 생성·갱신된 venue id (projected 시)
}

// needs_clarification 상태 — 미답변 질문만 포함
{"ingest_id": "I0123456789", "status": "needs_clarification", "questions": [/* ... */]}

core_status projected가 되면 venue_ids로 챗봇 설정(config)을 바인딩할 수 있습니다. Core 조회가 일시적으로 불가하면 두 필드는 생략되며 세션 상태만 반환됩니다.

Conversation API

POST /vm/chat은 멀티턴 대화형 Q&A 엔드포인트입니다. Cortex 에이전트가 Core 지식 그래프를 근거로 답하며, 근거가 없으면 지어내지 않고 정직하게 abstain합니다. 이 엔드포인트는 항상 게스트(guest) 취급으로 처리되어, 호스트 전용 필드(내부 운영 정책, 계약/법무 정보, 호스트 PII, 재무 데이터 등)는 마스킹이 아니라 완전히 생략됩니다.

필드타입필수설명
config_idstring발급받은 chatbot config id
messagestring사용자 메시지
session_idstring | null아니오이전 대화를 이어갈 세션 id. 생략하면 새 세션 시작
curl -X POST https://cortex-api.shareit.kr/vm/chat \
  -H "Authorization: Bearer $CORTEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"config_id": "C0123456789", "message": "이 베뉴의 수용 인원은?"}'
// 200 OK
{
  "session_id": "S0123456789",
  "answer": "이 베뉴의 스탠딩 수용 인원은 100명입니다.",
  "grounded": true,
  "abstain": false,
  "sources": ["core:venue:01KT41HB68EQ1YS4QEKBPZBX8H"],
  "config_id": "C0123456789"
}

grounded는 Core 조회 도구가 사실을 반환했고 최종 답변이 그 사실에 근거했을 때만 true입니다 — 도구를 호출했더라도 답이 그 결과를 쓰지 않았다면(예: 찾아낸 사실이 질문과 무관) grounded는 false로 남습니다. abstain grounded가 false인 모든 경우(근거 없음·장소 미발견·Core 접근 불가)에 true가 되는 정직 신호이며, 두 필드가 동시에 true가 되는 경우는 없습니다 — 절대 사실을 지어내지 않는다는 보증이 이 상호배타 관계로 드러납니다. Core 조회가 일시적으로 실패하면 내부적으로 폴백 단계(RAG → static brief, 현재는 데이터 시딩 전 stub)를 차례로 시도하며, 모두 소진되면 정직하게 abstain 답변을 반환합니다.

다음 턴에 같은 대화를 이어가려면 응답의 session_id를 그대로 다음 요청에 실어 보내면 됩니다. 최근 10턴까지의 대화가 컨텍스트로 함께 주입됩니다.

에러

Status본문조건
401{"detail": "missing_bearer"}Authorization 헤더 누락
401{"detail": "invalid_api_key"}키가 존재하지 않거나 폐기됨
422{"detail": "config_not_found"}config_id에 해당하는 chatbot config 없음
422{"detail": "invalid_scope_type"}scope_type이 venue/group이 아님
422{"detail": "invalid_scope_id"}scope_id가 유효한 cortex_id 형식이 아님
422{"detail": "session_not_found"}session_id를 지정했으나 해당 세션 없음
429{"detail": "rate_limit_exceeded"}분당 요청 한도 초과(기본 60/분). Retry-After 헤더 포함
429{"detail": "daily_quota_exceeded"}키에 설정된 일일 쿼터 초과