Skip to content

kyopark2014/agentic-solver

Repository files navigation

Agentic Workflowλ₯Ό μ΄μš©ν•˜μ—¬ λ³΅μž‘ν•œ 문제 ν•΄κ²°ν•˜κΈ°

License

μ—¬κΈ°μ—μ„œλŠ” LangGraphλ₯Ό μ΄μš©ν•˜μ—¬ plan and execute νŒ¨ν„΄μ˜ agentic workflowλ₯Ό κ΅¬ν˜„ν•˜κ³ , 이λ₯Ό μ΄μš©ν•˜μ—¬ λ³΅μž‘ν•œ 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법에 λŒ€ν•΄ μ„€λͺ…ν•©λ‹ˆλ‹€. LLM의 ν•œκΈ€ λŠ₯λ ₯ 및 지λŠ₯적 뢄석 λŠ₯λ ₯을 ν™•μΈν•˜κΈ° μœ„ν•˜μ—¬ λŒ€ν•™ μˆ˜ν•™ λŠ₯λ ₯ μ‹œν—˜μ€‘μ— κ΅­μ–΄ μ˜μ—­μ— λŒ€ν•΄ ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

수λŠ₯ κ΅­μ–΄ 문제

λ³΅μž‘ν•œ 문제둜 수λŠ₯ κ΅­μ–΄λ₯Ό μ„ νƒν•œ 이유

μˆ˜ν•™ λŠ₯λ ₯ μ‹œν—˜μ˜ κ΅­μ–΄ μ˜μ—­μ€ LLM을 μ΄μš©ν•œ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ ν•œκ΅­μ–΄μ— λŒ€ν•œ 이해λ₯Ό μΈ‘μ •ν•˜κΈ° 쒋은 μ£Όμ œμž…λ‹ˆλ‹€. 지문과 선택지-화법과 μž‘λ¬Έμ€ json포맷으둜 λ¬Έμ œμ™€ 닡을 μ œκ³΅ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, 수λŠ₯ 문제의 κ²½μš°μ— 정닡이 μ•Œλ €μ Έμžˆκ³  μƒμ„Έν•œ ν•΄μ„€μ„œλ„ κ²°κ³Όλ₯Ό 확인할 λ•Œμ— μ°Έκ³ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, 2023λ…„ 수λŠ₯의 κ΅­μ–΄(화법과 μž‘λ¬Έ)의 1λ“±κΈ‰ 컷은 92μ μž…λ‹ˆλ‹€.

μ—¬κΈ°μ—μ„œλŠ” Anthropic의 Claude Sonnet 3.5와 LangGraphλ₯Ό κ΅¬ν˜„ν•œ plan and execute νŒ¨ν„΄μ˜ agentic workflowλ₯Ό μ΄μš©ν•˜μ—¬ 92점을 μ–»μ—ˆμŠ΅λ‹ˆλ‹€.

전체 Architecture

μ—¬κΈ°μ—μ„œ μ‚¬μš©ν•œ ArchitectureλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. API Gatewayλ₯Ό μ΄μš©ν•˜μ—¬ ν΄λΌμ΄μ–ΈνŠΈμ™€ WebSocket으둜 λŒ€ν™”λ₯Ό μˆ˜ν–‰ν•˜κ³ , AWS Lambda와 LangGraphλ₯Ό μ΄μš©ν•˜μ—¬ agentic workflowλ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€. Workflowλ₯Ό μˆ˜ν–‰ν•˜κΈ° μœ„ν•˜μ—¬ μ™ΈλΆ€ μ €μž₯μ†Œλ‚˜ 인터넷 검색이 ν•„μš”ν•œ κ²½μš°μ—λŠ” Vector/Lexical 검색이 κ°€λŠ₯ν•œ Knowledge Base와 Tavily 검색을 ν™œμš©ν•©λ‹ˆλ‹€. λ‹€λ§Œ, 수λŠ₯ κ΅­μ–΄ 문제의 κ²½μš°μ—λŠ” κ΅­μ–΄ μžμ²΄μ— λŒ€ν•œ 해석 λŠ₯λ ₯을 ν™•μΈν•˜κΈ° μœ„ν•˜μ—¬ μ™ΈλΆ€ 데이터λ₯Ό ν™œμš©ν•˜μ§€ μ•Šκ³  LLM의 지적λŠ₯λ ₯λ§Œμ„ ν™œμš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ—μ„œλŠ” plan and execute νŒ¨ν„΄λ°©μ‹μ˜ workflowλ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ multi region을 μ΄μš©ν•œ λ³‘λ ¬μ²˜λ¦¬λ₯Ό 톡해 속도와 LLM의 throttling 이슈λ₯Ό ν•΄κ²°ν•©λ‹ˆλ‹€.

image

Agentic Workflow κ΅¬ν˜„ν•˜κΈ°

Plan and exeuction νŒ¨ν„΄μ„ μ΄μš©ν•˜λ©΄ λ³΅μž‘ν•œ 문제λ₯Ό step by step으둜 μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ LangGraphλ₯Ό μ΄μš©ν•΄ agentic workflowλ₯Ό κ΅¬ν˜„ν•˜λŠ” 것은 LangGraph둜 κ΅¬ν˜„ν•˜λŠ” Agentic Workflow을 μ°Έμ‘°ν•©λ‹ˆλ‹€. Workflow의 λ…Έλ“œλ“€ 간에 데이터 κ΅ν™˜μ„ μœ„ν•΄ State 클래슀λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.

class State(TypedDict):
    plan: list[str]
    past_steps: Annotated[List[Tuple], operator.add]
    info: Annotated[List[Tuple], operator.add]
    paragraph: str
    question: str
    question_plus: str
    choices: list[str]
    answer: str
    select: int

