Skip to content

It shows an advanced RAG with Reflection where LangGraph is used for workflow management.

Notifications You must be signed in to change notification settings

kyopark2014/rag-with-reflection

Repository files navigation

Reflection์„ ์ด์šฉํ•œ RAG์˜ ์„ฑ๋Šฅ ํ–ฅ์ƒ

License

์—ฌ๊ธฐ์—์„œ๋Š” LangGraph๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ธฐ๋ณธ RAG๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , reflection๊ณผ query transformation์„ ์ด์šฉํ•˜์—ฌ RAG์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ๋น„๊ตํ•˜์—ฌ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. RAG๋ฅผ ์ด์šฉํ•จ์œผ๋กœ์จ ํ•„์š”ํ•œ ์‹œ์ ์— ์ ์ ˆํ•œ ๋น„์šฉ์œผ๋กœ ๊ธฐ์—…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ž์—ฐ์œผ๋กœ ์งˆ์˜๋˜๋Š” ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์œผ๋กœ๋ถ€ํ„ฐ ์‚ฌ์šฉ์ž์˜ ์˜๋„๋ฅผ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๊ณ , ๊ด€๋ จ๋œ ๋ฌธ์„œ๋“ค๋กœ ๋ถ€ํ„ฐ ๊ผญ ํ•„์š”ํ•œ ๋ฌธ์„œ๋งŒ์„ ์„ ํƒํ•˜๊ณ , ํ•œ๊ตญ์–ด์™€ ์˜์–ด ๋ฌธ์„œ๋ฅผ ๋ชจ๋‘ ์กฐํšŒํ•˜๋ ค๋ฉด ๋‹ค์–‘ํ•œ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Reflection๊ณผ transformation์„ ์ด์šฉํ•˜์˜€์„ ๋•Œ์˜ activity diagram์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ "(a) RAG with reflection"์€ RAG๋ฅผ ์กฐํšŒํ•˜์—ฌ ์–ป์€ ๋ฌธ์„œ๋“ค์„ ์ด์šฉํ•˜์—ฌ ๋‹ต๋ณ€์„ ๊ตฌํ•œ ํ›„์—, reflection์„ ํ†ตํ•˜์—ฌ ๋‹ต๋ณ€์—์„œ ๊ฐœ์„ ํ•˜์—ฌ์•ผ ํ•  ๋ชฉ๋ก ๋ฐ ์ถ”๊ฐ€๋กœ ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ์–ป์€ ๋ฌธ์„œ๋“ค๋กœ ํ–ฅ์ƒ๋œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. "(b) RAG with Transformation"์€ ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์„ ๊ฐฑ์‹ (rewrite)ํ•œ ํ›„์— ์ถ”๊ฐ€์ ์œผ๋กœ ๊ฒ€์ƒ‰ํ•  ์งˆ๋ฌธ๋“ค์„ ์ƒ์„ฑ(decompose)ํ•˜์—ฌ RAG๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

image

Reflection์€ ์ƒ์„ฑํ•œ ๋‹ต๋ณ€์œผ๋กœ ๋ถ€ํ„ฐ ์ถ”๊ฐ€ ์งˆ๋ฌธ์„ ์ถ”์ถœํ•˜์—ฌ RAG๋ฅผ ์กฐํšŒํ•˜๋ฏ€๋กœ, ์ฒ˜์Œ ๋‹ต๋ณ€๋ณด๋‹ค ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, RAG๋ฅผ ํ†ตํ•ด ์–ป์€ ๋‹ต๋ณ€์„ ๊ฐฑ์‹ ํ•˜๋Š” ๊ณผ์ •์—์„œ ๋‹ค์ˆ˜์˜ ์ƒˆ๋กœ์šด ์ถ”๊ฐ€ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•˜์—ฌ ์กฐํšŒํ•˜๋ฏ€๋กœ, RAG ์กฐํšŒ ๋น„์šฉ์ด ๋Œ€ํญ ๋Š˜์–ด๋‚˜๊ณ , ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ฆ๊ฐ€๊ฐ€ ๋ถˆ๊ฐ€ํ”ผํ•ฉ๋‹ˆ๋‹ค.

