# XenRouter(젠라우터) 개발자 매뉴얼 v2

공개 문서. 페이지: https://xenlook.com/ai-lab/xenrouter  
다운로드: https://xenlook.com/download/xenrouter-developer-manual-ko.md

이 파일이 외부에서 읽는 XenRouter 설명의 기준입니다. 내부 연구노트·레지스트리 원문은 공개하지 않습니다. 숫자·지연·판정 규칙은 이 문서가 가리키는 공개 API와 쇼케이스 코드의 동작입니다. 문서에 적힌 ms를 “항상 이 속도”로 인용하지 마십시오. `latencyMs`는 요청마다 측정합니다.

면책. XenRouter의 BUY / SELL / HOLD, 뉴스 톤, 다음 행동, 핫딜 문장은 투자 권유·매수 권유·세무 자문이 아닙니다. 시세는 거래소·시세 제공자의 공개 응답을 읽고, 뉴스는 연합뉴스 RSS와 입력 문장을 규칙으로 분류합니다. 사람이 최종 판단을 합니다.

---

## 0. 이 문서를 읽는 순서

처음 온 사람은 1장(개발 배경)과 2장(기대 효과)만 읽어도 왜 라우터가 LLM 앞에 있는지 알 수 있습니다. 화면을 눌러 보려는 사람은 3장(사용자 흐름)을 따라 판정 창 → 시세 데모 → 뉴스 데모 순서로 움직입니다. 붙이려는 개발자는 5장부터 9장까지 요청·응답 필드를 그대로 구현합니다. 운영·라이선스는 12장과 13장입니다.

문서 안의 예시는 모두 `GET` 입니다. 본문은 JSON을 받지 않습니다. 인증 헤더가 없는 공개 데모입니다. 상업 트래픽을 이 엔드포인트에 무제한으로 걸지 마십시오. 제품 연동은 Life Hub와 별도 계약 경로를 사용합니다.

용어를 먼저 고정합니다.

| 말 | 뜻 |
|---|---|
| System-One | LLM이 문장을 만들기 전에, 규칙과 지표로 의도·신호를 확정하는 앞단 |
| decide | 시세 또는 뉴스를 신호·톤·다음 행동으로 가르는 계산 |
| fetch | 업비트·야후·RSS처럼 바깥 데이터를 가져오는 시간 |
| latencyMs | 해당 요청에서 측정한 경과. 고정 상수가 아님 |
| 판정 창 | 이 페이지의 게스트 전체화면. 버튼을 누른 뒤에만 3분 타이머가 돈다 |
| 다음 행동 | 뉴스에 대해 읽기·쇼핑 비교·금융·부동산·세금·훑어보기 중 하나. 매수·매도가 아님 |

---

## 1. 개발 배경

대화 모델은 문장을 잘 잇습니다. 시세·뉴스·세금 일정처럼 숫자가 먼저인 질문에서는 그 장점이 약점이 됩니다. 모델은 방금 받은 호가를 모른 채 “대략 오릅니다”라고 말할 수 있고, 지연을 18밀리초처럼 특정 숫자로 꾸밀 수 있으며, 뉴스 헤드라인에 매수·매도라는 주식 용어를 붙여 독자를 거래 화면으로 오인하게 만들 수 있습니다. XenRouter는 그 세 가지를 모델에게 맡기지 않으려고 만들었습니다.

첫째, 사실의 출처를 모델 가중치 밖으로 뺍니다. 비트코인·이더리움은 업비트 공개 시세를 읽고, 엔비디아와 삼성전자는 주식 시세 조회를 읽습니다. 조회가 실패하면 데모는 마지막 수단으로 고정 견적을 넣는데, 그 값은 실측이 아니라 폴백입니다. 이 매뉴얼은 그 사실을 숨기지 않습니다. 폴백이 켜진 응답을 라이브 호가로 저장하거나 백테스트 입력으로 쓰면 안 됩니다.

둘째, 판정 어휘를 도메인마다 나눕니다. 시세 데모의 출력은 BUY, SELL, HOLD 세 가지입니다. 뉴스 데모의 출력은 톤(POSITIVE, NEGATIVE, NEUTRAL)과 다음 행동(read, compare_shop, check_finance, check_property, check_tax, skim)입니다. 뉴스에 매수·매도를 붙이지 않는 이유는 단순합니다. 기사는 호가가 아니고, 쇼핑 비교와 환율 확인과 세금 일정은 같은 버튼이 아니기 때문입니다.

셋째, 속도를 배지로 만들지 않습니다. 예전 소개에 나오던 고정 18.2ms, 18.4ms, 20ms, Gold 100% 같은 문구는 쓰지 않습니다. 빠른 편이라도 그 요청의 `latencyMs`만 말합니다. 쇼케이스 벤치 표에 적힌 비교 수치는 그 표의 골드셋 설명이지, 이 공개 GET이 매 호출마다 보장하는 SLA가 아닙니다.

