콘텐츠로 이동

Sohri Graph Builder

Scenario v2 JSON을 실행해 NLU, 대화 상태, Policy, 응답 생성과 node routing을 처리하는 Dialogue Manager 런타임입니다.

빠른 시작

설치

python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

개발과 테스트 의존성:

python -m pip install -r requirements-dev.txt

기본 환경변수

프로젝트 루트의 .env 또는 현재 shell에 설정합니다.

cp .env.example .env

.env.example에는 변수 이름과 안전한 기본값만 두고 실제 API key는 .env에 입력합니다. .env는 Git에서 제외됩니다.

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
CORE_DEFAULT_LLM=gpt-4o
CORE_NLU_LLM=gpt-4o-mini

Azure OpenAI를 사용하는 기존 환경은 다음 변수를 사용할 수 있습니다.

AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
AZURE_OPENAI_KEY=your-api-key
AZURE_OPENAI_VERSION=2024-12-01-preview

전체 환경변수와 Skill Runtime 설정은 배포 문서와 Skill Runtime 문서를 참고합니다.

CLI 실행

프로젝트 루트에서 실행합니다.

python -m cli.chatbot --scenario dm/scenario/senimate.json

자주 사용하는 명령:

# 턴 상세 정보
python -m cli.chatbot \
  --scenario dm/scenario/senimate.json \
  --output-level detailed

# Scenario 검증
python -m cli.chatbot \
  --scenario dm/scenario/senimate.json \
  --validate-only

# Node 목록
python -m cli.chatbot \
  --scenario dm/scenario/senimate.json \
  --list-nodes

# 특정 node에서 반복 테스트
python -m cli.chatbot \
  --scenario dm/scenario/senimate.json \
  --node daily_life_check \
  --output-level detailed

Validation은 JSON/그래프 구조뿐 아니라 Runtime이 소비하지 않는 params 키도 거부합니다. 예를 들어 free_conversation은 지원하지 않으므로 제거해야 합니다. 허용 목록은 dm/graph/scenario_contract.py에서 관리합니다.

주요 CLI 옵션

옵션 기본값 역할
--scenario <path> 필수 실행할 Scenario JSON
--output-level <level> compact chat, compact, detailed, debug
--llm-choice <model> CORE_DEFAULT_LLM NLG model
--nlu-llm-choice <model> CORE_NLU_LLM NLU model
--max-fallback-retries <count> node 또는 3 세션 fallback 재시도 override
--stt-confidence-threshold <score> 0 세션 STT fallback 기준
--user-id <id> 빈 값 세션 사용자 ID. Maumon 장기 기억 조회 key
--locale <locale> 빈 값 세션 사용자 locale
--date-time <ISO 8601> 서버 현재 일시 날짜 기반 Skill에 전달할 일시
--location <place> 빈 값 문자열 위치
--latitude, --longitude 빈 값 WGS84 좌표. 반드시 함께 입력
--completion-history 비활성 종료 payload에 전체 대화 포함
--redis 비활성 Redis context store 사용
--bot-first Scenario 설정 봇 선행 발화 강제
--validate-only 비활성 검증 후 종료
--node <node_id> 빈 값 특정 node 집중 테스트

전체 옵션과 진단 방법은 검증과 테스트를 기준으로 관리합니다.

이전 세션의 과거 기록을 현재 대화의 참고 메모리로 시험하려면 --user-metadata에 JSON object를 전달합니다. Shell에서 JSON이 분리되지 않도록 작은따옴표로 감싸야 합니다. Maumon Vector DB 조회까지 시험할 Scenario는 최상위 metadata에 다음 설정을 포함해야 합니다.

{
  "metadata": {
    "memory_rag": {
      "enabled": true
    }
  }
}

general_conversation.json에는 이 설정이 적용되어 있습니다. Maumon 접속 환경 변수와 세션 사용자 ID를 포함한 CLI 실행 예시는 다음과 같습니다.