Transformation์€ RAG๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ์งˆ๋ฌธ์„ ๋ช…ํ™•ํžˆ ํ•˜๊ณ  ๊ด€๋ จ๋œ ์„ธ๋ถ€ ์งˆ๋ฌธ์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•˜์—ฌ ๊ฒ€์ƒ‰ํ•˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ๊ณผ ์ข€๋” ๊ฐ€๊นŒ์šด ๋ฌธ์„œ๋“ค์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Query tansformation์€ ์งˆ๋ฌธ(query)์ด ์งง์€ ๊ฒฝ์šฐ์—๋Š” ์งˆ๋ฌธ์„ rewriteํ•˜๊ฑฐ๋‚˜ decomposeํ•˜๋Š” ํšจ๊ณผ๊ฐ€ ๋†’์ง€ ์•Š์œผ๋ฉฐ, chatbot๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์ด์ „ history๋ฅผ ์ด์šฉํ•ด ์งˆ๋ฌธ์„ rephrase์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ query transformation์„ ํ†ตํ•ด ์งˆ๋ฌธ์„ ๋ช…ํ™•ํ•˜๊ฒŒ(rewrite) ํ•˜๋Š” ๊ณผ์ •์ด ์ค‘๋ณต ๋™์ž‘์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๊ตฌํ˜„๋œ architecture๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์œ ์ง€๋ณด์ˆ˜ ๋ฐ ๋ณ€ํ™”ํ•˜๋Š” ํŠธ๋ž˜ํ”ฝ์— ๊ฐ•์ ์ด ์žˆ๋Š” serverless architecture๋ฅผ ์‚ฌ์šฉํ•˜์˜€๊ณ , RAG๋กœ๋Š” Amazon Bedrock Knowledge Base๋ฅผ ํ™œ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

LangGraph๋ฅผ ์ด์šฉํ•œ RAG์˜ ๊ตฌํ˜„

Basic RAG

๊ธฐ๋ณธ RAG๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ activity diagram ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. Retrieve ๋…ธ๋“œ์—์„œ RAG๋กœ ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์„ ์ „๋‹ฌํ•˜์—ฌ ๊ด€๋ จ๋œ ๋ฌธ์„œ(relevant document)๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. RAG์— ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋“ค์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ๊ด€๋ จ๋„๊ฐ€ ๋–จ์–ด์ง€๋Š” ๋ฌธ์„œ๋“ค์ด ์„ ํƒ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ grading์„ ํ†ตํ•ด ๋ฌธ์„œ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ generate ๋…ธ๋“œ์—์„œ ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.

LangGraph๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด workflow๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ๋…ธ๋“œ(node)์—๋Š” retrieve_node, parallel_grader, generate_node๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. retrieve_node๋Š” RAG์— ์งˆ์˜ํ•˜๊ณ , parallel_grader๋Š” ๊ฐ€์ ธ์˜จ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ๊ฒ€์ฆํ•˜๊ณ , generate_node์—์„œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

class State(TypedDict):
    query: str
    draft: str
    relevant_docs: List[str]
    filtered_docs: List[str]
    reflection : List[str]
    sub_queries : List[str]
    revision_number: int

def buildRagBasic():
    workflow = StateGraph(State)

    # Add nodes
    workflow.add_node("retrieve_node", retrieve_node)
    workflow.add_node("parallel_grader", parallel_grader)
    workflow.add_node("generate_node", generate_node)

    # Set entry point
    workflow.set_entry_point("retrieve_node")
    
    workflow.add_edge("retrieve_node", "parallel_grader")
    workflow.add_edge("parallel_grader", "generate_node")
            
    return workflow.compile()

๊ธฐ๋ณธ RAG๋ฅผ ์‹คํ–‰ํ•  ๋•Œ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ž…๋ ฅ์œผ๋กœ query๋ฅผ ์ด์šฉํ•˜์—ฌ LangGraph๋กœ ์ƒ์„ฑํ•œ ์•ฑ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜ํ–‰์ด ์™„๋ฃŒ๋˜๋ฉด State์˜ draft๋ฅผ ์ถ”์ถœํ•˜์—ฌ ๋‹ต๋ณ€์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

def run_rag_basic(connectionId, requestId, query):    
    app = buildRagBasic()
    
    # Run the workflow
    isTyping(connectionId, requestId)        
    inputs = {
        "query": query
    }    
    config = {
        "recursion_limit": 50
    }
    
    output = app.invoke(inputs, config)
    
    return output['draft']

RAG์—์„œ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. State์—์„œ query๋ฅผ ์ถ”์ถœํ•˜์—ฌ Bedrock Knowledge Base๋ฅผ ์ด์šฉํ•ด ๊ตฌ์„ฑํ•œ RAG์— ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ์ถ”์ถœํ•ด์„œ ์ „๋‹ฌํ•˜๋„๋ก ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

def retrieve_node(state: State):
    print("###### retrieve ######")
    query = state['query']
    relevant_docs = retrieve_from_knowledge_base(query)
    
    return {
        "relevant_docs": relevant_docs
    }

์•„๋ž˜์™€ ๊ฐ™์ด RAG์˜ ์ง€์‹์ €์žฅ์†Œ๋กœ๋ถ€ํ„ฐ ์–ป์–ด์ง„ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ gradingํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ฒˆ grading์„ ์ˆ˜ํ–‰ํ•˜์—ฌ์•ผ ํ•˜๋ฏ€๋กœ Process-based parallelism์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