넷째, LLM 토큰을 아낍니다. “지금 비트코인 어때?”를 매번 장문 프롬프트로 넣으면, 모델은 시세를 모르면서도 시세처럼 보이는 문단을 만듭니다. 라우터가 가격·등락·신호·근거 한 줄을 먼저 만들면, 뒤의 대화는 그 JSON을 읽고 말투만 입히면 됩니다. 숫자를 지어내는 일과 말을 고르는 일이 갈라집니다.

다섯째, 생활 결정은 주식 화면 하나가 아닙니다. 오늘 일정, 세금, 부동산, 여행, 쇼핑은 Life Hub 탭으로 나뉩니다. 뉴스 라우터의 다음 행동은 그 탭으로 사람을 보냅니다. 반도체 기사는 부품 가격 비교로, 환율 기사는 금융 확인으로, 아파트 기사는 부동산 탭으로, 세금 문구는 세금 일정으로 갑니다. 이 분기가 개발 배경의 핵심입니다. 하나의 빨간·파란 신호로 세상 뉴스를 줄이지 않습니다.

---

## 2. 기대 효과

효과를 과장하지 않고, 라우터가 실제로 덜어 주는 일만 적습니다.

토큰. 시세 턴에서 모델이 호가·등락·RSI를 지어 쓰지 않아도 됩니다. 프롬프트에는 이미 계산된 `price`, `change`, `signal`, `reason`, `latencyMs`가 들어갑니다. 절감량은 뒤 대화의 길이에 따라 달라지므로, 이 문서는 “몇 퍼센트 절감”을 고정하지 않습니다. 절감의 방향만 단정합니다. 숫자 생성 구간이 모델 밖에서 끝납니다.

정직. 지연은 측정값입니다. 업비트 1분봉이 비면 빈 배열로 두고, 있는 척하지 않습니다. 뉴스 입력이 없으면 연합뉴스 경제 RSS를 읽고, RSS도 없으면 데모 폴백 문장을 출처 필드에 드러냅니다. 출처 문자열이 `데모 fallback`이면 그 응답을 기사 원문으로 인용하면 안 됩니다.

오인 감소. 뉴스 카드에 BUY가 없으면, 독자는 기사를 주문으로 읽지 않습니다. 다음 행동이 `compare_shop`이면 가격 비교이고, `check_tax`이면 일정 확인입니다. 기대 효과는 클릭의 목적지가 기사 내용과 맞게 갈라지는 것입니다.

확장. 거래소나 종목을 추가할 때도 LLM 프롬프트를 고치지 않고, 시세 조회와 판정 함수를 추가합니다. 현재 공개 데모가 이름 붙여 처리하는 자산은 BTC, ETH, NVDA입니다. 화면의 SOL, XRP 버튼은 별도 업비트 마켓 분기가 이 라우트에 없습니다. 그 심볼은 삼성전자 시세 경로로 떨어집니다. 이 한계를 숨긴 채 “6종목 라이브”라고 말하지 않습니다. 붙이려면 BTC·ETH와 같은 분기를 코드에 추가해야 합니다.

운영. 게스트는 판정 창을 3분만 봅니다. 타이머는 페이지를 연 순간이 아니라 버튼을 누른 순간 시작합니다. 끝나면 가입·로그인으로 넘깁니다. 기대 효과는 무제한 공개 시세 화면을 멤버십과 분리하는 것입니다.

하지 않는 효과. 수익을 보장하지 않습니다. 쇼핑 검색은 다나와 HTML에서 제목·링크만 파싱하며, 가격·할인 숫자는 API·UI에 넣지 않습니다. 실시간 최저가·재고 보장이 아닙니다. 4축 벡터는 키워드 가중치이지 임베딩 모델의 코사인이 아닙니다. RSI는 이 데모에서 등락률로 추정한 값이지, 거래소가 내려 준 14기간 RSI 원문이 아닙니다. 기대 효과를 말할 때 이 다섯 가지를 빼면 문서가 거짓이 됩니다.

---

## 3. 사용자 흐름

화면은 위쪽에서 아래로 읽습니다. 색은 장식 무지개가 아니라 단계 표시입니다.

1. 노란 단락 — 개발 배경. 왜 LLM 앞에 라우터를 두었는지.
2. 청록 단락 — 사용. 판정 창, 시세 데모, 뉴스 데모 중 어디를 누르는지.
3. 녹색 단락 — 기대 효과. 토큰·정직·오인 감소. 보장하지 않는 것 포함.
4. 붉은 단락 — 한계. 폴백 시세, 주식 1분봉 합성, 뉴스에 매수·매도 없음.
5. 판정 창. 청록 버튼. 누른 뒤에만 3분.
6. 라이브 데모 탭. 시세 판정 또는 뉴스·쇼핑.
7. 벤치마크 탭. 비교 표. SLA로 인용 금지.
8. 매뉴얼. 이 파일 다운로드.