MAUMON_BASE_URL=https://maumon-be.lemong.ai \
MAUMON_API_KEY=replace-with-api-key \
python -m cli.chatbot \
  --scenario dm/scenario/general_conversation.json \
  --user-id MO-2026-0004 \
  --user-metadata '{
    "risk_level": 2,
    "long_term_memory": "사용자는 야구를 좋아하고 바둑을 잘 둡니다.",
    "memories": [
      {
        "recorded_at": "2026-09-09T15:00:00+09:00",
        "summary": "사용자는 오늘이 자신의 생일이며 김치찌개를 먹었다고 말했다.",
        "keywords": ["김치찌개", "생일"]
      }
    ],
    "source": "webrtc"
  }' \
  --output-level detailed

Vector DB 조회는 매 턴 실행되지 않습니다. NLU가 지원되는 named entity를 추출하거나 사용자가 지난 대화 회상을 명시적으로 요청하고, 현재 node, dialogue act, 중복 조회 cooldown 등의 조건을 통과한 턴에만 실행됩니다. 어제, 이틀 전, 9월 7일 같은 시간 표현은 세션 기준 절대 날짜로 변환해 검색합니다. 상세 조건은 Maumon 장기 기억 조회를 참고합니다.

metadata는 현재 세션 이전의 과거 기록으로 NLU/NLG에서만 참고하며, 현재 사용자 발화와 충돌하면 현재 사용자 발화를 우선합니다. long_term_memory에는 취미나 선호처럼 날짜와 무관한 장기 정보를 넣습니다. 여러 과거 대화는 memories 배열로 묶으며 각 항목의 recorded_at과 summary는 필수이고, keywords는 생략할 수 있습니다.

각 summary 내부의 오늘, 어제, 지금은 현재 세션이 아니라 해당 recorded_at 시점을 기준으로 해석합니다. 이 metadata는 세션 연결 시 전달되는 참고 정보이며 Graph Builder가 자동 누적하거나 외부 memory에 저장하지 않습니다. --user-metadata의 최상위 값은 JSON array나 문자열이 아닌 JSON object여야 합니다. API의 정확한 field 정의는 ChatbotSessionMetadata schema를 참고합니다.

Skill 시나리오 테스트

senimate_weather.json(위치 기반 날씨)과 senimate_days.json(공휴일·절기)은 Skill Runtime을 호출합니다. 시작 시 Skill로 첫 발화를 만드는 시나리오는 Skill entry node → 사용자 응답 node 분리를 권장합니다. 같은 Skill node에 action: stay를 두면 다음 턴에 Skill이 다시 실행될 수 있습니다. 자세한 구조는 bot-first 시작 Skill 패턴을 참고하세요. 그래프만 확인할 때는 --validate-only로 충분하고, 실제 Skill 응답을 보려면 SKILL_RUNTIME_BASE_URL이 필요합니다.

# 그래프 검증
python -m cli.chatbot \
  --scenario dm/scenario/senimate_weather.json \
  --validate-only

python -m cli.chatbot \
  --scenario dm/scenario/senimate_days.json \
  --validate-only

# Node 목록
python -m cli.chatbot \
  --scenario dm/scenario/senimate_weather.json \
  --list-nodes

python -m cli.chatbot \
  --scenario dm/scenario/senimate_days.json \
  --list-nodes

호스트에서 Skill까지 포함해 대화 테스트하는 예입니다. 9213은 별도 Skill Runtime 배포가 해당 포트를 공개한 환경을 가정합니다.

# 위치 기반 날씨
SKILL_RUNTIME_BASE_URL=http://localhost:9213 \
python -m cli.chatbot \
  --scenario dm/scenario/senimate_weather.json \
  --locale ko-KR \
  --latitude 37.5172 \
  --longitude 127.0473 \
  --output-level detailed

# 날짜 기반 공휴일·절기
SKILL_RUNTIME_BASE_URL=http://localhost:9213 \
python -m cli.chatbot \
  --scenario dm/scenario/senimate_days.json \
  --locale ko-KR \
  --date-time 2026-08-15T09:00:00+09:00 \
  --output-level detailed

동일한 Docker network의 container에서는 내부 DNS를 사용합니다.

export SKILL_RUNTIME_BASE_URL=http://sohri-skill-runtime:59213