def parallel_grader(state: State):
    print("###### parallel_grader ######")
    query = state['query']
    relevant_docs = state['relevant_docs']
    
    global selected_chat    
    filtered_docs = []    

    processes = []
    parent_connections = []
    
    for i, doc in enumerate(relevant_docs):
        parent_conn, child_conn = Pipe()
        parent_connections.append(parent_conn)
            
        process = Process(target=grade_document_based_on_relevance, args=(child_conn, query, doc, multi_region_models, selected_chat))
        processes.append(process)

        selected_chat = selected_chat + 1
        if selected_chat == len(multi_region_models):
            selected_chat = 0
    for process in processes:
        process.start()
            
    for parent_conn in parent_connections:
        doc = parent_conn.recv()

        if doc is not None:
            filtered_docs.append(doc)

    for process in processes:
        process.join()    
    
    return {
        "filtered_docs": filtered_docs
    }    

์—ฌ๊ธฐ์—์„œ๋Š” ๊ด€๋ จ๋„๋ฅผ ํŒ๋‹จํ•˜๊ธฐ ์œ„ํ•˜์—ฌ LLM์„ ์ด์šฉํ•˜ grading์„ "yes/no"๋กœ ํŒ์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

def get_retrieval_grader(chat):
    system = """You are a grader assessing relevance of a retrieved document to a user question. \n 
    If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant. \n
    Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question."""

    grade_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", system),
            ("human", "Retrieved document: \n\n {document} \n\n User question: {question}"),
        ]
    )
    
    structured_llm_grader = chat.with_structured_output(GradeDocuments)
    retrieval_grader = grade_prompt | structured_llm_grader
    return retrieval_grader

def grade_document_based_on_relevance(conn, question, doc, models, selected):     
    chat = get_multi_region_chat(models, selected)
    retrieval_grader = get_retrieval_grader(chat)
    score = retrieval_grader.invoke({"question": question, "document": doc.page_content})
    
    grade = score.binary_score    
    if grade == 'yes':
        print("---GRADE: DOCUMENT RELEVANT---")
        conn.send(doc)
    else:  # no
        print("---GRADE: DOCUMENT NOT RELEVANT---")
        conn.send(None)
    
    conn.close()

RAG with Reflection

๊ธฐ๋ณธ RAG์— reflection์„ ์ถ”๊ฐ€ํ•˜์—ฌ, RAG๋กœ ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.

ย ย 

์•„๋ž˜๋Š” reflection์„ ์œ„ํ•œ workflow์ž…๋‹ˆ๋‹ค. reflect_node์—์„œ๋Š” ์ด์ „ ๋‹ต๋ณ€(draft)๋กœ ๋ถ€ํ„ฐ ๊ฐœ์„ ์ ์„ ์ถ”์ถœํ•˜๊ณ , ๊ด€๋ จ๋œ 3๊ฐœ์˜ query๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. parallel_retriever๋Š” 3๊ฐœ์˜ query๋ฅผ ๋ณ‘๋ ฌ๋กœ ์กฐํšŒํ•˜์—ฌ ๊ด€๋ จ๋œ ๋ฌธ์„œ(relevant documents)๋ฅผ ์–ป๊ณ , parallel_grader๋ฅผ ์ด์šฉํ•˜์—ฌ gradingํ•œ ํ›„์— revise_node๋ฅผ ์ด์šฉํ•˜์—ฌ ํ–ฅ์ƒ๋œ ๋‹ต๋ณ€์„ ์–ป์Šต๋‹ˆ๋‹ค.

def buildRagWithReflection():
    workflow = StateGraph(State)

    # Add nodes
    workflow.add_node("retrieve_node", retrieve_node)
    workflow.add_node("parallel_grader", parallel_grader)
    workflow.add_node("generate_node", generate_node)
    
    workflow.add_node("reflect_node", reflect_node)    
    workflow.add_node("parallel_retriever", parallel_retriever)    
    workflow.add_node("parallel_grader_subqueries", parallel_grader)
    workflow.add_node("revise_node", revise_node)

    # Set entry point
    workflow.set_entry_point("retrieve_node")
    
    workflow.add_edge("retrieve_node", "parallel_grader")
    workflow.add_edge("parallel_grader", "generate_node")
    
    workflow.add_edge("generate_node", "reflect_node")
    workflow.add_edge("reflect_node", "parallel_retriever")    
    workflow.add_edge("parallel_retriever", "parallel_grader_subqueries")    
    workflow.add_edge("parallel_grader_subqueries", "revise_node")
    
    workflow.add_conditional_edges(
        "revise_node", 
        continue_reflection, 
        {
            "end": END, 
            "continue": "reflect_node"}
    )
        
    return workflow.compile()