시세 데모를 쓰는 순서. 자산을 고릅니다. 오른쪽 카드의 지연, 가격, 등락 색, 종합 판정 배지, 근거 문장, 지표 네 칸, 1분봉, 체결 표를 위에서 아래로 읽습니다. JSON 토글은 마지막입니다. 화면을 믿기 전에 JSON의 `signal`이 배지와 같은지 봅니다.

뉴스·쇼핑 데모를 쓰는 순서. 검색창에 「4070 노트북」처럼 입력하거나 토픽 탭을 고릅니다. **다나와 검색** 라벨 확인 → 라우터 판정(카테고리·nextAction·단계 ms) → 최저가 비교 · 순위 · 신뢰 · 추천 % 카드를 읽습니다. 가격은 prod.danawa.com 실측이며 구매·최저가 보장이 아닙니다.

---

## 4. 아키텍처

세 층입니다.

L0 수신. 브라우저 또는 curl이 `GET /api/ai-lab/xenrouter`를 호출합니다. 쿼리는 `type=market|news`, 시세면 `asset`, 뉴스면 `newsText`입니다. 서버가 업비트, 주식 시세, 연합뉴스 RSS를 가져옵니다. 이 구간의 시간은 판정 시간 안에 포함되어 `latencyMs`로 돌아옵니다. fetch만 따로 떼어 주는 필드는 이 공개 데모 JSON에 없습니다.

L1 판정. 시세는 `computeXenRouterSignal`이 등락률과 RSI 추정치로 BUY, SELL, HOLD를 고릅니다. 뉴스는 `routeNewsText`가 카테고리, 톤, 다음 행동, 4축 벡터, 임팩트, 긴급도를 고르고, 별도 규칙 목록이 핫딜 문구를 고릅니다. 이 층은 가중치 파일을 로드하지 않습니다. 정규식과 임계값입니다.

L2 소비. 쇼케이스 UI, Life Hub 위젯, 이후 대화 모델이 L1 JSON을 읽습니다. 모델은 신호를 다시 계산하지 않습니다. 말투와 설명만 얹습니다. 모델이 JSON과 다른 가격을 말하면 그 문장은 라우터 출력이 아닙니다.

선택 경로로 System-One HTTP(`:8010`)와 Python 패키지 `projects/xenrouter-oss`가 있습니다. 공개 쇼케이스 GET과 프로세스 내부 포트를 같은 것이라고 가정하지 마십시오. 이 페이지의 버튼은 `https://xenlook.com/api/ai-lab/xenrouter`를 칩니다.

---

## 5. 시세 판정 규칙

입력은 이름, 심볼, 카테고리(crypto|stock), 가격, 등락률, 통화입니다.

RSI 추정. `50 + changePct * 4.5`를 15에서 88 사이로 자르고, 소수 한 자리로 반올림합니다. 거래소 RSI가 아닙니다.

모멘텀. `changePct * 1.8`, 소수 한 자리.

흐름 점수. `80 + abs(changePct) * 3`을 60에서 99 사이로 자릅니다.

신호.

- 등락률이 1.5% 이상이거나 RSI 추정이 62보다 크면 BUY. 근거 문장은 상승 모멘텀과 RSI를 포함합니다.
- 등락률이 -1.5% 이하이거나 RSI 추정이 38보다 작으면 SELL. 근거 문장은 변동성과 지지 이탈을 포함합니다. 문장에 “리밸런싱 권고”가 있어도 주문 지시가 아닙니다.
- 그 외는 HOLD. 박스권·관망 문장입니다.

신뢰도. `85 + abs(changePct) * 2.2`를 82.4에서 98.8 사이로 자릅니다. 표본 수가 아니라 등락 크기의 함수입니다. 통계적 신뢰구간으로 해석하지 않습니다.

변동성 라벨. 등락 절대값이 2보다 크면 HIGH, 아니면 NORMAL.

가격 문자열. USD는 달러 소수 두 자리. KRW는 원 단위 반올림.

1분봉.

- BTC, ETH. 업비트 분봉 API 25개. 수신 실패 시 신호 객체의 봉 배열은 비어 있을 수 있습니다.
- NVDA, 그리고 NVDA가 아닌 비-BTC·비-ETH 심볼(현재 삼성전자 경로). 분봉은 조회 가격 주변에서 난수로 합성합니다. 거래소 체결 원장이 아닙니다. 합성 봉의 신호는 봉의 시가 대비 종가 변화로, NVDA는 ±0.08%, 삼성 경로는 ±0.05%를 넘으면 BUY 또는 SELL입니다. 종합 신호의 1.5% 규칙과 봉 라벨 규칙이 다르므로, 표의 마지막 봉 신호와 카드의 종합 신호가 다를 수 있습니다. 카드의 종합 신호가 그 요청의 판정입니다.

