
    VZj8"                        d Z ddlZddlZddlZddlZddlZddlZddlZg dZ	dZ
dZej                  j                  e
d      Zej                  j                  e
d      ZdZd	Zej                  j%                  d
      ZdZdZdZdez   dz   Zd Zd ZddZd Zd Zd Zd Zd Zd Z e!dk(  rLdejD                  v r6 eedg i      Z# eedg i      Z$ e%d e&e#d          e&e$d         fz         y e         yy)ud  
qa_forge.py — QA뱅크 대량 증식 파이프라인 [CEO 2026-07-18]

목적: 문서(공정서·씨앗·옵시디언) 하나하나에서 '현장에서 나올 법한 질문+답' 쌍을
로컬 Ollama(비용 0)로 대량 생성해 QA뱅크(E:\VLA시스템\_QA뱅크)에 적재.

CEO 원칙 반영:
- 용접은 사슬의 한 고리일 뿐 — 절단→선가공→제관·취부→(선)용접→그라인딩→도장→후가공이
  꼬리에 꼬리를 무는 "공정 연결 질문"을 반드시 섞는다.
- 숫자·사실은 문서에 있는 것만. 지어내면 그 쌍은 버린다(문서 대조 검증).
- 중요/판단형 질문 일부는 페이블(claude CLI)이 답해 증류쌍(FINETUNE/distill_pairs)으로 저장.

실행: python qa_forge.py            (밤샘 — Ctrl+C 안전, 재개 가능)
      python qa_forge.py --status  (진행 현황만 출력)
    N)u   E:\VLA시스템u+   E:\지식정제시스템\05_KNOWLEDGE\seedsu   E:\옵시디언u   E:\VLA시스템\_QA뱅크u&   E:\VLA시스템\FINETUNE\distill_pairsz_forge_state.jsonzauto_forge.qa.jsonz#http://localhost:11434/api/generatezdeepseek-r1:14bz~\.local\bin\claude.exe<   i,  uR  용접은 전체 공정 사슬(절단→선가공→제관·취부→선용접→본용접→그라인딩→도장→후가공)의 한 고리다. 질문 중 최소 2개는 앞뒤 공정 연결 질문으로 만들어라 (예: "이 부재 절단면 상태가 취부에 어떤 영향 주나", "용접 후 그라인딩 전 확인할 것은").u   너는 용접·제관 현장 20년 숙련자다. 아래 문서를 읽고, 현장 작업자가 실제로 물어볼 법한 질문과 답 6개를 만들어라. 반말/구어체/오타 섞인 질문도 포함(현장 말투 다양화). u    답은 반드시 문서에 있는 내용만 사용하고, 문서에 없는 수치를 지어내지 마라. 출력은 JSON 배열만: [{"q":"질문","a":"답","type":"조회|판단|공정연결"}]

[문서: %s]
%sc               #      K   t         D ]p  } t        j                  |       D ]V  \  }}t        fddD              r|D ]6  }|j	                  d      st        j
                  j                  |       8 X r y w)Nc              3   &   K   | ]  }|v  
 y w)N ).0xdps     qa_forge.py	<genexpr>zdocs.<locals>.<genexpr>,   s     j$iq17$is   )
EMBEDDINGSMODELSOUTPUTFINETUNEz	.obsidianz.trashz.md)ROOTSoswalkanyendswithpathjoin)root_fnsfnr	   s       @r
   docsr   )   sf     ''$-JB3j$ijj;;u%'',,r2..  ( s   AA>)A>c                 h    t         j                  j                  |       }d|v ryd|v ryd|v sd|v ryy)	NGOLDr   u	   공정서   SILVERBRONZE      )r   r   basename)pns     r
   priorityr&   3   s;    
A{1a1}Aa    c                 T   t        j                  t        | ddddd      j                         }t        j
                  j                  t        j
                  j                  t        |ddi      |	      }t        j                  |j                               j                  d