Reflection์€ ์ดˆ์•ˆ(draft)๋กœ ๋ถ€ํ„ฐ ์•„๋ž˜์™€ ๊ฐ™์ด structured output์„ ์ด์šฉํ•˜์—ฌ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. ์ถ”์ถœ๋œ ๊ฒฐ๊ณผ์—๋Š” reflection๊ณผ ๊ด€๋ จํ•˜์—ฌ missing, advisable, superfluous์„ ์–ป์–ด์„œ ๋ฌธ์ž์˜ ๊ฐœ์„ ์— ๋„์›€์„ ์ค„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, sub_queries๋ฅผ ์ด์šฉํ•ด 1-3๊ฐœ์˜ ์ƒˆ๋กœ์šด ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. RAG์—์„œ๋Š” embedding์„ ํ†ตํ•ด ํ•œ๊ตญ์–ด๋กœ ์˜์–ด๋กœ๋œ ๋ฌธ์„œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์œผ๋‚˜ ์œ ์‚ฌํ•œ ํ•œ๊ธ€ ๋ฌธ์„œ๊ฐ€ ๋งŽ์€ ๊ฒฝ์šฐ์—๋Š” ์œ ์šฉํ•œ ์˜์–ด ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์—ฌ๊ธฐ์—์„œ๋Š” ์ƒ์„ฑ๋œ sub_query๊ฐ€ ํ•œ๊ตญ์–ด์ผ ๊ฒฝ์šฐ์—๋Š” ๋ฒˆ์—ญํ•˜์—ฌ ์˜์–ด๋กœ ๋œ sub_query๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ reflection์œผ๋กœ ๋‹ต๋ณ€์— ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์ด๋‚˜, ์ถ”๊ฐ€๋˜์–ด์•ผํ•  ๋‚ด์šฉ, ๊ธธ์ด/์Šคํƒ€์ผ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜์—ฌ ๋‹ต๋ณ€์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class Reflection(BaseModel):
    missing: str = Field(description="Critique of what is missing.")
    advisable: str = Field(description="Critique of what is helpful for better writing")
    superfluous: str = Field(description="Critique of what is superfluous")
class Research(BaseModel):
    """Provide reflection and then follow up with search queries to improve the question/answer."""

    reflection: Reflection = Field(description="Your reflection on the initial answer.")
    sub_queries: list[str] = Field(
        description="1-3 search queries for researching improvements to address the critique of your current answer."
    )
class ReflectionKor(BaseModel):
    missing: str = Field(description="๋‹ต๋ณ€์— ์žˆ์–ด์•ผํ•˜๋Š”๋ฐ ๋น ์ง„ ๋‚ด์šฉ์ด๋‚˜ ๋‹จ์ ")
    advisable: str = Field(description="๋” ์ข‹์€ ๋‹ต๋ณ€์ด ๋˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ํ•˜์—ฌ์•ผ ํ•  ๋‚ด์šฉ")
    superfluous: str = Field(description="๋‹ต๋ณ€์˜ ๊ธธ์ด๋‚˜ ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ๋น„ํ‰")
class ResearchKor(BaseModel):
    """๋‹ต๋ณ€์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค."""

    reflection: ReflectionKor = Field(description="๋‹ต๋ณ€์— ๋Œ€ํ•œ ํ‰๊ฐ€")
    sub_queries: list[str] = Field(
        description="๋‹ต๋ณ€๊ณผ ๊ด€๋ จ๋œ 3๊ฐœ ์ด๋‚ด์˜ ๊ฒ€์ƒ‰์–ด"
    )

def reflect_node(state: State):
    print("###### reflect ######")
    query = state['query']
    draft = state['draft']
        
    reflection = []
    sub_queries = []
    for attempt in range(5):
        chat = get_chat()        
        if isKorean(draft):
            structured_llm = chat.with_structured_output(ResearchKor, include_raw=True)
            qa = f"์งˆ๋ฌธ: {query}\n\n๋‹ต๋ณ€: {draft}"    
        else:
            structured_llm = chat.with_structured_output(Research, include_raw=True)
            qa = f"Question: {query}\n\nAnswer: {draft}"
        
        info = structured_llm.invoke(qa)                
        if not info['parsed'] == None:
            parsed_info = info['parsed']
            reflection = [parsed_info.reflection.missing, parsed_info.reflection.advisable]
            sub_queries = parsed_info.sub_queries
                
            if isKorean(draft):
                translated_search = []
                for q in sub_queries:
                    chat = get_chat()
                    if isKorean(q):
                        search = traslation(chat, q, "Korean", "English")
                    else:
                        search = traslation(chat, q, "English", "Korean")
                    translated_search.append(search)
                        
                sub_queries += translated_search

            break
    return {
        "reflection": reflection,
        "sub_queries": sub_queries,
    }

parallel_retriever๋Š” sub_queries๋งŒํผ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ์†๋„๋ฅผ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ retriever๋Š” ์™„์ ผ๊ด€๋ฆฌํ˜•์ธ RAG ์„œ๋น„์Šค์ธ knowledge base์—์„œ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