자산 분기.

| asset | 시세 | 분봉 | 비고 |
|---|---|---|---|
| BTC | 업비트 KRW-BTC | 업비트 1분 | 실패 시 가격 96450000, 등락 2.45 폴백 |
| ETH | 업비트 KRW-ETH | 업비트 1분 | 실패 시 가격 3640000, 등락 -0.82 폴백 |
| NVDA | 주식 시세 조회 | 합성 | 실패 시 128.5달러, 등락 3.8 폴백 |
| 그 외(SEC 포함, SOL·XRP 포함) | 005930.KS | 합성 | 실패 시 61500원, 등락 -1.2 폴백 |

폴백 상수는 네트워크가 막혔을 때의 데모 견적입니다. 응답에 `fallback: true` 플래그는 현재 없습니다. 폴백 여부를 소비자에게 알리려면 이후 버전에서 필드를 추가해야 합니다. 그 전까지는 가격이 위 상수와 정확히 같고 시각이 시세 공백과 겹치면 폴백을 의심하십시오.

---

## 6. 뉴스 라우팅 규칙

`type=news`. `newsText`가 있으면 그 문장이 헤드라인입니다. 출처는 `실시간 입력 텍스트`입니다. 없으면 연합뉴스 경제 RSS 최대 8건을 읽고, 첫 제목을 헤드라인으로 씁니다. 출처는 `연합뉴스 경제속보 RSS`입니다. 둘 다 없으면 헤드라인 `경제·증시 브리핑`, 출처 `데모 fallback`입니다.

카테고리. 정규식 우선순위는 IT/반도체, 부동산, 금융/환율, 소비재/유통, 그 외 사회/일반입니다. AI·반도체·GPU·엔비디아는 IT입니다. 아파트·전세·청약은 부동산입니다. 증시·환율·비트코인·금리는 금융입니다. 유통·세일·마트는 소비재입니다.

톤.

- 급등, 돌파, 호실적, 상승, 흑자, 성장 등이 있으면 POSITIVE.
- 급락, 하락, 위기, 적자, 우려, 경고, 붕괴가 있으면 NEGATIVE.
- 둘 다 아니면 NEUTRAL. 긍정 패턴이 먼저 검사되므로 한 문장에 상승과 하락이 같이 있으면 POSITIVE가 될 수 있습니다. 문장 전체 감성 모델이 아닙니다.

4축. 각 축은 키워드가 있으면 높은 상수, 없으면 낮은 상수입니다. IT 0.92 또는 0.12, 금융 0.88 또는 0.15, 소비 0.78 또는 0.1, 부동산 0.85 또는 0.08. 합이 1이 아닙니다. 확률분포로 정규화하지 않습니다.

임팩트. 네 축의 최댓값에 100을 곱해 반올림합니다.

긴급도. 임팩트 85 이상 HIGH, 50 이상 MEDIUM, 그 미만 LOW.

다음 행동.

- 종합소득세, 납부, 신고, 환급, 국세, 지방세가 있으면 check_tax.
- 아니면 IT 또는 소비재면 compare_shop.
- 금융/환율이면 check_finance.
- 부동산이면 check_property.
- 그 외 긴급도가 LOW면 skim, 아니면 read.

신뢰도. 임팩트에 0.92를 곱하고, 중립이면 8, 아니면 12를 더한 뒤 55에서 99로 자릅니다.

쇼핑 offers. `compare_shop` 판정이면 서버가 다나와 검색 HTML을 한 번 가져와 `pcode`·제목·`prod.danawa.com/info/?pcode=...` 링크를 파싱합니다. `verified.pricesShown`은 항상 false입니다. 가격·할인·재고 필드는 만들지 않습니다. 파싱 실패·0건이면 offers를 꾸며 내지 않습니다. `GET ?type=search&query=...` 응답에 `steps[]` · `routeMs`/`fetchMs`/`totalMs`가 포함됩니다.

뉴스 RSS 경로의 `dealPrice`는 「—」이며, 쇼핑 비교 시 offers[]만 실측 링크로 채웁니다.

최근 헤드라인. RSS가 있으면 최대 8건을 같은 규칙으로 라우팅합니다. 사용자가 문장을 넣었으면 0번 항목을 그 입력으로 덮어씁니다.

---

## 7. REST — 시세

요청.

