From edde23f15e9ffe271ca3716610d00a1294597461 Mon Sep 17 00:00:00 2001 From: hbyang Date: Wed, 9 Sep 2026 15:11:45 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B3=A0=EC=A0=95=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=85=8B=201,000=EA=B1=B4=20=EC=A0=81=EC=9E=AC=20+=20?= =?UTF-8?q?=EB=A1=9C=EC=BB=AC=20=EC=9E=84=EB=B2=A0=EB=94=A9=20+=20?= =?UTF-8?q?=EB=A7=A4=EC=B9=AD=20=EC=BD=98=EC=86=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 정책 변경: 주기 수집 없이 고정 데이터셋을 1회 적재한다. - data/gunsan-pension-keywords.json — "군산 펜션" 키워드·태그 1,000건 실제 군산 지명·관광지·숙박 시설 어휘 × 로컬 검색 패턴으로 작성 - 임베딩을 LlmProvider 에서 EmbeddingProvider 로 분리 (mock | local:multilingual-e5-small | openai), e5 의 query/passage 비대칭 반영 - vector(1536) → vector(384) 마이그레이션, keyword 에 source/kind/category 추가 - scripts/ingest-dataset.ts — 어휘 중복만 자동 병합, 벡터 근접쌍은 검토 목록만 출력 - POST /v1/match — 업체명(띄어쓰기 무관) 또는 문장 → 사전에서 매칭 업체는 프로필 전체를 질의문으로 조립해 임베딩 - GET /demo — 매칭 콘솔 (public/demo.html) 실측으로 코사인 자동 병합 임계값을 0.92 → 0.99 로 정정. 짧은 한글 키워드는 같은 도메인이면 0.93+ 가 기본이라 0.92 는 오병합을 부른다. 적재 결과: 1,000건 → 어휘 중복 27 병합, 금칙어 2 차단 → 971건. Co-Authored-By: Claude Opus 5 (1M context) --- .env.example | 12 +- README.md | 92 +- data/gunsan-pension-keywords.json | 7008 ++++++++++++++++++++++++++ docs/architecture.pptx | Bin 58519 -> 0 bytes docs/site-ontology 아키텍쳐.pptx | Bin 0 -> 47393 bytes drizzle/0001_embedding_384.sql | 22 + package-lock.json | 899 +++- package.json | 5 +- public/demo.html | 235 + scripts/build-dataset.mjs | 176 + scripts/ingest-dataset.ts | 88 + src/app.module.ts | 9 +- src/config/env.ts | 8 +- src/embedding/embedding.module.ts | 19 + src/embedding/local.provider.ts | 48 + src/embedding/mock.provider.ts | 14 + src/embedding/openai.provider.ts | 21 + src/embedding/types.ts | 8 + src/generation/generation.service.ts | 6 +- src/keywords/dedup.service.ts | 6 + src/llm/mock.provider.ts | 3 - src/llm/openai.provider.ts | 8 - src/llm/types.ts | 1 - src/serving/demo.controller.ts | 13 + src/serving/serving.controller.ts | 6 + src/serving/serving.module.ts | 6 +- src/serving/serving.service.ts | 95 +- 27 files changed, 8775 insertions(+), 33 deletions(-) create mode 100644 data/gunsan-pension-keywords.json delete mode 100644 docs/architecture.pptx create mode 100644 docs/site-ontology 아키텍쳐.pptx create mode 100644 drizzle/0001_embedding_384.sql create mode 100644 public/demo.html create mode 100644 scripts/build-dataset.mjs create mode 100644 scripts/ingest-dataset.ts create mode 100644 src/embedding/embedding.module.ts create mode 100644 src/embedding/local.provider.ts create mode 100644 src/embedding/mock.provider.ts create mode 100644 src/embedding/openai.provider.ts create mode 100644 src/embedding/types.ts create mode 100644 src/serving/demo.controller.ts diff --git a/.env.example b/.env.example index e3559db..4b6f0b4 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,13 @@ DATABASE_URL=postgres://ontology:ontology@localhost:55432/ontology REDIS_HOST=localhost REDIS_PORT=56379 +# --- 임베딩 --- +# local : 로컬 multilingual-e5-small (384차원, 최초 1회 모델 다운로드 후 오프라인) +# mock : 문자 bigram 해싱 — 의미는 못 잡음 +# openai : text-embedding-3-small (dimensions=384 로 요청) +EMBEDDING_PROVIDER=local +EMBEDDING_LOCAL_MODEL=Xenova/multilingual-e5-small + # --- LLM --- # mock : API 키 없이 로컬에서 전체 파이프라인 동작 (기본값) # openai : 실제 OpenAI 호출 @@ -17,8 +24,9 @@ OPENAI_MODEL=gpt-4.1-mini OPENAI_EMBEDDING_MODEL=text-embedding-3-small # --- 생성/중복제거 튜닝 --- -# 코사인 유사도가 이 값 이상이면 기존 키워드에 alias 로 흡수 -DEDUP_COSINE_THRESHOLD=0.92 +# 코사인 자동 병합 임계값. 짧은 한글 키워드는 같은 도메인이면 0.93+ 가 기본으로 나오므로 +# 0.92 는 오병합을 부른다. 실측상 어순 변형만 0.999 대에 모이므로 0.99 로 둔다. +DEDUP_COSINE_THRESHOLD=0.99 # trigram 유사도 사전 필터 DEDUP_TRIGRAM_THRESHOLD=0.6 # 벡터 비교 대상 상위 후보 수 diff --git a/README.md b/README.md index 280d18a..6c3bf47 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ o2o-site-AEO 가 발행한 사이트에 **업체별 SEO/AEO 키워드**를 제공하는 온톨로지 서비스. -- 주기적으로 LLM 에게 업체 정보를 주고 키워드·태그·Q&A 를 생성 -- **4단계 계단식 중복제거**로 전역 키워드 사전을 오염 없이 유지 -- 발행 사이트는 REST 로 SEO/AEO payload 만 받아 쓴다 (JSON-LD 조립은 후속 단계) +- **고정 데이터셋 1회 적재** 정책 — 주기 수집 없음 (`data/gunsan-pension-keywords.json`, 1,000건) +- 로컬 임베딩(`multilingual-e5-small`, 384차원)으로 pgvector 에 적재 후 의미 검색 +- 업체명 또는 자연어 문장 → 사전에서 잘 맞는 키워드를 골라주는 **매칭 API + 데모 콘솔** +- 어휘 단계 중복제거는 자동, 벡터 근접쌍은 자동 병합하지 않고 검토 목록으로만 ## 빠른 시작 (로컬) @@ -14,10 +15,15 @@ cp .env.example .env # 기본값은 LLM_PROVIDER=mock — API 키 불 npm run db:up # postgres(pgvector) + redis npm run db:migrate npm run db:seed # 업종/지역 계층 + 데모 업체 3곳 +npm run dataset:build # 1,000건 데이터셋 생성 → data/ +npm run dataset:ingest # 임베딩 + pgvector 적재 (최초 1회 모델 다운로드) npm start # http://localhost:3100 -npm run smoke # (다른 터미널) 엔드투엔드 점검 ``` +브라우저에서 **http://localhost:3100/demo** 를 열면 매칭 콘솔이 뜬다. +입력창에 `스테이 머뭄` 을 넣으면 (띄어쓰기가 달라도) 업체를 해석하고 +적재된 971건 사전에서 잘 맞는 키워드를 순위대로 보여준다. + `npm run db:reset` 은 볼륨까지 지우고 migrate + seed 를 다시 돌린다. ### 실제 OpenAI 로 전환 @@ -48,6 +54,52 @@ OPENAI_EMBEDDING_MODEL=text-embedding-3-small 키워드는 업체에 복제하지 않고 전역 사전 + 연결 테이블로 둔다. 그래야 임베딩이 하나만 저장되고, `강남 미용실` 을 쓰는 업체가 100곳이어도 중복제거가 성립한다. +## 데이터셋 + +`data/gunsan-pension-keywords.json` — "군산 펜션" 주제로 직접 작성한 1,000건. +실제 군산 지명(선유도·고군산군도·새만금·은파호수공원·경암동 철길마을 …)과 +숙박 시설 용어를 어휘로 두고, 한국 로컬 숙박 검색에서 실제로 쓰이는 패턴만 전개했다. + +| 카테고리 | 건수 | 예시 | +|---|---:|---| +| 롱테일 | 374 | 군산 커플 오션뷰 펜션 | +| 시설 | 104 | 군산 자쿠지 펜션 | +| 권역 | 99 | 선유도 독채펜션 | +| 동반자 | 98 | 군산 애견동반 펜션 | +| 시즌 | 72 | 군산 여름휴가 펜션 | +| 관광지 | 64 | 경암동 철길마을 근처 숙소 | +| 태그 | 63 | 오션뷰 · 불멍 · 애견운동장 | +| 코어 | 51 | 군산 펜션 추천 | +| 질문형 | 33 | 군산 펜션 바베큐 가능한가요 | +| 의도 | 13 | 군산 펜션 실시간예약 | + +적재 결과: 1,000건 → 어휘 중복 27건 병합, 금칙어 2건 차단 → **971건 적재**. + +`npm run dataset:build` 로 다시 만들고 `npm run dataset:ingest` 로 다시 넣는다. +재적재는 `ON CONFLICT (normalized, locale) DO UPDATE` 라 몇 번을 돌려도 971건을 유지한다. + +## 임베딩 임계값 — 실측으로 정정한 부분 + +설계 초안의 코사인 자동 병합 임계값 0.92 는 **틀렸다.** 짧은 한글 키워드에서는 +같은 도메인이기만 하면 절대 코사인이 기본적으로 높게 나온다. + +| 쌍 | 실제 관계 | cos (e5-small) | +|---|---|---:| +| 군산 키즈룸 펜션 ↔ 군산 펜션 키즈룸 | 중복 (어순) | **0.9995** | +| 군산 애견동반 펜션 ↔ 군산 반려견 동반 펜션 | 중복 (동의어) | 0.9886 | +| 선유도 펜션 ↔ 선유도 팬션 | 중복 (오타) | 0.9585 | +| 군산 펜션 ↔ 군산 호텔 | **별개** | 0.9698 | +| 선유도 펜션 ↔ 새만금 펜션 | **별개** | 0.9356 | + +중복과 별개의 분포가 겹치므로 단일 임계값으로는 깨끗하게 못 가른다 +(`paraphrase-multilingual-MiniLM-L12-v2` 도 동일). + +그래서 정책을 이렇게 바꿨다. + +- **자동 병합의 주력은 어휘 단계(1~2)** — 공백/구두점 정규화와 `pg_trgm` 이 오타·표기 변형을 잡는다 +- **벡터 단계는 0.99 로 올려 잡는다** — 어순 변형처럼 확실한 것만 걸린다 +- **적재 시에는 벡터 병합을 아예 하지 않고 검토 목록만 출력한다** (`npm run dataset:ingest` 끝부분) + ## 중복제거 4단계 값비싼 벡터 비교를 마지막에 두고, 후보 집합 안에서만 수행한다. @@ -75,7 +127,9 @@ OPENAI_EMBEDDING_MODEL=text-embedding-3-small | `GET` | `/v1/merchants` `/v1/merchants/:id` | 조회 | | `GET` | `/v1/sites/:id/seo?limit=20` | **발행 사이트가 렌더링 시 호출** — title/description/keywords/tags | | `GET` | `/v1/sites/:id/aeo?limit=10` | 답변엔진용 topics/FAQ/structuredDataHints | +| `POST` | `/v1/match` | **업체명 또는 문장 → 사전에서 잘 맞는 키워드** (데모 콘솔이 쓰는 API) | | `POST` | `/v1/keywords/search` | 의미 기반 키워드 검색 (어드민) | +| `GET` | `/demo` | 매칭 콘솔 (로컬 확인용) | | `POST` | `/v1/sites/:id/performance` | Search Console·유입 로그 피드백 → 저성과 키워드 강등 | `:id` 는 `external_id` 또는 내부 UUID 둘 다 받는다. @@ -110,6 +164,36 @@ curl 'http://localhost:3100/v1/sites/site-1001/seo?limit=8' } ``` +### 매칭 예시 + +```bash +curl -s -X POST http://localhost:3100/v1/match \ + -H 'content-type: application/json' -d '{"query":"스테이 머뭄","limit":5}' +``` + +업체명이면 상호만으로 임베딩하지 않고 **프로필 전체를 질의문으로 조립**한다. +상호는 브랜드명이라 그것만으로는 매칭이 얕아지기 때문이다. + +``` +해석: 스테이머뭄 (군산 / 펜션) ← "스테이 머뭄" 과 띄어쓰기가 달라도 해석됨 +질의문: 스테이머뭄 군산 펜션 고군산군도 초입에 자리한 독채 펜션 … 오션뷰 애견동반 … + +0.8765 군산 독채펜션 예약 [transactional] 코어 +0.8762 군산 애견동반 독채펜션 [local] 동반자 +0.8751 고군산군도 독채펜션 [local] 권역 +0.8735 군산 바베큐 펜션 예약 [transactional] 시설 +0.8718 군산 오션뷰 펜션 예약 [transactional] 시설 +``` + +업체가 해석되지 않으면 입력 문장을 그대로 질의로 쓴다. + +``` +"선유도 근처에서 바베큐 되는 독채" + 0.9166 선유도 독채펜션 + 0.9155 선유도 바베큐 펜션 + 0.9051 선유도해수욕장 근처 숙소 +``` + ## 생성 주기 - **발행 즉시** — `/v1/merchants/publish` 가 BullMQ 에 적재 (60초 dedupe 창) diff --git a/data/gunsan-pension-keywords.json b/data/gunsan-pension-keywords.json new file mode 100644 index 0000000..616bf31 --- /dev/null +++ b/data/gunsan-pension-keywords.json @@ -0,0 +1,7008 @@ +{ + "topic": "군산 펜션", + "locale": "ko-KR", + "generatedBy": "hand-authored vocabulary × search-pattern expansion", + "count": 1000, + "items": [ + { + "keyword": "군산 펜션", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.97 + }, + { + "keyword": "군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.97 + }, + { + "keyword": "군산 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.97 + }, + { + "keyword": "군산 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.97 + }, + { + "keyword": "군산 스파펜션", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.97 + }, + { + "keyword": "군산 애견펜션", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.97 + }, + { + "keyword": "군산 감성펜션", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.97 + }, + { + "keyword": "군산 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 숙소 추천", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 숙소 예약", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 숙소 가격", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 숙소 후기", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 숙소 순위", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 풀빌라 추천", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 풀빌라 예약", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 풀빌라 가격", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 풀빌라 후기", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 풀빌라 순위", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 독채펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 독채펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 독채펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 독채펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 독채펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 스파펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 스파펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 스파펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 스파펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 스파펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 애견펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 애견펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 애견펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 애견펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 애견펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 감성펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 감성펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 감성펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 감성펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 감성펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "코어", + "relevance": 0.93 + }, + { + "keyword": "군산 글램핑", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 카라반", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 캠핑장", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 게스트하우스", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 민박", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 리조트", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 한옥펜션", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 촌집", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 별장", + "intent": "local", + "kind": "keyword", + "category": "코어", + "relevance": 0.86 + }, + { + "keyword": "군산 펜션 저렴한곳", + "intent": "local", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 가성비", + "intent": "local", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 최저가", + "intent": "transactional", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 실시간예약", + "intent": "transactional", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 당일예약", + "intent": "transactional", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 특가", + "intent": "transactional", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 할인", + "intent": "transactional", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 위치", + "intent": "local", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 주차", + "intent": "local", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 전화번호", + "intent": "local", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 체크인시간", + "intent": "informational", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 조식포함", + "intent": "local", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "군산 펜션 청소상태", + "intent": "informational", + "kind": "keyword", + "category": "의도", + "relevance": 0.88 + }, + { + "keyword": "선유도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "선유도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "선유도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "선유도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "무녀도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "무녀도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "무녀도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "무녀도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "장자도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "장자도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "장자도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "장자도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "대장도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "대장도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "대장도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "대장도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "신시도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "신시도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "신시도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "신시도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "야미도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "야미도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "야미도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "야미도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "고군산군도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "고군산군도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "고군산군도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "고군산군도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.9 + }, + { + "keyword": "새만금 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "새만금 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "새만금 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "새만금 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "비응항 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "비응항 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "비응항 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "비응항 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "오식도 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "오식도 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "오식도 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "오식도 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "은파호수공원 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "은파호수공원 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "은파호수공원 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "은파호수공원 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "월명동 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "월명동 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "월명동 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "월명동 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "나운동 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "나운동 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "나운동 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "나운동 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "수송동 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "수송동 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "수송동 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "수송동 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "미룡동 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "미룡동 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "미룡동 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "미룡동 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "옥도면 펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "옥도면 숙소", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "옥도면 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "옥도면 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.85 + }, + { + "keyword": "선유도 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "선유도 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "선유도 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "선유도 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "선유도 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "무녀도 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "무녀도 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "무녀도 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "무녀도 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "무녀도 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "장자도 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "장자도 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "장자도 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "장자도 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "장자도 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "대장도 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "대장도 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "대장도 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "대장도 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "대장도 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "신시도 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "신시도 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "신시도 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "신시도 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "신시도 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "야미도 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "야미도 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "야미도 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "야미도 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "야미도 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "고군산군도 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "고군산군도 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "고군산군도 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "고군산군도 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "고군산군도 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "권역", + "relevance": 0.82 + }, + { + "keyword": "군산 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.91 + }, + { + "keyword": "군산 커플 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 커플 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 커플 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 커플 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 커플 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 커플 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 커플 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 커플 스파펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 커플 애견펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.91 + }, + { + "keyword": "군산 가족 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 가족 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 가족 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 가족 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 가족 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 가족 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 가족 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 가족 스파펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 가족 애견펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.91 + }, + { + "keyword": "군산 친구 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 친구 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 친구 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 친구 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 친구 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 친구 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 친구 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 친구 스파펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 친구 애견펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.91 + }, + { + "keyword": "군산 애견동반 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 애견동반 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 애견동반 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 애견동반 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 애견동반 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 애견동반 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 애견동반 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 애견동반 스파펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 애견동반 애견펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.91 + }, + { + "keyword": "군산 단체 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 단체 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 단체 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 단체 펜션 후기", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 단체 펜션 순위", + "intent": "informational", + "kind": "keyword", + "category": "동반자", + "relevance": 0.8 + }, + { + "keyword": "군산 단체 풀빌라", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 단체 독채펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 단체 스파펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 단체 애견펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.78 + }, + { + "keyword": "군산 신혼 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 신혼 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 신혼 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 아이동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 아이동반 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 아이동반 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 유아동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 유아동반 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 유아동반 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 부모님 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 부모님 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 부모님 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 효도여행 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 효도여행 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 효도여행 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 대학생 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 대학생 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 대학생 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 MT 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 MT 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 MT 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 워크샵 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 워크샵 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 워크샵 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 회사 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 회사 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 회사 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 태교여행 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 태교여행 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 태교여행 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 혼자 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 혼자 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 혼자 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 여자끼리 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 여자끼리 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 여자끼리 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 4인 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 4인 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 4인 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 6인 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 6인 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 6인 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 10인 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 10인 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 10인 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 20인 펜션", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.82 + }, + { + "keyword": "군산 20인 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.75 + }, + { + "keyword": "군산 20인 숙소", + "intent": "local", + "kind": "keyword", + "category": "동반자", + "relevance": 0.73 + }, + { + "keyword": "군산 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 오션뷰", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 오션뷰 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 오션뷰 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 오션뷰 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 바다뷰", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 바다뷰 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 바다뷰 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 바다뷰 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 독채", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 독채 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 독채 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 독채 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 프라이빗", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 프라이빗 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 프라이빗 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 프라이빗 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 스파", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 스파 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 스파 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 스파 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 자쿠지", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 자쿠지 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 자쿠지 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 자쿠지 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 바베큐", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 바베큐 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 바베큐 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 바베큐 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.89 + }, + { + "keyword": "군산 펜션 수영장", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.76 + }, + { + "keyword": "군산 수영장 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 수영장 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 수영장 펜션 가격", + "intent": "transactional", + "kind": "keyword", + "category": "시설", + "relevance": 0.72 + }, + { + "keyword": "군산 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 노을뷰", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 일출뷰", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 월풀", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 온수풀", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 야외수영장", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 인피니티풀", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 불멍", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 화로대", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 넷플릭스", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 빔프로젝터", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 노래방", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 파티룸", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 복층", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 테라스", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 마당", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 벽난로", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 애견운동장", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 키즈룸", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 트램폴린", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 무료주차", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 조식", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "군산 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.79 + }, + { + "keyword": "군산 펜션 세미나실", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.7 + }, + { + "keyword": "선유도 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "선유도 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "선유도 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "선유도 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "선유도 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "선유도 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "선유도 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "선유도 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "무녀도 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "장자도 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "대장도 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "시설", + "relevance": 0.74 + }, + { + "keyword": "군산 여름휴가 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 여름휴가 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "여름휴가 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 물놀이 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 물놀이 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "물놀이 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 해수욕 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 해수욕 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "해수욕 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 겨울 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 겨울 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "겨울 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 연말 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 연말 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "연말 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 크리스마스 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 크리스마스 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "크리스마스 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 신정 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 신정 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "신정 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 설날 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 설날 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "설날 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 추석 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 추석 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "추석 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 봄 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 봄 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "봄 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 벚꽃 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 벚꽃 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "벚꽃 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 가을 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 가을 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "가을 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 단풍 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 단풍 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "단풍 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 일출 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 일출 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "일출 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 낙조 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 낙조 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "낙조 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 불꽃놀이 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 불꽃놀이 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "불꽃놀이 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 성수기 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 성수기 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "성수기 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 비수기 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 비수기 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "비수기 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 주말 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 주말 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "주말 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 평일 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.8 + }, + { + "keyword": "군산 평일 펜션 예약", + "intent": "transactional", + "kind": "keyword", + "category": "시즌", + "relevance": 0.71 + }, + { + "keyword": "평일 군산 숙소", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.68 + }, + { + "keyword": "군산 생일 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.75 + }, + { + "keyword": "군산 생일 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.69 + }, + { + "keyword": "군산 기념일 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.75 + }, + { + "keyword": "군산 기념일 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.69 + }, + { + "keyword": "군산 결혼기념일 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.75 + }, + { + "keyword": "군산 결혼기념일 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.69 + }, + { + "keyword": "군산 프러포즈 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.75 + }, + { + "keyword": "군산 프러포즈 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.69 + }, + { + "keyword": "군산 100일 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.75 + }, + { + "keyword": "군산 100일 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.69 + }, + { + "keyword": "군산 가족여행 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.75 + }, + { + "keyword": "군산 가족여행 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.69 + }, + { + "keyword": "군산 단합대회 펜션", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.75 + }, + { + "keyword": "군산 단합대회 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "시즌", + "relevance": 0.69 + }, + { + "keyword": "선유도해수욕장 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "선유도해수욕장 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "선유도해수욕장 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "선유도해수욕장 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "새만금방조제 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "새만금방조제 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "새만금방조제 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "새만금방조제 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "경암동 철길마을 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "경암동 철길마을 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "경암동 철길마을 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "경암동 철길마을 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "근대역사박물관 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "근대역사박물관 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "근대역사박물관 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "근대역사박물관 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "초원사진관 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "초원사진관 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "초원사진관 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "초원사진관 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "이성당 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "이성당 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "이성당 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "이성당 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "동국사 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "동국사 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "동국사 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "동국사 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "진포해양테마공원 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "진포해양테마공원 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "진포해양테마공원 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "진포해양테마공원 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "신흥동 일본식가옥 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "신흥동 일본식가옥 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "신흥동 일본식가옥 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "신흥동 일본식가옥 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "은파호수공원 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "은파호수공원 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "은파호수공원 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "은파호수공원 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "월명공원 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "월명공원 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "월명공원 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "월명공원 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "금강하구둑 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "금강하구둑 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "금강하구둑 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "금강하구둑 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "철새조망대 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "철새조망대 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "철새조망대 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "철새조망대 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "채만식문학관 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "채만식문학관 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "채만식문학관 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "채만식문학관 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "째보선창 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "째보선창 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "째보선창 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "째보선창 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "시간여행마을 근처 펜션", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.83 + }, + { + "keyword": "시간여행마을 근처 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.8 + }, + { + "keyword": "시간여행마을 펜션 추천", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.72 + }, + { + "keyword": "시간여행마을 가까운 숙소", + "intent": "local", + "kind": "keyword", + "category": "관광지", + "relevance": 0.66 + }, + { + "keyword": "오션뷰", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "바다뷰", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "독채", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "프라이빗", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "스파", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "자쿠지", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "바베큐", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "수영장", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "노을뷰", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "일출뷰", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "월풀", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "온수풀", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "야외수영장", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "인피니티풀", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "불멍", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "화로대", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "넷플릭스", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "빔프로젝터", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "노래방", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "파티룸", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "복층", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "테라스", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "마당", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "벽난로", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "애견운동장", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "키즈룸", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "트램폴린", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "무료주차", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "조식", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "세미나실", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "커플", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "가족", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "친구", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "애견동반", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "단체", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "펜션", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "숙소", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "풀빌라", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "독채펜션", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "스파펜션", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "애견펜션", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "감성펜션", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "글램핑", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "카라반", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "캠핑장", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "게스트하우스", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "민박", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "리조트", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "한옥펜션", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "촌집", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "별장", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "오션뷰객실", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "반려동물동반", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "금연객실", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "엘리베이터", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "와이파이", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "취사가능", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "단체가능", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "조용한", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "신축", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "리모델링", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "뷰맛집", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "인생샷", + "intent": "local", + "kind": "tag", + "category": "태그", + "relevance": 0.6 + }, + { + "keyword": "군산 커플 펜션 어디가 좋아요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산 가족 펜션 어디가 좋아요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산 친구 펜션 어디가 좋아요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산 애견동반 펜션 어디가 좋아요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산 단체 펜션 어디가 좋아요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산 아이 펜션 어디가 좋아요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산 부모님 펜션 어디가 좋아요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산에 오션뷰 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "군산에 바다뷰 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "군산에 독채 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "군산에 프라이빗 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "군산에 스파 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "군산에 자쿠지 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "군산에 바베큐 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "군산에 수영장 펜션 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.67 + }, + { + "keyword": "선유도 펜션 어떻게 가나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.64 + }, + { + "keyword": "선유도 숙소 예약 언제 해야 하나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.6 + }, + { + "keyword": "무녀도 펜션 어떻게 가나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.64 + }, + { + "keyword": "무녀도 숙소 예약 언제 해야 하나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.6 + }, + { + "keyword": "장자도 펜션 어떻게 가나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.64 + }, + { + "keyword": "장자도 숙소 예약 언제 해야 하나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.6 + }, + { + "keyword": "대장도 펜션 어떻게 가나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.64 + }, + { + "keyword": "대장도 숙소 예약 언제 해야 하나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.6 + }, + { + "keyword": "신시도 펜션 어떻게 가나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.64 + }, + { + "keyword": "신시도 숙소 예약 언제 해야 하나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.6 + }, + { + "keyword": "군산 펜션 1박 얼마인가요", + "intent": "transactional", + "kind": "keyword", + "category": "질문형", + "relevance": 0.72 + }, + { + "keyword": "군산 펜션 바베큐 가능한가요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.7 + }, + { + "keyword": "군산 펜션 체크인 몇시인가요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.66 + }, + { + "keyword": "군산 펜션 주차 되나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.66 + }, + { + "keyword": "군산 애견동반 펜션 추가요금 있나요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.63 + }, + { + "keyword": "군산 펜션 성수기 언제인가요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.61 + }, + { + "keyword": "선유도 들어가는 배 시간표", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.55 + }, + { + "keyword": "군산 여행 몇박이 좋을까요", + "intent": "informational", + "kind": "keyword", + "category": "질문형", + "relevance": 0.54 + }, + { + "keyword": "군산 커플 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 커플 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 커플 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 커플 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 커플 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 커플 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 커플 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 커플 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 가족 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 친구 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 애견동반 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "군산 단체 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.5 + }, + { + "keyword": "선유도 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "선유도 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "선유도 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "선유도 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "선유도 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "무녀도 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "무녀도 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "무녀도 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "무녀도 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "무녀도 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "장자도 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "장자도 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "장자도 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "장자도 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "장자도 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "대장도 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "대장도 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "대장도 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "대장도 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "대장도 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "신시도 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "신시도 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "신시도 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "신시도 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "신시도 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "야미도 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "야미도 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "야미도 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "야미도 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "야미도 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "고군산군도 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "고군산군도 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "고군산군도 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "고군산군도 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "고군산군도 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.48 + }, + { + "keyword": "군산 여름휴가 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 여름휴가 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 여름휴가 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 여름휴가 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 여름휴가 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 물놀이 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 물놀이 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 물놀이 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 물놀이 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 물놀이 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 해수욕 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 해수욕 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 해수욕 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 해수욕 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 해수욕 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 겨울 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 겨울 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 겨울 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 겨울 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 겨울 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 연말 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 연말 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 연말 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 연말 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 연말 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 크리스마스 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 크리스마스 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 크리스마스 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 크리스마스 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 크리스마스 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 신정 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 신정 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 신정 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 신정 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 신정 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 설날 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 설날 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 설날 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 설날 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 설날 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 추석 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 추석 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 추석 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 추석 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 추석 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 봄 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 봄 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 봄 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 봄 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 봄 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 벚꽃 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 벚꽃 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 벚꽃 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 벚꽃 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 벚꽃 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 가을 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 가을 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 가을 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 가을 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 가을 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 단풍 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 단풍 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 단풍 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 단풍 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 단풍 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 일출 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 일출 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 일출 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 일출 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 일출 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 낙조 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 낙조 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 낙조 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 낙조 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 낙조 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 불꽃놀이 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 불꽃놀이 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 불꽃놀이 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 불꽃놀이 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 불꽃놀이 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 성수기 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 성수기 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 성수기 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 성수기 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 성수기 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 비수기 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 비수기 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 비수기 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 비수기 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 비수기 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 주말 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 주말 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 주말 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 주말 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 주말 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 평일 커플 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 평일 가족 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 평일 친구 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 평일 애견동반 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 평일 단체 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.44 + }, + { + "keyword": "군산 오션뷰 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 오션뷰 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바다뷰 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 독채 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 프라이빗 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 스파 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 자쿠지 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 바베큐 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 노을뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 일출뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 월풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 온수풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 야외수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 인피니티풀 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 불멍 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 화로대 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 넷플릭스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 빔프로젝터 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 노래방 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 파티룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 복층 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 테라스 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 마당 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 벽난로 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 애견운동장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 키즈룸 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 트램폴린 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 무료주차 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 조식 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "군산 수영장 세미나실 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.4 + }, + { + "keyword": "새만금 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "새만금 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "새만금 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "새만금 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "새만금 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "새만금 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "새만금 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "새만금 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "비응항 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "오식도 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 바베큐 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "은파호수공원 수영장 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "월명동 오션뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "월명동 바다뷰 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "월명동 독채 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "월명동 프라이빗 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "월명동 스파 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + }, + { + "keyword": "월명동 자쿠지 펜션", + "intent": "local", + "kind": "keyword", + "category": "롱테일", + "relevance": 0.42 + } + ] +} diff --git a/docs/architecture.pptx b/docs/architecture.pptx deleted file mode 100644 index 0c3216d329eaf3ac7f9eaf30f733fa52fde4f2ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58519 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૫h2M5zQ;n(-Hc z451&lQK@~#pveR`ff8D6{IXT8ons;Wnd~mvB+B)8mj%MP0kwpp?Ib%yoC9ahu`F9l z)HJ`eq19Td1F39?C~gnWa2@`izkLAs&KQGC{6Lt}a}Qy}(&#qz&I81f=+Onml;F`B zQu4?S;+^H{NVjVeX_!>nGcx!DI?AZqsx&D z{5aC=cF6;L_uuhp7y{OjyRmjkNVh%%S1+|` zyznC+l6*z@@V5P8=NTlOMu4h#GC*k6ks2TOGNGQsHXg`daDMO=QNXAkX<1lmm-)?x z2B&zSUchfeJmSGmgnnQ|GcsC^e^=SNhx)7D>tNp-`dz2CRU$ra=uFN&=C(hTwhNt$ zQSOl){+5wyKzIV#C5Ae8EvU8w@WQgRS!qz&g`zNk!wx4_ax0w>=R?R&Hl*=$&ANN-Ac3zX9q z37HL6L{h1vJX{KTnE7ecQ3yk1H%D+eXJ9vk8eBa3GOs$f0P=I43!PO%6UmTBYT%5a zX@Qxu&XO@({!AKoTCeprt+7fg%k_<(|LLad!?dS{F%@b&B@b0bV5f~o-wd@AVvS|v zRvUyTehnG*2WL(1d-Fycng)#(38b5h$b*V>~idp}bgr>giNLbcG;!G+2<69PV(q0AdgnLNP8f zf6xtEQ=Amm)b18zp!fCe^?`|GufS`^o*{-gy%~sMAL-PbB6xum%}7XGdk(iNKS(Gq zf0O^o*IBU45@Nuc6F#wiBaHRSbm~Z3d}dmKlRTB=M1PJRNLbyUpurks9KxyxcL~EHyq6+c5_yWy*%<;g+O^+NoLeNI zSNl(O46YyFAJ6NTN9|n+a2lIU6@?Ini+o`sMFTr<+*K#-S=<`0YXF`iKAuUF{f;h5 zqy=M1M9*X4%U!yx8)9v;Tv0*y$!El!?GF5HcaXyG?(jZeoZTI;J5%hr&5iB#u~t;M zr>~wa#ylMw zP%&>am2ac`BJo*kzT>YuAS9`yyT@oI*x)nIt{2O?e);*xYyV!WQYn=hbR%K!?*UO||X9mcvcJ%7yw0Qjp)Dk9(XDW+Z#TOer zJU8Y^40T+*sj#w&sV(xlE|N$`FEEOBNTnV@KOv>#zm>L*20mRWZ)|~ZEIP}ixHcmo zErY`orY^8>8k&T^+(Bg!SiDPTify^I@i8;lOpQ-nh|oWDMSaYcbw`;Vrs{1me1VS0 zz+gd2m5HZeN5hWxK}Mf*j$M-^2N zrx`5djz7XQ!?ePVub{*Tc*+NMZ-HlR=x_t@AbwLS(aM4S25x<6fVxc(60lpQ@dDmQ zc|~}d5x8Ty@bO+f}i6zs1>E9$orW3EGFFy zPE?ElSh^Fr*Pp;BkAt+7v_)JlB%m^^L{-L*+?fr!`>H~Zi@9_sZZx_QZ>5lgq)aIf zbETq`jKm<~CL>Q|5_ZjsrQb8pxcLIAC#l+;*IC>4`-1AX zn)J^HCsJbl5`p;1WbbWnKl-MFuGZ?{0PXZW1w5Z~-`o-JrKn1{+zf!dFmwS3Y5D8* z3`xfUlz4Y2Xn4a=IsJ9FS>4nk=*w?ZAD*SSQY+T{6?H)+z9$mO5Iu$z>8b_J8DmAO zpks9ok|5A+dZVHzS!2XwXraf^-#jKqqaE(Os2TI#I6rLI;KwzaPPG!_pVb1h3T*@GL_g#?y3;3BZDebmU;b zvuZLnA(w43ElKEqbobU#RjuFt|E7`d?nb)1Te`bJkS^(N0g)~#>F#a>DQTp;ySsn; zc<%ii_2~VMasRu&G zmW&g%#Hf19cQ$+NwX_9FU*;~a_OWzDtFaje+eA-{)Fde;E`}=wtCWJIw!CpCw3Bcv z`&0;b75?Mh2><2gq0161`*C}NTki;aYseR6j^hh|?j`V0;c$=s83j*9?Z+QcW80O(#ep=slP9_Y zTFldPI!0!P7c+e+<19#i0~2B`b<;vbMvyz(N}THD5lpb%07YZJJW+ncX!gRCvab1{ z7a}f6e0xNJsTF*mUFuKfY&M?=PF@wq+qOZvZVJT5Hxl?JZiU8!Ld^U1LBz+Cbm*6@ zu37qK2vC>;%9lR{?{f@C6e73k`*zGPKrD6TZ?=xx@<7`SIy23GVVx)hh_)W?O|p0* zq#za2n=tO;C8gGYG}j-rJ9xCA&*T%>MW*DivPre8onIZZ78^9}e#HkTejPJ&&YNc; z2S!jJmZc8?KGo-kAtJGzxgH3wxFH~hpxVd^+j8l4Yzw>el?Nc80TI8+_t0g5X!kae z(c>AzBr}_MF;D+!d;`;ND=&x{q19zFbwbF%m?AuxHZKk+yC<74Afrb4X+1}FDu82Y zm{w1#gCYE8mcousd)o7yW#Jjh-98jEw-ExI<-)RgdB2QE%{fg_ZU4_<=F8SD9Noq^;Ms zG%n7qw(U?i8TU80csD1L0v#(iR8)z2>rngNDCHw-Yx`%z{QWu?=`BY@GC>q!dKCU^ zDbOWzPK59?7H}#&yhJLI{@=t){geH$8DZGe*&Ic;o+OSZM-`s(p#po9vyftO)`n1& zND4(y@3T^Y9!3%QQ%y+rTfe+S-wOynX7oz4XC-AT~M3J?p_=`sjag{JMZmaqisD z>)s;T{nRVo=x7F*CUQM7yqe#ac{mJcNjVquDdt8+d;TD6zvnsMD?4q+jSsq|%CMfAr+g@JC!H%5DZ<46t-Ta|Gad+2r zbG)R!8nGquJpPNS!qRM~W%c$nNDGgB}@FV<$d2R{}l_E2qWP)&$7h6KJNZ9ETH&0wHID|ylMYKYB_T#m1|Qf;?VHHTJn5!GeEN(495 ztZ8^CBF>EgS}CRKrLUY1K1^@LBr~lTO^&N})fqTj!8aKb%02GIuKHG-H6Rm~uEdCY z)KF&BK51stK{dclyEr*cy#rjR%WRKc9iM|q%q$NbKCM+cex)%mkG?Rr%v$BLvB>ZS zTZMWE3%aC0oSlhsh}zM?|6@}(d{n0cwuS4`mlArTFJh^>+c^GFxh3&m`>44~5}bYB zn!fNWN|%LxVNgghNP%VBPC~ZsFYfJ+Yp%CTYMSM<-)BCl2?ZLi5E{$3t?!sioX!m zLyD83Y6$%hpyP=>M`7^J3RDqWuQ{xa~R#n7d%+0xCrmz_4JKv$?x*2fw3+O z^YsWDdM7wznT8F!P_J(mcqB?L)hDgFDp@A6%tawD?NG9np#4A(s(KPF!7|kH1GvpC_TA+!E*q{T`gBL&$Vc;dq>Z>}C^FKa$qcK%&Jm^eKV3DDgZJ0ubz`J)pvZ;u)m&jw;X!rq zeJv$-?S!6v%Qr^*vNy200~XtGjnj@Ftu7zz$hlaG9mn?@Zf~z^P*FnFM-jB)Xv3X& zxHY}KH9fkqv7EcX?oRPNke>58=Prl@+4tfNR1Kd>A!P>*4p+m)qC~}%lx(%=jKmd` zs<5sIDq%uq69=R%MJ zp8Xu8HqaH_TXitVxnuf@ch=t_Yvc{NGQ}(Tzqna*Kd0}+Z)zEFRyEsLr?ViBPnc+Q zG|X=4ft;PCM7#>2TlH=%?W-BBzVH83XGXh2V;a;E6;id3@sQoLR@68MkK(XMTOk5BtaOkWw1O z^9%cSiC7kgS|FSs+V$ts?P0LEE@jtWI-N?0otz`C;8?4DQ<&^z5)_9bGHZD^la=OF zj78yC9q#Mdih%cas6rM0M*AzKNj}0HgPsyF>qUMf3KtQ?t!35v=si-Fk>gwbsDumx z;!zz}yVRyMttwp|p^BSRUL>>=j};ZQ%wTKLSV(cCJF#h|q9i6OaVU1Pmi(xm$KmPg z;Gbr6(h$uz^FiJ`WIuKlBYz$a>g_Ak zrlYLTI=Z2xzoApI1+{4R#w0ayLUNc|Hcvlx-jfw`F@!jJ?_vfU1$7Th z!>Yc72~+@m)?Ee-4}$0z*4^m*U~(USee5Mja9bDgP_y~y*u1C%wUnqGA>@e^&UAZ~ zbg*XHXfOhLgqv#i;W|$~+-+m_CM5 zS!!AZo-}x5r(-*UBk@C6xVq3-`z+Ml(0xwjVMgCfr*$(3x4vh;4b1Wzzv0DZlXIRb zkwcAhV!l{K7N|J^e)8pS>!g|y%`Oeu&Fykp&7|O|u!vF0^_J-7LK_m`x)N;RM|>`Y z*9WzAMo|$6lEZgh!)lB3Ug8;pqHZJm7X@GVFPxs{b`~tE;_7c}9G3nr=(X)ia> zcX)4b?*|&1Wj~n6&zC+%vl<+z!W7*f?O$)_&s;urRs?s6xNT77d@`7(b?<|KC@`EX zQb3oqQUSKqIa(BPBNJ0$4nrd`GItVQw)Q$YF(6>?{_Gb@kWjF*iUzFh08aHWI7L=r z*8kno_=`7<0i6Q2<>+NXAa&J0(>gf!JE=+1{FqV-Wyei#5+ZRuPRc- zi_I;A`T>gwDzX6>7){M!xvy1B=cMZm_jZDby=+And)CI@H`6_&TNh8ylxVTz^mOc8 z;*CO8BfjP%yMr+_?qP@gT=bcW)-OXcJedlOW&q+;;|+P2yeN~V;a1Z9=`%IB^oj&S zR@71chQJ75b|L$IKkD(~0s3vA4#`(0p-BfOzAms6+)1aW6#>8~9wS0&ynVx~^ElYA zz9+}inTr-m7;?hNtrCXVpO^Z!9?;vUP2CVRjalz?QCY)9&A%nJ4+6)9mBN zf5B64Wz}>DmO~SOyn6<+ zeQ826u8ggS(H&q@LaR7ZcYhD_IgpG-g0s?I^iCVH3cnU%SdmDZqtDm9uFg<>Y6*8{ zSIB^d65Pr!@g{<#FGPgLE}sFsMiK5XJ98-iLy&3NH4BflI}(pP{`ew5+o%Iw1D|J7puPZihy?+1K3+-Sj|!1>J-wciJP-^&dj>MQ~iaFKnE z^($Ax%MqE|shLAPD@r+`R~8_g=cd8L6sz1HUIHsOhJyyntKV2*Zd;bl?DqR9HQWMp zR4Nz6J94Q;1vUx3Og@k5(o^?`nS3?ls$@Vjh;2?yTTHthI#8IKozI~E2&plyTW+GA z%;cPk4c3%9dmE!V81cBbD)Wi}EWKRIZL)%nkEJfl!#G3kVXe;bE6W*xt*%AeB3SFh zYa2t>W6s~EXsO|F2g6;hLRa}xag1)<4Y>b0DFt$fDMIw4MSoV-)iC2BzWTM(S9LGe zQE!+6i@hu-je?Hqk{fb{()&n-F-)Q zhrW2cEBLV!`uQ0iN#drz#H!sk zil9xFY%x{U^L_0{&Y00~0&tgaPG#xa6zQxx#gj<9Jcc*2K5&QKQRkn+|*rN|?7RgnY)3O2k|mce*Pn!1Ljo2H1!m@s5WihK~^ zb8r8u0E1zPuzz6Hz^wU3$>^>V9oHY?0bPQj0Oxa$*CO+Qq>qLAW6nAjchZP|b6vZ_ zc!!z0Xs+9~^4Mx9Q?0-luU62=HApy3f42^Et3p~O&MsD8!0O=j=VV;IRC2|wTmsJO zh=>c|-q8{Nq$6{h&U5n9K`9Z?6jP04)nJed!4*CDmH;!=n=ax|g{SpB1oNqkjz?91 ze#z%WpNC`kg-4vHL(2}14<8t;L50g-oMdx=B&>_dAGR>Ijwb z-4rzf9ps`QI(@^$=LRpEsaXzg$*{YU0T0|EXm<`U=p#%6_wj*UaYs)m?woqbjEeNF zxU_eIbks11>Hh{nr6Ym*zs%U8X@-=?);1%@b8}c$eGdQakj@^2 zs{B)8_<~TofFAC3676>)<#Mv+itntml^o5Bgw=_ho1)#rPD@I?c0s<18)}-^J1wiV z3h5kAu+S=Ej7Ka~<4C=c0>@B8GUuay;GIw`K=>aI&;HKq|h*dN=D{Kk5ZPG^(kv7vaO+&2)NBCvtdv8Bi5R=T$ zT|3R;6^xVgIslf{?d>gL8-8~GIrq^Se@n{F>gg9UC`oV*#IHaj!SP;jqS)!6;lX7J z-M9oW#(ImeiaDSIZ&|5U1_7I%zg;op_IY_TqCo$QKWCc^7 zeW=t!itje1e@MGfl2pYhah}3o@?=ga6FL|FT5YtGdgFeUkP(g+19INB}(snagjEsWAwNX z&Vi?S-lYo0PRw!2N)~Pq zoA}_0M#;#FNe=1&L-OjgFBx2e0+!%-?HVHyeKe!5NzE5~L7h7yKVQZgedCY|%W3ui z)C622POT6I9K|Dwg~cAXkGMR?_W0OZOL!b-%1BH1E`~$y==trS1dHGdb!FK}K9Chp z7Wg$&2rJjxo%%&~wrn%J4Uzr{woR$!*$TE#*Lt0EdM8yN`X!+37bv(qfTN5|bCix+ z5e*trLim-5%N2Q#cMs(zwrcZ^L_+mf8J(A{1|GP#QK#n7-4Yn!xWG2v)AgI8Q0i-) zFUC*pUv6qymV*}CcEyrKeT&xZ7=WyTDT$VDSL|=4EY6W{m3Y5lg06_{O3m+@`MfLK zKlU>F;ZUS;m$JCSJ=;wORvtYit@Zg!K;wvy7lVfxz46>!V&-f?SHPq_62{7egPjVj zv!0>$3k2MgUG~1{;~1@Y)P`Fe2K;+l5UD!w3N4YmpaqODKE-*nXl#&}Ky1RnDni13 zP*3pbIGe2tUVb*lFZsO6Y;Z^flS(x8=?ANFFKs~xt=g;z5=BNr6H06|YFyPLl}9 z{`G=H8ymSwQ{2Gh1>qbU^!x^9(g)Ca8~;N`$JC8a9WpKmY`Hw!1s1^``fvI5Vce37 zw)c;mG&VCY{Q+6wfe0TG(ryI)H&cV(L#O}$)Dqi`fta2GMv5&~zmF8Z*Ec^*n|GM+ z!R|f2N8@Qx^e}WplZlYYq>b6&qSl!;*dXiHKTay)tBo` z(Q_pz(&?7s1V49`Eu^zpRb*lZEvT**IjIhAmg@}n#_VoZ={fnVH+X8RsPMm>EnKd8 zo5qj~JAH6B#JxOF^lY-)1YeXZjIiljIC&u)nRsXm9lIGI^CZll(CR?+P8kse0popk zmhEo%QOFdD;3K!I(F%Q4yeA@LQP_aPo=`Jw!XeJ{Yin2y1Yj*vXh(y>DAF(hL{MUf zmeCl5Se+(?WVfeBGs zU3AqHHGC9aE*o$j$-4B9J{p(Y6P9of6(j|vu9~q8e!MhN5I=PvgGUR9g4QGj&&8Kv z(ss_SDMQWq@)$UF)uIn8kLg)~lO}-f@X~f_{vj2gPMwos=#^cPP9t$rhi=inX=z{0 zX{8O0n(I>y*uL5RTM;OZ?M}9@t;%%%@)wM%V_f{IOUuUTbFJftv=hg5fGwH;ug3<_8;Vx)H&z@!nE>aanG`K%yB8$Q($&>6v zf5!jN(rQL`w@v`9`ZRHdv(siVF*5I4c5p?&x;rI(oMGE~jO$w~RloSk3PN}RM)FS0 zml31#B>_8_YLAA{Cv+sKxZFV)r7FCA9501DnfmC< zKbOlxrx#d%9b@C!9|1kOrEDWjW4U;B_N7~mOkB;WtI0Cq0;QqTS%QV;fHO$sDy};W zO(^(P2@5iEa12saG)d*Py;`@+Ne{8|8f?6hD?n@anoFS!8*VgN)VHa8n9+K0FH%tF znN3B9qM=vyW!E}5H^cDAkPoLuuq~~KIdFL=w*ky!i>g@uJT#tYnJJvJX1uPj+t^UNkTj?yKf>3>4!Lu z3CQPWpYBDX8jZ-${Km>VkOySsEjHk~2t#ztpW-|TxhjtXC@;KB^pa5dQ($rqy(Uid zS~m3HA1=P=CpXUl+r5F1!4e_v9HAb6Db~MAuW7QK2S~TC?X%A<%JImY?9w8E`1BWP zIR?8{RXe`Q+j+T;csI`XmHw3d$mzfOQOb>gU;4C z))6JLqRJXaoUj}>)OGB^i$(cY$DfT4(h2lb`ZH!jQZz5oaiTD* zfs=i)A@9$kdhsbL`empRGjY56v=srnU&J7#*j8_q>sW1<*Zu6A+IpLoRKGeYx*FL`n%IYx zEd0q0U(>btiIYHJyxL?q+Mu520Z!{6kG)`R8+=b6rFYZgK45KAiuVD;Sm2@x3dKps!9g<5+rQx^1s<0Vqm=C8q3|15h_M|1* zuWvlqQXwkNa{#AGaa1YKzvnqn;EyB`FF|*w%hCu%h}9u#jMa$-?a|A?%|ek}^Ep3E zip=|2G`Js~4$%;WnvDV(8fEf^*D&H8Nhno74BV?81o8pjt^{R=8PRyLpk;5o%}N%k z?GNXauTr@ zL}d}>W|&ddU0TV7!Eae-HbSf8EBm<&P$MAICjzJ&Ew(f>;Wo$GxQ%03HcI4^WyyTZ zw?EZi*VQ+sFN7dQ7_2ez>JAN)#;i{#V@IXpzs~@Tg6_{K=`lf)WRydcRM8|d*-@EM zPPWb(j@67AV&`fXWj+~XmD~UHhNJ*o*tDj4IMifq=??ll(2$M8xb5Ze7?`TVJ2NhrpVK{UXr zgf-zMcB4O!UlT5+dxB%^l5z@gv@o2IRv@W*>kFAK_Hu=HoUxGLr!qp$f4eJMx}&gW z^F4q{*3tyyRrt0%pE=)n%!j{KO0m4bcp3dv*~P27yz6}BPXOY2$W~rGac>oG;{%J( zB97!UwQ`)jcVBu`tlCuB3pRH#g|jrpLw`WX{xIr4^m=Mz*YcrvaY@TDrzyX&F}IqZ zpX2>|cO!Ut>tc5g+ty+3MJ>psbL%_LS&t=fr`FZO)(ZlThe(I*uQvI+y$r3-=*N;7 z4AtN?CAgOahZKl4p_8Nv&nIYQWCijnsw$_^1q`Ukc(_%puyskFyo2wQ9|GwYflBu; zx)ca3r#xB@T5WC=6I+L?^|JBrDp?j2Wsng!&5c9qoL$w&yv`$?5qt$s)HrTy)sSvE zT=KL%!`3t$I^xh?`1&Ov;&5ATU(9I|y#y>`@(6V}UnSRGVQGIXxXCQ)3SGUp#5pAU z(D4cqRB)9^)Hf#7UknXb6d6JcVJM1}t`r~b(@eaL;l97}+e_F8Ub?O# zTJprJ4o{1iDNhSxpDY3Q=Zp$W>^yt?Ulm{bsol;0{dv+E$wGs4;KDeM5CDMjbNOIL zt2ZX5OyB!q{54UE+xvZ@lt`fuf@Jyn)I15sWK0Q+vy8ZL=X}>SXNKUo%!N|Z0?D@y zTP49n)Gs$wh$_(ryBXw0P+F!0lpbc|ua)sz0h23!s+c^grn}Icam1GS#o5`#+2z55 zgH+G+^~n}LHK*Po&DRe8&BJm;z;e1y!-Eu*-abCgO38v{`6+LJiJ*L3m3hsmgz}Gj zd0t&?b7?W)3Af&e4tm8M;1V3>+UQJ_I2jR}FCRtcPf$fdO^YjH*`JgdT2i2Z zK=e)(@y#e6Cg;2SLmZ?N+TQ+Gf31%nHY}yuY$AI}_23~HL`)Uae8T%6WnTM6(GV)u zv8!;bKY!YMpM=+t4G|AraO=rU^X&x|Ge6U!3Eb9-D1+Wb6;^9OUY!X@o`0pThx=@R z-{(`Xzv}6>Lf8(CKC;a{{Z&s_lLedQp)9!3z!C)UFntT+uX?%=#NZ3u)Ojp=qWg^? zbjuVGbJ#v;uP`4vLXY%gD#6kBNfxU+?M#)=Y6hR0(6HBn%@+_v&<=4maQ~B6Dk#SaGN_^)q>}(B-?GR-%aM zuR{SiceaL5c6vl3?UsiLisvg#L4g)SBRIrfDcqd5vdCkQ9JFZ-w{Yim&559&{E)fak`CfmUWfcjp3vPx zRCQt{r?a*ew18R_GWd-Pn%ptgIN?aaVzNN1V{?c=l_`PCUmN{*dpkG@xH1vd2uqqi z&^hoL_b@j_L~LuVZ1h%FL;|Lio z!3OM}8B2BGu@?`OI%!v z)S`Q0?~GK&tds}oR#xt{^B5^E2kSE4YWw8r1h@YTQH_$p0R31H9K%7h3$Z|}(2XiO z9yI9JR;%o(dNI!#U&c~F0s$dcSOP>t+?0^`$_EC?r}oz76DZ$1WbeBaE0z8w_Rk|l z8~c!#M;o_y8W)(*k3%nKmxVV@`KjB)U0q;nox^U#;JvkRB`VOLDlMwR^r!CiWgph& zi9WasI->{8^<4D@>zK64Oi6oNof!MI(1W^{lb(I3AIf$Qb*|DE1(m*9UQ^#^ebu3F zvzo~E;MH{v^^q-bpWO2XOmv{;I5_~k@j6V4`?uai_4OX?F0t?J3(rv;*99t`Np9idH-LV+BBT**L=xybl{RnBnDjfD zlvOt=w0z3-dfTtDz;es6^%{!FDmmJP$=z0AACdC}t~si!Kk5r5hhz_OH9A!U>)?Er z#AylVr8)0(Z{CB@3=6rI*S+)ruYb}1>`_4{$iO24sKp!cpm7z~4 zz&l5zyt{1m*yRM3>Zt(?s`JCnCRrhx08ACVSQQBIH^WbEC&z6s;;%bU(T+ks9%0(l z!XEq8pvE9(TbuI(L=QPmglcQ;1BsAA0JzcHwM2M?0k7dwPzBeQ$1|t8aJ&^y<;(2M zXG@426h1Rpk_LXP)NaOc1+|>u^$b5zzB|+{){t;*m3)aP0WD<=Tn94hkSYQ*v! z#9kuoQZ%m*K%Q%1S|5XVBFlAEjOlZ_t(d zdd35NhG<(D=xLlGal?pgrbx&olpswfrAHXTCtq>;Jj6#l)YsEVWf8h&ZebKR?x-Ir z^B?1vYJ46ZCZ4MPDxK@HO-Zg8{pBh{+u?;`Ot0qidF!!2yL=@5L)-FNsT9ZdZfwG? zW^lajetydFC7*N_=F9*W$VX!_wP>zg6LUu^l3|)>%u(l_QGWP2JRY=!VR_a>Hg^;s?!TtHSp;OX;mKw-xJu-r}jb42rFLxi9DeEr{#!cRBnlCY8QCgV-}k!O{!WMSo1~gh^k2OS zGg3tnKzYKsKi;5-${kM4a{7jwTQjUVr6pcvTqxo_horBHC6Ir05Cm@Zg#%6ANaYe4 z_S`;;4rw|2;-o5}-0l1_PZ6slU;#i9{2GHsypi!FWZpP24rHUDNKxhO1W}M5_8Ocg z(?v1Ol&J&*$&M9g;-v8F&u>e=&X#1$MiuSBrC=~+mYU3|P6p{Vr_#;s=_+VVR`d4u z&lYJhF&p~q(>~IDwli=3Y7#b?$zFyamU$Lf85@B~=_T?|>rq1q zol^XJR-P&Ak4SZ2J_TjakPdM^y<_eT(P3$1CE#lFbW$Kra!b}6?p2X|2JsM@Z zD?vMLCOvcR*pr>hQzoG6dkjAJr3D8Zz%>PjIdQ2mGE9&+4?KNd_`5BFJvOUyAFwUL zJD3Ro3)3O6ErKIxg6mY7A7I3gmS(kE%$;`Lz8twaY(|{exC{LCjQ!h}slBAg&NGa& zCQy{yf%!dhZJb?N#~u0&Da~~-M2=6qg>z&97v*sIQGSjU+3*~54XD-y!R&^hz&H-y z(sexOuQ;x>M95{q-bI}9iB0RTIBw50J8JB)tcl6yjUU2c-WFtNrv|VS2B`HRN*6gt z%-b}<1lt$lhnhZU6o18W!}>7EydS4{tcK)5XgFRXeqE5VGYvK%a_J!9&h*7)Lm{1oO`==z1gjx(5LTr!t@1^hX z5k_#}zSYu}&|4o`Ju9BU!sKD1C;MPOc#e{h7#YBuvF-*Df`w^9O_`4+551n02pZw& z#Yhj!Ma}VmZS-*RMB#Nh)RPDbb+&Wt@#>QEpa*jdZW(j4D z;$%AJ0S~g`H8h{Zr!$u=4Ygxeh+uEXSXU?+UAVwZ`JRM2OWRj+lB(-VXm12XWFeJN zq%!xKP}22H0?9dN;`Y4;3{Sgu?_iL1T=?p3@fQJ8g~B3{ba+O=TT<}gD*S}Euk!Yu zd5{Gmo4!URt>5|Cso>yax6YMzafG7AEcMmlzqaXAsF!@9X5O^7Axgyf9@WJ$)&OCeVlm5dyJImhN_>O&woDCff?vwdo7I7cP{+$|lCpt)eo{i_ zF==F6hsVJ|V--a0Q#Py%s9A_xP*-#ptV?8Lzm>pqPw?$Gf}^#~JkC0>7#`!=IE9j6 z-_C7U@(aN#vW*SkOtEn7V6BFl9NG=51|@{)Y8GV<`&d8PAg|EHwo9c@`aXDLDpDT= zwz1f|&$Jcvng@W!5Bq2v+aL~uX>_EI5f5w}RHVm;{L++&`rld>`2;X^GjbX9@##y) zr>moUhr5{i=!^sOcEP>qsWBkico`g9`I?h39U|OsGZRI{FqB6#5Zpaqk2+$e1^amc zZ-I;DkY&;egBd(HcwZY9Cg{kio4$PIZMcoI z$#tU1)oy2O`qJwxn6w`;aO=3D-9mfDEO)CmP(K`Yuws*6e?s7YisDr%IlX^c8QGk^ z?6BR@?5eqSrZdp2v^0skRJ5g*2c303ztp2mc7tv1=z?ECK)AW63iB?Wx(2xvRoDGh zjZ)U+e6E1&H}BZhd~V%40Dn4v1_bw|-_s$1N1SRgX;ap;PVn>Nhg>FJg1t-P>+M%GbR7*7h&7F@DGV;-I>k)eTIeA zb7Z;A)QhsaM`HQS!k(DlRAB;Riy$nvoBc!2!{W{s_UC4Y=71G|_P$f1g z&{G7SIcL#?`ul&0+sPBHU-rUtMuj?idTos1_TFsifMtV@?=>R+?pNzOgZES35g%^L zJ|m~9Y)bMR@{LvJ*Kn9>w9;ilJZ?369iP&U(i{&hA)k>>H9u6#*E@GmsSz-@1?>nR zq}; z&%$ymGiTUPs-iukBg8oL!u$BD&+Hu`epKwbQpTKF>%7F$^+zXDWVh@3ojKYphK$kp zMtKdS)6?oj24U#zwa}YQGtT*T!so4?-2LoO8&+UR=}di|GQPsbSa#~jqO!Z@Fc8Pj z%(?q{d3)-VX<?NlC*O)?1=&ePBFZNFZ9EmJ`u3JN1dFa z7!7^X!G^->c}^p$znr}Cxfu1%`+#Pv7dx8HgHAXZdxa`0k!`u6ANwQfz!mwU%XW zn~I9oQ9QZ@OjIKr0HN&BCC}&`pZ=$m$p0Zm{Qp_kEUiq0(rgF-U>y+vzywCdCbq^3 z4z_lV3{K{6t&MC9-&oOG*qNFgs#)5hs-p2XdvH)&4C-_K;xa>m6b>J8 zMCR~$I$;%q3=;@VC_HULOrF=w!d7749&AsI$t66zKY!&T7{wr>qf zq)F8ZfnXXE+dku744p?FDUh$?ATwlx$~BFD0U~tK2v>vQ%(QcQ8Q<*%@k33gsYDvyYjtCw?Gi?zD#I7SOqC?+iu!`*|GKc z6un65`smz*?aa0!t+cPsY>=2-S*efo{r+qL??UC>AhdH+5TR^7b-&sVa!uvX zA$UKcDmlb9rIE^Bp8M*3*Sq30bm#8IP(svzNDDTzGU<~beJW2WL36f;tBK%=>}{OZ{w>&8>6j_W)v|u@tne@nAs2-iNN== zm~O{x%wCip(ssidP_Ki7_$z#9g$1?4hFkGvW?aVtMdl+o@gt-Ib%&_AN#e;wwk~vY z4$58w&lXDNlQdg@S;(~qW0svt)K4s~6nf07B()Vwq67z76}2FN5rtBN5$kr^c$IUx zt7W!sKP8Bjt+b@XXxj(BO+K}cq<*UdmCoxjd9d=93%1fTh6aG5M%0sX!fD{xsPAJ9&X5MvnFvMbtKaaztwC02BDun}L;X zZ-84T*^(-aJ1DLLDuLDuL$Ar)mpe((6*wPoeAutB>#^#weOmGcsEfO;x11da?V1}F z7q$&n+#B?cL~IzYA+Pnu^%ieDyRY@eaFwO`3N7e-_06WD*dP==e4Kyh@4+rph#05z%M5nktq{+lPb+BvY#zP97-CHj5v&QS(HBt{Qzg z5k7?iSBmZhuQ#h;z&gy9bfQd-Wd$-_f(@C!Y^$IXBZNjKWQDO%?duISNpqJaa@)o< z3x&e&#?$)!@@JflcukQ9W+Jy9-}_cz3K7 zEWQf&;0cy!K1Kq^O;zE8u7L`?Rkpbr!(D9287@4H;B}49r@TUM2ElfqvbH5;`Z(T* z)7x86mi52XsJEA%BRh{=Qj8SYrofyE$L|`ib`<~6pT_)FYe{#)Y?;r@M)8y{0mbr- zW@BkqUY*S@^MYQY_6e)2S1~!)!oqk{-QlMYLN}*J;|~IrZ_btF1c=FG}%!m0~Gxd_aND#td|%6eOd%~ffjtCirko`B!l zMS;#e{JsbiG1p8!osV&oH$IQ5navRYSz1#^LwMT0Z{wt6my3@!kVgG1GuDd{KREc4 zp*a>#aPTvy=-pr53R1_qpk#k#5Jp;nv{5{y+a23;_6o>;vEV&u~LK zyWfp&ffIUb2mIi3cmUupWBm8w3jz%V0NB|%0b84!Is(t!(CLk>&F>UmzcYg|0)Boe zFaeD4zlQ_=^#v&czVnyD-{=0T&Tnm@lPZBG3KV6$)fmADe-C#uH?=keI(y*H?~%=~*K+3Z z{fAuO$=3n@{(KkB0%RoP zuPMqJIs)6z|5pd>zhC^82iU>s79MDj9^l2K{Yh>C?>BNk%18C+_ zpkYaVQf49eL0QJo&GxO+e}Vq}g6X%UzNV|8^*|kVAT8a`dO-RAocMq1{XqJo>Tgj2 zj|cECf!_rP;4Sg9P1#6)BmLdL|0VjnOY|e@&o)h`{EghdN&gc4EhvckPPjGDrU}4% z{b$e&s^2L80Q$Y3*{?U_pFyR8-2ZQr{?jDwSLJYnLoH*VMK%6qQ5oR1`Jm z{3`cn&?Bbb$okyE)w-)`6hxz^~=cm!5<3H_AVNey^(atK6SKHTixc_b)-(C_sSewF((XtB_5c8`x&%E_&3Tw zfPUY{_p98WLC3^?Blj=Re>MmHsys9hHdzI{^q>EAkB*D~M)?QO?`h<}%KaI%OZqo* z{{sCdH~m*-t%;f#Hy|j}zd*ZXexv*Y==Y4DU*-M`>Z$k}xqpHFlScHbGDi>F&3oWI zIs|-<{_LB)lzyZ91L)tmI=|}uNcyvHdIj7Y`LD;_zexX9{p}-vXG3a?3y4}x1^@t` z@&7q|L1fhbmbP8+D@x$4E7y9jK{@QK)A2kxApVYozi{E;)KLg!a z{3P)M(4RfnuN^c00Xnw)N$oq(Z@t!^fredvlK27W&z|bnb~pb3b-Dhe_8sWA-s#Uk zMgc!b`~dW4kMwJ;?|*>Q1AkKc4)j|u^k*R3$e$#B0Q$4%`L*8SKR_lQep34m^jmN9 zXCS@gpCo<&`m=}mwIa?xK*}jUseK3ftylRokbL$}5O@RP(3K!5fOzvgQG17y|sliGKn z-+O}}iGB_&_&@)o@dMHS-~oP3JNO5Qwd*Ijzmfi*59^ zYvst9nKQ>!kOl@p0RRJl0000W1W0ivgB}C~0FZ+N06+$S0MZh+vvoGHb=Fhyus3ni zp>wyfCMW;_qR0aP`uqC-cl|g11*THRm`%G8OBLi zIa*9Qi+#MvB+I3iLy!uNkj8V~e%`V;jN1KJWkc8jjoK3c&tGj00$@~!OpxMM_jM}8 zc`nnU(-e?^n1H2gz{0@4_GN;e6liS(#AeQ=rEE9?lQi@!;xHnTCDRAsO|3qG{SOyJ?7r*8c1*S7^f{N_SeGp+FSz@ zHz4J#$T=!Q2o`y;i)~PUENr`?^au7Ekpe^R=zzkyaU#v$av59KHR8J=-5r<#e|j!R zFZHf^yx5eD*mr}5^D<&o+<%hGGw@Vn$}?*IHSnqZ9cKOxn*t>dU`$U~W6tveC32k+ z3N9{=NvGuWJAN>KW^a8o#+PV+i|OO+`sUD56}X&>3cJEb5@;%Cp0xJ&qyIJApC4cV zh5wEF=C98Az<;@a@R!=qf4Q&cXkzU|Pxr6!|8f4mG131Q=v4`uQoxLVIS;x^IOW5> zNkz0^Z6|V;J@yV@#MFtsjU!RA(9@m2XJ9}Z*gZAHkH1TzVfSo{a+A~mtJ~jDAsDyX zuf9v|Zi|oBO2Aae&H*M51(#&7sI6O-Yc4`3>`})UC3L6J--mNkH04PU)(#c#eeO)i zdE_Y59bL9tN8Vm>iJk7tl$Xvhyh0(N>0qG!7;|2j?Z+wM6xgw|gT%uU7WGfGgQGNeBK!d#m~4J6Kv z;k!LpHfM8pJ-&W5-27@B)4hq6DiXgl_?L z*hcyqjX6An@eymJLz%!wjO=c7?x(FOZb&1i!YbN&ftR*x5c9v_c5!e00sT)NgTbk% z0^#qS1AG7g)c+m*Ke!qHh5L_&wB4ZyPA~Btu2@e&4Zg!?ct*{Fctt_WIoolcgQYxD z;oVv^xy1zJ6<`D^I0*v0BAkT8p8#O+;8!`>Uf_qiLlW7HruhLnDY$3%lb@`g+1@wu zbzi<{Mht=sQOI(u6J}xHa1cq1cw3#1vx4?x3j(9KU<7iIRR-M^Eyhwv*8cD=`tL)&BxI6KUWnrk=wq=aV6ax-V7~+ECcW2bEqQwZH zrd*IO48)RXgY2c0Ii1xiP9mPBZMF33Ot8IA(0@)e9#Vkm)VTjMk{7hpN1T zX)$OkN*VRSNkalK7g)#h=7xB1_fpA3@aQ{hZdh^#K2k8q4x|z8YLk|xUye?l^8oPCq9cUpfjXrw5v7hR)#$ywvY>6svQM!i8jv` z_)0~`tjv*)n4;y;1yaf;@dT3CJ|not-^t4+#{){JiLO`_*!>!PC=)V`nmvCKk$%Hb z8*7vpSJ&&;)42+?|3bfJ{E#82reebAjg=<^_MJlLt5IGG01O+2B;pu{h9Ez|wwR!- z?}mp%UWo#-k8~_2(4s4zj0<7ew9u%oex4uhSG#;HaM|MeRIjQ)8Gd^JG2d*QtLXwX z*DAU4c&5q57E6{tS!1KFIj@pF`|+}my3&N)ieym(XthYoIj;q}4k7SmR_OgYJh|?q z?>{@fDa`%p@_KqX`>5@|(&OLhd3S|7VgKlU_HBjN^gPT1z1IEadOxq+w6pCy3tM&5 z>+1PBHhtVYwR(tVwna?jgH(i=P*Y@Elea}B_ZVT5-F)iC%(=6fVNU*pc%mJR1A?!M z4p#{NrSu!ad5{Mf!?z$nF8ZyNSXXH;)qKL_mo0PVJ#+6g$S)q}$>SFMmU_MI-Ob%@ zJEO&Pz8dCiH<~uoMoUz2;pHZnQVzU(R2LBx>X*(X2k4F}`7bvue17sulQl;w+hvx1!qz*R5_Na()ji@xL6eCp~KSoAXpD?G@&{Vdrru|~K zlvFvz5kBkMX`vlMG{;pV!d)lr*`2z%&=5A7M|CtH1CjJkLM#HQRKIpBw%<%yKPjoM z9~ik^X$QsQ_(JGp?=9r&CATaEPw2?iBt7KF)mIsz^x@8k^O~V1?rPsZ!=V=JGMxq+ zZgsm>#o;V50-5u^eeX%g5|y(He3NRHfXA|5>{Y3L;^1jHeNC)!bcSh+A}>cOSn)NU zT4W-@ur)9Lc>(c~StHqk0sts3{m(9x=^q!Wqh+@_hU!Z{_rox!992WKBZ^s%-mzw} zQI*)-eP$%6oJ2z-T0wHPrbDwQ+hnr>sQ}z;NL~+EDsc!lRuEYYqD#JB5ZB~_Naob^zr{!JFZUVX< z7M=pv!Mg$lm5L(Co9&5541Ao>b$8J{P-kp96wGyM`@$kmkoR0J@FX$_9`TM|Q;|5s z5#1zY8X0B^To+uz^G*KDO&|2KDlm&3#b+k|bYw?C86CweKQ!V!Q^Ev5(iji1niz3PwjB(k~-`3!9~naPh0pxE~t zZdRg@6&BN4RuzF3ReOGQoRYLckZKN!s+qN8wz&7a>yGV7B_5IY0hcrJbn3pklgY&YLQv>ATc(ZzFhbVO63Q4q<<3qqYU*VYn&toqfeNr5E7JFimnD%*qd z7oIsaDk`8;r+u{BnpETZYIly~CuiD_TSiHn8IBdT8RDvQo(&fK-|%^g8PfPjvOIYa za2XhT``XjbPOMIE+O)SsmrOQcl@?OL48`uL)tr3mfWPaWK-V)6H@so*Ne2g%@f2W; zSV|XBXcx*IIKEV3dWd>c!3eE5APK@#kKcdC}yiJ9G z>3W~{C)JwqRNr`Yn9=iv0+XjO6d*rZoKGctTTV7NG&O^L_Bv=TcZ8JWhuxgtw2;lY zni_dkb1>8^cByXUd3A;tiBgM(^0{(OGy>!d9*E}4CoWA_P2?I#>a3Gy>S-G1%Jg&U z(HOD&@Hrf#>_It7cVCLZPX{L6U41XO`6d~7MvD)}&n*aN>T!@~1+aPO?$fo|SQ85y zt>3+1Ox+ki0aUM>9VaP{b+Dw~s%n7NFocz9rrT&>#6dP_zSr~*dZdqsV1||2+Zbp| z4soVQ4=uRTqZ`k0gzGV5awH#i3m2Lg2rnhEY(v2o^DqYv3`k!DjzoF&bp)k zrffuse*!?O^gcy}IOXNPTU;r}5}(n%X$S(q zlj_rIu6XdSRNVxf9t>MWxD~XFoDf8&PLv)N_2fc7b;LC9JbG2(p*7Z!lZoVvm;A7t zn7h%-$tVcRL`dN->0gjyIcB7FIZ;K=JZ#u{blGBEuWinGx~Oz0gQI8`MqkbkM%NtCJBM+v;b&mI&rK527)G;QYt z&N8pN)!+1Nxm4LjbRUGe3Hmh?MgGk1MjXT-%zD|qgcNn0K$9|Q7(u-&H8w;mtSA7) zf_*t%XlYdnmREfJ2Nyh&zGgdgFiyv}Vc*`zn)Rc%+gt9DbKRZ*8&1Rr+*QoVefA6T`}0G@f=Fp!e@(C5wA=WpuH?}7Eoz?1opmE9dT7Ak`ReOiWEZNCb0=Htd{irTEM+1}7DN2L5v z`!$nHK2A;^>d}Zk|hYQ_*x=bOBl` z&2syWl8e0K=hXsNO@B_@%H7KG_QcM(;1*P<#>0DF^pz0GlWqjh$lGm$eG`gJT|lH; zf9;=-1fKGS2X7zeKG!6T0-HTO$>bb4PkEl6Jd%h*h9_@k#Z+Fp0!Tvf3@&U>#f0or zrSY;e3(J~8HtKOT!r!NxoS}?D^Idk<@bcY!SkT?HF3t>prXF!k#@H}9ow{~o?@E3u z*UrOVK2Lt-pR5x6%R9a9zN593W~$9Aob>iEZ*{>DZd7l?eCdz+a(<49rUBqzQ+`$B z{T$+CIjLOA^`1JXiS@tTj(jWN-kH+9KIV8czr3k};r6_AB^P|G7@pxUgv)`_F-eWTg7fQQi1Hx$<4qv(f713@?iC_;t|F5hVLAl z*$l3Kcls>V;rC+IvtxN@)jsT;t?Si+Y?Zno?B}n*5(=$7IR=9Hch`#wz*Sj z%=Pjkox@%pdDrdm_=@boR-XBmZPe}(k;kwf(ndp!0fM~gcMT)-v-v+fJkXxro;tL$ z3f+xwz9#5K;LRWkaX3CW6~%yx$~(VURc~OIzR#=^i*pE54{Es_U?Cg6M2v9hlCx%E ztzSaagAcde{1yVZfo(vR9b#8G_;@U(s7^3HwO{3PN8(letZ`U~BOAROpFDjy55ACl z(AoB7*y-<45P8hZ{(YcDW7aA6JSP;*0GB71vS;90!yO8dNRJWhiSNCIyauvEAUEGR z^Ig)&o0AG7$l(s+m(5NxO>~=J=lAyy482;69SgY=9Q+3Nki^uCW7i!_W(Z(!%0=7= zU-+mLbg-L2$rE}6R-vJ&R1?z!0Xhy-7WmxPb0#8h^BOrffo3~uzFB#XAfk%K4VJ-Sj?3;N zdozC59CBK~G&F|rjjN-#nAY!5GwNc*!0R+(527u2mC)(6l`&4 zDVLxD0ICcBb2!ZWKf_@$G+#aaA7DcL03Cf~hfp_Sx&7A!S~i<4j_pnc}BgriE2%<3InJl}R zthbY_LMQxcH5gM-x2*;fxzj!kw$ZZ^Q`8Al3$B;9J00719R>m;sht!ExDs$92blkS2JGg>Y7`R0OS6&3g|XQ{Wq!X2SzwP+q= z4Msi1NmIIx-Sbo5N_85m_pIXWs@s7fO6Hq+Ifi!&}hreAbi&k4V43r77q8qOGwffB@H7?+_gT(#NT5m1Twr%@f_ z2GCKp6#Pose&zShCnm#P;o_QZ7*~cUAMp@n+Tc?vY!~|93P*MN#P(6^u*}l zHUNYd34DDmQ*JtRM|G$g-1a-q2ntQ^KU_$WbQVGI*=VZw?N5f(8q4pe(pT+QuFkVt zPSc8uDmeGHm2-Kiq448F3PszgbQh55RQ}^h%?Ig6C zo)!IOdlQIN{*8py57xfSkZMg^C9Jj7g-{FtL4yrlQ$Hj#A2sP|tB&@f26B4Fbscbf zw-4@F@DY2S71;U2m7?)NI4~2zY}h?BV(1Q%8Qp4nDXJ>G_A0^-OP!FrVedQA0-LRo zVMM37nXwK0S~FtYC;>B*cMo*>c6UDVD} zw0}!T3&n&^#1_D!Z$d&W3c)xHtwh8ac@D@3JIp5tt4;u1jIZDZasw&oU5eGUVP?6B z{rzy!hjF@yaT*%y0FV!W$`nRM@QVb(1i=6YDCc-Ak#Yoll*3;r+~HA!f*UV=5p&zY zvrzYlL@)zWbjhzBS49tOvo#Z8SAKA`sedXSo9hIrW_vxaAzNYneU~5FcMGCIOJyLD zYQI(Wr!yB0{`s>s9q~SwncRdW@T-;I!~!j+Gme+gu6Oj%-Hmt6Gc|wSo6U>sgRr@c zOtmGfcBio3t4(}W8kyVEZS|`&^ZRS`@5gZ=-t54+vQeBewj=2_Y1bZ|m789g8YsFf zQ701b7m1DOMx6ol z>2fo^g5GVCs<84=pca6Yh4h}L84fFyI!P!+C)D*SuA@|LYm7WJK0rfXxoyjwpoRV( zdL9_hVqoWX%WsAg5^G&QjPYY{Br%c-fI8^gD=g_!w2Ed*10jj z(<hT%NOI6)gQsAicIKcy#WT7f@~m-sC#JU(S~>L>rhYHlaG}q&>qDq_be{is zBK+C8-j`;SybxlYsK{h3R^0T(^4`WlOM@(SU{O;zpMpx@7&dAQY#0*8lOm+17>C+} zm`tk+j6+5t=!-)IK?r{x4WA2if`mz~^rG1@v2c6NU8J4mGqnZ>`>L#~|3I@4uogPRO` zn-O##*5@ka7$uG0)cST$29y7rnR zv?41Gl$Cg6(~V)8bUlg1I<@Riy-BFw7kdpS7gl_z2HKl}Sc$ zvAzTY2&GD|C@gh{u?4&KeL_uG&9I;WVj*VBJk8LrqcbT9YPMf%G8*hz*L|A8iU3Hi zs>2o&9HM~)$CDF+WBqHX&0*M;ifm7|oKr+}m>;uv-0nlllOtUk1O-_$9&+jSFPz#A zqVz`?!t8^E>UJ+j86WySn@-b+4Y8xFFa7rTK~~Rdo%YihRE*YM2dz1{O#So{NFvX` zdl)6v0BWHS`jRIaqSrwBA)q|;il1_5zT9@V0V!FrghZinkBE^-QOu7>RrL*ldnrmO z24K4|B$vxw3cmPsSB2W}$oOg)ljEhA+wI7o_lA2UB?8uhEaPlkAR^F#!KN<|^Zwuh za>Z*U;U(d=kBo2a7ri_9IP*%-1rbr>2ne*}fkKF~h}-l9cAO@)9$|Ot{?|y>5MWVk zC?0~*Uy<&{+eEJmBzQ~J;T@>3i`C}FiEcFKGg9j`jLcE^v6E!uXS-lfSzXWzf z9|AISsnGAqg&$(9!P`1mt!>>Nm7(SkMa1XkxCDxc&8u2e#CgP@?A=tv*=b7(KKGAY zRmFde{A^=*+W)wh@MR0^zhAZn(J!9QFfm}_#Iv3wMT3GxjaZI(PXLSVlQy&!7D1BA zQdlIUPnCY}s9lBGeg3}oyJr-%R(yduM5ZwKL)P7`*XcJ$Jjx-fCtV(8%h8bM0=9@| zW_m&|pThipmNCYC`P~}(wH+fFaE@f&wc6_Jz@48^ZG#8$BMF^KXR92gl||5Yc);cW zif`{g4N*YgpCk?d52~QI1=U3pN(b!hAdTLNukR4G9V2GzQTceD&eB1heK9$>dXu(y zIj#RfSTdRFygZ+ht}!W6TYkta=tAw@iTPP~!@qI=f>#)#Sm%_fy|w}A+djJIH|4e1 zrNrgy4-HZc2t*jR;^r@a1BPHKh(!Y5pyOIV**N13ehWp4WyVNiV&Nq90i=0 z^LKwrCZG=XmWY2IM37e55*&h2w9xv8)Yds`A`0DD6sCVD3M%Y(y6JNoI%i6$pkg{C zRlt*d*_r_+S-(OMCk=H~pF^833|mg>S`#Zerah*;I$naJJ&fCNqE=?Mcbp`*Uci!N zm5#7@cfer8V91ksUFbSd1o-8sOn=XIPzW_yA`IudmSsmk0|hlscnP*7Vq4PHK>m2Z zuc5KPea^e3D|D{otj{FnpI>wDNsFImd84_G9LBiNfe5mkamZ^bI7W#+!a>GN_#}-i ze(B@P%x8;gt1!|wGdRrtt~KIpZ04sVEqCE9-oOI^Q`BW2LXj{Kk$FveaE=nS4aVw< zHeBl)O{511aMT9~6bMj?{wV-}vCd{xcgzazge6Ahpt%k@7rTR<NGOWMS!1eM*z6%V)R0Dj zvscmJ0aPy=BL<(?YT1W*xupZJ>&!fXuundPnBKcA&l6vD8>2vI!k!{8_jCzeRFu_} zayK3&>99%1yTqaNyX1ZN0{^Epfbcoc*XwT@&=CKh(*TzLnFfqu`uwXZj-ZeSni2jI zg=qFS3jmYt63i-q7e$;yqP3Ear;_A;=0gcB)FG)4;OcZZnBE8g=7<2sP$zGh2XRrz z<0t)BW!#mnh(bE4N%BwGU;Tg|KxqNlmc8P%o!4%5!|5AFLgxYshF z+!^WVE&65zCfvjMnCNIIN%D@OjKcS0TT^Wy z#Wd7RGUja?D8kCNX)7)gnoF91SwRnFpb~dS;3;R`+-XXRjdDbp&DTUz;VdMo4^^_E zo^_C0Zv}kCCIoD@XC(%p&x@8Z@TgItaAabAs*~9>W_@jVMH3vOrBa97V&&VD$;-fg z7U1r7cppF8_bJyOwJUQTO5#hr4uvJeQHI2=Vr+V`)^r}m(gg#^dtzgzn4lR_sE|#d ziZL+a`P+m|oaM6CjWCsUmfUN$k^9#K+q<4eSGcd4m&dK00=FqGlU~I!`E*Od+46W7 zG@}t&k-24vE2`;-C~W>*^bwBBxe8@&5H2amkW2e^Y<8Wmknql%e0ZNo^ilV7m>_;0 zFW`RXw-8&KK)N@OedfmcHD%G}%b>S{}m=`%d(+bJ*6}xWFD*uGgepsazHkvSgiwM4AM3 zY#yL&h!h7PYR@>u5Ic(%9F4ZhZPA|wGh!YU{FsWF*6#HMjtgThqp^MCr}yHoaf^1P z6#1aF8kxG#u!lC3Vs^SUz15dOeaX(tHp0ZaVi)fmx+MzY)`V^<o*VJ4lcFo%2^R&hpPu?qcBY%hZZ{8*wr%9eGN#gCKB*KGG1xB^_ehS)+axO(EsCPN z6*mKhoPjv3d+yxR{F$pWM&R!sd<9>>-Je@z61=GgJK~MBzAMolkXYLhA5ZC6EqA>jE`E+-(Jb^Q@e#&?1N^If|q} zmMF3mhvqd9OMtx*5y95}jq&VHYW8px;bmh7jHW8$uY`OlBW;>H7_5zJCDZHp6669%Lmit{Hvp|*NX(R#S$oc~$)kiLyySsXRc%!fD z4->o7?jLVZt8y>!o$F2KH~mqsrUSbg04C;ABbQj4tY7I%Llc@lv1SC4h-E$EPu(ST zy^j6OE)t4Vvj@7yaw6@O%+$ma(_)F#iHL9R5Kbva?;)amCK5P?bsX}XFBVSM7egZ$ zGd|8SM&e9*G^c!vOuKUs*I4AKZyEU+jdsnn7D{A$xtu3nUZq9%~$m3qH^Iq2M121C(7AiZ1_qxi#dfjYral(?3 z+M`1^^^)MXcNJy#M9a_bncA)4dq#=S`%&bpDw~Bz83~u_HnrQ36=vGUR-Cx(H7!@N zqI`|bF^a4Pil)ycrdF+2p*^H+Bw%!>o;{HHd1!P|bJaLQ$A=Hfmd*A{yIxBMZpA83tgS08D!J~%q^^eidv zH2qi?Nrm%JZLft}l({cmyZN{KhOGD}#Z^gDrr5)x`S)Sr8?}7vC;7*BcMx&>XK&X_ zZuebkOWZ{%R7ANiL^e%WV9#*0EfltY2@=mC~e6#aqUT%Lg%+`_8&*3$np zP?R%$GOb-FhHd$SDL#4b#x$qV;eVxlVikUI+JU%Ce_xTeBF|pRgrEFIRK@N>oC|qo z$>Ild98vJjt*U)MIh(4cU4gsjG(w+w=QLqVk$A2k!P%Kx;#U5))t6Wkv2 z%~5kQ!;#(jbAggcmCMOhe{$q4rX&KokLTg1x`%DLRu>JFJ6j>K#h)I$Sm{khIMn)+ zUp#l1wOQ}=tjF`uLQBdd20vl9>s9Ltm|hdr74uRPd_X>qHFYM8bKCY)aMCIBJ%ZH- zRd@Y)bu%Yf4I*W4g33vS1HaepuJvZSzRM9@V*?l>D$M_KkMKHs6Jo_(i|=XotoTOZ zY}9yUM+4x_TgKzzFW0}yi86WT8Nksy!|y%vix{W!i!stprix#xYarr@x-=q?NK}u{ zNL3I9)ocUz_wCxh zO@9Biao7lb7?S>P56hCpe~#AwX@C6lw})kG>P1g?v zl!5Rpo%xdlnDOLd9?0tw`zs7p{Z2Hcb_M9-mMFAjdfCst0s&tsJkbk&XH)m}WnB~6 zWQk{ix$E$(OLnuFtoDmPVU@7pReJ=IS5rBI-P)ogKBY7Mvj1_NHr88!!)v({{N*v0W6Yl4X59&Oei26&l%X=|SVVI){+ z?*b_^@d@eBF`~Vx7w4zNds`YTn4U2<)9W#V-w+qli*f*Jam=deiKk1P#Y((xEgQ$M ztCnlgzKCLvI(Vo~b7{gqQvQrC?bPT%e*L9(lJ^hPsX+Q#MSN6!qOgXpSnOW$9!7p0 z)KiLAP`b0(=8ny1ENATXxa!#zaT7X)xvDA6%MfYj!i`@`+TTU7dQKiKu{V}E0?ovV zniVWg-NOMM4mEK@{P$`B>Pbx zk`}fg;R1}GO%XuafHR`(#wSsZ;&I$5j^K{dCb2A`ELU^`{&4B4`IQ~MdF;9|sHXv< zcbTuV_4CYlr|+BVsiqiRaC4<@?X35J|R+3N`ofWuaU|q$mS1V zAjir1e2aSBQTu$disSj@@%U!)XahVz|B-BIea5#Yi(M7G-*ff;;9K+h=3XEI_?RB& z_h^DlxKjI^_VhM0E5MC@m$$XKx@r+hjTf z&Zr5YwNLNEXm>FLd=rqyF7?6efa!EWOk$BuVb&=ss{j-~C(01a=yWA6jOj>51<+Wf z%3HA?^Y`}$$FSKl_3hj^)L)yHo1K|{T7{--6TGX0EX?CBoRXRKUcv^q67yN|n1LX) zDY8&Xb@;7t6l1ZbRL4Abu(J1ps~tjC%lg{Xjq%kZ!}qlkzR1E(FVPw8dL zZ9nMRou>yP$9=ryr}^Hr6hsa3Oy8Zz>FV@Op$R4@GIG4vY1fY=Hf?TWh`HXs z#!?Gc;!)x?k>^n+*^pP3b;3FAl;Kxt&e!L%>BKpX4?M01PgY|vt7EB~TgJYP@YHyp zso2eyc7e&BD)^LOK!KSEpXUkWq=?CBe4;(up5yctaqtcXH)V1pmUSh~NKPn@(>A8j zM0@*n!!F?Xkn0@y_)Kl@5SrPBf~>{C0=!vTS>7c61RTpi0JbW$Q0<7EYG?=^GH;06 z$svH)f~Lst>du^80Ix>5p-v^r<}A4qHjZ+yd(0Fvi9)Bm1C-w2j$6BZ3lxRyBT4K7 zUW-uHP;Y^eUxvw)x(XvRWurY)q&lHb19}%ZRl+;p>-y(FZF`9H1|bUKl`!ES_uz!} zwg$`sE?t%g7F<6(MHH|~2R3EJNUYR|wUrP9OYL9d@VVkghFMW}%B-=kl$wtpc6Emj zbmDcU$YF0>IF5tZY1l2+>)q;W9Lu{~eWPU{@RagXs)lVUx-!aUu@aOs3vymHlLz2L zjie9BA?Jt`xDK#$6bbqm9c3k(cKH<+MBJcjD-@9xR!8iM6bM+>&};e;2WxbAuHlr4 z@K6y?V>tMPo9QmJYWzj`(AFg@R4fNzEfVMv+e^tojzwJ@BI3Jxy2b#!8C@K~Wt@Rs z;c8)HQI_BoWjWZ^=t|`W2#+1M~+*4aF1=x|HGU!Wh4yOyT)7e65#o~wG&z#}C z7Q=dWDWWCA?giWmSc{yglE7_6LPOknm!*E&8IXC>MI6E za+=8?qG$`g8H-e<%RNo+?%6e1$l6!EPHu!*a;)N5DWf4l~EuuOrAiTtjh|1ngLl-VG89CWc}V6D4ICk z7kP8f>6L}bWhK-Nmc}SlkzuX^k=_cy}iZ*q+%;A*<@ zb>=KW3;I4eVu{Y3ZIWW6+q19SiloX^fV|mROk(*f2^Y>8YWqu+3ha z`xD5pfxL#j_LSFw2Mq~JfCaCL(-II!s5|C)1*@q;g}|V^MeHvKm&}@hQ#v;Yp^WXz z&3yihpAy%c#^}vHRXO)9)aanHj@1m7s3W7oy2{Tv@Qyxh&6k{p?zO9LQrvpqIxZ^J zAgaM=6rgj!x6@_r5^G=Z4p@&rvg{IfwSExvynzh;`2HmeOM4@5f;g=}WOHk0vJUms z^>=qG_HH*mzbEcbeR8fbX)XJiOaF)8J8-vVnl7HV(*tnaXE7@OZp;mTvF>8n-49zh zyz9I@mdR%U=(BFk=%##l=}nKU=Ipa>m+3F~M(PN`mw8Wt2ZY_tb+3BagI?8%=D|8M zNQjhXkR=bl?jqr0GwH0VO6!Pl7lx|Vec1c#D}A1`Y{)B!Ttdxpr!LRlO6y&OjbSPU zz^kB_Yh2t7k#gt`sb3f10o=U2w{rfO^tLe|t$8?5(|@SH>;#;>zR9U+ zYQUg~abz0Bd#+ZL1)SaU9pdc&b6d#&nat*&s__4XGMoQ1^#8QR{I^B_PoMmMTlD`3 zt^O}WztkE7Nd8w^cl=jmh7SM%_%C_gzjbx?jwVijb?FAq7IwD(+6?*!{9l2*#!Y1JyuO{C!s1NPdrInGVBjMG6B@f+Yr{;joh~=B8DcL#~ zW@ou!f9}*&hoRk7hRvWCbT9Fc;O?fXfSp1ek;K^p)i2WmdZU4s|<+EVmD^^ zLhve-2B!f0u+_A_z%FvU5e$uLBaU5B!kTrftw$A4RXKB6a%8T__Jj1 zllqh_0AxG^g!CJ`OXilRWnMm^Hf9NSwoeL8R*2vg?(Qat?Wai1?`^TTDQJbOz*Eh9sf(cX1Py4MWE`l_(00WFrQ zNbQX>-Oz^YKKQzq%4<--dQVPS>^(Dhuh+Hg_h|ZD2{&%ah>-dn2ie5coTqQ~bXmsP zkw9@n_AqVD*Si%keR|407sI$`jj5{WGQ$fy%D%6inxJ^JEpGB@lL?*UXQ?<%h==Nn zzQbD(rSW@ut4OE(!eLvQ);jAvk@OcN{mR4`Y_w-Wv^sUE<8+y?`BJQ_uzznfh1%lA zH^-GD!K{+nNp9RxI9jpDjRbYMzy{wOUQ=1HL^j%Sh}xMo3^y6`?(A&eQ7JiOhapL+ z(+m{D(T{e^MsL7T@H9c9XFZSYnA1Y#d`U;u_)@SA{1U8)$}l330BDSvu+ySx`E7S4DVuh&>&tIccp z>`bJbbkW1-Nln1bm%PqBey=_#;nXNFe|dyp>OpYws}u7H7;$=u?N4gL`_??2oyDwp zI^XN9cnaZkKj*COJZyMwkEKfDrH^$kl!S4U!?T^R( z=-VNI@#c@YWsqv{nrWfB$Liuvs?h8G{McNj|G^EY^khgiG2cV4{zKY?KnjvT;&4`~ zXh@DXW6cv0_RIkd%nc2OQ7XwI2Y#BnT)a70sIA}=^;5|3;s?+F2BeVOzDJuoo|yIP zs}!Vk-%W+3Ye^58r?vNoKEm>HaO{OK=B|z&`PEeHs+#IkWf-0q`9{L2eTk&}aaXCL zG^3&CinAf0Y;F29vC;0Y-}tYdg;3av74mP4$BF&_?^*u6o1n27Q^*~XRAFfw$k@yOGaAeP zIykDDA|)Lz8F+7{(Ys&_d4d?3`1z*!%>#@2D+L1714b!n(8>cg;`8og6rT@8IAO$1 z|2LY7AqFRApp}Q51*3{8(zF5C7%G+iOwKgI41h`NtHy6ecY`&J3akv%N=ymIcFow2 zD)B}Qq<+RoG|VXr$Up-qipAWi#K0wUQT!$#;Q{@j3!UV%4*it{bQT?1y}7|fy9Scr zy9C+Gd)P+)jH3FaplDeT3Tgm3h>2ljB^kzG?<9J^k>MiuA;bQ!dqio;DZq-522I|w zp&^?d6kr#-Hz!_@!DSe;&xtO=>KYTYCe|O-Hymw6N-FX~Q??BLrZKUHO*D(<9f9qQ zBrR1cH}e?9OlDbgC{iCI)oOamYlX)U2~NXj`v>U1yGcz+YS{5@iSj&~0# zEU}C7s@|v+--vHpP%&$Ddv*3U2F#WQ(DSlbL_EIBAPpY>7}R5hJgzdXNmP?JWf1X@ zU9RK#YO|%qQyJE^aQDnxN?c^3x**Ov3{?`t7dtDi8mN+~ALov1&epsCqD}`alPIk0VJGq=LHKAD=%w9) z3I2I{Fv;O@x@YWL7tq&YAu*9Y2Zuu9^+vY-le0Y6u!Q;Ipf*>9`n0^Eq}5YR%*AiZ#ZhgpN@)xyf(v5Y>~GP)A-q3Katos(tyV?s-s0xRRlf{V z3OJE=aLAY#4d$j2Mh|7!P`CUVQP*8=MDw%VqzQ0<=#^~fU}EZC%Jnocr;%g9qL_w- zBg9bc(7Mm2Pl$$~2798|IF2dxsxab|w|If2nb?)^2WBkobj(IUX+e+*M`Fx4&*M6T z8jId(o>KSQU*FOJanXLkP2fL&66g>U8nrMEnI5kB8@3JS?0`HvwK8>skb1sfsP+Qw zC6B071gRtvELZ9<1F8vo(`u1yBtLGSM@|m_6<7X2Kk7bi(SZ-hsEh+tbNgo+8pm(5 zxk~-~w?XqJXmw#|);;fyM3nbQ>=Ub;wG8_jq$4V*68?Gve2`qqv-bM#t(5yY*-TTm zs@!XDtWWn8H)m^BjvZ|zhiFA&Ljr2nBLWHCq1b3D>ES(viwPukj8*`(h3Vc|xbxHy zp+;LMF`7YVvLr7jYL2P^|^)wU8t=ck+qZj_# zK5`cvzb68}F3n`_^lNWD%xc!QPe~#D3i_)2B{0Myv5=Y)1jj& z;U!i3Ehfhye}UY25N6%p-k7>iOA|k+oJgUWd7Gc;26c61P92+CQ_JXN{fB0+GNOhi z1nOfpJQ;taons)jz-pzVe-~O$08KofsI{0HQ7`Nem_RIjvc3*De2U$a5=oKD11 zb#toS4yMn_2s4bUsMd&p&G|_?z_rDEY2*1N3a3G9@Pzv+M$gBJGt3yL{+45VaEh!o zXpvxtSk0@(BY6eV$k!Gd<)nD!mfh&rdB*`@UXu2~gQSaSq;jE56rk|0Z$WGRuvFFAu$-N)>cl2^S@;JnRSxK~Tk~e%xo!HvIVzP-x+)JdA6q1^T>6 z_{iK1vTWJ6@25G#NjbQ~O3QL3v#psFN*VI6zldZC!|n&Efhrhi6H~^U$Qqj5HRQx6zZjXk&osRadrSc8Csdfty1F`zGPMu;8^?%oB}!&5!ony=Bn;76FRaXu#x{}^mQwQ)A1oeD%I1<}bA zgUShAFcDJ{)WtjMrN?>dUO*+cBellu$23nF9@kVxA#L&;s6 zQGNpbo-Nrc!>b9}jU9q3D@Ovxm6#TtR_US z1d!{91+X!n>;XlKu79FRB)3|&Mey*%??MzV+s zslvu@-Im(o`I=f)acsS}sf($f@JsrvUOI=Gl+bqI#jr^D`y>7*K|Jftc5&<@RYxE$;~}c+3N;sHo|2Ux1?h3Wjy|uUO|Aly{R-%@nymvL)!F5!0PLhN4HIb09LmV$D!_?(?irLlxYzW7lkqx(nG% z3nsQU|My-?;3xIy51F}!M6_sXq@7l(S!Pi!n%RnLpCGU-!;rc<38aJX(F3Uo;N`Su zy7n3Cm>svVy|B@rB;P35Lmx;VYzZ(14SA$Gi6jj_Svc(!BfC0q97!l;43tW#<9VrD z@1a$Eai|m!czwTpygY>Jw8r2?Hxkv8Ts+T;DFc>FEe@hmU}1#a?TacsF6X3%@Tom| z*x{N^l-kVPI6cN0>Q^>oE$voy?+d%C#QwQZY?w&DC^ee#tNxPvB>>!_sg}{vOs#4 zpnVXiq1v6hAyHqsz=SrIo)Y-kL~lMUtb;Et3`5XcH!Y;TuW?k$tvmxWv|@{fTuXnF zad8?k0;5d2Uoj;=Q1wxHV@v$a^X^80@I*<)l+#iqRTen50y|&EtyhzO?v6rl3xyrC zXbAn4gS1f;u4}F}N9h**`}n68xc+&WWDV=(T!do<^9H^_)I`16^&J0 zC<3fbAS0RMDi&)SbCw=dT|4zQRLG~f)FkpxHW2Y;y{pwpX1;73vWOPmMW?<((-u@Q zsv+6;3%YHFF3U=-58alt>;1s*DTz7-wbVn%yMP;Ou%>+A!a0_{#nvR ze4Hj;4z&j`Eaulq*0P=&^j*gRHwm-xLj%~U2sOdG&qPI%v)6Y0=hYE*SOIoxb~Po( zTKLACyQ!@Nb#9{d@E31T3L|K0$ALom0?k=s{Ux?4hRC%r4F~bY2)yNrpLIelxvDb(Ql+r~hJ zE5gIrgc#iT@>Dc$Tt=T#(Gbe=qo;s|yxoH@`TCujEg;j(m+eHY9r(o`IW)v#=oOh& zbP!Q!I_`Cu@z>ecoqe_qojPXNIyk??E0cJU${%!$^+bNA@}hUxzW937)DSYkPkXGT zDnZ_WHPB-5nIHG`@zeZvA;RwZ`VN2P>7(pAXJ>x!HZpIrcL2I0r$BD4MLK@28F&uY z*!02aYQ^3SzEgeH^m4;yY+MpNhG|@_#dPAd%iHqtRkzmT=Yw78n{=YkhY5-ATRM}J zCeu~JwwT9a7s!PKZctZZbo+3dqMjIj?Mi$(SOJxKkR}SSAj69V5iZq(38=#+{qRXE zPZU3GSR_;S$>pz~5_j6kQ8Vt*6*CeC!NnKVsoc>|^6ft>L?P_LB39LZ5k;;I$_cGa z7)VMa3wCVv5Oew|l8jwgP2lhzi7tzii?O@-4hb)+iz!VuF>Z+_I5V=+1fHA(RV(SL5DBt zr|*iIdVcJwOr*jJ?mzW6*w4jJ7!-&L)sbp*1)QIO0@p*inb-DY;nmLt0dwUk3 zOMLks_)fWhcZnTeSATbjp%<{X_+WS1v*LC`T%ar6QbYN8>dO zg=Y7BwO$9)cRo!gQ$VY9n<&T6;2xX4<4=MMB{sEDk_-&(q@X~*?IJsocBuGDl>edLDiSDsuD z>?!)_=BXFOWA0R3N%5wem*IYwTmLP0q9L8Bv!~wMU?|)>et~N&GRPYfTdpzo)T`MK z&pD=qvuYAvRV}4K@aE0@~W)XMVEo=z6yIf=gv$sj(D=?>oWNEE?a`fJyvy zjcvG6h@0|9j`Wx3?3-*g9`r&@>S-FlAnHREF*s6TM4+2Ci)(GP0hLQh7+K`%AmkSx z#FShnYW5MNqQLM((9CM@;8zOH+=!a&+(_L?#tWJxh=)UJ1cbJ|32Ua@{*+S zfKXJ#$?JxMSvxMc^$(l_3IfeL^|Xj2p_Vh*>j|BJS2r}0?>ZO z{N~w3n4J=_&lMsgxaniy9ctlJ@9uq;EbQJA?bnnw2QMSDebjAp1xRAE1CD`O!Cg%k zRM5d=^H~#f=OHG#Jbajoo-?EDLK>a-7y){BfO*Mlshk1Zc-s9&7yjB zBx~}=G1&}Uf3B5pxHtVb!@JNL6Rm(aKEW(En`V>*Or~~~w3E1tkJtO%mu}E1wa-=t zlf7Oww42Gv>@vjhOmMoi*CO<6v$7)dJ@XW-%d_UOF^XuH%=E`HQvdt7Py*g)nciRGLWb@h)6%M<$tdiJ>gVv_|KWF#lyXNcO`m@r zN-pca$hygn8$Mh-wVPCw2rlYK*V+uRA)edo$eVnd@M@Vl8~LQXUD6ZHX=;8R-l#wA z_kOI#n^N_?m$yp^iV25%khM~|BAX|6f>zn&bFa=^PfKDH4y`BpgFNx2L{ClnZu_r+ za%Q`#?GCPeK1P|0B=Tuab}Lmx5~zNdoiud2{qUunz$F+G;GryEti-jZczUG5B^_97 z-r3$*i%H@;??IS_EgX&|Rpvl{qFYF;c|6ue3LUcv({_yDqtcQWwZ_VO`LFX1?Yse& z`Lhsc*Ku?F;>)(<K6ch2zlPmh?T1YDin*-=UB{))$hzkP~mAt)sAy37wY_w);TO0enOF?%8DVI zhUMWxLxQ@%LbJhCB2lq@Ks=DcQzH{CPpx=QpNY_2haKfJOVz)fuM*gx$9@#|HL;^Js{FLZdLH^ilxF}!#kLmdeso3-+bQVd3()DtF~)x?-m zw$C#6I6q^)@G0FpLXvBpYvD{+ECwoXWZ;Z^YHw3Eg>VML52mTPt@)J;SV^_n4AWa* z(vfrVmMfF`getW$DEoV7jhmGSd_QL*{MSs1A7~z$TR-E*@A%1~hZ9Y+PGxS~l%jcj z8>xy>eAM257y=Uv^?57&d=xjD8Xqw=H664Ro>`ZBdq-y6=JpIwg3QY;8e)rOwc)s{ zx2@M5Xg(EZ3LqCY9Tq6#Ob_lNl;~AUHvKXwo&6bfHk)RVG56zXfBevII!~=3niPTU z61O~*vJn{#MJmyA=XYF2KYq2BrK=uum8`Mhk_BpxOuHy;Irp|~4}4x1i8bs^JsLF& z6K~RyeG)vaLlu*|Hp~bh+Y%(&@{@c=t^HujMjJ~e(=l(Ap2js{CdRlLsMQRw(uat& zs1A-_*0B>74jic2b?D)$g2WLhcpjX9a{h7JU7KZE=rlmGDdGMHlKm$KsZo7BcAWzW zFelrOXu+bIA=Pv=9AERgg6i8SgLUaev}kRJB8kjgY&MCIZ{=H-w^UfNsfC@z?zz2z zs9tE8%M`Zq&Be zQVnl1tIt6mw$(_6Y|g>MOVZQ=&Uji?k}><}yF~#2zlU|zmeLR2;vPca_7Xt2H$i2$ zqOR>}C-KR)wF-vCWU!4Q##$IGUsL$qS&FU-7W7bb-)|c}G z`Min+OZ>=vSHd=?n-$xM7#Fs;uI|CYbAWh*xy`uS21M}sxZUzff}o#|?_4%(ZsbJJ zt1nv=WcvaiW{R;_aJ zi%3e9$BLr^W^+wXmttMxp8OTEhLM6AxEJj-MjYp5qa#KwDO!CHvIvXBtat_&kQXxY zEi@a7iU-!rdDa#_&qcD|^!_`4kjfXfE8a-hdgI36{ns={=~E|Dw5o1?*{&boi141{ z9?@I%Q9g2go$MAve{qh@zs;B9yzF6r#A+~u@OO=6p}|dINpl42`&+{K;5pS*4D9;{ z5rQ7r(l4AC4{U)9SU*8+&ASWFTm)jP?+IVwT2IrzqeSZY2IeA_jCY-_&)>GYIjHk9 zOkbewq4@G;3>p|^$XVFqtu0@%)$ejaox5V_u500#t}ehQ@xcl_d^DhxRa8)XcI(F2 zbFcPOC^aV4s*FbYaW<|eheGM}dPKxi<-lR6vS}3HC)jk_t%-7Zi(?fQWc5UjlcX|fLOati`| z6|eY(P%Q>7I>#3AN+*B50PD>Am0aXYhD3gLSP;?3oM#4njo>N946JwvQOWdc^%<`! zBiFIgX$6vOeQ_I&<_3E^f(2p3Ru#)z&7nK~3B;)i*R}UgLb#+&ijitUx#qRU)&oRR zbO)yHjjtr-lMd9ZwOnj_dNFvP*dDrib6){N`G1enF3u)ibpQtV-^K3f|ItAIUrOHp zW$b7EyTnA8zTcJa^)B)I&MB&c!r9!Adwa!hfn)d9AlAwD3Xaw@X9I(Fecs-68~o%7 zJ(Z0yJ3_V9iAFLFvoF|x)C@0TfLYvP3~x#GXJXx-JJ0G`4(P^uIwpuI@!4QG$MP1h zz_AgJBaNm>Va^4&CYNgO@kj#ZAS$+9rPaTvG|@`gOALAA^3fkvzU04;!U7oxTk=)FIJj^+#m{|B`RXi=F_lAgIC(uxs2|l@u#diZQ!Vb_yMV`-rESCfz^ArQ zG-Z(pFcqJ+ps-&Uv36_Esb5NAij zjdUTLLS7gUb@Q)l>4ulO05=`K@O6o2PsHbYolUYj#vHtMr*d8=AC)Bdw+zdK>*u|LkNU@!SG0ofP3&=R1 z{2h3cFDwwGi!BxNc?-5vG7vC2k3s0D&B~~H-XbVPjXu70eGID_@=W=@GYA=i73MAb z?BfL)C1BAI>1C*oTh?3SXT>=T(0Oke_V+ir{>0w6)w~KH{t|n`LfI)^fy!ihcq=&+ zVq{%IBaA-?PmU3HsGAcdIVDU+{7dXjNmJ5`>6 z!`Xw48&s*!abY${v@4g3RA@ZYEAV@mzU|)hm(}>den@dCi6iLn)agTHhg0z!1g9k~LnoRHk1A-zu(ZB|m7KOD$gH#3G_zuFdQ zXDbD+3jgxrzA@KFB9wysXs)imhLCwZ-F1G;O%yeea-N!$04TUsJo?(n(&Jq;-41nN zh$co;E7Vitl6)-n`EpV-?RH-t{L_x`I(jBRW)zf7aEH^`GoL;a)M~JLut*suOZN|C zgXe9s#ReLt2G=IC30kVBPhg9BRH$})zn_)@R9t}|^>Y>k`k9{EWCC|fT3ksoISZ$F z^771QqhFz?tF#^|!wH!Z^Gp+SrYUlag6Z4Ch=(D7aNctX?H(PX*9C7PTN*9$U zudG|n7GVThM_o|1AhviNLS{3;%k~0d5Eb?qSY1qI%o3Wf$#q!DSc|5vhdNpHo6gu- zg8!(?%*gD(o`8J)_&>H=VI=)CB>*nD2Bd#3x_`#83C(qYOYX1Atr(^XChjR69XAb( zX_8^~8<2Q`oe{Niv1RRsall*kz017}PZ8O8YonZs;NxIX+4e*_H|x&K#pVS~XtOFu z@s`SXyCc2mXNJhc+LEPGKm`-}1*$&SKCn_18wK3cxBWQ5j!H^++L}>)TQm^-j`aBQ zCiRLOF8QQdE3ph%B^88=)KBW7y@k5mA!G(Z9)oy4-9$=B$XnXpuGwD(owW{J8Ls8T zTCGzg$HGBgC)=tr^>yBG?3#+;%b423BMsfeys0me30NUIqAgvr7@p7n7zvH^xkTT;Ur_GCy%!R}cw?$s^2UgOh`6H|m*H*?dHQvS70P_Udve;d6bfsBTnR5sQ6nw_DgeAcnV1B|oBj=hWWsx#FJ25iVVNRRMqfxNliG;Z z9%YOLhJ*7GnH2`6?R5oQQs~N@+~h#mkJfUGYz+nCu6rIFkuF8IiB@TgAdet;y;L}k z^~eL^k*Dk(2puvZb^?26k%+gW5cp!HzrHhn2ed=)qrvIplp9U~in5)c9d<|NBT$rsul zN|X+-%U_iUIx4ut@4ge|qKeJplXC09s{7~9QX|}c5=Q;NWg7^le-Y_~|M@fLWaxzU zOCGsv|60_~AhPVq4X$ssJ6e{*%m`UkUZL;Yn%b_@_-clkn=zT-Y zCpQr?&7+8?H22ePB$u`D;4piYpYAi0C0?E=nDeq97r&Cw^PUH1zW%x&;Lh~Y!kDr! z!R1l@eon#FJS<@9fCAAa80&;axk_w-d(I8%S}V7B@$>!h_@zZqA->Z5=puDS!ho|P zGe3r|H-QX0%om5!eP?@O=*G!(x}NDz&$9C4Rn9a(0hRgUI*hvu%rq z{fdcV%K>G*y@C^lO?mn*MXjj0AD#Ry?ev*(*CVhdoG^ha-VJy`)r9cca<89W5J{5= zC4Ji2rIgbhtgTZ|5^LM=utgg$-g4p7`~Yh6e^;ul3Ji$R0pAP*(hB}gjgm8RaW!-P zyLF`ga_Vn7^}wRs@71Khw|w$Fm>S~9c_WgNM^=MBTPV{E||)dZXj}583-)mG!@4?_Zsh|IKFk6OjHl(^CKUT=~7U9K#{|4aG%Yq$Gn0lWXw;ht$<{I=UZ1Pm{TED0f( zm;6S{SO?@c&?VRNqzL^}Ep=e!tkBNM@gZ*U?;*ZK+5Nny@=3&3D5B?W&b*yg^nd_~ z63B`5cxdOnKH_`dHXo`2M_1Fo%sJ;fS;5%1O4#?FYcMc?*Qv8pOT3K!7C4{dBSZTV zmgLa=S=XI5m7g-Ds1F3Y<0LVMKc)Vxd0maCm{pmdMtaQ1UN;;&;ZjX@EuE;_{|wuv zJzIVyCn0P7*~cmOA?k`t;aDqS$|8AOJ3KF1a;7`uz|5>DNmzt z({!&`in~Kvp62T_h>nG0t{&kws&s^}-!Ovj&SnSd)+yuGGqmCs#??NPQ>l@5dD4uH zUw_(yVZ^`$F%6Vl-!X-FR|9eU)Ud75)g%06_bviE@zTAa=V zb#2snboT77AAOnfxwhN(nc!_8vCCU z^ffRTdpPzZ*A~bn?7F>DRb)0(iPUN8GHP=MdZp{C^{;}Uki=a!Mi4taHw5cWDe^Nk z+<9X!+;~oxlI*Ezo{}+GW#GM<^U4Q=h@x(!m#RX+)hf`b+pkLuOj{ydz!V%&Oz}Dd z=?8Gvji)^q8&HQIjO-!|6jjzyCevrvqD_MWiKT-}iApU>mGcpnYU#B!*~W{DRq*){(SeJoR#7w*-z5Uh zQ_3yh-l?uql?sR~AtGL=14UQuPNUzxYB~{7EqHo-dknxO%YWF191q$pRL!sJ@kn^Q z^i)FVcPwun7(bv1pMO_lu+qMqFm@GQ&AoKt`iJGkY;*Sy`c)AONDNU`8 zq#}-n_m~cRqf(u;J^-UQ96lJp0+K-U%RvREJ?bdqj1JoiIsE?qJ`kR?RVfeWGWK+> zygNT+YH56p;+nq{^<>+KoDOw1EKYIjNaUsCXuKsz#nn%A`TkSwMFxJ-Y>|Mg-^kNF zqmd^YOZ%mZumuJ4#6u8nix2lj=?5W0rF$_+oQUg>7R~N2FNYo%o9}nf73>1C<8a%c zN2dwwnA})%7nJ$D;V;!)yNEjPS}0BOfUpb>M9u}`^VMa?DG$j}{}vBY6kv}r|ekt=v| zrsN*QPa*+0tY+NZ5Ef-&b}_K`CUQ)LZ9_sST$C+?x&q^DDDcu7m%P4v!}>>*@w|Fs z$kQwvCl@>LsyW#D8&~IhZa5v!S#3$nc9$n_Unpyrw-(XeC~JRi!T;>XW%De2xD18Y z??_td$7T0qzP-O3lA~jid0?sqwrGxR?-|FXqeqWj)a%LdSqpr26%$W$P>1v?tJGAb z=05asiorBExsE-pWnvFONw^su%qmr$T;|nT)4;UmYLj$J*G(PSKeK!<*m8SWE9+C- z6#m7IeiC}qg{~E4@0?$ti8uTW{^9JxLa^!U!WX>NuX&74}I zcXzOn55EOH+ZVu%b0=Fls*NMb!d|t$A);uktAYj@0pqTY9jPRss`Hw#5z0nVQ5FEh6 zT6^rawud~Q`L=Ey1$D?@f<38_*a&<<5q+Y(dOU4M5u?8`;vs?u4v}hZZ@ULV6&@>I zJsw{42a6cxZA0NYg2l#depHMt5^v=T&3KB!^Cgb-PF{o$`$dO=2=5|05V*&AzCQf9 zs1#@%7XqR;ub?6y8q{5PYL#|>z0L?V`nWjq=JztC`K66c51qOykCa^S-h-4Q;zqP} zUcTMnLcit~UC#vH+v#5zBT=fDuC6mRIRgzjWwY5+^*{+s9+ z|9dX{T@{P}vt0PQN+$njx$t+@Z2nESfPYde?+0+XlKc;{ApblC6MvgQj`U{+`Fk`) z2)V?%KO6MHEihI@bBJ<*_*_kAzL0^ECxrv*E$h5|7(Jjd1N z6a}oAhYk;v$INZlNemKUqF9&WwS6*K^^MB)pZXZM-pjl-7VhyVztQS0nRDh4vgS-4 zn#q^Y8K@PdR-?*OrNVS;rp6p~vJZROa3PPy5WFEJ{@{qLCVFxJ!z{^%f6{hqJqX#! znYJ`q_^yo4n%M6M3J1MxAm%a*9iZO%p2FF1ka{Y-e5zxg49;~7k?H=B&pKawr_Fg5 zv>nwIF`UU=yH#FbWIH8CzJ}pzcSMS7wP$A`aC8-N{wtLF7JRuoCMaA}S*+uaV=Cw!$8lPapBlr)}8da($8X(Ont}V(80HiRE-D)e1Cwvi8)o=j?8{ zO!=@)(Phv+;OQUvZ36}{QJ^lHHrp;$B>vRrCFkPJ=lpT-ua=Rz-1%5bq~+BQ4erjL z6XsYx3?BzQ2V>7hBjX`rIui2e;%48vcb zfru!|vCo<_eGq4Nu0VfUe2<;#+WoHdXqtV|UF{O`l%mgOrdI|Jma*1*qJ5s|wUF~n zP*z(wV5wzFUi=K+OrEH0U&X8@)fA8|CZ)bqe-7<~wH&yK-Qo~L*q(cA#ByB1NhIcb zB+L~Xa4OCSg~{aZC;|FoMNGT1a0}uu$Fo4o_#rrhPM!g)GTY$s`1*c`4trum*Qfok z07q`G|ME?N&_!M?0;B)gW#|`A5jGy*)=n~gpw;adhz_9p$GiJPPhit9&dq+@$L&*H z2cYXKWrU-Y-d{&|w{r+havJVeXLGU7a{_~jd{pOcu>viPm~#vH2F5%WGTyl34ojZ= zr$*$yioSPI2>ZI(odJVCeG}zN-2a<@PVD1asSo1`3d{Hf@GGbyoaB+$o6rH60VxHbk==7b>3X zqL9^4o@v(WilP&XTE6ZUNJ4wBdQTB>@!2W3k80GNo7Vqx^$dPyg@M4w&3M}MM-Gw3 zU19f7MfIWdNR3&QXL&f86rQJ5vSvgS3#{DkOonNIgV{S&K3fFE@zFJ^ZkQh16!)yh z+0UqV_g@9IzYf*XT_{#`bM~uF7@pnx1yDA>DgV6FD{ap{#pglD0un3y1yY)SrvQ)P znhWf6SM-W+Z@*+u-oEj-dhe4_Y(X){ zTFqTqV^$IfMJRw~1>;}bTRIxegX;E>`3F;!?M@;+)Y$qx_L_YsCqW}6I!5gR=hY_@ zZBr|EG++?%O(>yCn5IY`R2-p(r3DTePIQo^xwCdFL=nmIBnqfA{bIw?D*!?|IJZ8P z{t)ph4WF%)L_O z=8{7mkIiuNOHF67B!=g~+WAQMU{%I?_DqE%x}Seu2mM8G`X z6}P~`wIV5I(!LXrc}{#o0ct^_<7bu;Fwj=62YSGk7aM-z3aKYKh{sg$^S-)e_Pn~C zJ$|DseemPqGxo{8!_v3&6_0(Vc!&86g<9*(u`|Y_HmujrX z!a(qOceK#rr}z6IA|n1JKE$xuBh+wsQ05U9oE4p&3zU$_y){}P)DFv_Jc{#IA+cSd z2Qkxphbf`}1YOKkf{uPuOh@2a`3o%=LQz|q`gon5QzcvA2P$&Cu`CWfjruYDuN3(@ zT$!m(B2aO<^{!q{r*mb>V)s*f`@>$sS^XuoY= zDb`rLd3reb%)2yv^*U3%IzX$ig(^D|@DupC|( zC*yVuW%*cDv{g>WlfjE{j&fG))ob;QeWs;sZ%Ln3mf-zQnm*YT+KY~h27rN&U4#k~ zr1~+7rY{b~LqUwx>DJelY<&K;Nk=)hDxDRViFYot=elGR6v$@ zQ0N^UR)M?Gl0~>nvyLU#eELY0$Q=oM*ig2_7OHqtE=dpgpnm>T zR|S>AMMp4!wcW8{P1D;aX%UXh3ar6aqt?n97G5_F7XP-YNS|3}=8auW3EfY>BzgW3 zpHaqv8^SHJCMrl0F_>N2#o1O_`mO>J;AT9resH z9SB8(dMA^Eq^B!a(J+?hyp_WM*#X#!%5pk7jA3Pq2FoI9^^ZmBviKLq9DJE61_UM8 zDc{!b%O36=_1=0@q+yk`Nc1)2Z*t1-oCA?`t|XWh1n<%d^sZ1?C~*teD0_ zgELbN@e*2G6X9mTwzkL+PXi?y{CpxOy)D70)?xdGR)ev9yCTpA?iwOsBVS`RkWO{7 zqJYz7I6P7J3&b#TNJcZi+rsZ}Fu0gsv@z(AD%NhZI9)rZndKy7!O;aU{J7Z}+Z$QgGFUsB zS$wQ~Yylxji%W?E0Rv)UhJY8)$2L&3n5UIF5RjZ45DnnBP(a|oC_sS3B|x=2KqFur zAVB$*Kb7;OfpPy6R|cl~k2U}^KgG*@ssaI4-hgGP(0v1z$8E*$iSe;z#jvE(ttp~;J|<2&(8omU=UC+a0o~!Xc$;P zgC-;(5MWSH5HL`1@ZYxq_5pkk1cnTbLd+}-fvRK#N#caY5|EG&Mf$0(7hQSwl8n{Z zIS?8K0}~4yhn#|vikgOvor9B$n@2=cOk6@zN?Ju#OH1CQU*Rv5{lDl!2G9ip1_lZS`I|0a5Rc!4 zBZGkxGee*VD?u7Lp^~r!K%sp~$gk^#CS_H=L^pPxg~1?W+a|yIP1+yI{%3>*{!5ho zCt-i1YaK`g2;>idf`EX6gMxyBLx2MS0t(_cK*2!$0hs>)xIcjK8xa2!J^&X123!LS z3=9(RMuLTgMfzXE#|GfBMEY0*f&&ExJWQa-K!QNeKi{DnK7hzhhYap9!#;qdQoAK= zZleCd$x7^3EkfGf?rXZjqYoesG$k?TTv@;#=wI3VK4$)ZyYuY7d}s6_!2t!Z;fk&Z zhB`Qpa#o#sfqf7)uoOBWrD%f3nyH42pAw-Oou1XZ$tc>&WmrhiWG%Y>Qr!HkYpp45 zxnfER^5R{7mxuI_1KprMNKxHNx|fgD5XLNBzki9o^aJnVKsus(nh&9YVLtpZh#qSnvjT*1`S^!Y+Q*o;=tcJ zvuC-dF^AC{u*WQW@`l^B;s$W{9%e0mDdB7S0800nex1QU!+3eXF&2xp7ouv8Uuu%Pd~-T_voh~H{nIMUpu1Z`HR)@jRu#dUA* zN}E~D27{BzX->-QilHHsLzmLi1 zNG!ulU{{_V_h8M7ULdqOC>xaf~xnNY>n}K5&9EZslNy zx(=&YHqPZa24)6{Wzsw86IK06{b;pg2dZ#c8tzS5V(lf6h;Cc;i=HGE*D$IPN+~(~ zvcvoPZshvREh3C3s!*Kti(o6Wk7mwTZHeR?7{`unW8wxkv?3{y0;NaT$H zAbv3HrO_`)pnIw<&|11Vygh!@VdhpIF<4SqJzA^N8QnZh%{&?9$5z>YO-C0l$))r{ z>z9Aj);X0~X6??jsZcawZ(E!K+SGB5D{2}Mw_$=?zvt2veq^<_8C3X~DgAwhPs|9R zv}ChPs=5YA2CV{%2`(Cn2nmsY5U+R4SHRn^VfM~o4Rm;I{jsz^jUz2_wI2>sdob8V zd(^@@^#PfWQ+kN>pwg5PvizX<@dY!+Tjq25V_{GEXEz7RP`lXl@^!ukeS)z5rDujh zj1~v78J2ZSo9qDLr`6XIMK#IVm3c7wn?`F74;lnNJT-i?TN^o?YXxNX4jiF~kkNLk z=1mp&D)JkqOeiki@7%qGU%3^g;{r;Kdcg%kYk>O2Y&*oc_9@S>o0I-} z`4@d@m^WgvRf{iD-Tdu~48D{-#rOGL$wcm@LqSXTU_+MCq>f$4tYzG-?qCCuSAI96 zUBa+Uw3#Xk&2g=t$4i&g*L%uEgLEdiT2k05fm8QG>&0ZF}SO7G7GM! zMQGaTvZA4cQZX0WT?cUft@*S8Z5zF`>tK?5Q0gRJciW%s?w&)eIke6)FHpLJ>zu18=xm`oA`cWxffXOb_n;=F>8XUf z6oyO?!n7=i1fu%?*god}_5S23y&7Mz|7VkZ+q0xPBRadO>qN3U~onk9JG~bO#)X zd`;`rwYtmaF~LTZw_>_^F2B2>b!-LJ{WY5?j#xU47^k=?AgrW#9<_@ubXCF(@;ol8HFQjP&l?7i8lT-0_Prxz8iSx4)&ACj@yMSMItrRcQ>jQ}5dV z^RnHUAmpK6Ub<~epE3d^Q*57r|+PJqa2zvD)F<@MORGS(88zFswjULQ?D?y9-Dy=Kx~)tidUJo}`;uGG>w z@dBOwvgy(Ta#fvl*rOgEy3v^!jPRJR+^gzkOH`x6TK=od2M|>9!?pD6V~t?_B>Yk# zdC6DexsgCiyQ22k)!=cmR3bad&n&n8EZ1i9>F&x5W#~EB}>RSWE}%E5oubPl;J4(5(Faf>6>W~gpitV6g$yrTP6a++)sMz6~x-Ba=AI5wC zd52qE9p21QKDDn&Q>^PTpX{Vs(o&2}?%fh1llJX}I8hS?22}L*RV>M!&WwSX$1N{>DyV_7Z;N7Ge%hu!ZOOM z{%OalWF1j&&9N9It z3PUCPzBFa6FdVx##*!qmN6L~UYvP0uBFk7qmXm!8S-#geof&VY&-HzOeE)p! zb&YGrbv?g%-sirb=Y8gW?xp(4iP%2D8L6~cib1_9@1PB`Wt4}?XKzxAB`&?hCd}$g zQ4mWIY<-fvhY7|VTM$g+vI1|SUFewbl38XzUh=f9PF!^>&B<^PIvX_F{-U6fOR0qq znwWy9GO^6z@a4JL{(IH?5d{WIi15^(u|phaHO@33nJTcc2_UqU_TFs!k5{0hi($G9 z(Z>UyKp*H#Tl0y~vj&Wt9wa)+%$>lw@7}pgrlOJ8YU%v0r@fmv*Fu?<`{(N_*e?rw zUKdppR5&@r6F>P~--l|yc+GPM#{|oUGdj0Lmz=AbZsbNb-aB3B_^9*xr~B+lg9$#C zD?1Pfmtuow3(XDlh8I|CDVW;sJVRGc)o{fu4nW8lwVuK186*aaLV8MM#islPS7HimlDr*0y_Pm{>o>)SJ7G@H!MrnM5Z$ zA^Y9JzxuW5S{WX1G5KWelzKJskL^~N()xVy*o$| z-l5zq`sJV#tJVkjDb@WnI4&YSUu)hbzhQ zmKX=z*M&;`*%xxB!r{>J*KhCK1z-yiSxqTc-UpR3t_`FpVa zfA-BC|FduIQdc4&Jqm#W)~PM8OuD6Nmk7YTbR95L0SCs|#1hUHrY=~c*H#{h5UqMu z!2i9JeU>_A8aD2ynVeq6Qlw3s>0e@Bn5l8?s-f1E@&u=z1sV4-?yioB1}otNo4@Wp zt4tC;Guo^A{y5TtJgq*yLLgyUEbX8l|U!i@AxU+ zWV^>>ns@O<$72MUykO(PfwTibtx0*SNREJ5w=#S62IKML8)%(%*yQ|hQK4WVwa?H5^W8q`R zZ7z#npN=@~JDzUwSY~|EAC;f@;(>jZcAWJSaeBML(5B9UtG{91+g_7CU*T;IlV?Wv zzFT)NJE$?~6nN09`*386M#%!?@SAVY3$htY$RQ%S+}TTEOs*?xB6Y>7t1wT569#F+ zuyi(#Bthf%aXCjG4$t?>A7`p^>6~k=zyGE&xGA&(Na*MDvd;C;d)a{83W3*=zvYKA zxn^amg>tnrb=;C5WrG5huOL1Ab6JoJa>JAFyavb7w=6s^e-WLEVjYJgQw|UHny$>D z1sbIllkZ=ixB(;C93VPeEYh1w2EFI*cOBkt-)nDCV4O=%;ab?AcFtbS)$!?3y|2yb z$cJ{&6h}wdJJy$yiX>wZW0Q+!8a>vpDO0a5ySGMj+!|#H8jOUt1tnC9W%;rs7re=+ z|8mDdop$Pz8f8{l0HB=wc*?m|F5(~MEJgQQc5l^+ zAaf^6aleS>JDV4zHBo!3l3#9|8D8taX1Ok-i3!VdNgPQvourR4nD~p4wOp-2ZPdy( zVIf{p{zQ}r9}5qqu*GerXo|xdS{^nxem|yifmdaw%!socXsAe&y<~dhpJTL2gPfBOXw#y5QYGzzo zY_7T+aV=D+(x7}}R!IeUI*X;~gtUcMRMiuUXRXUXfi(#!^^~o z4T>8ZdbLUha$$YXm#4UP6`JQE1)4y>l&uMz;?e;1Wd5k_Z>o>~ZMBMBe<kOPHWigMqmXM8*iZ6_lT3rC_RUt)5u%>6-S8k12dAw!P=WIO_lA!S`!9XjqkRV$)J=F zYfm-B$O;;(Xof@S8Y$wP&+t_aH3<0?&yGM0> zx~7ImJJE9;S@Vlc=-Q`=qmduj!~3AF*;dsg`TH!Ty1T`dFGRUrQJ#@(RPtmBPu90R z+buIVgqF2ti3Sv_5R%FbuBfUvqApHHrv~%BCQ~qDR4fKkO$}BTDKDH zRvIR%%Q42s%-oBSsbzO8_RlWSy6e*;87^I*2=Tr>kN(ibCh)}KqB0dryu#YJyxO46 zM$n99s3NU**Z9M(j}Jvldiw+b75sU{6}sOclmV=`bl4RaZ}MKZbuj(^?8Q!8>>m$w zrA?b4O~AB#>G@&z_74Fznb9gIuPk0jQ{*MKKfMI|Bd#+>vfGN;kK|6xm^;z5PvnG| z%Tsdhz~h^n9tjOaMLnWUbNi)`f5dP+6hiw%=vrv;)zy4bS&iW!^4CZ%gvO-$!TIYu za>&Pv3Ye!#odz_{%d^#y^7o>%4>4uHYDHjd4arp(X8TsixwXt z8@uvV=gxbDW7g!p3m6l9L&Pm20UGW61NQDgU+t__{$jKt8_s)G)6OU?6*vLzmBNFm? zA0_5=?rZ9sv6@Y@ol4z+SUDeDyDx5(I>F|}Q#ddYGCF#xTGDvTNbaVor`U&Cg!5b! zUNkxpF&|KB>c@6B2E|EH1?aabP=IP{yR`GVdfwIs1ynpU(ss2obu`%0cXg$m0OT?% z2{_4u3<6I$ARPtXwFXR??LbFtFdMibxWFK;r63R@^-n&x9kHmmpfE5^(6Ky=1~^R~ z1de5CaVX#&Kd`UIqhUj=uwGMOgu8MlM-OoDot6Pc*dP2Ef%TdOBW#$OxZ8kHc$NZ2 z_|X51z&e|P5v)tTUhV)QKLU&hz@^b~!yzL0GXguNU<6^|+&{Aeffvb& z^BvwhE&41W8qObk@3hEsglIS)_r231Bni=QaRXr5&f0;vfd{H1NNv-$2Mk=}ZPytB zE)IvoeTQ$uz!-3Mz{Rt01f~mkH}@kf89O(&KkTYl;EFjooQwRgal0xRxNrmxSE2H2 z95zH2e6!$O`8XVd#;wyiwC(EG%nc`m<&$Ygd+=C>_WzyiMu{%z-eS~xCyIY<93x6I3ENK z=U|VA`w^y$CoQ~Sd|~Yz(G~|hGC@7SB}M>`C}DDk<6g+PWCMFA_oDVf#s%>2 zojl~U7c$}f4>+_OC&6)NJn|0-V)YA8c6;yWB`-W2L9r2jLJ}66iuYc~grDSu$yz>p zArszl5GEsT?S)KuX9SK-!imkucNa3=vasvs5gaUl!!ZSJOsB6tFShdid-xnu43}G}z%9 wLm>UBcw_Ll`3~HAr^Nujb^H^&)<5hA>Pq_nyWm!COFl>h@H@lp>DH%z0oQtt=>Px# literal 0 HcmV?d00001 diff --git a/drizzle/0001_embedding_384.sql b/drizzle/0001_embedding_384.sql new file mode 100644 index 0000000..39b5dc9 --- /dev/null +++ b/drizzle/0001_embedding_384.sql @@ -0,0 +1,22 @@ +-- 임베딩 모델 전환: 로컬 multilingual-e5-small (384차원) +-- mock/openai 도 384 로 통일한다 (openai 는 dimensions 파라미터로 축소 요청). +DO $$ +BEGIN + IF (SELECT format_type(atttypid, atttypmod) FROM pg_attribute + WHERE attrelid = 'keyword'::regclass AND attname = 'embedding') <> 'vector(384)' THEN + DROP INDEX IF EXISTS keyword_embedding_hnsw; + ALTER TABLE keyword ALTER COLUMN embedding TYPE vector(384) USING NULL::vector(384); + CREATE INDEX keyword_embedding_hnsw ON keyword USING hnsw (embedding vector_cosine_ops); + END IF; + + IF (SELECT format_type(atttypid, atttypmod) FROM pg_attribute + WHERE attrelid = 'qa_pair'::regclass AND attname = 'embedding') <> 'vector(384)' THEN + ALTER TABLE qa_pair ALTER COLUMN embedding TYPE vector(384) USING NULL::vector(384); + END IF; +END $$; + +-- 데이터셋 출처 추적 (수작업 큐레이션 / LLM 생성 구분) +ALTER TABLE keyword ADD COLUMN IF NOT EXISTS source text NOT NULL DEFAULT 'llm'; +ALTER TABLE keyword ADD COLUMN IF NOT EXISTS kind text NOT NULL DEFAULT 'keyword'; +ALTER TABLE keyword ADD COLUMN IF NOT EXISTS category text; +CREATE INDEX IF NOT EXISTS keyword_kind_idx ON keyword (kind, category); diff --git a/package-lock.json b/package-lock.json index efb3eb9..ba5946e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "o2o-site-ontology", "version": "0.1.0", "dependencies": { + "@huggingface/transformers": "^4.2.0", "@nestjs/bullmq": "^11.0.2", "@nestjs/common": "^11.0.12", "@nestjs/core": "^11.0.12", @@ -197,6 +198,16 @@ "node": ">=0.1.90" } }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", @@ -639,6 +650,499 @@ "node": ">=18" } }, + "node_modules/@huggingface/jinja": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.10.tgz", + "integrity": "sha512-SgS1D1bglQ94ceD4ZCL6eayUDy9uV1xuyk61OjgSxU5GJh7upqZCKII0JoTYMc6wWsg3JUgaPRX0ElQzXPk3Cw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@huggingface/tokenizers": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@huggingface/tokenizers/-/tokenizers-0.1.3.tgz", + "integrity": "sha512-8rF/RRT10u+kn7YuUbUg0OF30K8rjTc78aHpxT+qJ1uWSqxT1MHi8+9ltwYfkFYJzT/oS+qw3JVfHtNMGAdqyA==", + "license": "Apache-2.0" + }, + "node_modules/@huggingface/transformers": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-4.2.0.tgz", + "integrity": "sha512-8BRCoBMH0XsWaEIamuR0LrJGAfftgHAfb2Vrffy0VKlSAE/MnUJ5/h/zTfEP3fDIft+nk7TqB8xXEyABGitBjQ==", + "license": "Apache-2.0", + "dependencies": { + "@huggingface/jinja": "^0.5.6", + "@huggingface/tokenizers": "^0.1.3", + "onnxruntime-node": "1.24.3", + "onnxruntime-web": "1.26.0-dev.20260416-b7804b056c", + "sharp": "^0.34.5" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@inquirer/ansi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", @@ -1392,6 +1896,63 @@ "tslib": "^2.1.0" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "license": "BSD-3-Clause" + }, "node_modules/@tokenizer/inflate": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", @@ -1797,6 +2358,15 @@ "acorn": "^8.14.0" } }, + "node_modules/adm-zip": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz", + "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, "node_modules/agentkeepalive": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", @@ -2020,6 +2590,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", @@ -2569,6 +3146,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2601,11 +3212,16 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -2737,6 +3353,12 @@ "node": ">= 0.4" } }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", @@ -2795,6 +3417,18 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -3003,6 +3637,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/flatbuffers": { + "version": "25.9.23", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", + "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==", + "license": "Apache-2.0" + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", @@ -3252,6 +3892,39 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3271,6 +3944,12 @@ "dev": true, "license": "ISC" }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "license": "ISC" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3281,6 +3960,18 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -3569,6 +4260,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3672,6 +4369,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/lru-cache": { "version": "11.5.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", @@ -3701,6 +4404,18 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4070,6 +4785,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -4107,6 +4831,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/onnxruntime-common": { + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.3.tgz", + "integrity": "sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==", + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.24.3.tgz", + "integrity": "sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==", + "hasInstallScript": true, + "license": "MIT", + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "adm-zip": "^0.5.16", + "global-agent": "^3.0.0", + "onnxruntime-common": "1.24.3" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.26.0-dev.20260416-b7804b056c", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.26.0-dev.20260416-b7804b056c.tgz", + "integrity": "sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw==", + "license": "MIT", + "dependencies": { + "flatbuffers": "^25.1.24", + "guid-typescript": "^1.0.9", + "long": "^5.2.3", + "onnxruntime-common": "1.24.0-dev.20251116-b39e144322", + "platform": "^1.3.6", + "protobufjs": "^7.2.4" + } + }, + "node_modules/onnxruntime-web/node_modules/onnxruntime-common": { + "version": "1.24.0-dev.20251116-b39e144322", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.0-dev.20251116-b39e144322.tgz", + "integrity": "sha512-BOoomdHYmNRL5r4iQ4bMvsl2t0/hzVQ3OM3PHD0gxeXu1PmggqBv3puZicEUVOA3AtHHYmqZtjMj9FOfGrATTw==", + "license": "MIT" + }, "node_modules/openai": { "version": "4.104.0", "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", @@ -4274,6 +5041,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "license": "MIT" + }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -4297,6 +5070,29 @@ "url": "https://github.com/sponsors/porsager" } }, + "node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -4460,6 +5256,23 @@ "dev": true, "license": "ISC" }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -4577,6 +5390,12 @@ "node": ">=10" } }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "license": "MIT" + }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", @@ -4603,6 +5422,21 @@ "url": "https://opencollective.com/express" } }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", @@ -4628,6 +5462,50 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/side-channel": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", @@ -4744,6 +5622,12 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -5081,6 +5965,18 @@ "fsevents": "~2.3.3" } }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", @@ -5291,6 +6187,7 @@ "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", diff --git a/package.json b/package.json index af30d52..9322461 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,12 @@ "db:migrate": "tsx src/db/migrate.ts", "db:seed": "tsx src/db/seed.ts", "db:reset": "docker compose down -v && docker compose up -d --wait && npm run db:migrate && npm run db:seed", - "smoke": "tsx scripts/smoke.ts" + "smoke": "tsx scripts/smoke.ts", + "dataset:build": "node scripts/build-dataset.mjs", + "dataset:ingest": "tsx scripts/ingest-dataset.ts" }, "dependencies": { + "@huggingface/transformers": "^4.2.0", "@nestjs/bullmq": "^11.0.2", "@nestjs/common": "^11.0.12", "@nestjs/core": "^11.0.12", diff --git a/public/demo.html b/public/demo.html new file mode 100644 index 0000000..3d6bb30 --- /dev/null +++ b/public/demo.html @@ -0,0 +1,235 @@ + + + + + +키워드 매칭 콘솔 · o2o-site-ontology + + + +
+
+