def retriever(conn, query):
    relevant_docs = retrieve_from_knowledge_base(query)    
    print("---RETRIEVE: RELEVANT DOCUMENT---")
    
    conn.send(relevant_docs)    
    conn.close()
    
    return relevant_docs
    
def parallel_retriever(state: State):
    print("###### parallel_retriever ######")
    sub_queries = state['sub_queries']
    print('sub_queries: ', sub_queries)
    
    relevant_docs = []
    processes = []
    parent_connections = []
    
    for i, query in enumerate(sub_queries):
        print(f"retrieve sub_queries[{i}]: {query}")        
        parent_conn, child_conn = Pipe()
        parent_connections.append(parent_conn)
            
        process = Process(target=retriever, args=(child_conn, query))
        processes.append(process)

    for process in processes:
        process.start()
            
    for parent_conn in parent_connections:
        docs = parent_conn.recv()
        
        for doc in docs:
            relevant_docs.append(doc)

    for process in processes:
        process.join()    

    return {
        "relevant_docs": relevant_docs
    }

revise_node์—์„œ๋Š” reflection์œผ๋กœ ์–ป์–ด์ง„ reflection critique์™€ sub-quries๋ฅผ ์ด์šฉํ•ด ์กฐํšŒํ•œ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋“ค์„ ์ด์šฉํ•˜์—ฌ ์•„๋ž˜์™€ ๊ฐ™์ด ์ดˆ์•ˆ(draft)๋ฅผ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

def revise_node(state: State):   
    print("###### revise ######")
    draft = state['draft']
    reflection = state['reflection']
    
    if isKorean(draft):
        revise_template = (
            "๋‹น์‹ ์€ ์žฅ๋ฌธ ์ž‘์„ฑ์— ๋Šฅ์ˆ™ํ•œ ์œ ๋Šฅํ•œ ๊ธ€์“ฐ๊ธฐ ๋„์šฐ๋ฏธ์ž…๋‹ˆ๋‹ค."                
            "draft์„ critique๊ณผ information ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์ •ํ•˜์‹ญ์‹œ์˜ค."
            "์ตœ์ข… ๊ฒฐ๊ณผ๋Š” ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜๊ณ  <result> tag๋ฅผ ๋ถ™์—ฌ์ฃผ์„ธ์š”."
                            
            "<draft>"
            "{draft}"
            "</draft>"
                            
            "<critique>"
            "{reflection}"
            "</critique>"

            "<information>"
            "{content}"
            "</information>"
        )
    else:    
        revise_template = (
            "You are an excellent writing assistant." 
            "Revise this draft using the critique and additional information."
            "Provide the final answer with <result> tag."
                            
            "<draft>"
            "{draft}"
            "</draft>"
                        
            "<critique>"
            "{reflection}"
            "</critique>"

            "<information>"
            "{content}"
            "</information>"
        )
                    
    revise_prompt = ChatPromptTemplate([
        ('human', revise_template)
    ])    
    filtered_docs = state['filtered_docs']
              
    content = []   
    if len(filtered_docs):
        for d in filtered_docs:
            content.append(d.page_content)        

    chat = get_chat()
    reflect = revise_prompt | chat
           
    res = reflect.invoke(
        {
            "draft": draft,
            "reflection": reflection,
            "content": content
        }
    )
    output = res.content
        
    revised_draft = output[output.find('<result>')+8:len(output)-9]
            
    revision_number = state["revision_number"] if state.get("revision_number") is not None else 1
            
    return {
        "draft": revised_draft,
        "revision_number": revision_number + 1
    }

์ด๋•Œ, ์ดˆ์•ˆ์€ MAX_REVISIONS๋งŒํผ ๋ฐ˜๋ณตํ•˜์—ฌ refection์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MAX_REVISIONS = 1

def continue_reflection(state: State, config):
    print("###### continue_reflection ######")
    max_revisions = config.get("configurable", {}).get("max_revisions", MAX_REVISIONS)
            
    if state["revision_number"] > max_revisions:
        return "end"
    return "continue"

Query Transformation

RAG์— ์งˆ๋ฌธํ•˜๊ธฐ ์ „์— ์ž…๋ ฅ๋œ query๋ฅผ ๋ณ€ํ™˜ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด rewrite์™€ decompse ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. rewrite_node๋Š” RAG์—์„œ ์ข€๋” ์ข‹์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป๋„๋ก query์˜ ๋‚ด์šฉ์„ ์ž์„ธํ•˜๊ฒŒ ํ’€์–ด ์ ์Šต๋‹ˆ๋‹ค. ์ดํ›„ decompse_node์—์„œ๋Š” RAG์—์„œ ์กฐํšŒํ• ๋•Œ ์‚ฌ์šฉํ•  sub-quries๋“ค์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” query_transformations.ipynb์„ ์ฐธ์กฐํ•˜์—ฌ query transformation์„ ์œ„ํ•œ prompt๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