```bash
curl -sS "https://xenlook.com/api/ai-lab/xenrouter?type=market&asset=BTC"
```

`asset` 생략 시 BTC입니다. `type` 생략 시 market입니다.

응답 껍데기.

```json
{ "ok": true, "type": "market", "data": { } }
```

`data` 필드.

| 필드 | 형식 | 의미 |
|---|---|---|
| asset | string | 표시 이름. 예: 비트코인 (BTC/KRW) |
| name | string | asset과 같은 표시 이름 |
| symbol | string | BTC, ETH, NVDA, SEC |
| category | crypto 또는 stock | |
| price | string | 이미 포맷된 가격. 숫자로 다시 파싱할 때 쉼표·원·달러 기호를 제거 |
| change | string | +1.20% 형태 |
| changePct | number | 부호 있는 등락률 |
| isUp | boolean | changePct >= 0 |
| signal | BUY, SELL, HOLD | 종합 판정 |
| confidence | number | 5장 공식. 신뢰구간 아님 |
| reason | string | 한 줄 근거. 주문 지시로 실행 금지 |
| latencyMs | number | 이 요청의 측정값 |
| indicators.rsi14 | number | 추정 RSI |
| indicators.momentum5m | number | 등락 기반 모멘텀. 5분 창 실측이 아닐 수 있음 |
| indicators.volatility | HIGH 또는 NORMAL | |
| indicators.flowScore | number | 60–99 |
| minuteCandles | array | 업비트 또는 합성. 5장 |
| asOf | string | 서버 시각 ISO8601 |

봉 객체. `time`(HH:MM), `open`, `high`, `low`, `close`, `volume`, `changePct`, `signal`.

jq 예.

```bash
curl -sS "https://xenlook.com/api/ai-lab/xenrouter?type=market&asset=ETH" \
  | jq '.data | {symbol, signal, latencyMs, changePct, rsi: .indicators.rsi14, n: (.minuteCandles|length)}'
```

에러. 이 라우트는 바깥 시세 실패를 HTTP 500으로 돌리지 않고 폴백 가격으로 200을 줍니다. `ok: false`를 기대해 재시도 루프를 짜면 돌지 않습니다. 429·네트워크 오류는 호출자(curl, 브라우저) 수준에서만 납니다.

CORS. `Access-Control-Allow-Origin: *`. 브라우저에서 직접 GET 할 수 있습니다. 비밀 헤더를 보내지 마십시오. 공개 응답입니다.

---

## 8. REST — 뉴스

요청.

```bash
curl -sS --get "https://xenlook.com/api/ai-lab/xenrouter" \
  --data-urlencode "type=news" \
  --data-urlencode "newsText=원·달러 환율 1,400원 돌파 우려"
```

`newsText`를 생략하면 RSS입니다. 한글은 반드시 쿼리 인코딩합니다. 공백을 `+`로 둔 예도 동작하지만, 쉼표·중점은 `--data-urlencode`가 안전합니다.

`data` 필드.

| 필드 | 의미 |
|---|---|
| headline | 입력 또는 RSS 제목 또는 폴백 문장 |
| source | 실시간 입력 텍스트 / 연합뉴스 경제속보 RSS / 데모 fallback |
| matchedCategory | 핫딜 규칙의 카테고리 문구. 4축 카테고리 문자열과 다를 수 있음 |
| dealProduct | 데모 상품명 |
| dealPrice | 데모 가격 문자열 |
| discount | 데모 할인 문구 |
| dealUrl | 쇼핑 검색 URL |
| summary | 규칙이 정한 한 줄 설명 |
| confidence | 6장 공식 |
| latencyMs | 측정값 |
| sentiment | POSITIVE, NEGATIVE, NEUTRAL |
| nextAction | read, compare_shop, check_finance, check_property, check_tax, skim |
| impactScore | 0–100 근처의 축 최댓값 |
| recentHeadlines | 최대 8건. 각 항목은 sentiment, nextAction, category, vector, urgency, impactScore, latencyMs |

`vector`는 `itTech`, `finance`, `consumer`, `realEstate` 네 수입니다.

다음 행동의 한국어 라벨은 UI 사전이 정합니다. API는 영어 열거형만 줍니다. 화면 문구를 서버가 주지 않으므로, 다른 언어 클라이언트가 열거형을 직접 번역해야 합니다.

| nextAction | 화면 라벨 | 사람이 할 일 |
|---|---|---|
| read | 읽기 | 기사 본문을 연다 |
| compare_shop | 쇼핑 비교 | 가격 비교. 즉시 결제 아님 |
| check_finance | 환율·금융 확인 | 금융 탭·시세. 주문 아님 |
| check_property | 부동산 탭 | 전월세·청약 정보 |
| check_tax | 세금 일정 | 신고·납부 일정 확인. 세무 자문 아님 |
| skim | 훑어보기 | 긴급도 낮음. 깊게 읽지 않아도 됨 |