Agentic workflowλ₯Ό μ•„λž˜μ™€ 같이 μ •μ˜ν•©λ‹ˆλ‹€. μ—¬κΈ°μ—λŠ” plan, execute, replan, final_answer둜 λ…Έλ“œλ₯Ό μƒμ„±ν•˜κ³  ν•œκ°œμ˜ conditional edge인 should_endλ₯Ό 가지고 μžˆμŠ΅λ‹ˆλ‹€.

def buildPlanAndExecute():
    workflow = StateGraph(State)
    workflow.add_node("planner", plan_node)
    workflow.add_node("executor", execute_node)
    workflow.add_node("replaner", replan_node)
    workflow.add_node("final_answer", final_answer)
    
    workflow.set_entry_point("planner")
    workflow.add_edge("planner", "executor")
    workflow.add_edge("executor", "replaner")
    workflow.add_conditional_edges(
        "replaner",
        should_end,
        {
            "continue": "executor",
            "end": "final_answer",
        },
    )
    workflow.add_edge("final_answer", END)

    return workflow.compile()

μ•„λž˜μ˜ activity diagram을 μ΄μš©ν•˜λ©΄ λ³΅μž‘ν•œ workflow의 λ™μž‘μ„ μ‰½κ²Œ 이해할 수 μžˆμŠ΅λ‹ˆλ‹€.

image

수λŠ₯ κ΅­μ–΄ λ¬Έμ œμ—μ„œλŠ” 지문인 paragraphκ°€ 주어지고 κ²½μš°μ— 따라 보기가 주어지고, 보톡 5κ°œμ •λ„μ˜ 선택지가 μ£Όμ–΄μ§‘λ‹ˆλ‹€. μ•„λž˜μ™€ 같이 step by stepν˜•νƒœλ‘œ κ³„νšμ„ μ„ΈμšΈμˆ˜ μžˆλ„λ‘ ν”„λ‘¬ν”„νŠΈλ₯Ό μ€€λΉ„ν•©λ‹ˆλ‹€. 각 단계λ₯Ό list둜 κ΄€λ¦¬ν•˜κΈ° μœ„ν•˜μ—¬ μ•„λž˜μ™€ 같이 ν•œμ€„λ‘œ μ§ˆλ¬Έμ„ ν•΄κ²°ν•˜λŠ” 단계λ₯Ό μƒμ„±ν•˜λ„λ‘ 예제λ₯Ό μ΄μš©ν•΄ ν”„λ‘¬ν”„νŠΈλ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

def plan_node(state: State, config):
    print("###### plan ######")
            
    list_choices = ""
    choices = state["choices"]
    for i, choice in enumerate(choices):
        list_choices += f"({i+1}) {choice}\n"
    
    system = (
        "당신은 λ³΅μž‘ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ step by step plan을 μƒμ„±ν•˜λŠ” AI agentμž…λ‹ˆλ‹€."                
        
        "문제λ₯Ό μΆ©λΆ„νžˆ μ΄ν•΄ν•˜κ³ , 문제 해결을 μœ„ν•œ κ³„νšμ„ λ‹€μŒ ν˜•μ‹μœΌλ‘œ 4단계 μ΄ν•˜μ˜ κ³„νšμ„ μ„Έμ›λ‹ˆλ‹€."                
        "각 λ‹¨κ³„λŠ” λ°˜λ“œμ‹œ ν•œμ€„μ˜ λ¬Έμž₯으둜 AI agentκ°€ μˆ˜ν–‰ν•  λ‚΄μš©μ„ λͺ…ν™•νžˆ λ‚˜νƒ€λƒ…λ‹ˆλ‹€."
        "1. [μ§ˆλ¬Έμ„ ν•΄κ²°ν•˜κΈ° μœ„ν•œ 단계]"
        "2. [μ§ˆλ¬Έμ„ ν•΄κ²°ν•˜κΈ° μœ„ν•œ 단계]"
        "..."                
    )
    
    human = (
        "<paragraph> tag의 주어진 λ¬Έμž₯을 μ°Έμ‘°ν•˜μ—¬ <question> tag의 μ§ˆλ¬Έμ— λŒ€ν•œ μ μ ˆν•œ 닡변을 <choice> tagμ•ˆμ—μ„œ μ„ νƒν•˜κ°€ μœ„ν•œ 단계별 κ³„νšμ„ μ„Έμš°μ„Έμš”."
        "단계별 κ³„νšμ— <result> tagλ₯Ό λΆ™μ—¬μ£Όμ„Έμš”."
        
        "주어진 λ¬Έμž₯:"
        "<paragraph>"
        "{paragraph}"
        "</paragraph>"

        "질문:"
        "<question>"
        "{question}"
                        
        "{question_plus}"                
        "</question>"

        "선택지:"
        "<choices>"
        "{list_choices}"
        "</choices>"
    )
                        
    planner_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", system),
            ("human", human),
        ]
    )
    chat, select = get_llm(state["select"])
    planner = planner_prompt | chat
    response = planner.invoke({
        "paragraph": paragraph,
        "question": question,
        "question_plus": question_plus,
        "list_choices": list_choices
    })
    result = response.content
    output = result[result.find('<result>')+8:result.find('</result>')]
    
    plan = output.strip().replace('\n\n', '\n')
    planning_steps = plan.split('\n')
    
    return {
        "plan": planning_steps,
        "select": select
    }

