[{"data":1,"prerenderedAt":603},["ShallowReactive",2],{"doc:\u002Fclaude":3},{"id":4,"title":5,"body":6,"description":16,"extension":596,"meta":597,"navigation":598,"path":599,"seo":600,"stem":601,"__hash__":602},"docs\u002FCLAUDE.md","Malgn Helper — 고객상담 AI 챗봇",{"type":7,"value":8,"toc":584},"minimark",[9,13,17,22,25,89,93,130,134,216,220,230,234,271,275,295,300,306,340,398,402,478,481,580],[10,11,5],"h1",{"id":12},"malgn-helper-고객상담-ai-챗봇",[14,15,16],"p",{},"NotebookLM 수준의 사내 솔루션 전문 고객상담 AI 챗봇.\n자사 솔루션 사용 방법·안내를 자동화하여 상담 비용을 절감하고 24\u002F7 응답 채널을 확보한다.",[18,19,21],"h2",{"id":20},"프로젝트-구성","프로젝트 구성",[14,23,24],{},"이 워크스페이스는 역할별로 5개의 독립 디렉토리로 분리되어 있다 (제품 4 + 관리 허브 1).",[26,27,28,41,50,59,80],"ul",{},[29,30,31,35,36,40],"li",{},[32,33,34],"code",{},"malgn-helper\u002F"," — ",[37,38,39],"strong",{},"사용자 프론트엔드"," (고객이 챗봇과 대화하는 화면). Nuxt 3 \u002F Cloudflare Pages.",[29,42,43,35,46,49],{},[32,44,45],{},"malgn-helper-admin\u002F",[37,47,48],{},"관리자 프론트엔드"," (자료 업로드, 표준 답변 관리, 상담 로그\u002F에스컬레이션 검토).",[29,51,52,35,55,58],{},[32,53,54],{},"malgn-helper-api\u002F",[37,56,57],{},"API 서버"," (Hono on Cloudflare Workers). 검색·LLM·DB 접근을 모두 담당.",[29,60,61,35,64,67,68,71,72,75,76,79],{},[32,62,63],{},"malgn-helper-pms\u002F",[37,65,66],{},"PMS 애드온"," (맑은프로젝트게시판 PMS에 탑재되는 상담사 도우미). PMS 내부에서 동작하며 ",[32,69,70],{},"malgn-helper-api","를 호출해 ",[37,73,74],{},"고객 문의 답변 추천"," + ",[37,77,78],{},"고객 문의 분석\u002F브리핑","을 PMS 상담사에게 제공.",[29,81,82,35,85,88],{},[32,83,84],{},"malgn-helper-mng\u002F",[37,86,87],{},"프로젝트 관리 허브"," (대시보드·현황판·WBS·문서·작업 이력). 제품 코드가 아닌 이 프로젝트의 운영·조망용. Nuxt 3 \u002F Cloudflare Pages + D1.",[18,90,92],{"id":91},"핵심-요구사항","핵심 요구사항",[26,94,95,101,107,113,119,125],{},[29,96,97,100],{},[37,98,99],{},"NotebookLM 수준의 답변 품질",": 업로드된 자료에 근거한 정확한 답변 + 출처 인용",[29,102,103,106],{},[37,104,105],{},"정확성·일관성 우선",": 같은 질문에 매번 같은 답변, 잘못된 안내 방지",[29,108,109,112],{},[37,110,111],{},"표준 답변 우선 사용",": 회사가 검증한 답변이 있으면 그것을 사용",[29,114,115,118],{},[37,116,117],{},"\"모르면 모른다\"",": 추측 금지. 모호하면 상담사에게 에스컬레이션",[29,120,121,124],{},[37,122,123],{},"데이터 소스",": 매뉴얼 문서, 동영상, 기존 Q&A DB",[29,126,127],{},[37,128,129],{},"한국어 컨텐츠 비중 높음",[18,131,133],{"id":132},"인프라-제약","인프라 제약",[26,135,136,143,154,161,167,178],{},[29,137,138,139,142],{},"프론트엔드\u002F백엔드는 ",[37,140,141],{},"Cloudflare"," (Pages + Workers)",[29,144,145,146,149,150,153],{},"RDB는 ",[37,147,148],{},"Aurora MySQL",", ",[37,151,152],{},"Hyperdrive","로 연결",[29,155,156,157,160],{},"검색 인프라는 외부 ",[37,158,159],{},"AWS OpenSearch Service"," (k-NN + BM25 하이브리드)",[29,162,163,164],{},"원본 파일 저장소는 ",[37,165,166],{},"Cloudflare R2",[29,168,169,170,173,174,177],{},"인덱싱은 ",[37,171,172],{},"MVP에서 동기 처리",". 동영상\u002F대용량 자료가 늘어나면 ",[37,175,176],{},"Cloudflare Queues + Indexer Worker","로 2단계 도입.",[29,179,180,181,184,185,188,189,192,193,196,197],{},"LLM 호출은 ",[37,182,183],{},"Cloudflare AI Gateway"," → ",[37,186,187],{},"OpenAI"," (기본 ",[32,190,191],{},"gpt-4o-mini",", 고품질 작업은 ",[32,194,195],{},"gpt-4o",")\n",[26,198,199,205],{},[29,200,201,202],{},"Gateway 이름: ",[32,203,204],{},"malgn-helper",[29,206,207,208,211,212,215],{},"Worker 환경변수: ",[32,209,210],{},"AI_GATEWAY_URL"," (vars), ",[32,213,214],{},"OPENAI_API_KEY"," (secret)",[18,217,219],{"id":218},"데이터-흐름","데이터 흐름",[221,222,227],"pre",{"className":223,"code":225,"language":226},[224],"language-text","사용자 브라우저       관리자 브라우저       PMS 상담사 화면\n      │                  │                      │\n      ▼                  ▼                      ▼\nmalgn-helper      malgn-helper-admin       malgn-helper-pms\n(Nuxt 3 \u002F Pages)  (Nuxt 3 \u002F Pages)         (Nuxt 3 \u002F Pages, PMS 임베드)\n      │                  │                      │\n      └──────────────────┼──────────────────────┘\n                         ▼\n                  malgn-helper-api\n                  (Hono \u002F Workers)\n                         │\n         ┌───────────────┼───────────────┐\n         ▼               ▼               ▼\n   Hyperdrive       OpenSearch           R2\n      │             (k-NN+BM25)       (원본파일)\n      ▼\n Aurora MySQL\n\n                         ▼\n                  AI Gateway → OpenAI\n\n  ※ 향후 동영상\u002F대용량 자료 도입 시:\n    Queue → Indexer Worker 비동기 파이프라인 추가\n","text",[32,228,225],{"__ignoreMap":229},"",[18,231,233],{"id":232},"작업-시-유의사항","작업 시 유의사항",[26,235,236,243,246,249,252,255,258],{},[29,237,238,239,242],{},"답변 생성 로직은 ",[37,240,241],{},"출처 인용이 누락되지 않도록"," 보장한다.",[29,244,245],{},"검색은 하이브리드(벡터 + BM25)를 전제로 한다. 한쪽만 쓰는 변경은 의도와 영향 범위를 명확히.",[29,247,248],{},"\"표준 답변\" 매칭이 검색·생성 파이프라인보다 우선한다.",[29,250,251],{},"LLM이 자신 없을 때는 답변을 짜내지 말고 에스컬레이션 경로로 빠진다.",[29,253,254],{},"DB 접근은 Worker → Hyperdrive 경유. 직접 연결 코드 금지.",[29,256,257],{},"모든 LLM 호출은 AI Gateway를 통과시켜 캐싱·로깅·rate limit을 적용한다.",[29,259,260,263,264,267,268,270],{},[32,261,262],{},"malgn-helper-pms","는 PMS 시스템 내부에서 동작하지만 ",[37,265,266],{},"DB·LLM 직접 접근 금지"," — 반드시 ",[32,269,70],{},"를 통해 호출한다 (인증\u002F권한·로깅 일원화).",[18,272,274],{"id":273},"배포-절차","배포 절차",[14,276,277,278,280,281,280,284,280,287,290,291,294],{},"서브프로젝트(",[32,279,204],{}," \u002F ",[32,282,283],{},"-admin",[32,285,286],{},"-api",[32,288,289],{},"-pms",") 배포 시 ",[37,292,293],{},"커밋 → 푸시 → Cloudflare deploy → 이력 기록","을 일괄 처리한다.",[296,297,299],"h3",{"id":298},"일괄-스크립트-권장","일괄 스크립트 (권장)",[14,301,302,303,305],{},"워크스페이스 루트(",[32,304,204],{},")에서:",[221,307,311],{"className":308,"code":309,"language":310,"meta":229,"style":229},"language-bash shiki shiki-themes github-light github-dark",".\u002Fscripts\u002Fdeploy.sh \u003Crepo-name> \"\u003Ccommit message>\"\n","bash",[32,312,313],{"__ignoreMap":229},[314,315,318,322,326,330,334,337],"span",{"class":316,"line":317},"line",1,[314,319,321],{"class":320},"sScJk",".\u002Fscripts\u002Fdeploy.sh",[314,323,325],{"class":324},"szBVR"," \u003C",[314,327,329],{"class":328},"sZZnC","repo-nam",[314,331,333],{"class":332},"sVt8B","e",[314,335,336],{"class":324},">",[314,338,339],{"class":328}," \"\u003Ccommit message>\"\n",[26,341,342,361],{},[29,343,344,347,348,350,351,350,354,350,356,350,358],{},[32,345,346],{},"\u003Crepo-name>",": ",[32,349,204],{}," | ",[32,352,353],{},"malgn-helper-admin",[32,355,70],{},[32,357,262],{},[32,359,360],{},"malgn-helper-mng",[29,362,363,364],{},"스크립트가 자동 실행하는 4단계:\n",[365,366,367,373,378,388],"ol",{},[29,368,369,372],{},[32,370,371],{},"git add -A && git commit -m \"\u003Cmessage>\""," (변경 없으면 commit skip)",[29,374,375],{},[32,376,377],{},"git push",[29,379,380,381,35,384,387],{},"해당 repo에서 ",[32,382,383],{},"pnpm deploy",[32,385,386],{},"wrangler.toml"," 존재 여부로 Pages\u002FWorkers 자동 분기",[29,389,390,393,394,397],{},[32,391,392],{},"doc\u002Fhistory\u002Fhistory.{yyyyMMdd}.md","의 ",[32,395,396],{},"## 배포"," 섹션에 항목 append (파일 없으면 생성)",[296,399,401],{"id":400},"수동-절차-스크립트-미사용-시","수동 절차 (스크립트 미사용 시)",[221,403,405],{"className":308,"code":404,"language":310,"meta":229,"style":229},"cd ~\u002FProjects\u002F\u003Crepo>\ngit add . && git commit -m \"\u003Cmessage>\"\ngit push\npnpm deploy\n# doc\u002Fhistory\u002Fhistory.{yyyyMMdd}.md에 배포 항목 직접 추가\n",[32,406,407,428,454,462,471],{"__ignoreMap":229},[314,408,409,413,416,419,422,425],{"class":316,"line":317},[314,410,412],{"class":411},"sj4cs","cd",[314,414,415],{"class":328}," ~\u002FProjects\u002F",[314,417,418],{"class":324},"\u003C",[314,420,421],{"class":328},"rep",[314,423,424],{"class":332},"o",[314,426,427],{"class":324},">\n",[314,429,431,434,437,440,443,445,448,451],{"class":316,"line":430},2,[314,432,433],{"class":320},"git",[314,435,436],{"class":328}," add",[314,438,439],{"class":328}," .",[314,441,442],{"class":332}," && ",[314,444,433],{"class":320},[314,446,447],{"class":328}," commit",[314,449,450],{"class":411}," -m",[314,452,453],{"class":328}," \"\u003Cmessage>\"\n",[314,455,457,459],{"class":316,"line":456},3,[314,458,433],{"class":320},[314,460,461],{"class":328}," push\n",[314,463,465,468],{"class":316,"line":464},4,[314,466,467],{"class":320},"pnpm",[314,469,470],{"class":328}," deploy\n",[314,472,474],{"class":316,"line":473},5,[314,475,477],{"class":476},"sJ8bj","# doc\u002Fhistory\u002Fhistory.{yyyyMMdd}.md에 배포 항목 직접 추가\n",[296,479,480],{"id":480},"규칙",[26,482,483,486,493,496,499,544,562],{},[29,484,485],{},"변경된 repo만 배포. 전체 일괄 배포 금지.",[29,487,488,489,492],{},"Secret 변경은 별도 — ",[32,490,491],{},"wrangler secret put \u003CKEY>"," 후 deploy.",[29,494,495],{},"배포 실패 시 history에 실패 사유 함께 기록.",[29,497,498],{},"이력 파일은 누적 — 같은 날 추가 배포는 기존 파일에 항목만 추가.",[29,500,501,504,505],{},[37,502,503],{},"account_id 주입 방식",":\n",[26,506,507,518,537],{},[29,508,509,510,513,514,517],{},"Workers (",[32,511,512],{},"wrangler.jsonc",")는 ",[32,515,516],{},"account_id"," 필드를 지원 → 파일에 명시.",[29,519,520,521,513,523,525,526,184,529,532,533,536],{},"Pages (",[32,522,386],{},[32,524,516],{}," 필드 ",[37,527,528],{},"미지원",[32,530,531],{},"scripts\u002Fdeploy.sh","에서 ",[32,534,535],{},"CLOUDFLARE_ACCOUNT_ID"," 환경변수로 주입.",[29,538,539,540,543],{},"수동 배포 시 Pages는 ",[32,541,542],{},"CLOUDFLARE_ACCOUNT_ID=... pnpm run deploy"," 형태로 실행 필요.",[29,545,546,35,554,557,558,561],{},[37,547,548,550,551],{},[32,549,383],{}," 대신 ",[32,552,553],{},"pnpm run deploy",[32,555,556],{},"deploy","는 pnpm 예약어라 충돌. 항상 ",[32,559,560],{},"run","을 명시.",[29,563,564,35,570,393,572,575,576,579],{},[37,565,566,567],{},"Nuxt Cloudflare Pages 출력 디렉토리는 ",[32,568,569],{},"dist\u002F",[32,571,386],{},[32,573,574],{},"pages_build_output_dir","도 ",[32,577,578],{},"dist","로 설정.",[581,582,583],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":229,"searchDepth":456,"depth":456,"links":585},[586,587,588,589,590,591],{"id":20,"depth":430,"text":21},{"id":91,"depth":430,"text":92},{"id":132,"depth":430,"text":133},{"id":218,"depth":430,"text":219},{"id":232,"depth":430,"text":233},{"id":273,"depth":430,"text":274,"children":592},[593,594,595],{"id":298,"depth":456,"text":299},{"id":400,"depth":456,"text":401},{"id":480,"depth":456,"text":480},"md",{},true,"\u002Fclaude",{"title":5,"description":16},"CLAUDE","E4oqMVxN36p86l5LbGwi2v2lD72KLSVEDn4itTZFyTI",1780986551103]