jq 예.

```bash
curl -sS --get "https://xenlook.com/api/ai-lab/xenrouter" \
  --data-urlencode "type=news" \
  --data-urlencode "newsText=엔비디아 차세대 AI 칩 양산" \
  | jq '.data | {latencyMs, sentiment, nextAction, impactScore, headline, dealProduct}'
```

통합 체크리스트.

- `signal` 또는 BUY를 뉴스 JSON에서 찾지 않습니다. 없습니다.
- `source`가 데모 fallback이면 사용자에게 폴백임을 보여 줍니다.
- `dealPrice`를 결제 금액으로 저장하지 않습니다.
- `recentHeadlines[0].title`과 `headline`이 입력 덮어쓰기 때문에 같을 수 있습니다. 피드를 그릴 때는 0번이 입력이라는 표시를 답니다.
- 벡터 합으로 파이 차트를 만들지 않습니다. 합이 1이 아닙니다.

---

## 9. Python 패키지

저장소 안 경로 `projects/xenrouter-oss`. 패키지 이름은 `xenrouter`. 공개 쇼케이스 GET과 같은 프로세스가 아닙니다. 로컬에서 라우터를 붙일 때의 진입점입니다.

설치.

```bash
pip install -e projects/xenrouter-oss
```

뉴스.

```python
from xenrouter import NewsEngine

engine = NewsEngine()
out = engine.process_query("오늘 AI 반도체 뉴스 정리해줘")
decision = out["decision"]
print(decision["latency_ms"], decision["category"])
```

이 호출은 환경에 따라 System-One HTTP와 RSS를 사용합니다. 쇼케이스의 `latencyMs`와 자릿수·키가 다를 수 있습니다. 키 이름은 스네이크(`latency_ms`)인 쪽이 패키지이고, 카멜(`latencyMs`)인 쪽이 공개 JSON입니다. 한쪽 클라이언트로 양쪽을 파싱하지 마십시오.

질문 JSON이 필요한 경로는 `XenRouter.decide(message, questions)`입니다. Choice·Noul 형식의 질문 목록을 넘깁니다. 시세 BUY/SELL과는 다른 진입점입니다. 질문 배열이 비어 있으면 판정할 선택지가 없습니다.

패키지를 서비스에 넣을 때.

- RSS 실패를 빈 기사 성공으로 보이지 않게, 출처 필드를 로그에 남깁니다.
- 상업 매출이 라이선스 기준을 넘으면 13장을 먼저 읽습니다.
- 모델 서버와 한 프로세스에 묶어 OOM을 만들지 않습니다. 라우터는 가볍게 두고, 생성 모델은 뒤에 둡니다.

---

## 10. Life Hub

생활 탭은 주식 판정 창의 확장입니다. 탭 이름은 today, tax, property, travel, shopping입니다. 각 탭은 `@xenlook/kr-life-api-hub-engine`의 도메인 위젯으로 XenRouter 판정을 붙입니다. 위젯은 판정 전용입니다. 축이 있는 차트 대신 신호, 측정 지연, 세션 라벨을 보여 줍니다.

조회 예.

```text
https://devhub.xenlook.com/api/life-hub?tab=today
https://devhub.xenlook.com/api/life-hub?tab=tax
https://devhub.xenlook.com/api/life-hub?tab=property
https://devhub.xenlook.com/api/life-hub?tab=travel
https://devhub.xenlook.com/api/life-hub?tab=shopping
```

회원 화면으로 가는 링크는 `https://on.xenlook.com/chat?lang=ko&panel=life_hub&tab=stocks`입니다. `tab=stocks`를 빼면 주식 판정으로 바로 열리지 않을 수 있습니다.

캐시. 탭 응답은 오전·오후 슬롯으로 나뉘고, 슬롯이 오래되면 자동으로 버립니다. 클라이언트가 1초마다 폴링하면 캐시 의미를 없앱니다. 사람이 탭을 연 시점의 조회면 충분합니다. 초단위 호가가 필요하면 시세 GET을 따로 호출하고, Life Hub JSON을 호가 원장으로 쓰지 않습니다.

뉴스 다음 행동과의 대응.

- compare_shop → shopping
- check_finance → 금융·시세 탭
- check_property → property
- check_tax → tax
- read, skim → 기사에 머무름. 탭 이동 강제 없음

여행 탭은 뉴스 다음 행동 열거형에 없습니다. 캠핑·휴가 키워드는 핫딜 규칙에서 레저로 갈 수 있으나, nextAction은 카테고리가 소비재면 compare_shop입니다. 여행 탭으로 자동 점프한다고 적지 마십시오.

---