μ„Έμ›Œμ§„ κ³„νšμ—μ„œ 첫번째 κ³„νšμ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ 주어진 문제λ₯Ό paragraph, question, choice tagλ₯Ό μ΄μš©ν•΄ μ„€λͺ…ν•˜κ³ , 첫번째 κ³„νšμ„ task둜 μˆ˜ν–‰ν•©λ‹ˆλ‹€. μ΄λ•Œ LLM이 μΆ©λΆ„νžˆ μƒκ°ν•˜λ„λ‘ λͺ¨λ“  선택지에 λŒ€ν•œ κ·Όκ±°λ₯Ό μ„€λͺ…ν•˜λ„λ‘ μš”μ²­ν•˜κ³  μ„ νƒμ§€μ—μ„œ ν•œκ°œλ₯Ό κ³ λ₯΄λ„둝 μš”μ²­ν•©λ‹ˆλ‹€. Agentic workflowλŠ” κ²°κ³Όκ°€ λ‚˜μ˜¬λ•ŒκΉŒμ§€ λ°˜λ³΅ν•˜κ²Œ λ˜μ–΄μ„œ μˆ˜ν–‰μ‹œκ°„μ΄ κΈΈμ–΄μ§‘λ‹ˆλ‹€. μˆ˜ν–‰μ‹œκ°„μ„ λ‹¨μΆ•ν•˜κΈ° μœ„ν•˜μ—¬ LLM이 ν˜„μž¬μ˜ 선택에 λŒ€ν•œ 신뒰도(confidence)λ₯Ό μ œμ‹œν•˜λ„λ‘ ν•˜κ³ , 졜고 신뒰도(μ—¬κΈ°μ„œλŠ” 5)인 κ²½μš°μ— 싀행을 μ™„λ£Œν•˜λ„λ‘ ν•©λ‹ˆλ‹€. 신뒰도가 μ΅œκ³ κ°’μ„ 가지면 κ³„νš(plan)을 λΉ„μ›Œμ„œ λ‹€μŒ λ…Έλ“œμ—μ„œ μ™„λ£Œλ˜λ„λ‘ ν•©λ‹ˆλ‹€.

def execute_node(state: State, config):
    print("###### execute ######")
    plan = state["plan"]
    
    list_choices = ""
    choices = state["choices"]
    for i, choice in enumerate(choices):
        list_choices += f"({i+1}) {choice}\n"
    
    task = plan[0]
    context = ""
    for info in state['info']:
        if isinstance(info, HumanMessage):
            context += info.content+"\n"
        else:
            context += info.content+"\n\n"
                    
    system = (
        "당신은 κ΅­μ–΄ 수λŠ₯문제λ₯Ό ν‘ΈλŠ” μΌνƒ€κ°•μ‚¬μž…λ‹ˆλ‹€."
    )
    human = (
        "λ‹Ήμ‹ μ˜ λͺ©ν‘œλŠ” <paragraph> tag의 주어진 λ¬Έμž₯으둜 λΆ€ν„° <question> tag의 주어진 μ§ˆλ¬Έμ— λŒ€ν•œ μ μ ˆν•œ 닡변을 <choice> tag의 μ„ νƒμ§€μ—μ„œ μ°ΎλŠ”κ²ƒμž…λ‹ˆλ‹€."
        "<previous_result> tag에 μžˆλŠ” 이전 λ‹¨κ³„μ˜ κ²°κ³Όλ₯Ό μ°Έμ‘°ν•˜μ—¬, <task> tag의 μ‹€ν–‰ 단계λ₯Ό μˆ˜ν–‰ν•˜κ³  μ μ ˆν•œ 닡변을 κ΅¬ν•©λ‹ˆλ‹€."
        "문제λ₯Ό 풀이할 λ•Œ λͺ¨λ“  μ„ νƒμ§€λ§ˆλ‹€ κ·Όκ±°λ₯Ό 주어진 λ¬Έμž₯μ—μ„œ μ°Ύμ•„ μ„€λͺ…ν•˜μ„Έμš”."
        "μ„ νƒμ§€μ˜ μ£Όμš” λ‹¨μ–΄λ“€μ˜ 의미λ₯Ό 주어진 λ¬Έμž₯κ³Ό λΉ„κ΅ν•΄μ„œ 꼼꼼히 차이점을 μ°ΎμŠ΅λ‹ˆλ‹€."
        "μ§ˆλ¬Έμ— λŒ€ν•œ 닡을 선택지 쀑에 ν•œ 개만 κ³¨λΌμ„œ λŒ€λ‹΅ν•΄μ•Ό ν•©λ‹ˆλ‹€."
        "μ΅œμ’… 결과의 λ²ˆν˜Έμ— <result> tagλ₯Ό λΆ™μ—¬μ£Όμ„Έμš”."
        "μ΅œμ’… 결과의 신뒰도λ₯Ό 1-5 μ‚¬μ΄μ˜ 숫자둜 λ‚˜νƒ€λƒ…λ‹ˆλ‹€. μ‹ λ’°λ˜λŠ” <confidence> tagλ₯Ό λΆ™μž…λ‹ˆλ‹€."  
                            
        "주어진 λ¬Έμž₯:"
        "<paragraph>"
        "{paragraph}"
        "</paragraph>"
            
        "주어진 질문:"
        "<question>"
        "{question}"
            
        "{question_plus}"
        "</question>"
        
        "선택지:"
        "<choices>"
        "{list_choices}"
        "</choices>"
        
        "이전 λ‹¨κ³„μ˜ κ²°κ³Ό"
        "<previous_result>"
        "{info}"
        "</previous_result>"

        "μ‹€ν–‰ 단계:"
        "<task>"
        "{task}"
        "</task>"
    )
    prompt = ChatPromptTemplate.from_messages(
        [
            ("system", system),
            ("human", human),
        ]
    )
    chat, select = get_llm(state["select"])
    chain = prompt | chat                        
    response = chain.invoke({
        "paragraph": state["paragraph"],
        "question": state["question"],
        "question_plus": state["question_plus"],
        "list_choices": list_choices,
        "info": context,
        "task": task
    })
    
    result = response.content
    confidence = result[result.find('<confidence>')+12:result.find('</confidence>')]    
    transaction = [HumanMessage(content=task), AIMessage(content=result)]
    
    if confidence == "5":
        plan = []
        answer = result
    else:
        plan = state["plan"]
        answer = ""
    
    return {
        "plan": plan,
        "info": transaction,
        "past_steps": [task],
        "answer": answer,
        "select": select
    }