O2O-SITE-ONTOLOGY

+

키워드 매칭 콘솔

+

업체명이나 문장을 넣으면 적재된 “군산 펜션” 키워드 사전에서 잘 맞는 것을 골라 보여줍니다.

+
연결 확인 중…
+
+ +
+ + + +
+
+ +
+ +
+ +
+
+
+
+ + +
#키워드유사도의도카테고리
매칭 버튼을 눌러 시작하세요
+
+
+
+
+ + + + diff --git a/scripts/build-dataset.mjs b/scripts/build-dataset.mjs new file mode 100644 index 0000000..6fdf0cf --- /dev/null +++ b/scripts/build-dataset.mjs @@ -0,0 +1,176 @@ +/** + * "군산 펜션" SEO/AEO 키워드·태그 데이터셋 1,000건 생성. + * node scripts/build-dataset.mjs → data/gunsan-pension-keywords.json + * + * 어휘는 실제 군산 지명·관광지·숙소 시설 용어로 구성했고, + * 패턴은 한국 로컬 숙박 검색에서 실제로 쓰이는 조합만 전개한다. + * 가치가 높은 순으로 방출하므로 1,000건에서 잘라도 상위 의도가 남는다. + */ +import { writeFileSync, mkdirSync } from 'node:fs'; + +// ──────────────────────────────────────────────── 어휘 (실제 군산 기반) +const REGION = '군산'; + +// 고군산군도·해안 권역 +const ISLANDS = ['선유도', '무녀도', '장자도', '대장도', '신시도', '야미도', '고군산군도']; +// 시내·주요 권역 +const AREAS = ['새만금', '비응항', '오식도', '은파호수공원', '월명동', '나운동', '수송동', '미룡동', '옥도면']; +// 관광지 (근처 숙소 검색의 앵커) +const SPOTS = [ + '선유도해수욕장', '새만금방조제', '경암동 철길마을', '근대역사박물관', '초원사진관', + '이성당', '동국사', '진포해양테마공원', '신흥동 일본식가옥', '은파호수공원', + '월명공원', '금강하구둑', '철새조망대', '채만식문학관', '째보선창', '시간여행마을', +]; + +// 숙소 유형 +const STAY_CORE = ['펜션', '숙소', '풀빌라', '독채펜션', '스파펜션', '애견펜션', '감성펜션']; +const STAY_ALT = ['글램핑', '카라반', '캠핑장', '게스트하우스', '민박', '리조트', '한옥펜션', '촌집', '별장']; + +// 검색 의도어 +const INTENT_CORE = ['추천', '예약', '가격', '후기', '순위']; +const INTENT_MORE = ['저렴한곳', '가성비', '최저가', '실시간예약', '당일예약', '특가', '할인', + '위치', '주차', '전화번호', '체크인시간', '조식포함', '청소상태']; + +// 동반자 +const WITH_CORE = ['커플', '가족', '친구', '애견동반', '단체']; +const WITH_MORE = ['신혼', '아이동반', '유아동반', '부모님', '효도여행', '대학생', 'MT', '워크샵', + '회사', '태교여행', '혼자', '여자끼리', '4인', '6인', '10인', '20인']; + +// 시설·특징 +const FEAT_CORE = ['오션뷰', '바다뷰', '독채', '프라이빗', '스파', '자쿠지', '바베큐', '수영장']; +const FEAT_MORE = ['노을뷰', '일출뷰', '월풀', '온수풀', '야외수영장', '인피니티풀', '불멍', '화로대', + '넷플릭스', '빔프로젝터', '노래방', '파티룸', '복층', '테라스', '마당', '벽난로', + '애견운동장', '키즈룸', '트램폴린', '무료주차', '조식', '세미나실']; + +// 시즌·행사 +const SEASON = ['여름휴가', '물놀이', '해수욕', '겨울', '연말', '크리스마스', '신정', '설날', '추석', + '봄', '벚꽃', '가을', '단풍', '일출', '낙조', '불꽃놀이', '성수기', '비수기', '주말', '평일']; +const OCCASION = ['생일', '기념일', '결혼기념일', '프러포즈', '100일', '가족여행', '워크샵', '단합대회']; + +// ──────────────────────────────────────────────── 방출 +const rows = []; +const seen = new Set(); + +const INTENT_MAP = { + 예약: 'transactional', 실시간예약: 'transactional', 당일예약: 'transactional', + 가격: 'transactional', 최저가: 'transactional', 특가: 'transactional', 할인: 'transactional', + 후기: 'informational', 순위: 'informational', 청소상태: 'informational', 체크인시간: 'informational', +}; +const intentOf = (m) => INTENT_MAP[m] ?? 'local'; + +function add(keyword, { intent = 'local', kind = 'keyword', category, relevance }) { + const k = keyword.replace(/\s+/g, ' ').trim(); + if (!k || seen.has(k)) return false; + seen.add(k); + rows.push({ keyword: k, intent, kind, category, relevance: Math.round(relevance * 100) / 100 }); + return true; +} + +// T1 — 코어: 지역 × 숙소유형 × 의도 +for (const s of STAY_CORE) add(`${REGION} ${s}`, { category: '코어', relevance: 0.97 }); +for (const s of STAY_CORE) for (const m of INTENT_CORE) + add(`${REGION} ${s} ${m}`, { intent: intentOf(m), category: '코어', relevance: 0.93 }); +for (const s of STAY_ALT) add(`${REGION} ${s}`, { category: '코어', relevance: 0.86 }); +for (const m of INTENT_MORE) add(`${REGION} 펜션 ${m}`, { intent: intentOf(m), category: '의도', relevance: 0.88 }); + +// T2 — 섬·권역 × 숙소유형 +for (const g of [ISLANDS, AREAS]) for (const p of g) for (const s of STAY_CORE.slice(0, 4)) + add(`${p} ${s}`, { category: '권역', relevance: g === ISLANDS ? 0.9 : 0.85 }); +for (const p of ISLANDS) for (const m of INTENT_CORE) + add(`${p} 펜션 ${m}`, { intent: intentOf(m), category: '권역', relevance: 0.82 }); + +// T3 — 동반자 +for (const w of WITH_CORE) { + add(`${REGION} ${w} 펜션`, { category: '동반자', relevance: 0.91 }); + for (const m of INTENT_CORE) add(`${REGION} ${w} 펜션 ${m}`, { intent: intentOf(m), category: '동반자', relevance: 0.8 }); + for (const s of STAY_CORE.slice(2, 6)) add(`${REGION} ${w} ${s}`, { category: '동반자', relevance: 0.78 }); +} +for (const w of WITH_MORE) { + add(`${REGION} ${w} 펜션`, { category: '동반자', relevance: 0.82 }); + add(`${REGION} ${w} 펜션 추천`, { category: '동반자', relevance: 0.75 }); + add(`${REGION} ${w} 숙소`, { category: '동반자', relevance: 0.73 }); +} + +// T4 — 시설·특징 +for (const f of FEAT_CORE) { + add(`${REGION} ${f} 펜션`, { category: '시설', relevance: 0.89 }); + add(`${REGION} 펜션 ${f}`, { category: '시설', relevance: 0.76 }); + for (const m of INTENT_CORE.slice(0, 3)) add(`${REGION} ${f} 펜션 ${m}`, { intent: intentOf(m), category: '시설', relevance: 0.72 }); +} +for (const f of FEAT_MORE) { + add(`${REGION} ${f} 펜션`, { category: '시설', relevance: 0.79 }); + add(`${REGION} 펜션 ${f}`, { category: '시설', relevance: 0.7 }); +} +for (const p of ISLANDS.slice(0, 4)) for (const f of FEAT_CORE) + add(`${p} ${f} 펜션`, { category: '시설', relevance: 0.74 }); + +// T5 — 시즌·행사 +for (const s of SEASON) { + add(`${REGION} ${s} 펜션`, { category: '시즌', relevance: 0.8 }); + add(`${REGION} ${s} 펜션 예약`, { intent: 'transactional', category: '시즌', relevance: 0.71 }); + add(`${s} ${REGION} 숙소`, { category: '시즌', relevance: 0.68 }); +} +for (const o of OCCASION) { + add(`${REGION} ${o} 펜션`, { category: '시즌', relevance: 0.75 }); + add(`${REGION} ${o} 펜션 추천`, { category: '시즌', relevance: 0.69 }); +} + +// T6 — 관광지 앵커 +for (const sp of SPOTS) { + add(`${sp} 근처 펜션`, { category: '관광지', relevance: 0.83 }); + add(`${sp} 근처 숙소`, { category: '관광지', relevance: 0.8 }); + add(`${sp} 펜션 추천`, { category: '관광지', relevance: 0.72 }); + add(`${sp} 가까운 숙소`, { category: '관광지', relevance: 0.66 }); +} + +// T7 — 태그 (칩 UI 용 짧은 패싯) +const TAGS = [...FEAT_CORE, ...FEAT_MORE, ...WITH_CORE, ...STAY_CORE, ...STAY_ALT, + '오션뷰객실', '반려동물동반', '금연객실', '엘리베이터', '와이파이', '취사가능', + '단체가능', '조용한', '신축', '리모델링', '뷰맛집', '인생샷']; +for (const t of TAGS) add(t, { kind: 'tag', category: '태그', relevance: 0.6 }); + +// T8 — 질문형 (AEO) +const Q = []; +for (const w of [...WITH_CORE, '아이', '부모님']) Q.push([`${REGION} ${w} 펜션 어디가 좋아요`, 'informational', 0.7]); +for (const f of FEAT_CORE) Q.push([`${REGION}에 ${f} 펜션 있나요`, 'informational', 0.67]); +for (const p of ISLANDS.slice(0, 5)) { + Q.push([`${p} 펜션 어떻게 가나요`, 'informational', 0.64]); + Q.push([`${p} 숙소 예약 언제 해야 하나요`, 'informational', 0.6]); +} +Q.push([`${REGION} 펜션 1박 얼마인가요`, 'transactional', 0.72]); +Q.push([`${REGION} 펜션 바베큐 가능한가요`, 'informational', 0.7]); +Q.push([`${REGION} 펜션 체크인 몇시인가요`, 'informational', 0.66]); +Q.push([`${REGION} 펜션 주차 되나요`, 'informational', 0.66]); +Q.push([`${REGION} 애견동반 펜션 추가요금 있나요`, 'informational', 0.63]); +Q.push([`${REGION} 펜션 성수기 언제인가요`, 'informational', 0.61]); +Q.push([`선유도 들어가는 배 시간표`, 'informational', 0.55]); +Q.push([`${REGION} 여행 몇박이 좋을까요`, 'informational', 0.54]); +for (const [k, i, r] of Q) add(k, { intent: i, category: '질문형', relevance: r }); + +// T9 — 롱테일: 동반자 × 시설 / 권역 × 동반자 / 시즌 × 동반자 +const LONGTAIL = []; +for (const w of WITH_CORE) for (const f of FEAT_CORE) LONGTAIL.push([`${REGION} ${w} ${f} 펜션`, 0.5]); +for (const p of ISLANDS) for (const w of WITH_CORE) LONGTAIL.push([`${p} ${w} 펜션`, 0.48]); +for (const s of SEASON) for (const w of WITH_CORE) LONGTAIL.push([`${REGION} ${s} ${w} 펜션`, 0.44]); +for (const f of FEAT_CORE) for (const f2 of FEAT_MORE) LONGTAIL.push([`${REGION} ${f} ${f2} 펜션`, 0.4]); +for (const a of AREAS) for (const f of FEAT_CORE) LONGTAIL.push([`${a} ${f} 펜션`, 0.42]); +for (const [k, r] of LONGTAIL) { + if (rows.length >= 1000) break; + add(k, { category: '롱테일', relevance: r }); +} + +const dataset = rows.slice(0, 1000); +mkdirSync('data', { recursive: true }); +writeFileSync('data/gunsan-pension-keywords.json', JSON.stringify({ + topic: '군산 펜션', + locale: 'ko-KR', + generatedBy: 'hand-authored vocabulary × search-pattern expansion', + count: dataset.length, + items: dataset, +}, null, 2) + '\n'); + +const by = (f) => dataset.reduce((a, r) => (a[r[f]] = (a[r[f]] ?? 0) + 1, a), {}); +console.log(`✅ data/gunsan-pension-keywords.json ${dataset.length}건`); +console.log(' 카테고리:', by('category')); +console.log(' 의도 :', by('intent')); +console.log(' 종류 :', by('kind')); diff --git a/scripts/ingest-dataset.ts b/scripts/ingest-dataset.ts new file mode 100644 index 0000000..eaefb16 --- /dev/null +++ b/scripts/ingest-dataset.ts @@ -0,0 +1,88 @@ +/** + * data/gunsan-pension-keywords.json 을 pgvector 에 적재한다. + * npx tsx scripts/ingest-dataset.ts + * + * 정책: 주기 수집 없음. 고정 데이터셋 1회 적재. + * 중복제거는 어휘 단계(정규화 완전일치)만 자동 병합하고, + * 벡터 유사도는 자동 병합하지 않고 "검토 목록"으로만 뽑는다. (이유는 README 참조) + */ +import { readFileSync } from 'node:fs'; +import { createSql, toVector } from '../src/db/db'; +import { normalizeKeyword, canonicalizeKeyword, isBanned } from '../src/keywords/normalize'; +import { LocalEmbeddingProvider } from '../src/embedding/local.provider'; +import { MockEmbeddingProvider } from '../src/embedding/mock.provider'; +import { env } from '../src/config/env'; + +type Item = { keyword: string; intent: string; kind: string; category: string; relevance: number }; + +async function main() { + const sql = createSql(); + const embedder = + env.embedding.provider === 'mock' ? new MockEmbeddingProvider() : new LocalEmbeddingProvider(); + + const raw = JSON.parse(readFileSync('data/gunsan-pension-keywords.json', 'utf8')); + const items: Item[] = raw.items; + console.log(`📦 데이터셋 ${items.length}건 · 임베딩 ${embedder.name}`); + + // 1) 파일 내 어휘 중복 정리 + const byNorm = new Map(); + let banned = 0; + for (const it of items) { + const canonical = canonicalizeKeyword(it.keyword); + const norm = normalizeKeyword(it.keyword); + if (!norm || isBanned(canonical)) { banned++; continue; } + const hit = byNorm.get(norm); + if (hit) { + if (!hit.aliases.includes(canonical) && hit.item.keyword !== canonical) hit.aliases.push(canonical); + if (it.relevance > hit.item.relevance) hit.item = it; + } else { + byNorm.set(norm, { item: { ...it, keyword: canonical }, aliases: [] }); + } + } + const uniq = [...byNorm.entries()]; + console.log(` 어휘 중복제거 → ${uniq.length}건 (병합 ${items.length - uniq.length - banned}, 금칙어 ${banned})`); + + // 2) 임베딩 (배치) + const t0 = Date.now(); + const vecs = await embedder.embed(uniq.map(([, v]) => v.item.keyword), 'passage'); + console.log(` 임베딩 ${vecs.length}건 · ${embedder.dimensions}차원 · ${Date.now() - t0}ms`); + + // 3) 적재 + const region = 'kr.jeonbuk.gunsan'; + const industry = 'stay.pension'; + let inserted = 0, updated = 0; + await sql.begin(async (tx) => { + for (let i = 0; i < uniq.length; i++) { + const [norm, v] = uniq[i]; + const res = await tx>` + INSERT INTO keyword + (canonical, normalized, locale, aliases, intent, kind, category, source, + industry_id, region_id, embedding) + VALUES (${v.item.keyword}, ${norm}, 'ko-KR', ${v.aliases}, + ${v.item.intent}::keyword_intent, ${v.item.kind}, ${v.item.category}, 'dataset', + ${industry}, ${region}, ${toVector(vecs[i])}::vector) + ON CONFLICT (normalized, locale) DO UPDATE SET + canonical = EXCLUDED.canonical, aliases = EXCLUDED.aliases, intent = EXCLUDED.intent, + kind = EXCLUDED.kind, category = EXCLUDED.category, source = EXCLUDED.source, + embedding = EXCLUDED.embedding, updated_at = now() + RETURNING (xmax = 0) AS inserted`; + res[0]?.inserted ? inserted++ : updated++; + if (i % 100 === 0) process.stdout.write(`\r 적재 ${i}/${uniq.length}`); + } + }); + console.log(`\r ✅ 신규 ${inserted} · 갱신 ${updated} `); + + // 4) 벡터 근접쌍 — 자동 병합하지 않고 검토 목록으로만 + const near = await sql>` + SELECT k1.canonical AS a, k2.canonical AS b, 1 - (k1.embedding <=> k2.embedding) AS sim + FROM keyword k1 JOIN keyword k2 + ON k1.id < k2.id AND k1.embedding <=> k2.embedding < 0.02 + WHERE k1.source = 'dataset' AND k2.source = 'dataset' + ORDER BY sim DESC LIMIT 15`; + console.log(`\n🔍 벡터 근접쌍 검토 목록 (cos ≥ 0.98, 자동 병합 안 함) — 상위 ${near.length}`); + for (const n of near) console.log(` ${Number(n.sim).toFixed(4)} ${n.a} ↔ ${n.b}`); + + await sql.end(); +} + +main().catch((e) => { console.error('❌', e); process.exit(1); }); diff --git a/src/app.module.ts b/src/app.module.ts index c48aa35..477c904 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,6 +3,7 @@ import { Controller, Get, Module } from '@nestjs/common'; import { ScheduleModule } from '@nestjs/schedule'; import { env } from './config/env'; import { DbModule } from './db/db.module'; +import { EmbeddingModule } from './embedding/embedding.module'; import { GenerationModule } from './generation/generation.module'; import { MerchantsHttpModule } from './merchants/merchants.controller.module'; import { ServingModule } from './serving/serving.module'; @@ -11,13 +12,19 @@ import { ServingModule } from './serving/serving.module'; class HealthController { @Get('health') health() { - return { status: 'ok', llmProvider: env.llm.provider, ts: new Date().toISOString() }; + return { + status: 'ok', + llmProvider: env.llm.provider, + embeddingProvider: env.embedding.provider, + ts: new Date().toISOString(), + }; } } @Module({ imports: [ DbModule, + EmbeddingModule, ScheduleModule.forRoot(), BullModule.forRoot({ connection: { host: env.redis.host, port: env.redis.port } }), GenerationModule, diff --git a/src/config/env.ts b/src/config/env.ts index 5a8f39b..bd0c19c 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -15,8 +15,12 @@ export const env = { model: process.env.OPENAI_MODEL ?? 'gpt-4.1-mini', embeddingModel: process.env.OPENAI_EMBEDDING_MODEL ?? 'text-embedding-3-small', }, + embedding: { + provider: (process.env.EMBEDDING_PROVIDER ?? 'local') as 'mock' | 'local' | 'openai', + localModel: process.env.EMBEDDING_LOCAL_MODEL ?? 'Xenova/multilingual-e5-small', + }, dedup: { - cosineThreshold: num(process.env.DEDUP_COSINE_THRESHOLD, 0.92), + cosineThreshold: num(process.env.DEDUP_COSINE_THRESHOLD, 0.99), trigramThreshold: num(process.env.DEDUP_TRIGRAM_THRESHOLD, 0.6), candidateLimit: num(process.env.DEDUP_CANDIDATE_LIMIT, 20), }, @@ -26,5 +30,5 @@ export const env = { }, } as const; -export const EMBEDDING_DIM = 1536; +export const EMBEDDING_DIM = 384; export const PROMPT_VERSION = 'kw-v1'; diff --git a/src/embedding/embedding.module.ts b/src/embedding/embedding.module.ts new file mode 100644 index 0000000..b24c6f0 --- /dev/null +++ b/src/embedding/embedding.module.ts @@ -0,0 +1,19 @@ +import { Global, Module } from '@nestjs/common'; +import { env } from '../config/env'; +import { LocalEmbeddingProvider } from './local.provider'; +import { MockEmbeddingProvider } from './mock.provider'; +import { OpenAiEmbeddingProvider } from './openai.provider'; +import { EmbeddingProvider } from './types'; + +const IMPL = { + local: LocalEmbeddingProvider, + openai: OpenAiEmbeddingProvider, + mock: MockEmbeddingProvider, +} as const; + +@Global() +@Module({ + providers: [{ provide: EmbeddingProvider, useClass: IMPL[env.embedding.provider] }], + exports: [EmbeddingProvider], +}) +export class EmbeddingModule {} diff --git a/src/embedding/local.provider.ts b/src/embedding/local.provider.ts new file mode 100644 index 0000000..07b1dbb --- /dev/null +++ b/src/embedding/local.provider.ts @@ -0,0 +1,48 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { EMBEDDING_DIM, env } from '../config/env'; +import { EmbedKind, EmbeddingProvider } from './types'; + +/** CommonJS 빌드에서 ESM 전용 패키지를 로드하기 위한 우회 (TS 가 require 로 바꾸지 못하게 한다) */ +const esmImport = new Function('s', 'return import(s)') as (s: string) => Promise; + +/** + * 로컬 multilingual-e5-small (384차원, onnxruntime CPU). + * 최초 1회 모델을 내려받아 캐시하며 그 뒤로는 오프라인 동작한다. + */ +@Injectable() +export class LocalEmbeddingProvider extends EmbeddingProvider { + readonly name = 'local:multilingual-e5-small'; + readonly dimensions = EMBEDDING_DIM; + private readonly logger = new Logger(LocalEmbeddingProvider.name); + private extractor: any | null = null; + private loading: Promise | null = null; + + private async pipe() { + if (this.extractor) return this.extractor; + if (!this.loading) { + this.loading = (async () => { + const t0 = Date.now(); + const { pipeline } = await esmImport('@huggingface/transformers'); + const fe = await pipeline('feature-extraction', env.embedding.localModel); + this.logger.log(`model ready: ${env.embedding.localModel} (${Date.now() - t0}ms)`); + this.extractor = fe; + return fe; + })(); + } + return this.loading; + } + + async embed(texts: string[], kind: EmbedKind = 'passage'): Promise { + if (texts.length === 0) return []; + const fe = await this.pipe(); + const prefixed = texts.map((t) => `${kind}: ${t}`); + const out: number[][] = []; + const BATCH = 64; + for (let i = 0; i < prefixed.length; i += BATCH) { + const slice = prefixed.slice(i, i + BATCH); + const res = await fe(slice, { pooling: 'mean', normalize: true }); + out.push(...(res.tolist() as number[][])); + } + return out; + } +} diff --git a/src/embedding/mock.provider.ts b/src/embedding/mock.provider.ts new file mode 100644 index 0000000..2d77849 --- /dev/null +++ b/src/embedding/mock.provider.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@nestjs/common'; +import { EMBEDDING_DIM } from '../config/env'; +import { hashEmbedding } from '../llm/mock.provider'; +import { EmbedKind, EmbeddingProvider } from './types'; + +/** 모델 다운로드 없이 파이프라인을 돌리기 위한 문자 bigram 해싱 임베딩. 의미는 잡지 못한다. */ +@Injectable() +export class MockEmbeddingProvider extends EmbeddingProvider { + readonly name = 'mock:bigram-hash'; + readonly dimensions = EMBEDDING_DIM; + async embed(texts: string[], _kind: EmbedKind = 'passage'): Promise { + return texts.map((t) => hashEmbedding(t, EMBEDDING_DIM)); + } +} diff --git a/src/embedding/openai.provider.ts b/src/embedding/openai.provider.ts new file mode 100644 index 0000000..f61d21c --- /dev/null +++ b/src/embedding/openai.provider.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import OpenAI from 'openai'; +import { EMBEDDING_DIM, env } from '../config/env'; +import { EmbedKind, EmbeddingProvider } from './types'; + +@Injectable() +export class OpenAiEmbeddingProvider extends EmbeddingProvider { + readonly name = `openai:${env.llm.embeddingModel}`; + readonly dimensions = EMBEDDING_DIM; + private readonly client = new OpenAI({ apiKey: env.llm.apiKey }); + + async embed(texts: string[], _kind: EmbedKind = 'passage'): Promise { + if (texts.length === 0) return []; + const res = await this.client.embeddings.create({ + model: env.llm.embeddingModel, + input: texts, + dimensions: EMBEDDING_DIM, // 스키마와 차원을 맞춘다 + }); + return res.data.map((d) => d.embedding as number[]); + } +} diff --git a/src/embedding/types.ts b/src/embedding/types.ts new file mode 100644 index 0000000..31dc8b5 --- /dev/null +++ b/src/embedding/types.ts @@ -0,0 +1,8 @@ +/** e5 계열은 query 와 passage 를 비대칭으로 인코딩한다 — 검색 품질에 직접 영향. */ +export type EmbedKind = 'query' | 'passage'; + +export abstract class EmbeddingProvider { + abstract readonly name: string; + abstract readonly dimensions: number; + abstract embed(texts: string[], kind?: EmbedKind): Promise; +} diff --git a/src/generation/generation.service.ts b/src/generation/generation.service.ts index 888f270..b866415 100644 --- a/src/generation/generation.service.ts +++ b/src/generation/generation.service.ts @@ -4,6 +4,7 @@ import { asJson, Sql, toVector } from '../db/db'; import { env, PROMPT_VERSION } from '../config/env'; import { DedupAction, DedupService } from '../keywords/dedup.service'; import { canonicalizeKeyword, normalizeKeyword } from '../keywords/normalize'; +import { EmbeddingProvider } from '../embedding/types'; import { LlmProvider, MerchantContext } from '../llm/types'; import { MerchantsService } from '../merchants/merchants.service'; @@ -35,6 +36,7 @@ export class GenerationService { @Inject(PG) private readonly sql: Sql, private readonly merchants: MerchantsService, private readonly llm: LlmProvider, + private readonly embedder: EmbeddingProvider, private readonly dedup: DedupService, ) {} @@ -72,7 +74,7 @@ export class GenerationService { // 임베딩은 한 번에 배치 호출 (후보 수만큼 왕복하지 않는다) const texts = output.keywords.map((k) => canonicalizeKeyword(k.keyword)); - const embeddings = texts.length ? await this.llm.embed(texts) : []; + const embeddings = texts.length ? await this.embedder.embed(texts, 'passage') : []; const stats: GenerationStats = { runId, @@ -193,7 +195,7 @@ export class GenerationService { pairs: Array<{ question: string; answer: string }>, ): Promise { if (pairs.length === 0) return 0; - const embeddings = await this.llm.embed(pairs.map((p) => p.question)); + const embeddings = await this.embedder.embed(pairs.map((p) => p.question), 'passage'); let created = 0; for (let i = 0; i < pairs.length; i++) { diff --git a/src/keywords/dedup.service.ts b/src/keywords/dedup.service.ts index b6bc8ee..2c2089d 100644 --- a/src/keywords/dedup.service.ts +++ b/src/keywords/dedup.service.ts @@ -61,6 +61,12 @@ export class DedupService { } // 2~3단계 — trigram 후보 + 벡터 ANN 후보를 모아 최고 유사도 판정 + // + // 주의: 짧은 한글 키워드에서는 문장 임베딩의 절대 코사인이 변별력이 약하다. + // 실측(multilingual-e5-small): '선유도 펜션' ↔ '새만금 펜션' = 0.936, + // '군산 펜션' ↔ '군산 호텔' = 0.970 — 전혀 다른 키워드인데도 높게 나온다. + // 반면 어순만 바뀐 진짜 중복('군산 키즈룸 펜션' ↔ '군산 펜션 키즈룸')은 0.999 대에 몰린다. + // 그래서 임계값을 0.99 로 올려 잡고, 자동 병합의 주력은 1~2단계(어휘)에 둔다. const candidates = await this.repo.findDedupCandidates( input.embedding, normalized, diff --git a/src/llm/mock.provider.ts b/src/llm/mock.provider.ts index 40b029b..7d28e24 100644 --- a/src/llm/mock.provider.ts +++ b/src/llm/mock.provider.ts @@ -22,9 +22,6 @@ export class MockLlmProvider extends LlmProvider { readonly name = 'mock'; readonly model = 'mock-keyword-v1'; - async embed(texts: string[]): Promise { - return texts.map((t) => hashEmbedding(t)); - } async generate(ctx: MerchantContext): Promise { const region = ctx.regionName ?? ''; diff --git a/src/llm/openai.provider.ts b/src/llm/openai.provider.ts index ba35213..1b90db7 100644 --- a/src/llm/openai.provider.ts +++ b/src/llm/openai.provider.ts @@ -48,14 +48,6 @@ export class OpenAiLlmProvider extends LlmProvider { private readonly logger = new Logger(OpenAiLlmProvider.name); private readonly client = new OpenAI({ apiKey: env.llm.apiKey }); - async embed(texts: string[]): Promise { - if (texts.length === 0) return []; - const res = await this.client.embeddings.create({ - model: env.llm.embeddingModel, - input: texts, - }); - return res.data.map((d) => d.embedding as number[]); - } async generate(ctx: MerchantContext): Promise { const res = await this.client.chat.completions.create({ diff --git a/src/llm/types.ts b/src/llm/types.ts index 3f00f81..e1c9db6 100644 --- a/src/llm/types.ts +++ b/src/llm/types.ts @@ -43,5 +43,4 @@ export abstract class LlmProvider { abstract readonly name: string; abstract readonly model: string; abstract generate(ctx: MerchantContext): Promise; - abstract embed(texts: string[]): Promise; } diff --git a/src/serving/demo.controller.ts b/src/serving/demo.controller.ts new file mode 100644 index 0000000..52ea5aa --- /dev/null +++ b/src/serving/demo.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get, Header } from '@nestjs/common'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +/** 로컬 확인용 매칭 데모 페이지. 빌드 산출물이 아니라 public/ 에서 직접 읽는다. */ +@Controller() +export class DemoController { + @Get('demo') + @Header('content-type', 'text/html; charset=utf-8') + demo(): string { + return readFileSync(join(process.cwd(), 'public', 'demo.html'), 'utf8'); + } +} diff --git a/src/serving/serving.controller.ts b/src/serving/serving.controller.ts index 533f748..c41dbf4 100644 --- a/src/serving/serving.controller.ts +++ b/src/serving/serving.controller.ts @@ -23,6 +23,12 @@ export class ServingController { return this.serving.searchKeywords(body.query, Math.min(body.limit ?? 10, 50)); } + /** 자유 입력(업체명/문장) → 적재된 사전에서 잘 맞는 키워드 */ + @Post('match') + match(@Body() body: { query: string; limit?: number }) { + return this.serving.match(body.query ?? '', Math.min(body.limit ?? 40, 200)); + } + /** 성과 피드백 주입 (Search Console / 유입 로그) */ @Post('sites/:id/performance') performance( diff --git a/src/serving/serving.module.ts b/src/serving/serving.module.ts index 3d5eb89..7e637fa 100644 --- a/src/serving/serving.module.ts +++ b/src/serving/serving.module.ts @@ -1,13 +1,13 @@ import { Module } from '@nestjs/common'; import { KeywordsModule } from '../keywords/keywords.module'; -import { LlmModule } from '../llm/llm.module'; import { MerchantsModule } from '../merchants/merchants.module'; +import { DemoController } from './demo.controller'; import { ServingController } from './serving.controller'; import { ServingService } from './serving.service'; @Module({ - imports: [MerchantsModule, KeywordsModule, LlmModule], - controllers: [ServingController], + imports: [MerchantsModule, KeywordsModule], + controllers: [ServingController, DemoController], providers: [ServingService], }) export class ServingModule {} diff --git a/src/serving/serving.service.ts b/src/serving/serving.service.ts index cedba96..d6fd9be 100644 --- a/src/serving/serving.service.ts +++ b/src/serving/serving.service.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { PG } from '../db/db.module'; -import { Sql } from '../db/db'; +import { Sql, toVector } from '../db/db'; import { KeywordRepository } from '../keywords/keyword.repository'; -import { LlmProvider } from '../llm/types'; -import { MerchantsService } from '../merchants/merchants.service'; +import { normalizeKeyword } from '../keywords/normalize'; +import { EmbeddingProvider } from '../embedding/types'; +import { MerchantsService, MerchantWithTaxonomy } from '../merchants/merchants.service'; export interface SeoPayload { merchant: { id: string; externalId: string; name: string; siteUrl: string | null }; @@ -34,7 +35,7 @@ export class ServingService { @Inject(PG) private readonly sql: Sql, private readonly merchants: MerchantsService, private readonly keywords: KeywordRepository, - private readonly llm: LlmProvider, + private readonly embedder: EmbeddingProvider, ) {} async seo(idOrExternalId: string, limit: number): Promise { @@ -95,8 +96,88 @@ export class ServingService { }; } + /** + * 자유 입력(업체명 또는 문장) → 적재된 키워드 사전에서 잘 맞는 것을 골라준다. + * 업체명이면 먼저 업체를 해석해 프로필 전체를 질의문으로 쓴다 — + * 상호만으로 임베딩하면 브랜드명 하나로 검색하는 것과 같아 매칭이 얕아진다. + */ + async match(rawQuery: string, limit: number) { + const query = rawQuery.trim(); + const merchant = await this.resolveMerchant(query); + + const queryText = merchant + ? [ + merchant.name, + merchant.region_name, + merchant.industry_name, + merchant.description, + ...toStrings(merchant.profile?.['services']), + ...toStrings(merchant.profile?.['features']), + ...toStrings(merchant.profile?.['audiences']), + ...toStrings(merchant.profile?.['nearby']), + ].filter(Boolean).join(' ') + : query; + + const [embedding] = await this.embedder.embed([queryText], 'query'); + const rows = await this.sql>` + SELECT k.id, k.canonical, k.intent, k.kind, k.category, k.aliases, k.usage_count, + 1 - (k.embedding <=> ${toVector(embedding)}::vector) AS score, + (mk.merchant_id IS NOT NULL) AS linked + FROM keyword k + LEFT JOIN merchant_keyword mk + ON mk.keyword_id = k.id AND mk.merchant_id = ${merchant?.id ?? null} + WHERE k.embedding IS NOT NULL + ORDER BY k.embedding <=> ${toVector(embedding)}::vector + LIMIT ${limit}`; + + return { + input: query, + resolved: merchant + ? { + id: merchant.id, externalId: merchant.external_id, name: merchant.name, + region: merchant.region_name, industry: merchant.industry_name, + description: merchant.description, siteUrl: merchant.site_url, + profile: merchant.profile ?? {}, + } + : null, + queryText, + embeddingProvider: this.embedder.name, + total: await this.dictionarySize(), + matches: rows.map((r) => ({ ...r, score: Number(r.score) })), + }; + } + + /** 상호는 띄어쓰기가 흔들린다 — 정규화 일치 → trigram 순으로 해석 */ + private async resolveMerchant(query: string) { + const norm = normalizeKeyword(query); + if (!norm) return null; + const rows = await this.sql>` + SELECT m.*, + i.name AS industry_name, i.path::text AS industry_path, + r.name AS region_name, r.path::text AS region_path, + similarity(regexp_replace(lower(m.name), '\\s', '', 'g'), ${norm}) AS sim + FROM merchant m + LEFT JOIN industry i ON i.id = m.industry_id + LEFT JOIN region r ON r.id = m.region_id + WHERE regexp_replace(lower(m.name), '\\s', '', 'g') = ${norm} + OR m.external_id = ${query} + OR similarity(regexp_replace(lower(m.name), '\\s', '', 'g'), ${norm}) >= 0.45 + ORDER BY sim DESC NULLS LAST + LIMIT 1`; + return rows[0] ?? null; + } + + async dictionarySize() { + const [row] = await this.sql>` + SELECT count(*)::int AS n FROM keyword WHERE embedding IS NOT NULL`; + return row?.n ?? 0; + } + async searchKeywords(query: string, limit: number) { - const [embedding] = await this.llm.embed([query]); + const [embedding] = await this.embedder.embed([query], 'query'); return this.keywords.searchByVector(embedding, 'ko-KR', limit); } @@ -136,6 +217,10 @@ export class ServingService { } } +function toStrings(v: unknown): string[] { + return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : []; +} + function buildDescription(name: string, desc: string, keywords: string[]): string { const base = desc?.trim() || `${name} 안내`; const tail = keywords.slice(0, 3).join(', ');