# -*- coding: utf-8 -*-
# dzw_qa_builder.py — 공정서 QA뱅크 생성기 [자비스 즉답 엔진 2026-07-06]
#   CEO 설계: "공정서가 지도의 기준점 + 질문을 미리 엄청나게 만들어놓으면 즉답 — 지연 병목 제거"
#   ①규칙기반: 조건표·속도·공정단계에서 조합형 Q&A 자동 생성 (LLM 불필요, 즉시)
#   ②LLM확장: Ollama(llama3.1:8b, 로컬·무료)로 문서당 예상질문 추가 생성 (--llm 옵션, 밤새 배치 가능)
#   출력: E:\VLA시스템\_QA뱅크\<공정서명>.qa.json  → dzw_knowledge_server.py가 로드해 /api/qa 즉답
import os, re, json, sys, urllib.request

ROOT = r'E:\VLA시스템'
OUT  = os.path.join(ROOT, '_QA뱅크')

def rule_qa(name, txt):
    """공정서 표·규칙에서 조합형 Q&A 추출 — 근거는 항상 공정서 원문"""
    qa = []
    prod = re.sub(r'^!_|_용접공정서\.md$|\.md$', '', name)
    # 조건표 행: | W01 | BOSS부 | 220 | 24 | ... 형태 유연 매칭
    for m in re.finditer(r'\|\s*([^|\n]{2,30})\s*\|\s*(\d{2,3})\s*A?\s*\|\s*([\d.]{1,5})\s*V?\s*\|', txt):
        part, amp, volt = m.group(1).strip(), m.group(2), m.group(3)
        if not (60 <= int(amp) <= 500): continue
        a = '%s의 %s 조건은 전류 %sA / 전압 %sV 입니다. (출처: %s)' % (prod, part, amp, volt, name)
        for q in ('%s %s 전류 얼마야' % (prod, part), '%s 조건 알려줘' % part,
                  '%s %s 전압은' % (prod, part)):
            qa.append({'q': q, 'a': a})
    # 속도 — 표 칸(|) 건너 매칭 허용, 종류당 첫 정의(조건표)만 채택 (중복=모호답 방지)
    seen_kind = set()
    for m in re.finditer(r'(직선|곡선|협소|BOSS)[^\n]*?(\d{2,3})\s*[~〜]?\s*(\d{2,3})?\s*cm/min', txt):
        kind, lo, hi = m.group(1), m.group(2), m.group(3) or m.group(2)
        if kind in seen_kind: continue
        seen_kind.add(kind)
        a = '%s %s부 용접속도는 %s~%scm/min (JBI V=%.1f~%.1f) 입니다. (출처: %s)' % (
            prod, kind, lo, hi, int(lo)/6, int(hi)/6, name)
        qa.append({'q': '%s %s 속도 얼마야' % (prod, kind), 'a': a})
        qa.append({'q': '%s 구간 용접속도' % kind, 'a': a})
    # 암묵지 규칙 문장 (mm 하향/상향)
    for m in re.finditer(r'([^\n]*?(\d+(?:\.\d+)?)\s*(?:~\s*\d+(?:\.\d+)?)?\s*mm\s*(하향|상향)[^\n]*)', txt):
        line = m.group(1).strip().lstrip('-* ')
        if len(line) < 8: continue
        qa.append({'q': '%s 오프셋 보정 규칙' % prod, 'a': line + ' (출처: %s)' % name})
    # 공정 단계
    steps = re.findall(r'\[공정\s*(\d+)\]\s*([^\n]+)', txt)
    if steps:
        a = prod + ' 공정 순서: ' + ' → '.join('%s.%s' % (n, s.strip()) for n, s in steps[:12]) + ' (출처: %s)' % name
        qa.append({'q': '%s 공정 순서 알려줘' % prod, 'a': a})
        qa.append({'q': '%s 용접 어떻게 시작해' % prod, 'a': a})
    return qa

def llm_qa(name, txt, n=15):
    """Ollama로 예상질문 확장 — 실패해도 규칙기반은 이미 저장됨"""
    prompt = ('아래 용접 공정서를 읽고 현장 작업자가 물어볼 질문 %d개와 답을 만들어라. '
              '답은 반드시 공정서 내용만 근거로, 각 줄을 Q: ... / A: ... 형식으로.\n\n' % n) + txt[:5000]
    body = json.dumps({'model': 'llama3.1:8b', 'prompt': prompt, 'stream': False,
                       'options': {'temperature': 0.3, 'num_predict': 1200}}).encode()
    r = urllib.request.urlopen(urllib.request.Request(
        'http://localhost:11434/api/generate', data=body,
        headers={'Content-Type': 'application/json'}), timeout=300)
    out, cur = [], None
    for line in json.loads(r.read()).get('response', '').splitlines():
        line = line.strip()
        if line.startswith('Q:'): cur = line[2:].strip()
        elif line.startswith('A:') and cur:
            out.append({'q': cur, 'a': line[2:].strip() + ' (출처: %s)' % name}); cur = None
    return out

def bank_qa(txt, name):
    """수기 질문은행(MD, Q:/A: 쌍) — CEO·전문가가 직접 편집하는 질문공간"""
    qa, cur = [], None
    for line in txt.splitlines():
        line = line.strip()
        if line.startswith('Q:'): cur = line[2:].strip()
        elif line.startswith('A:') and cur:
            qa.append({'q': cur, 'a': line[2:].strip()}); cur = None
    return qa

def main(use_llm=False):
    os.makedirs(OUT, exist_ok=True)
    total = 0
    # 수기 질문은행 우선 수집
    for fn in os.listdir(OUT):
        if '질문은행' in fn and fn.endswith('.md'):
            qa = bank_qa(open(os.path.join(OUT, fn), encoding='utf-8', errors='ignore').read(), fn)
            if qa:
                op = os.path.join(OUT, fn.replace('.md', '') + '.qa.json')
                json.dump({'src': fn, 'qa': qa}, open(op, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
                print('[질문은행] %s → %d문항' % (fn, len(qa))); total += len(qa)
    for dp, _, fns in os.walk(ROOT):
        if any(x in dp for x in ('EMBEDDINGS', 'MODELS', 'OUTPUT', 'FINETUNE', '_QA뱅크')): continue
        for fn in fns:
            if not fn.endswith('.md') or ('공정서' not in fn and '불량' not in fn): continue
            txt = open(os.path.join(dp, fn), encoding='utf-8', errors='ignore').read()
            qa = rule_qa(fn, txt)
            if use_llm:
                try: qa += llm_qa(fn, txt)
                except Exception as e: print('  [LLM 생략]', fn, e)
            if qa:
                op = os.path.join(OUT, fn.replace('.md', '') + '.qa.json')
                json.dump({'src': fn, 'qa': qa}, open(op, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
                print('[QA뱅크] %s → %d문항' % (fn, len(qa))); total += len(qa)
    print('총 %d문항 생성' % total)

if __name__ == '__main__':
    main(use_llm='--llm' in sys.argv)