처음 μƒμ„±ν•œ κ³„νšμ„ 이후 μ‹€ν–‰ κ³Όμ •μ—μ„œ μ—…λ°μ΄νŠΈν•˜λ©΄ 더 쒋은 κ²°κ³Όλ₯Ό 얻을 μžˆμŠ΅λ‹ˆλ‹€. Execution λ…Έλ“œμ—μ„œ 첫번째 κ³„νšμ„ μ„Έμ› μœΌλ―€λ‘œ replan λ…Έλ“œμ—μ„œλŠ” μ‹€ν–‰ν•œ κ³„νšμ„ μ œμ™Έν•œ κ³„νšμ„ μ—…λ°μ΄νŠΈ ν•©λ‹ˆλ‹€. ν˜„μž¬μ˜ λͺ©ν‘œλ₯Ό remind μ‹œν‚€κ³  λ‚˜μ„œ, μ›λž˜μ˜ κ³„νšκ³Ό μ™„λ£Œλœ κ³„νšμ„ μ•Œλ €μ£Όκ³  μƒˆλ‘œμš΄ κ³„νšμ„ κ΅¬μ„±ν•˜λ„λ‘ ν”„λ‘¬ν”„νŠΈλ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€. μˆ˜μ •λœ κ³„νšμ„ ν”„λ‘¬ν”„νŠΈλ₯Ό μ΄μš©ν•΄ ν•œμ€„μ”©μœΌλ‘œ μ •μ˜ν•˜λ„λ‘ ν•˜κ³  state의 plan을 μ—…λ°μ΄νŠΈ ν•©λ‹ˆλ‹€.

def replan_node(state: State, config):
    print('#### replan ####')            
    list_choices = ""
    choices = state["choices"]
    for i, choice in enumerate(choices):
        list_choices += f"({i+1}) {choice}\n"
    
    if len(state["plan"])==0:
        return {"plan": []}
    
    system = (
        "당신은 λ³΅μž‘ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ step by step plan을 μƒμ„±ν•˜λŠ” AI agentμž…λ‹ˆλ‹€."
    )        
    human = (
        "λ‹Ήμ‹ μ˜ λͺ©ν‘œλŠ” <paragraph> tag의 주어진 λ¬Έμž₯으둜 λΆ€ν„° <question> tag의 주어진 μ§ˆλ¬Έμ— λŒ€ν•œ μ μ ˆν•œ 닡변을 <choice> tagμ•ˆμ—μ„œ μ„ νƒμ§€μ—μ„œ μ°ΎλŠ”κ²ƒμž…λ‹ˆλ‹€."
        
        "주어진 λ¬Έμž₯:"
        "<paragraph>"
        "{paragraph}"
        "</paragraph>"
        
        "주어진 질문:"
        "<question>"
        "{question}"
        
        "{question_plus}"
        "</question>"
        
        "선택지:"
        "<list_choices>"
        "{list_choices}"
        "</list_choices>"
        
        "λ‹Ήμ‹ μ˜ μ›λž˜ κ³„νšμ€ μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€." 
        "<original_plan>"                
        "{plan}"
        "</original_plan>"

        "μ™„λ£Œν•œ λ‹¨κ³„λŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€."
        "<past_steps>"
        "{past_steps}"
        "</past_steps>"
        
        "당신은 <original_plan> tag의 μ›λž˜ κ³„νšμ„ 상황에 맞게 μˆ˜μ •ν•˜μ„Έμš”."
        "κ³„νšμ— 아직 ν•΄μ•Ό ν•  λ‹¨κ³„λ§Œ μΆ”κ°€ν•˜μ„Έμš”. 이전에 μ™„λ£Œν•œ λ‹¨κ³„λŠ” κ³„νšμ— ν¬ν•¨ν•˜μ§€ λ§ˆμ„Έμš”."                
        "μˆ˜μ •λœ κ³„νšμ—λŠ” <plan> tagλ₯Ό λΆ™μ—¬μ£Όμ„Έμš”."
        "λ§Œμ•½ 더 이상 κ³„νšμ„ μ„Έμš°μ§€ μ•Šμ•„λ„ <question> tag의 주어진 μ§ˆλ¬Έμ— λ‹΅λ³€ν•  μžˆλ‹€λ©΄, μ΅œμ’… 결과둜 <question>에 λŒ€ν•œ 닡변을 <result> tagλ₯Ό λΆ™μ—¬ μ „λ‹¬ν•©λ‹ˆλ‹€."
        
        "μˆ˜μ •λœ κ³„νšμ˜ ν˜•μ‹μ€ μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€."
        "각 λ‹¨κ³„λŠ” λ°˜λ“œμ‹œ ν•œμ€„μ˜ λ¬Έμž₯으둜 AI agentκ°€ μˆ˜ν–‰ν•  λ‚΄μš©μ„ λͺ…ν™•νžˆ λ‚˜νƒ€λƒ…λ‹ˆλ‹€."
        "1. [μ§ˆλ¬Έμ„ ν•΄κ²°ν•˜κΈ° μœ„ν•œ 단계]"
        "2. [μ§ˆλ¬Έμ„ ν•΄κ²°ν•˜κΈ° μœ„ν•œ 단계]"
        "..."         
    )                    
    replanner_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", system),
            ("human", human),
        ]
    )        
    chat, select = get_llm(state["select"])
    replanner = replanner_prompt | chat        
    response = replanner.invoke({
        "paragraph": state["paragraph"],
        "question_plus": state["question_plus"],
        "question": state["question"],
        "list_choices": list_choices,
        "plan": state["plan"],
        "past_steps": state["past_steps"]
    })
    result = response.content
    
    if result.find('<plan>') == -1:
        return {"plan":[], "select":select, "answer":result}
    else:
        output = result[result.find('<plan>')+6:result.find('</plan>')]
        
        plans = output.strip().replace('\n\n', '\n')
        planning_steps = plans.split('\n')
    
        return {"plan": planning_steps, "select":select}