Transformation์„ ์œ„ํ•œ workflow๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. RAG๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์ „์— rewrite_node๋กœ ์งˆ๋ฌธ์„ ํ’€์–ด์„œ ์“ฐ๊ณ , decompose_node๋กœ ์ƒ์„ธํ•œ ์งˆ๋ฌธ๋“ค์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

def buildRagWithTransformation():
    workflow = StateGraph(State)

    # Add nodes
    workflow.add_node("rewrite_node", rewrite_node)
    workflow.add_node("decompose_node", decompose_node)
    workflow.add_node("parallel_retriever", parallel_retriever)
    workflow.add_node("parallel_grader", parallel_grader)
    workflow.add_node("generate_node", generate_node)
    
    # Set entry point
    workflow.set_entry_point("rewrite_node")
    
    # Add edges
    workflow.add_edge("rewrite_node", "decompose_node")
    workflow.add_edge("decompose_node", "parallel_retriever")
    workflow.add_edge("parallel_retriever", "parallel_grader")
    workflow.add_edge("parallel_grader", "generate_node")    
    workflow.add_edge("generate_node", END)

Query Rewrite

rewrite_node์—์„œ๋Š” ์งˆ๋ฌธ์„ ๊ฒ€์ƒ‰์— ๋งž๊ฒŒ ์ƒ์„ธํ•˜๊ฒŒ ํ’€์–ด์ค๋‹ˆ๋‹ค.

def rewrite_node(state: State):
    print("###### rewrite ######")
    query = state['query']
    
    query_rewrite_template = (
        "You are an AI assistant tasked with reformulating user queries to improve retrieval in a RAG system."
        "Given the original query, rewrite it to be more specific," 
        "detailed, and likely to retrieve relevant information."
        "Put it in <result> tags."

        "Original query: {original_query}"
        "Rewritten query:"
    )
    
    rewrite_prompt = ChatPromptTemplate([
        ('human', query_rewrite_template)
    ])

    chat = get_chat()
    rewrite = rewrite_prompt | chat
           
    res = rewrite.invoke({"original_query": query})    
    revised_query = res.content
    
    revised_query = revised_query[revised_query.find('<result>')+8:len(revised_query)-9] # remove <result> tag                   
    
    return {
        "query": revised_query
    }

Query Decompose

์งˆ๋ฌธ์ด ์—ฌ๋Ÿฌ๊ฐœ์˜ Subquery๋ฅผ ๊ฐ€์งˆ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด Decompose๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

def decompose_node(state: State):
    print("###### decompose ######")
    query = state['query']
    
    if isKorean(query):
        subquery_decomposition_template = (
            "๋‹น์‹ ์€ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ฅผ RAG ์‹œ์Šคํ…œ์— ๋” ๊ฐ„๋‹จํ•œ ํ•˜์œ„ ์ฟผ๋ฆฌ๋กœ ๋ถ„ํ•ดํ•˜๋Š” AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. "
            "์ฃผ์–ด์ง„ ์›๋ž˜ ์ฟผ๋ฆฌ๋ฅผ 1-3๊ฐœ์˜ ๋” ๊ฐ„๋‹จํ•œ ํ•˜์œ„ ์ฟผ๋ฆฌ๋กœ ๋ถ„ํ•ดํ•˜์„ธ์š”. "
            "์ตœ์ข… ๊ฒฐ๊ณผ์— <result> tag๋ฅผ ๋ถ™์—ฌ์ฃผ์„ธ์š”."

            "<query>"
            "{original_query}"
            "</query>"

            "๋‹ค์Œ์˜ ์˜ˆ์ œ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์ฟผ๋ฆฌ๋Š” ํ•œ ์ค„์„ ์ฐจ์ง€ํ•ฉ๋‹ˆ๋‹ค:"
            "<example>"
            "์งˆ๋ฌธ: ๊ธฐํ›„ ๋ณ€ํ™”๊ฐ€ ํ™˜๊ฒฝ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? "

            "ํ•˜์œ„ ์งˆ๋ฌธ:"
            "1. ๊ธฐํ›„ ๋ณ€ํ™”๊ฐ€ ํ™˜๊ฒฝ์— ๋ฏธ์น˜๋Š” ์ฃผ์š” ์˜ํ–ฅ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?"
            "2. ๊ธฐํ›„ ๋ณ€ํ™”๋Š” ์ƒํƒœ๊ณ„์— ์–ด๋–ค ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๊นŒ? "
            "3. ๊ธฐํ›„ ๋ณ€ํ™”๊ฐ€ ํ™˜๊ฒฝ์— ๋ฏธ์น˜๋Š” ๋ถ€์ •์ ์ธ ์˜ํ–ฅ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?"
            "</example>"
        )
    else:
        subquery_decomposition_template = (
            "You are an AI assistant tasked with breaking down complex queries into simpler sub-queries for a RAG system."
            "Given the original query, decompose it into 1-3 simpler sub-queries."
            "Provide the final answer with <result> tag."

            "<query>"
            "{original_query}"
            "</query>"

            "Create queries referring to the following example. Each query occupies one line."
            "<example>"
            "Query: What are the impacts of climate change on the environment?"

            "Sub-queries:"
            "1. What are the impacts of climate change on biodiversity?"
            "2. How does climate change affect the oceans?"
            "3. What are the effects of climate change on agriculture?"
            "</example>"
        )    
        
    decomposition_prompt = ChatPromptTemplate([
        ('human', subquery_decomposition_template)
    ])

    chat = get_chat()
    
    decompose = decomposition_prompt | chat
    
    response = decompose.invoke({"original_query": query})
    print('response: ', response.content)
    
    result = response.content[response.content.find('<result>')+8:len(response.content)-9]
    print('result: ', result)
    
    result = result.strip().replace('\n\n', '\n')
    decomposed_queries = result.split('\n')        
    print('decomposed_queries: ', decomposed_queries)

    sub_queries = []    
    if len(decomposed_queries):
        sub_queries = decomposed_queries    
    else:
        sub_queries = [query]
    
    return {
        "sub_queries": [query] + sub_queries
    }    