d      S )NFgffffff?i(
  )temperaturenum_predict)modelpromptstreamoptionszContent-Typezapplication/json)dataheaders)timeoutresponse )jsondumps	GEN_MODELencodeurllibrequesturlopenRequestOLLAMAloadsreadget)r,   r1   bodyrs       r
   ollamarB   ;   s    ::	Vu25d"KM NNTfh 	v~~55f4(68J'K  6  MV] 	 	_A::affh##J33r'   c                    t        j                  dd| t         j                        } t        j                  d| t         j                        }|sg S t	        j
                  |j                  d            }g }t        |t              r|ng D ]  }t        |j                  dd            j                         t        |j                  dd            j                         }}t        |      dk\  sdt        |      dk\  ss|j                  ||t        |j                  d	d            d
        |S # t        $ r g cY S w xY w)Nz<think>.*?</think>r3   )flagsz\[.*\]r   qa   type)rE   rF   rH   )resubSsearchr4   r=   group	Exception
isinstanceliststrr?   striplenappend)txtmarroutitrE   rF   s          r
   parse_pairsrZ   D   s    
&&&Crtt
<C
		)S"$$'ARizz!''!*%
CT*c2266#r?#))+SR-A-G-G-I1q6Q;3q6Q;JJQQBFF624F0GHI 3 J Ris   $D0 0D>=D>c                     t        j                  dd|       } t        |       dkD  r+t        t        |       dz
        D ch c]
  }| ||dz     c}S t	               S c c}w )Nz\sr3   r   r!   )rI   rJ   rS   rangeset)sis     r
   _bgr`   R   sV    
ub!A47FQJeCFQJ/0/Aa!H/0ICEI0s   Ac                 l   t        j                  dd|      t        |      }g }| D ]  }t        j                  d|d         }t	        fd|D              r1t        |d         }|rt        ||z        t        |      z  dk  r_t        j                  d|d         ry|j                  |        |S )u   [2026-07-18 강화] ①답 속 수치가 문서에 실존 ②답 내용이 문서와 실제로 겹침(바이그램 12%+)
    — 둘 중 하나라도 어기면 폐기. llama3.1이 그럴싸한 오답을 지어내던 사고 재발 방지.z[\s,]r3   z\d+(?:\.\d+)?rF   c              3   F   K   | ]  }t        |      d k\  xr |v  yw)r!   N)rS   )r   r   flats     r
   r   z!verify_numbers.<locals>.<genexpr>_   s&     ;ds1v{,q},ds   !gQ?u   [一-鿿぀-ゟ゠-ヿ])rI   rJ   r`   findallr   rS   rL   rT   )pairsdoc_txtdbkeepr$   numsabrc   s          @r
   verify_numbersrk   W   s     66(B(D	WBDzz*AcF3;d;;X3[Sb\CG+d2H99/38(A  Kr'   c                    d|d d z   dz   | z   }t        j                  t        dddg|j                  d      dd	
      }|j                  j                  dd      j                         }|j                  dk7  s|st        d      |S )Nu   너는 도진웍스 용접공정 코파일럿 SHIN이다. 근거 안에서만, 판단형이면 현상→원인→결론 순으로 한국어로 답하라. 불확실하면 "확인 필요".

[근거]
  u   

[질문] z-pz--modelzclaude-fable-5utf-8T   )inputcapture_outputr1   ignore)errorsr   u   fable 실패)	
subprocessrun
CLAUDE_EXEr7   stdoutdecoderR   
returncodeRuntimeError)rE   ctxr,   rA   rX   s        r
   fable_answerr|   g   s    ?ADUdLN]^`abF
D)5EFfmm\cNd&*C	9A
((//'(/
3
9
9
;C||q<+G%GJr'   c                 d    t        j                  t        | d            S # t        $ r |cY S w xY w)Nrn   encoding)r4   loadopenrN   )r$   defaults     r
   	load_jsonr   q   s'    		$q734	4$Wn$s   ! //c                     t        t        g i d      } t        t        dg d      }t        | d         }t	        j
                  d      }| d   j                  |d      }t        t               D cg c]	  }||vs| c}t              }t        d	t        |      t        |      t        |d
         fz         t        j                  t        d       t        |      D ]e  \  }}	 t!        |dd      j#                         }t        |      t$        k  r|j'                  |       Ht        j(                  j+                  |      }	t-        t/        t0        |	|d d fz              }
t3        |
|      }
|
D ]  }||d<   	 |d
   j5                  |
       |
D cg c]  }|j                  d      dv s| }}|r|t6        k  rt        j(                  j9                  t:              r	 t=        |d   d   |d d       }t!        t        j(                  j?                  t        d|z        dd      5 }|jA                  tC        jD                  t	        j
                  d      d|d   d   |d d |ddd      dz          d d d        |dz  }|j'                  |       |d z  dk(  s|t        |      dz
  k(  rt        |      | d<   || d   |<   tC        jH                  | t!        t        d!d      d       tC        jH                  |t!        t        d!d      dd"       t        d#|dz   t        |      t        |d
         ||	d d$ fz         h t        |      | d<   || d   |<   tC        jH                  | t!        t        d!d      d       tC        jH                  |t!        t        d!d      dd"       t        d(t        |d
         |fz         y c c}w c c}w # 1 sw Y   _xY w# tF        $ r Y iw xY w# tJ        $ r Y  tF        $ r9}t        d%|d&tM        |      d d'        |j'                  |       Y d }~\d }~ww xY w))N)done
fable_usedu%   auto_forge(공정사슬 자동증식))srcqar   z%Y%mr   r   )keyu>   [forge] 대상 %d개 문서 (완료 %d, QA뱅크 현재 %d쌍)r   T)exist_okrn   rr   )r   rs   rm   r   rH   )u   판단u   공정연결rE   zdistill_%s.jsonlrF   r~   z%Y-%m-%d %H:%M:%Szclaude:claude-fable-5qa_forge)tsr+   rE   r{   rF   viaF)ensure_ascii
r   
   w)r   indentu)   [forge] %d/%d | QA %d쌍 | 증류 %d | %s(   u   [forge] 건너뜀 z: P   u)   [forge] 종료 — QA %d쌍, 증류 %d건)'r   STATEOUTr]   timestrftimer?   sortedr   r&   printrS   r   makedirsDISTILL_DIR	enumerater   r>   MIN_DOCaddr   r#   rZ   rB   PROMPTrk   extendFABLE_PER_RUNexistsrv   r|   r   writer4   r5   rN   dumpKeyboardInterruptrQ   )stbankr   monthfusedr$   targetsr_   rU   namere   prr   hardfafes                    r
   mainr   v   s   	52R8	9BS"IQSTUDr&z?DMM&!E|  *E9A1D=a9xHG	
JcRYl\_`d\egjkoptkugvMw
wxKKd+'"1	Nq78<AACC3x'!X77##A&Dvs5Dz0B'B CDE"5#.ERYeJe$$Tu!f9S(SAuDT-"''..2L'%d1gclCJ?Bbggll;8JU8RSUXcjkop

$--@S2T_v15ac%4jWYbl,n8=!?AE!F G l QJEHHQK2v{a3w<!#33#D\6
U2l+;E+B		"d5#@uU		$S# @u]^_AQUCPWLZ]^bcg^hZikprvwzxzr{D||}5 #> BvJr,/6IIb$ucG45IIIdDcG45QRS	
5T$Z%8P
PQK : U lk
 !&$& ! 	 	N3q6#2;?@$((1++	Ns   0	O:O(?O4(A/O4O0O40O4%AO$-AO:O$B?O4O4O!	O$$	O1-O40O11O44	Q Q.P<<Q__main__z--statusr   r   u"   처리 문서 %d | 생성 QA %d쌍)i  )'__doc__r   rI   r4   sysr   rt   urllib.requestr8   r   QA_DIRr   r   r   r   r   r<   r6   
expanduserrv   r   r   
CHAIN_HINTr   r   r&   rB   rZ   r`   rk   r|   r   r   __name__argvr   r   r   rS   r   r'   r
   <module>r      s1   ; : : :`	%7
V01ggll6/0	.	WW :;

H
{yy/4J
 %
+R\ zSXXuvrl+IcD":4NT2c"V*os4PT:5WWX r'   