Conditional edge인 should_endμ—μ„œλŠ” plan을 보고 계속 싀행할지 μ’…λ£Œν• μ§€λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.

def should_end(state: State) -> Literal["continue", "end"]:
    print('#### should_end ####')    
    plan = state["plan"]
    print('plan: ', plan)
    if len(plan)<=1:
        next = "end"
    else:
        next = "continue"
    
    return next

직접 μ‹€μŠ΅ 해보기

사전 μ€€λΉ„ 사항

이 μ†”λ£¨μ…˜μ„ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 사전에 μ•„λž˜μ™€ 같은 μ€€λΉ„κ°€ λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

CDKλ₯Ό μ΄μš©ν•œ 인프라 μ„€μΉ˜

λ³Έ μ‹€μŠ΅μ—μ„œλŠ” us-west-2 리전을 μ‚¬μš©ν•©λ‹ˆλ‹€. 인프라 μ„€μΉ˜μ— 따라 CDK둜 인프라 μ„€μΉ˜λ₯Ό μ§„ν–‰ν•©λ‹ˆλ‹€.

μ‹€ν–‰κ²°κ³Ό

수λŠ₯ κ΅­μ–΄ 문제

μ±„νŒ… λ©”λ‰΄μ—μ„œ νŒŒμΌμ„ μ„ νƒν•˜μ—¬ μ—…λ‘œλ“œλ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. μ—¬κΈ°μ—μ„œλŠ” ν…ŒμŠ€νŠΈ κ³„μ •μ˜ quota ν•œκ³„λ‘œ 원본 νŒŒμΌμ„ 6개의 파일둜 λ‚˜λˆ μ„œ ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

2023_11_KICE_1.json을 λ‹€μš΄λ‘œλ“œ 후에 μ±„νŒ…μ°½ ν•˜λ‹¨μ˜ 파일 μ—…λ‘œλ“œ λ²„νŠΌμ„ μ„ νƒν•˜μ—¬ νŒŒμΌμ„ μ—…λ‘œλ“œν•œ 후에 κ²°κ³Όλ₯Ό ν™•μΈν•©λ‹ˆλ‹€. μ•„λž˜μ™€ 같이 전체 문제λ₯Ό λ§žμΆ”μ—ˆμŠ΅λ‹ˆλ‹€.

image

2023_11_KICE_2.json에 λŒ€ν•œ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. 2λ¬Έμ œμ—μ„œ μ˜€λ‹΅μ΄ λ‚˜μ™”μŠ΅λ‹ˆλ‹€.

image

μ˜€λ‹΅μΈ 15번 문제의 κ²½μš°μ—λŠ” 본문의 κ·Έλž˜ν”„μ— λŒ€ν•œ 이해가 ν•„μš”ν•˜μ§€λ§Œ json νŒŒμΌμ—λŠ” κ·Έλ¦Ό νŒŒμΌμ— λŒ€ν•œ 정보λ₯Ό μ œκ³΅ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ νŒλ‹¨ λΆˆκ°€λ‘œ μ²˜λ¦¬λ˜μ–΄μ„œ 닡을 κ΅¬ν•˜μ§€ λͺ»ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

noname

λ˜ν•œ 17번 문제의 κ²½μš°μ— μ§€λ¬Έμ˜ κ·Έλ¦Όκ³Ό ν•¨κ»˜ 보기의 그림도 같이 이해가 ν•„μš”ν•˜λ‚˜ 이에 λŒ€ν•œ 정보가 μ—†μ–΄μ„œ μ‹€νŒ¨ν•œκ²ƒμœΌλ‘œ λ³΄μ—¬μ§‘λ‹ˆλ‹€.

image

2023_11_KICE_3.json에 λŒ€ν•œ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

image

μ˜€λ‹΅ 문제λ₯Ό 보면 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

image

둜그둜 μ‹€νŒ¨ν•œ 이유λ₯Ό μ•Œμ•„λ³΄λ©΄ μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. 선택지인 (1)κ³Ό (3)이 μ μ ˆν•˜μ§€ μ•Šλ‹€κ³  νŒλ‹¨ν•˜μ˜€λŠ”λ°, (1)이 더 λΆ€μ μ ˆν•˜λ‹€κ³  νŒλ‹¨ν•œκ²ƒμœΌλ‘œ λ³΄μ—¬μ§‘λ‹ˆλ‹€. λ„μ‚°μ‹­μ΄κ³‘μ˜ μ΄ˆμ•Όμš°μƒμ„ μ•Œκ³  μžˆμ—ˆλ‹€λ©΄ 정닡을 선택할 수 μžˆμ—ˆμ„κ²ƒμœΌλ‘œ λ³΄μ—¬μ§‘λ‹ˆλ‹€.