--location과 좌표 옵션은 동시에 사용할 수 없습니다. --date-time을 생략하면 서버 현재 일시를 사용합니다. 자세한 연결 조건과 오류 확인은 Skill 사용 가이드를 참고합니다.

Maumon 장기 기억 테스트

Scenario의 metadata.memory_rag.enabled=true이면 NLU가 현재 발화에서 추출한 named_entities로 이전 Session의 대화·추억을 Maumon에서 검색할 수 있습니다. 사용자 기록을 연결할 --user-id와 API key가 모두 필요합니다.

MAUMON_BASE_URL="<maumon-base-url>" \
MAUMON_API_KEY="<secret>" \
MAUMON_TIMEOUT_MS=10000 \
MAUMON_RETRIEVAL_TIMEOUT_MS=10000 \
python -m cli.chatbot \
  --scenario dm/scenario/general_conversation.json \
  --user-id MO-2026-0004 \
  --output-level detailed

최대 3개 query를 병렬 조회하며 found=true인 Maumon answer를 사용합니다. 검색 실패나 빈 answer는 일반 대화를 중단하지 않습니다. detailed 출력에서는 Named entities, 검색 명사, Vector DB 실제 검색 여부, query와 answer를 확인할 수 있고, debug에서는 내부 status를 추가로 확인할 수 있습니다. 조회 조건, 환경변수, Docker/Helm 설정과 오류별 조치는 Maumon 장기 기억 조회를 참고합니다.

API 서버

python -m dm.api.app

옵션 지정:

python -m dm.api.app \
  --host 0.0.0.0 \
  --port 59212 \
  --reload \
  --log-level debug

주요 endpoint:

  • POST /v1/session: 등록된 Scenario로 session 생성
  • POST /v1/session/json: 요청의 Scenario JSON으로 session 생성
  • POST /v1/message: 사용자 메시지 처리
  • GET /v1/session/{session_id}: session 상태 조회
  • DELETE /v1/session/{session_id}: session 회수(대화 상태 + session 설정). 없는 session은 404이고, 404는 그 뜻 하나뿐입니다 — 회수 실패(lock 대기 초과·Redis 장애)는 503 + Retry-After입니다. 이 호출이 없으면 session은 TTL까지 남습니다.
  • POST /v1/session/validate: Scenario JSON 검증
  • GET /: Service 이름과 version 조회

Payload와 응답 계약은 API 문서에서 설명합니다.

STT confidence와 fallback

API의 message_input.confidence가 session의 stt_confidence_threshold 이하이면 일반 LLM NLU를 생략하고 이해 불가 fallback으로 처리합니다.

중요한 차이:

  • API session threshold 기본값: -0.7
  • CLI session threshold 기본값: 0
  • API MessageInput.confidence 기본값: 0.0
  • 키보드 CLI 입력은 confidence를 전달하지 않으므로 STT gate를 적용하지 않음

Threshold를 0으로 설정한 API session에서 confidence를 생략하면 0.0 <= 0이므로 fallback됩니다. 정확한 counter, 재질문과 turn count 규칙은 Policy에서만 상세히 설명합니다.

no_input과 not_understood에서 Scenario response를 고정 출력하지 않습니다. FallbackPolicy가 전체 맥락으로 LLM 응답을 만들고, responses.no_input/responses.not_understood는 LLM 호출 실패 시에만 안전망으로 사용합니다. 재시도 예산 소진은 Policy의 직접 종료가 아니라 graph routing으로 처리됩니다.

테스트

# 전체 테스트
python -m pytest dm/tests

# 특정 파일
python -m pytest dm/tests/test_skill_runtime_client.py

# 상세 출력
python -m pytest dm/tests -v

브랜치 규약

브랜치 용도 배포 파이프라인 대상 values
main GCP production Cloud Build trigger sohri-prd-graph-prd (^main$) deploy/values-prd.yaml
staging GCP development Cloud Build trigger sohri-dev-graph-dev (^staging$) deploy/values-stg.yaml
develop 온프렘 개발·릴리스 GitHub Actions CD (on-prem) (push: develop) deploy/values-onprem.yaml + 외부 mago-onprem-infra