์ง์ ‘ ์‹ค์Šต ํ•ด๋ณด๊ธฐ

์‚ฌ์ „ ์ค€๋น„ ์‚ฌํ•ญ

์ด ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์ „์— ์•„๋ž˜์™€ ๊ฐ™์€ ์ค€๋น„๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

CDK๋ฅผ ์ด์šฉํ•œ ์ธํ”„๋ผ ์„ค์น˜

๋ณธ ์‹ค์Šต์—์„œ๋Š” us-west-2 ๋ฆฌ์ „์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ธํ”„๋ผ ์„ค์น˜์— ๋”ฐ๋ผ CDK๋กœ ์ธํ”„๋ผ ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์‹คํ–‰๊ฒฐ๊ณผ

๊ธฐ๋ณธ RAG

์•„๋ž˜์™€ ๊ฐ™์ด ๋ฉ”๋‰ด์—์„œ "RAG (Basic)"์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

์ฑ„ํŒ…์ฐฝ์— "Advanced RAG์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”"๋ผ๊ณ  ์ž…๋ ฅํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ, RAG์— ํฌํ•จ๋œ ๋ฌธ์„œ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๋Š” ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” RAG์™€ ๊ด€๋ จ๋œ ๊ฐ์ข… PPT๋ฅผ ๋„ฃ์—ˆ์„๋•Œ์˜ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค. ํŒŒ์ผ์€ ํ™”๋ฉด ํ•˜๋‹จ์˜ ํŒŒ์ผ ์•„์ด์ฝ˜์„ ์ด์šฉํ•ด ๋„ฃ๊ฑฐ๋‚˜, Amazon S3 Console์—์„œ ์ง์ ‘ pushํ•ด์„œ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image

Transformation

๋ฉ”๋‰ด์—์„œ "RAG with Transformation"์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” "Advanced RAG์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”"์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค. ๋‹ต๋ณ€์˜ ๊ธธ์ด๋‚˜ ์„ค๋ช…์€ ์ข‹์œผ๋‚˜ Advanced RAG๊ฐ€ ์•„๋‹Œ ์ผ๋ฐ˜ RAG์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

noname

๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด rewriteํ›„์— ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒˆ๋กœ์šด ์งˆ๋ฌธ(revised_query)๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์œผ๋ฉฐ, ์ด๋•Œ์— advanced ๋‹จ์–ด๊ฐ€ ์ œ์™ธ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•œ "Advanced RAG"๋Š” ๋ฌธ์„œ์—์„œ ๊ณ ์œ ๋ช…์‚ฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์œผ๋‚˜ rewriteํ•˜๋Š” ๊ณผ์ •์—์„œ ์ œ์™ธ๋œ๊ฒƒ์œผ๋กœ ๋ณด์—ฌ์ง‘๋‹ˆ๋‹ค.

RAG(Retrieval-Augmented Generation) ๋ชจ๋ธ์€ ์ •๋ณด ๊ฒ€์ƒ‰๊ณผ ์ž์—ฐ์–ด ์ƒ์„ฑ์„ ๊ฒฐํ•ฉํ•œ ์ตœ์‹  ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค.
RAG ๋ชจ๋ธ์˜ ๊ตฌ์กฐ์™€ ์ž‘๋™ ์›๋ฆฌ, ์žฅ๋‹จ์ , ์ฃผ์š” ์‘์šฉ ๋ถ„์•ผ์™€ ์‚ฌ๋ก€์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•ด์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.
๋˜ํ•œ RAG ๋ชจ๋ธ์˜ ๋ฐœ์ „ ๋ฐฉํ–ฅ๊ณผ ํ–ฅํ›„ ์ „๋ง์— ๋Œ€ํ•ด์„œ๋„ ์–ธ๊ธ‰ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค

๋”ฐ๋ผ์„œ ์„ธ๋ถ€ ์งˆ๋ฌธ(sub-queries)์—๋„ ์•„๋ž˜์™€ ๊ฐ™์ด "advanced"๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ, Advanced RAG๊ฐ€ ์•„๋‹Œ ์ผ๋ฐ˜ RAG์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ด ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

1. RAG ๋ชจ๋ธ์˜ ๊ตฌ์กฐ์™€ ์ž‘๋™ ์›๋ฆฌ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
2. RAG ๋ชจ๋ธ์˜ ์žฅ๋‹จ์ ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
3. RAG ๋ชจ๋ธ์˜ ์ฃผ์š” ์‘์šฉ ๋ถ„์•ผ์™€ ์‚ฌ๋ก€๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
4. RAG ๋ชจ๋ธ์˜ ๋ฐœ์ „ ๋ฐฉํ–ฅ๊ณผ ํ–ฅํ›„ ์ „๋ง์€ ์–ด๋– ํ•ฉ๋‹ˆ๊นŒ?

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์— transformation์€ ์งˆ๋ฌธ์„ ๋ช…ํ™•ํžˆํ•˜๊ณ  ๋‹ค์–‘ํ•œ ๊ด€์ ์—์„œ RAG๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ์–ด์„œ ์„ฑ๋Šฅ์— ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ผ๋ถ€ ์—ฃ์ง€ ์ผ€์ด์Šค์—์„œ๋Š” ์›๋ž˜ ์˜๋„ํ–ˆ๋˜ ๋‚ด์šฉ๊ณผ ๋‹ค๋ฅธ ๋‹ต๋ณ€์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Reflection

๋ฉ”๋‰ด์—์„œ "RAG with Reflection"์„ ์„ ํƒํ•œ ํ›„์— ์•„๋ž˜์™€ ๊ฐ™์ด "Advanced RAG์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”"๋ผ๊ณ  ์งˆ๋ฌธํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด, reflection ๋™์ž‘์„ ํ†ตํ•ด ๋‹ต๋ณ€๊ณผ ๊ด€๋ จ๋œ ๋‹ค์–‘ํ•œ query๊ฐ€ ์ˆ˜ํ–‰๋˜์—ˆ์œผ๋ฏ€๋กœ, ๊ธฐ๋ณธ RAG ๋ณด๋‹ค ๋” ์ƒ์„ธํ•œ ๋‚ด์šฉ์˜ ๋‹ต๋ณ€์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

noname

๊ฒฐ๋ก 

LangGraph๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ธฐ๋ณธ RAG๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ ,ย Reflection๊ณผย Query Transformation์„ ์ด์šฉํ•˜์—ฌ RAG์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ๋น„๊ตํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ•œ๊ตญ์–ด/์˜์–ด ๋ฌธ์„œ๋“ค์„ ๋™์‹œ์— ๊ฒ€์ƒ‰ํ•˜๊ณ , ์ƒ์„ฑ๋œ ๋‹ต๋ณ€์„ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋‹ค์ˆ˜์˜ query๋กœ ์ธํ•ด ๊ฐ™์€ ๋ฌธ์„œ๊ฐ€ ๊ฒ€์ƒ‰๋˜์—ˆ๋•Œ์— ๋Œ€ํ•œ ์ค‘๋ณต์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•˜์—ฌ ์ˆ˜ํ–‰์‹œ๊ฐ„์„ ๊ฐœ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” prompt์„ ์ด์šฉํ•œ relevant document ๊ฒ€์ฆํ•˜์˜€๊ณ , ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜ ๋ฐ ๋ณ€ํ™”ํ•˜๋Š” ํŠธ๋ž˜ํ”ฝ์— ๋Œ€ํ•ด ์œ ์—ฐํ•˜๊ฒŒ ์šด์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, AWS CDK๋ฅผ ์ด์šฉํ•˜์—ฌ ํŽธ๋ฆฌํ•˜๊ฒŒ AWS Cloud์— ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌ์†Œ์Šค ์ •๋ฆฌํ•˜๊ธฐ

๋”์ด์ƒ ์ธํ”„๋ผ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์— ์•„๋ž˜์ฒ˜๋Ÿผ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. API Gateway Console๋กœ ์ ‘์†ํ•˜์—ฌ "rest-api-for-rag-with-reflection", "ws-api-for-rag-with-reflection"์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

  2. Cloud9 Console์— ์ ‘์†ํ•˜์—ฌ ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋กœ ์ „์ฒด ์‚ญ์ œ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.

cd ~/environment/rag-with-reflection/cdk-rag-with-reflection/ && cdk destroy --all

About

It shows an advanced RAG with Reflection where LangGraph is used for workflow management.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published