<보기>의 λ‚΄μš©μ„ λ°”νƒ•μœΌλ‘œ (κ°€)와 (λ‚˜)의 νŠΉμ§•μ„ λ‹€μŒκ³Ό 같이 νŒŒμ•…ν•  수 μžˆμŠ΅λ‹ˆλ‹€:
(κ°€) 도산십이곑:
- κ°•ν˜ΈλŠ” μžμ—°μ˜ μ΄μΉ˜μ™€ 인간이 지ν–₯ν•˜λŠ” μ΄μΉ˜κ°€ 일치된 이상적 곡간
- μ‘°ν™”λ‘œμš΄ μžμ—°κ³Ό ν•©μΌν•˜λŠ” ν™”μžκ°€ λ“±μž₯
(λ‚˜) μ§€μˆ˜μ •κ°€:
- κ°•ν˜ΈλŠ” μžμ—°μ—μ„œ μƒν™œν•˜λ©΄μ„œ μžμ—°μ˜ κ°€μΉ˜λ₯Ό μƒˆλ‘­κ²Œ λ°œκ²¬ν•  수 μžˆλŠ” 곡간
- μžμ—°μ˜ ꡬ체적인 λͺ¨μŠ΅μ„ λ¬˜μ‚¬ν•˜λ©° μžμ—°μ˜ κ°€μΉ˜λ₯Ό ν™•μΈν•œ ν™”μžκ°€ λ“±μž₯
μ΄λŸ¬ν•œ νŠΉμ§•μ„ λ°”νƒ•μœΌλ‘œ 각 선택지λ₯Ό λΆ„μ„ν•˜κ² μŠ΅λ‹ˆλ‹€.
<task>2. 각 선택지λ₯Ό λΆ„μ„ν•˜μ—¬ μ μ ˆμ„±μ„ νŒλ‹¨ν•©λ‹ˆλ‹€.</task>
(1) "μ΄ˆμ•Όμš°μƒ(θ‰ι‡Žζ„šη”Ÿ)이 이렇닀 μ–΄λ– ν•˜λ£Œ"λΌλŠ” ν‘œν˜„μ€ ν™”μžκ°€ μžμ‹ μ„ κ²Έμ†ν•˜κ²Œ ν‘œν˜„ν•œ κ²ƒμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 이것이 λ°˜λ“œμ‹œ "인간이 지ν–₯ν•˜λŠ” μ΄μΉ˜μ™€ μžμ—°μ˜ μ΄μΉ˜κ°€ 일치된 곡간"에 μ‘΄μž¬ν•œλ‹€κ³  λ³Ό μˆ˜λŠ” μ—†μŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ 이 μ„ νƒμ§€λŠ” μ μ ˆν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
(2) "일쑰(δΈ€ζœ)에 진면λͺ©μ„ λ‚΄ 혼자 μ•Œμ•˜λ…ΈλΌ"λŠ” ν‘œν˜„μ€ ν™”μžκ°€ μžμ—°μ—μ„œ μƒν™œν•˜λ©΄μ„œ μžμ—°μ˜ κ°€μΉ˜λ₯Ό μƒˆλ‘­κ²Œ λ°œκ²¬ν–ˆμŒμ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€. μ΄λŠ” <보기>의 λ‚΄μš©κ³Ό μΌμΉ˜ν•˜λ―€λ‘œ μ μ ˆν•©λ‹ˆλ‹€.
(3) "μ²œμ„κ³ ν™©(ζ³‰ηŸ³θ†θ‚“)"은 μžμ—°μ„ μ‚¬λž‘ν•˜λŠ” 마음이 κ³ μ§ˆλ³‘μ²˜λŸΌ κΉŠλ‹€λŠ” 의미둜, ν™”μžκ°€ μžμ—°κ³Ό ν•©μΌλœ μƒνƒœλ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€. λ”°λΌμ„œ 이λ₯Ό μ•„μ‰¬μ›€μœΌλ‘œ ν•΄μ„ν•˜λŠ” 것은 μ μ ˆν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 반면 "무릉도원"은 ν™”μžκ°€ ν˜„μ‹€μ˜ 곡간을 이상적 κ³΅κ°„μœΌλ‘œ μΈμ‹ν•˜λŠ” 것을 보여주어 μ μ ˆν•©λ‹ˆλ‹€. 이 μ„ νƒμ§€λŠ” λΆ€λΆ„μ μœΌλ‘œλ§Œ μ μ ˆν•©λ‹ˆλ‹€.
(4) "μ‚¬μ‹œ κ°€ν₯(佳興)이 μ‚¬λžŒκ³Ό ν•œκ°€μ§€λΌ"λŠ” μžμ—°μ˜ μ΄μΉ˜μ™€ 인간이 지ν–₯ν•˜λŠ” μ΄μΉ˜κ°€ μΌμΉ˜ν•¨μ„ λ‚˜νƒ€λ‚΄λ©°, "가지가지 λ‹€ μ’‹κ΅¬λ‚˜"λŠ” μžμ—°μ˜ κ°€μΉ˜λ₯Ό ν™•μΈν•œ ν™”μžμ˜ 심정을 λ“œλŸ¬λƒ…λ‹ˆλ‹€. λ‘˜ λ‹€ <보기>의 λ‚΄μš©κ³Ό μΌμΉ˜ν•˜λ―€λ‘œ μ μ ˆν•©λ‹ˆλ‹€.
(5) "μΆ˜ν’μ— ν™”λ§Œμ‚°ν•˜κ³  좔야에 μ›”λ§ŒλŒ€λΌ"λŠ” μ‘°ν™”λ‘œμš΄ μžμ—°μ„, "λ²½λ₯˜λŠ” μ½Έμ½Έ 흘러 μ˜₯ μˆ μž”μ„ λ•Œλ¦¬λŠ” λ“―"은 ν™”μžκ°€ λ°œκ²¬ν•œ μžμ—°μ˜ 아름닀움을 ν‘œν˜„ν•©λ‹ˆλ‹€. λ‘˜ λ‹€ <보기>의 λ‚΄μš©κ³Ό μΌμΉ˜ν•˜λ―€λ‘œ μ μ ˆν•©λ‹ˆλ‹€.
<task>3. 뢄석 κ²°κ³Όλ₯Ό λ°”νƒ•μœΌλ‘œ κ°€μž₯ μ μ ˆν•˜μ§€ μ•Šμ€ 선택지λ₯Ό μ„ νƒν•©λ‹ˆλ‹€.</task>
뢄석 κ²°κ³Ό, (1)번 선택지가 κ°€μž₯ μ μ ˆν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 'μ΄ˆμ•Όμš°μƒ'μ΄λΌλŠ” ν‘œν˜„μ΄ λ°˜λ“œμ‹œ 이상적 곡간에 μ‘΄μž¬ν•˜λŠ” ν™”μžλ₯Ό λ‚˜νƒ€λ‚Έλ‹€κ³  λ³Ό 수 μ—†μœΌλ©°, μ΄λŠ” <보기>의 λ‚΄μš©κ³Όλ„ 직접적인 연관성이 μ—†κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