main push는 production 배포를 트리거합니다. main에 머지하는 순간 Cloud Build가 image를 빌드하고 deploy/values-prd.yaml의 image.tag를 commit SHA로 갱신하며, Argo CD가 그 desired state를 prd cluster에 반영합니다. 검증되지 않은 변경을 main에 직접 넣지 않습니다.

온프렘 배포는 main이 아니라 develop push에서만 시작합니다 (.github/workflows/onprem-cd.yml). 따라서 GCP production 릴리스가 온프렘 배포를 일으키지 않습니다.

배경 (오너 결정 2026-08-26)

이전에는 main이 온프렘 릴리스 branch였고 GCP production에 대응하는 branch가 없었습니다. 오너 결정으로 main = GCP prd, 온프렘 = develop 으로 재정렬했습니다. 이 결정 이전의 문서·주석에서 "main = 온프렘"으로 설명하는 부분은 폐기된 내용입니다.

배포 계약 요약

  • GCP development는 Cloud Build trigger sohri-dev-graph-dev가 staging push에서 cloudbuild.yaml을 실행해 image와 deploy/values-stg.yaml tag를 갱신합니다.
  • GCP production은 Cloud Build trigger sohri-prd-graph-prd가 main push에서 같은 파일을 실행해 deploy/values-prd.yaml tag를 갱신합니다. .github/workflows/cd.yml은 workflow_dispatch 전용 fallback입니다.
  • On-prem workflow는 develop push에서 image를 push하고 외부 mago-onprem-infra/main의 서비스별 image.yaml을 갱신합니다. Kubernetes sync는 관리자가 수행합니다.
  • Argo CD Application과 sync policy는 이 저장소가 아니라 인프라 저장소에서 관리하므로 실제 활성 상태는 그쪽과 함께 확인합니다.
  • Session runtime이 process memory에 있으므로 Helm validation은 replica 1과 multi-replica HPA 금지를 강제합니다.

브랜치별 배포 라인과 현재 제한은 배포 가이드를 확인합니다.

HTML 문서 사이트

python -m pip install -r requirements-docs.txt
python scripts/build_docs_site.py

기본 출력 경로:

/home/galois/mago-services/www/sohri/graph_builder

www/sohri는 여러 Sohri 구성 요소가 함께 사용하는 상위 문서 공간입니다. 이 빌드는 graph_builder 하위 폴더만 정리하며 Skill Runtime 등 다른 구성 요소의 형제 폴더는 변경하지 않습니다.

HTML에서도 저장소 구조를 유지합니다.

www/sohri/
├── index.html
└── graph_builder/
    ├── index.html             # 저장소 README
    └── docs/
        ├── index.html         # docs/README.md
        ├── api/
        ├── graph_builder/
        ├── scenario/
        ├── generator/
        ├── skills/
        └── deploy/

8180 포트로 제공합니다.

python -m http.server 8180 \
  --bind 0.0.0.0 \
  --directory /home/galois/mago-services/www

다른 PC에서는 다음 주소로 접속합니다.

http://<서버-IP>:8180/sohri/
http://<서버-IP>:8180/sohri/graph_builder/
http://<서버-IP>:8180/sohri/graph_builder/docs/

프로젝트 구조

sohri-graph-builder/
├── cli/                 # 대화형 CLI
├── dm/
│   ├── api/             # FastAPI routes와 schema
│   ├── core/            # DST, NLU, policy, executor, transition
│   ├── scenario/        # Scenario JSON
│   ├── service/         # DialogManager
│   ├── skills/          # Skill Runtime HTTP contract
│   ├── storage/         # Context store
│   └── tests/           # Test suite
├── docs/                # 역할별 기술 문서
├── deploy/              # Helm과 배포 설정
└── scripts/             # 문서·검증·simulation 도구

문서 기준

필요한 내용 기준 문서
프로젝트 설치·실행 이 README
전체 문서 탐색 docs/README.md
현재 runtime 내부 계약 docs/graph_builder/
HTTP 계약 docs/api/
Scenario 작성 docs/scenario/
시나리오 생성 docs/generator/
Skill 사용 docs/skills/
배포·운영 docs/deploy/