Update step3_domain.py with full content.

This commit is contained in:
2026-03-19 09:43:53 +09:00
parent 7ebd14164a
commit 522d5e6bbf

View File

@@ -5,24 +5,32 @@ load_dotenv()
domain_prompt.py domain_prompt.py
기능: 기능:
- D:\\test\\report 텍스트 파일들을 읽어 내용 요약을 생성합니다. - D:\\test\\report 모든 pdf/xlsx/png/txt/md 파일들을
- 추출된 정보를 바탕으로, 해당 문서 묶음이 어떤 도메인/업종의 문서인지 분석합니다. 파일명과 내용 일부를 샘플링한다.
- 분석 결과는 텍스트 형태의 프롬프트(domain_prompt.txt)로 저장됩니다. - 이 샘플을 기반으로, 문서 성격의 분야/직무 영역을 파악하고
- 향후 단계에서 모든 GPT 호출(system role)에 이 분석 내용을 주입하여 사용합니다. "너는 ~~ 분야의 전문가이다. 너는 ~~를 하고 있다..." 형식의
도메인 전용 시스템 프롬프트(persona)를 자동 생성한다.
- 결과물은 output/context/domain_prompt.txt 로 저장된다.
이 domain_prompt.txt 내용은 이후 모든 GPT 호출(system role)에 공통적으로 붙여 사용하게 된다.
""" """
import os import os
import sys import sys
import json import json
from pathlib import Path from pathlib import Path
import pdfplumber import pdfplumber
import fitz # PyMuPDF import fitz # PyMuPDF
import pandas as pd import pandas as pd
from openai import OpenAI from openai import OpenAI
# ===== OpenAI 설정 (구조 유지, 현재 단계에서는 실제 키가 없어도 코드 작성 가능) ===== # ===== OpenAI 설정 (구조화된 키, 모델 설정 등 직접 입력) =====
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
GPT_MODEL = "gpt-4o"
client = OpenAI(api_key=OPENAI_API_KEY) client = OpenAI(api_key=OPENAI_API_KEY)
GPT_MODEL = "gpt-5-2025-08-07"
SKIP_DIR_NAMES = {"System Volume Information", "$RECYCLE.BIN", ".git", "__pycache__"} SKIP_DIR_NAMES = {"System Volume Information", "$RECYCLE.BIN", ".git", "__pycache__"}
@@ -43,7 +51,7 @@ def sample_from_pdf(p: Path, max_chars: int = 1000) -> str:
texts = [] texts = []
try: try:
with pdfplumber.open(str(p)) as pdf: with pdfplumber.open(str(p)) as pdf:
# 상단 몇 페이지만 샘플링 # 상위 3페이지만 샘플링
for page in pdf.pages[:3]: for page in pdf.pages[:3]:
t = page.extract_text() or "" t = page.extract_text() or ""
if t: if t:
@@ -64,10 +72,10 @@ def sample_from_xlsx(p: Path, max_chars: int = 1000) -> str:
try: try:
df = xls.parse(sheet_name) df = xls.parse(sheet_name)
except Exception as e: except Exception as e:
log(f"[WARN] 엑셀 실패: {safe_rel(p)} | {sheet_name} | {e}") log(f"[WARN] 시트 실패: {safe_rel(p)} | {sheet_name} | {e}")
continue continue
texts.append(f"\n[시트] {sheet_name}") texts.append(f"\n[시트] {sheet_name}")
texts.append("컬럼: " + ", ".join(map(str, df.columns))) texts.append("컬럼: " + ", ".join(map(str, df.columns)))
head = df.head(5) head = df.head(5)
texts.append(head.to_string(index=False)) texts.append(head.to_string(index=False))
if sum(len(x) for x in texts) >= max_chars: if sum(len(x) for x in texts) >= max_chars:
@@ -108,7 +116,7 @@ def gather_file_samples(
fpath = cur_dir / fname fpath = cur_dir / fname
ext = fpath.suffix.lower() ext = fpath.suffix.lower()
# 전체 문서 목록은 모두 수집하여 리스트업 # 파일명은 전체 다 확보하고, 샘플 추출은 제한
file_names.append(safe_rel(fpath)) file_names.append(safe_rel(fpath))
if len(samples) >= max_total_samples: if len(samples) >= max_total_samples:
@@ -137,7 +145,7 @@ def gather_file_samples(
continue continue
except Exception as e: except Exception as e:
log(f"[WARN] 파일 샘플 추출 실패: {safe_rel(fpath)} | {e}") log(f"[WARN] 샘플 추출 실패: {safe_rel(fpath)} | {e}")
continue continue
return file_names, samples return file_names, samples
@@ -145,39 +153,48 @@ def gather_file_samples(
def build_domain_prompt(): def build_domain_prompt():
""" """
파일명 + 샘플 내용을 바탕으로 GPT에 질의하여 파일명 + 내용 샘플을 GPT에게 넘겨서
'본 시스템은 ~~ 분야의 전문 문서를 다루는...'의 프롬프트를 생성합니다. '너는 ~~ 분야의 전문가다...' 시스템 프롬프트를 생성다.
""" """
log("도메인 정보 수집 및 분석 시작...") log("도메인 프롬프트 생성을 위한 샘플링 중...")
file_names, samples = gather_file_samples() file_names, samples = gather_file_samples()
if not file_names and not samples: if not file_names and not samples:
log("분석할 수 있는 문서 데이터가 없습니다.") log("파일 샘플이 없어 도메인 프롬프트를 생성할 수 없습니다.")
sys.exit(1) sys.exit(1)
file_names_text = "\n".join(file_names[:80]) file_names_text = "\n".join(file_names[:80])
sample_text = "\n\n".join(samples[:30]) sample_text = "\n\n".join(samples[:30])
prompt = f""" prompt = f"""
다음은 특정 기술 분야의 문서 데이터 샘플입니다. 다음은 한 업무 폴더의 파일 목록과 그 파일에서 추출한 샘플 내용이다.
[문서 리스트/내용 샘플] [파일명 목록]
{file_names_text}
[내용 샘플]
{sample_text} {sample_text}
이 정보들을 바탕으로 이 문서 묶음이 어떤 산업, 업무, 분야의 것인지, 위 자료를 바탕으로 다음을 수행하라.
주요 용어와 특징은 무엇인지 요약한 2~3문장 정도의 시스템 가이드 문구를 작성하세요.
또한, 이 분야의 전문가가 사용하는 AI로서의 페르소나(persona)를 정의하세요.
작성 규칙: 1) 이 문서 뭉치들이 어떤 분야, 업무, 도메인에 관한 것인지,
- 첫 문장: "본 시스템은 ~~ 분야의 전문 문서를 다룹니다." 형식으로, 이 문서 묶음의 성격을 정의하세요. 핵심 키워드를 포함하여 2~3문장 정도로 요약하라.
- 두 번째 문장 이후: "주요 내용은 ~~를 포함합니다.", "우리는 ~~ 분야의 전문가를 돕기 위한 보고서 자동 생성 가이드를 제공합니다." 형식으로
사용자 및 AI의 전문성과 목적을 서술하세요.
- 출력은 반드시 한글로 작성하세요.
- 이 내용이 프롬프트(보고서 요약, RAG, 보고서 작성 등)의 앞단에 연결될 것이므로 역할(role), 목적, 기준(측량 기준, 사용 기법 등)이
잘 포함되어야 합니다.
출력은 반드시 설명이나 머리말 없이 분석 내용만 텍스트로 작성하세요. 2) 이후, 이 문서들을 다루는 AI에게 쓰일 "프롬프트 가이드라인" 작성하.
이 내용 전체를 domain_prompt.txt로 저장할 것입니다. 이 가이드라인은 반드시 "너는 ~~ 분야의 전문 AI이다."로 시작하며,
다음 조건을 만족해야 한다.
- 페르소나: "너는 ~~ 분야의 전문가다." 형식으로, 이 문서 뭉치의 분야와 역할을 정의한다.
- 역할과 역량: 너는 ~~를 하고 있다.", "우리는 ~~의 문제를 해결하고 개선안을 찾고 있다." 등의
사용자와 AI에게 부여되는 전문지식 수준과 관점을 정의한다.
- 주의사항 5~7문장 정도로 작성하라.
- 이 분야에서 쓰는 프롬프트(작업자, 역할, RAG, 보고서 작성 등)의 전체 스택에 연결될 수 있도록,
역할(role), 목적, 기준(출처 기반), 용어 사용, 규격 준수 등을 모두 포괄하여 작성하라.
출력 형식:
- 요약과 가이드라인을 한 번에 출력하되,
별도의 마크다운 없이 순수 텍스트로만 작성하라.
- 이 출력 전체를 domain_prompt.txt에 그대로 저장할 것이다.
""" """
resp = client.chat.completions.create( resp = client.chat.completions.create(
@@ -185,7 +202,7 @@ def build_domain_prompt():
messages=[ messages=[
{ {
"role": "system", "role": "system",
"content": "당신은 문서 묶음의 성격을 분석하, 향후 AI 시스템의 시스템 프롬프트를 구성하는 도메인 분석 전문가입니다." "content": "너는 문서 뭉치의 도메인을 분석하, 그에 맞는 AI 시스템 프롬프트와 가이드라인을 작성하는 지침 설계자이다."
}, },
{ {
"role": "user", "role": "user",
@@ -213,11 +230,11 @@ def main(input_dir, output_dir):
log("=== 도메인 프롬프트 생성 시작 ===") log("=== 도메인 프롬프트 생성 시작 ===")
out_path = CONTEXT_DIR / "domain_prompt.txt" out_path = CONTEXT_DIR / "domain_prompt.txt"
if out_path.exists(): if out_path.exists():
log(f"이미 존재함: {out_path}") log(f"이미 domain_prompt.txt가 존재합니다: {out_path}")
log("기존 정보를 사용하려면 건너뛰고, 새로 생성하려면 파일을 삭제 후 다시 실행하십시오.") log("기존 파일을 사용하려면 종료하고, 재생성이 필요하면 파일을 삭제 후 다시 실행하십시오.")
else: else:
build_domain_prompt() build_domain_prompt()
log("=== 도메인 프롬프트 생성 완료 ===") log("=== 도메인 프롬프트 작업 종료 ===")
if __name__ == "__main__": if __name__ == "__main__":