2023_11_KICE_4.json에 λŒ€ν•œ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. λͺ¨λ“  문제λ₯Ό λ§žμΆ”μ—ˆμŠ΅λ‹ˆλ‹€.

image

2023_11_KICE_5.json에 λŒ€ν•œ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. λͺ¨λ‘ 정닡을 λ§žμΆ”μ—ˆμŠ΅λ‹ˆλ‹€.

image

2023_11_KICE_6.json에 λŒ€ν•œ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. λͺ¨λ‘ μ •λ‹΅μž…λ‹ˆλ‹€.

image

이와 같이 전체 100점쀑에 92점을 νšλ“ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 문제 μ μˆ˜μ—λŠ” LLM의 지적λŠ₯λ ₯이 맀우 μ€‘μš”ν•˜λ©° ν”„λ‘¬ν”„νŠΈλ₯Ό μ΄μš©ν•΄ LLM이 λ¬Έμ œμ— λŒ€ν•΄ μΆ©λΆ„νžˆ μƒκ°ν•˜λ„λ‘ μœ λ„ν•˜μ—¬μ•Ό ν•©λ‹ˆλ‹€.

수λŠ₯ ν•œκ΅­μ‚¬ 문제

μ—¬κΈ°μ—μ„œ ν•œκ΅­μ‚¬ μ˜μ—­μ˜ μ‹œν—˜λ¬Έμ œμ˜ ν•œκ΅­μ‚¬ 문제λ₯Ό μ°Έμ‘°ν•˜μ˜€μŠ΅λ‹ˆλ‹€. ν•œκ΅­μ‚¬μ˜ κ²½μš°λŠ” 인터넷 검색을 톡해 지문에 λŒ€ν•œ 정보λ₯Ό μ‘°νšŒν•˜κ³  이λ₯Ό μ΄μš©ν•΄ 닡변을 μˆ˜ν–‰ν•©λ‹ˆλ‹€. ν•œκ΅­μ‚¬μ— λŒ€ν•œ 자료λ₯Ό RAGλ₯Ό κ΅¬ν˜„ν•˜λ©΄ 더 μ •ν™•ν•œ 검색 κ²°κ³Όλ₯Ό 얻을 수 μžˆμ§€λ§Œ μ—¬κΈ°μ—μ„œλŠ” plan and execute λ°©μ‹μ˜ agentic workflow νŒ¨ν„΄μ„ μ΄μš©ν•΄ λ³΅μž‘ν•œ workflowλ₯Ό ν•΄κ²°ν•˜λŠ” 방법에 λŒ€ν•΄ λ™μž‘μ„ ν…ŒμŠ€νŠΈ ν•˜κΈ° μœ„ν•˜μ—¬ 인터넷 κ²€μƒ‰λ§Œμ„ ν™œμš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

11. λ‹€μŒ A, B λŒ€ν™”μ˜ 배경으둜 κ°€μž₯ μ μ ˆν•œ 것은? 

A: 이보게,  μ’…λ‘œμ—μ„œ 거의 λ‚ λ§ˆλ‹€ λ³΄μ•ˆνšŒκ°€ μ£Όκ΄€ν•˜λŠ” λŒ€μ€‘ μ§‘νšŒκ°€ 열리고 μžˆλ‹€κ³  ν•˜λ„€. 수천 λͺ…이 λͺ¨μ—¬ ν•œ 뺌의 ꡭ토도 μ™Έκ΅­μΈμ—κ²Œ 내쀄 수 μ—†λ‹€λŠ” μ£Όμž₯을 νŽΌμΉœλ‹€λŠ” κ΅°
B. μ§€λ°©μ—μ„œλŠ” μ΄λŸ¬ν•œ μ£Όμž₯에 ν˜Έμ‘ν•˜μ—¬ 이곳 μ‘°κ³³μ—μ„œ λ³΄μ•ˆνšŒμ— μ˜μ—°κΈˆμ„ λ³΄λ‚Έλ‹€κ³ ν•©λ‹ˆλ‹€. μ„œμšΈμ˜ 상인듀도 κ°€κ²Œ 문을 λ‹«κ³  μ΄λ“€μ˜ νˆ¬μŸμ„ μ§€μ›ν•œλ‹€λ”κ΅°μš”.

β‘  μ‚°λ―Έ 증식 κ³„νšμ΄ μ‹œν–‰λ˜μ—ˆλ‹€. 
β‘‘ μ•”νƒœλ„ μ†Œμž‘ μŸμ˜κ°€ λ°œμƒν•˜μ˜€λ‹€. 
β‘’ 일본이 ν•œκ΅­μ— 황무지 κ°œκ°„κΆŒμ„ μš”κ΅¬ν•˜μ˜€λ‹€. 
β‘£ μ‘°μ„  총독뢀가 토지 쑰사 사업을 μ‹€μ‹œν•˜μ˜€λ‹€. 
β‘€ νšŒμ‚¬ 섀립을 ν—ˆκ°€μ œλ‘œ ν•˜λŠ” νšŒμ‚¬λ Ήμ΄ μ œμ •λ˜μ—ˆλ‹€.