## 11. 게스트 판정 창

페이지의 「판정 창」 버튼이 세션을 시작합니다. `sessionStorage`에 시작 시각을 넣고, 3분이 줄어듭니다. 새로고침은 같은 탭이면 남은 시간을 이어 갑니다. 버튼을 누르기 전에 페이지에 오래 머물러도 3분이 깎이지 않습니다.

만료되면 창 안의 시세는 가입·로그인 카드로 가립니다. 가입 URL은 현재 페이지를 redirect로 넣은 캐노니컬 인증 주소입니다. 서브도메인에 자체 가입 폼을 만들지 않습니다. 인증은 xenlook.com/auth 쪽입니다.

창 안에서 15초마다 시세를 다시 읽습니다. 종합 판정, 등락 색, 봉, 표가 갱신됩니다. 창을 닫으면 인터벌은 멈춥니다. 만료 후에는 갱신을 돌리지 않습니다.

면책 문장은 창 아래에 있습니다. 투자 권유가 아니고, 공개 API 실측이라는 뜻입니다. 합성 분봉(주식 경로)은 공개 시세 원장과 다릅니다. UI가 그 차이를 봉마다 쓰지 않으므로, 주식을 보여줄 때는 이 매뉴얼 5장을 운영 화면에 한 줄로 남기는 편이 맞습니다.

---

## 12. 쇼케이스 화면과 색

색은 정보를 가리지 않게 네 가지로 제한합니다.

- 청록. 브랜드, 활성 탭, 지연 숫자, 주요 버튼, 뉴스의 다음 행동.
- 녹. 상승, BUY, 긍정 톤, 기대 효과 단락.
- 적. 하락, SELL, 부정 톤, 한계 단락, 벤치에서 0%인 행.
- 황. HOLD, 개발 배경 단락, 중간 점수 행.

배경과 본문은 짙은 회색입니다. 카드마다 그라데이션을 넣지 않습니다. 아이콘을 무지개로 칠하지 않습니다. 단락이 바뀌면 왼쪽 색 띠와 단계 번호가 바뀝니다. 독자는 “노랑을 읽고, 청록 버튼을 누르고, 초록에서 결과를 읽고, 빨강에서 한계를 확인”하면 됩니다.

차트 봉은 상승 녹, 하락 적입니다. HOLD 봉까지 노란 글자를 모든 캔들에 붙이면 표가 다시 산만해지므로, 종합 판정은 카드 하나에서 크게 보여 줍니다.

---

## 13. 라이선스

듀얼입니다.

MIT. 연구, 개인, 비상업 오픈소스. 저작권 고지와 라이선스 본문을 유지하면 사용·수정·재배포할 수 있습니다.

상업. 연매출 또는 투자 기준이 10만 달러를 넘는 조직의 상업 이용은 별도 계약입니다. 문의는 licensing@xenlook.com. 저장소의 `projects/xenrouter-oss/LICENSE`가 법적 본문입니다. 이 매뉴얼의 요약이 본문과 다르면 LICENSE가 이깁니다.

공개 데모 API를 재판매 상품의 백엔드로 프록시하는 것은 상업 이용입니다. MIT 범위로 포장하지 않습니다.

---

## 14. 통합 순서

하루 작업으로 끝낼 수 있는 순서입니다.

1. BTC GET을 호출하고 `ok`, `signal`, `latencyMs`, `minuteCandles` 길이를 로그합니다. 봉 길이가 0이면 업비트 실패입니다. 가격이 96450000이고 등락이 2.45이면 폴백을 의심합니다.
2. 같은 시각에 ETH를 한 번 더 호출합니다. 두 지연이 같을 필요가 없습니다.
3. NVDA를 호출하고, 분봉이 있어도 종합 신호는 `changePct` 임계로 계산된다는 점을 테스트에 적습니다.
4. 뉴스에 `newsText` 없이 호출합니다. `source`를 표시합니다.
5. 뉴스에 세금 문장을 넣어 `nextAction`이 check_tax인지 봅니다.
6. 뉴스에 GPU 문장을 넣어 compare_shop인지 봅니다.
7. 응답에 BUY 키가 없는지 테스트로 고정합니다.
8. UI에서는 신호 배지와 JSON의 signal이 같은지, 등락 색이 changePct 부호와 같은지만 확인합니다.
9. 폴링 주기를 1초로 두지 않습니다. 판정 창은 15초입니다. 그보다 잦으면 공개 API를 낭비합니다.
10. 라이선스 기준을 넘는 서비스면 호출을 프록시로 복제하기 전에 13장을 처리합니다.

테스트로 고정하면 좋은 문장.

