[작성자:] sp

  • RAG 사전 적용

    이 JSON(사전 항목 리스트)도 “텍스트 → 검색 → 상위 n개만 LLM에 전달” 흐름으로 쓰면 됩니다. 항목별로 definition, example, pos 같은 필드를 묶어 한 문서로 보고, BM25+FAISS로 질의와 가까운 항목만 뽑아 LLM 프롬프트에 넣으면 OK.

    아래는 지금 쓰신 구조를 그대로 JSON에 맞춰 살짝 바꾼 예시에요:

    # pip install rank_bm25 langchain-openai faiss-cpu
    
    import json, os
    from dotenv import load_dotenv
    from openai import OpenAI
    
    from langchain_community.retrievers import BM25Retriever
    from langchain_community.vectorstores import FAISS
    from langchain_openai import OpenAIEmbeddings
    from langchain.retrievers import EnsembleRetriever
    
    # 1) 환경 변수 & 클라이언트 ---------------------------------------------------
    load_dotenv()
    api_key = os.getenv("OPENAI_API_KEY"); assert api_key
    client = OpenAI(api_key=api_key)
    CHAT_MODEL = "gpt-4o-mini"
    EMB_MODEL  = "text-embedding-3-small"
    
    # 2) JSON 로드: 항목별 텍스트/메타 준비 ---------------------------------------
    JSON_PATH = "/mnt/data/llm_dictionary_2letter_20250812_000751.json"
    
    with open(JSON_PATH, "r", encoding="utf-8") as f:
        data = json.load(f)
    
    entries = data["entries"]  # [{word, prefix, length, pos, definition, example, ...}, ...]
    
    texts, metadatas = [], []
    for i, e in enumerate(entries):
        # 한 항목을 하나의 "문서"로 구성 (필요한 필드만 선택)
        content = "\n".join([
            f"word: {e.get('word','')}",
            f"pos: {e.get('pos','')}",
            f"definition: {e.get('definition','')}",
            f"example: {e.get('example','')}",
        ])
        texts.append(content)
        metadatas.append({
            "idx": i,
            "word": e.get("word",""),
            "prefix": e.get("prefix",""),
            "length": e.get("length",""),
            "rarity": e.get("rarity",""),
            "source": f"json_entry_{i}"
        })
    
    # 3) 앙상블 검색기(BM25 + FAISS) ---------------------------------------------
    bm25 = BM25Retriever.from_texts(texts, metadatas=metadatas); bm25.k = 3
    emb  = OpenAIEmbeddings(api_key=api_key, model=EMB_MODEL)
    faiss_store = FAISS.from_texts(texts, emb, metadatas=metadatas)
    faiss = faiss_store.as_retriever(search_kwargs={"k": 3})
    retriever = EnsembleRetriever(retrievers=[bm25, faiss], weights=[0.3, 0.7])
    
    # 4) 프롬프트 빌드 -------------------------------------------------------------
    def build_prompt(query: str, docs):
        ctx = []
        for i, d in enumerate(docs, 1):
            m = d.metadata
            ctx.append(
                f"[문서{i}] (source={m.get('source')}, word={m.get('word')}, prefix={m.get('prefix')}, length={m.get('length')}, rarity={m.get('rarity')})\n"
                f"{d.page_content}"
            )
        context = "\n\n".join(ctx) if ctx else "N/A"
        return f"""아래 '자료'만 근거로 한국어로 간결히 답하세요.
    - 자료 밖 정보는 추측하지 마세요.
    - 답할 수 없으면 '제공된 문서에서 찾지 못했습니다.'라고 말하세요.
    
    질문:
    {query}
    
    자료:
    {context}
    """
    
    def ask_llm(prompt: str) -> str:
        resp = client.chat.completions.create(
            model=CHAT_MODEL,
            temperature=0,
            messages=[
                {"role": "system", "content": "You are a helpful assistant that answers in Korean."},
                {"role": "user", "content": prompt},
            ],
        )
        return resp.choices[0].message.content.strip()
    
    # 5) 실행 예 -------------------------------------------------------------------
    if __name__ == "__main__":
        query = "‘aer-’로 시작하고 공기/비행과 관련된 단어의 정의와 예문을 알려줘."
        docs = retriever.invoke(query) or []
    
        if not docs:
            print("제공된 문서에서 찾지 못했습니다.")
        else:
            prompt = build_prompt(query, docs)
            answer = ask_llm(prompt)
            print(answer)
    

    빠른 팁:

    • JSON처럼 “레코드가 많은 구조”는 항목별로 하나의 문서로 보고 인덱싱하세요.
    • 어떤 필드를 묶어 텍스트로 쓸지가 검색 품질에 직결됩니다. 사전은 보통 word + pos + definition + example 조합이 가장 효과적.
    • 데이터가 큰 경우, 위처럼 즉석에서 임베딩을 매번 만들지 말고 FAISS 인덱스를 디스크에 저장/재사용하세요.
    • 한글 질의 ↔ 영어 문서면, 질의를 영어로 번역한 뒤 듀얼 질의(한글+영문)로 검색하는 게 종종 성능이 좋습니다.

    요약하면: 네, 이 JSON도 읽어서 검색으로 상위 문서만 추려 LLM에 전달하면 RAG 맞습니다.