Agent의 κ²½μš°μ— 인터넷 검색을 톡해 μ•„λž˜μ™€ 같이 5λ²ˆμ„ λ‹΅ν–ˆμœΌλ‚˜, 정닡은 3λ²ˆμž…λ‹ˆλ‹€.

noname

μ΄λ•Œ, plan and execute νŒ¨ν„΄μ„ λ”°λ₯΄λŠ” agentic solverλŠ” μ•„λž˜μ™€ 같이 3번 정닡을 찾을 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

noname

LangSmithλ₯Ό 보면 μ•„λž˜μ™€ 같이 1회 replanν›„ κ²°κ³Όλ₯Ό μ–»μ—ˆμŠ΅λ‹ˆλ‹€.

image

μ΄λ•Œμ˜ μ‹€ν–‰ λ‹¨κ³„λŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

{
  "steps": [
    "λŒ€ν™”μ˜ 핡심 λ‚΄μš© νŒŒμ•…",
    "λŒ€ν™”μ˜ 배경이 될 수 μžˆλŠ” 역사적 사건 κ²€ν† ",
    "μ œμ‹œλœ 선택지 뢄석",
    "역사적 λ°°κ²½ 확인",
    "μ΅œμ’… λ‹΅μ•ˆ 선택"
  ]
}

λ©”λ‰΄μ˜ Problem Solver μ‹€ν–‰ κ²°κ³Ό

λ©”λ‰΄μ˜ Problem SolverλŠ” 인터넷 검색 κ²°κ³Όλ₯Ό μ΄μš©ν•΄ 얻어진 정보λ₯Ό ν™œμš©ν•©λ‹ˆλ‹€. μ±„νŒ…μ°½μ—μ„œ "μ„œμšΈμ—μ„œ 뢀산을 κ±°μ³μ„œ 제주둜 κ°€λŠ” κ°€μž₯ μ €λ ΄ν•œ 방법은?"μ΄λΌλŠ” μ§ˆλ¬Έμ„ ν•˜κ³  닡변을 ν™•μΈν•©λ‹ˆλ‹€.

noname

μ΄λ•Œμ˜ λ™μž‘μ„ LangSmith둜 ν™•μΈν•©λ‹ˆλ‹€.

image

κ²°λ‘ 

μ—¬κΈ°μ—μ„œλŠ” LangGraphλ₯Ό μ΄μš©ν•˜μ—¬ agentic workflow λ°©μ‹μ˜ agentλ₯Ό κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 λ³΅μž‘ν•œ 문제λ₯Ό plan and execute νŒ¨ν„΄μœΌλ‘œ ν•΄κ²°ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 이 νŒ¨ν„΄μ€ CoT (Chain of Thought)처럼 문제λ₯Ό step by step으둜 풀도둝 μœ λ„ν•¨μœΌλ‘œμ¨ λ³΅μž‘ν•œ 문제의 의미λ₯Ό νŒŒμ•…ν•˜μ—¬ 더 쒋은 κ²°κ³Όλ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ 반볡적인 λ™μž‘μ΄ ν•„μˆ˜μ μœΌλ‘œ μš”μ²­λ˜λ―€λ‘œ zero shot 방식에 λΉ„ν•˜μ—¬ μˆ˜ν–‰μ‹œκ°„μ΄ μ¦κ°€ν•©λ‹ˆλ‹€. μ—¬κΈ°μ—μ„œλŠ” multi region을 μ΄μš©ν•œ λ³‘λ ¬μ²˜λ¦¬λ₯Ό 톡해 μˆ˜ν–‰μ‹œκ°„μ„ λ‹¨μΆ•ν•˜κ³ , 단계λ₯Ό 싀행쀑에 신뒰도(confidence)ν™•λ³΄ν•˜λ©΄ μ€‘λ‹¨ν•˜λŠ” λ°©μ‹μœΌλ‘œ 속도λ₯Ό κ°œμ„ ν•˜μ˜€μŠ΅λ‹ˆλ‹€. μ˜€λ‹΅μ€ 문제λ₯Ό μ΄ν•΄ν• λ•Œ ν…μŠ€νŠΈλΏ μ•„λ‹ˆλΌ 이미지λ₯Ό 같이 ν™œμš©ν•˜κ±°λ‚˜, 더 지λŠ₯적인 LLM으둜 더 λ³΅μž‘ν•œ λ¬Έμž₯ ꡬ쑰λ₯Ό μ΄ν•΄ν•˜λŠ” λ°©μ‹μœΌλ‘œ κ°œμ„ μ΄ κ°€λŠ₯ν•  κ²ƒμœΌλ‘œ λ³΄μ—¬μ§‘λ‹ˆλ‹€.

λ¦¬μ†ŒμŠ€ μ •λ¦¬ν•˜κΈ°

더이상 인프라λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” κ²½μš°μ— μ•„λž˜μ²˜λŸΌ λͺ¨λ“  λ¦¬μ†ŒμŠ€λ₯Ό μ‚­μ œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  1. API Gateway Console둜 μ ‘μ†ν•˜μ—¬ "api-agentic-solver", "api-chatbot-for-agentic-solver"을 μ‚­μ œν•©λ‹ˆλ‹€.

  2. Cloud9 Console에 μ ‘μ†ν•˜μ—¬ μ•„λž˜μ˜ λͺ…λ Ήμ–΄λ‘œ 전체 μ‚­μ œλ₯Ό ν•©λ‹ˆλ‹€.

cd ~/environment/agentic-solver/cdk-agentic-solver/ && cdk destroy --all