- 뉴스 JSON에 `action` 또는 `signal`이 없다.
- 시세 JSON의 signal은 BUY, SELL, HOLD만이다.
- latencyMs는 0보다 큰 수이다. 18.2와 같을 필요가 없다.
- 벡터 네 값은 0과 1 사이지만 합은 1이 아니다.

---

## 15. 자주 묻는 것

Q. 18ms에 끝나나요?  
A. 끝날 수도 있고 아닐 수도 있습니다. 그 요청의 latencyMs만 말합니다.

Q. Gold 100% 라우터인가요?  
A. 그 배지를 쓰지 않습니다. 쇼케이스 표의 비교 숫자는 그 표의 설명입니다.

Q. RSI가 거래소 값인가요?  
A. 아닙니다. 등락률로 추정합니다.

Q. 주식 1분봉을 저장해도 되나요?  
A. 연구 데모 화면 밖에서는 안 됩니다. 합성입니다. BTC·ETH 업비트 봉은 그 시각의 공개 분봉입니다. 재배포 조건은 업비트 이용 약관을 따릅니다.

Q. 뉴스 할인가로 결제해도 되나요?  
A. 안 됩니다. 카탈로그 문구입니다.

Q. SOL을 누르면 솔라나인가요?  
A. 이 버전의 라우트는 SOL·XRP 전용 분기가 없습니다. 삼성전자 경로입니다. 화면 버튼을 곧이곧대로 믿지 말고 symbol 필드를 보십시오.

Q. 모델을 끄면 문장 설명이 없나요?  
A. 판정 JSON은 모델 없이 나옵니다. 말투가 필요한 채팅만 모델이 담당합니다.

Q. 3분은 페이지를 연 순간부터인가요?  
A. 아닙니다. 판정 창 버튼을 누른 뒤입니다.

Q. 내부 논문 노트를 이 매뉴얼에 붙이나요?  
A. 붙이지 않습니다. 공개 근거는 이 파일과 쇼케이스 페이지입니다.

---

## 16. 변경 이력

v1. 짧은 개요, curl 두 줄, Python 다섯 줄, Life Hub 한 줄, 라이선스 한 줄. 외부 배포용으로 부족했습니다.

v2. 개발 배경, 기대 효과, 사용자 흐름, 시세·뉴스 규칙, 필드 표, 폴백·합성 분봉·SOL 경로의 한계, 통합 순서, 질문 답을 넣었습니다. 고정 지연과 Gold 배지는 여전히 쓰지 않습니다. 뉴스 매수·매도는 여전히 없습니다.

이후 버전에서 코드가 바뀌면 이 파일의 해당 장만 고칩니다. 화면 카드의 문장과 이 파일의 규칙이 다르면, 코드와 이 파일이 맞고 카드의 옛 문장은 틀린 것입니다.

---

## 17. 응답을 로그에 남기는 최소 필드

운영 로그에는 본문 전체를 넣지 않습니다. 시세는 `symbol`, `signal`, `changePct`, `latencyMs`, `minuteCandles` 길이, `asOf`만 남깁니다. 가격 문자열은 개인 화면이면 남겨도 되지만, 공유 로그에는 심볼과 신호만으로 충분합니다. 뉴스는 `source`, `sentiment`, `nextAction`, `impactScore`, `latencyMs`만 남깁니다. 쇼핑 검색은 `query`, `offers` 길이, `routeMs`, `fetchMs`, `verified.liveDanawaLinks`만 남깁니다. 가격·할인 필드는 로그에도 넣지 않습니다. `headline`은 RSS 제목일 수 있어 길면 80자로 자르고, 원문 URL이 없으면 URL을 만들어 내지 않습니다.

장애 구분. HTTP가 200이어도 폴백일 수 있습니다. 5장의 폴백 상수와 가격이 같고 분봉이 비어 있거나 합성 패턴이면, 로그 레벨을 warn으로 올리고 “live quote unverified”라고 적습니다. HTTP 5xx는 이 라우트가 시세 실패에 쓰지 않으므로, 5xx는 배포·프로세스 문제로 따로 봅니다. 타임아웃은 호출자 예산입니다. 공개 데모에 2초를 넘기면 그 호출은 버리고, 직전 성공 JSON을 실측인 척 재사용하지 않습니다. 재사용하면 latencyMs가 옛 요청의 값이 됩니다.

화면 카피와 문서의 우선순위. 배지 색은 신호를 빠르게 읽히게 할 뿐, 규칙을 대체하지 않습니다. 녹은 상승과 BUY와 긍정, 적은 하락과 SELL과 부정, 황은 HOLD와 개발 배경, 청록은 누르는 버튼과 지연과 다음 행동입니다. 색을 못 보는 사람을 위해 배지 안에 BUY·SELL·HOLD 문자와 다음 행동 라벨이 항상 있습니다. 색만으로 상태를 전달하지 않습니다.
