ReAct 에이전트란?

가장 기본적인 에이전트로, llm의 추론(Reasoning)을 통해 행동(Acting)하고, 다시 행동의 결과로 추론(Reasoning)-행동을 반복하는 구조를 갖고 있다.

이 에이전트는 텍스트 생성을 넘어 환경과 상호작용하는 것이 주 목적임.

더 정확하게는 3단계로 동작한다.

  1. 행동: llm이 주어진 상황을 분석하고 적절한 도구선택, 호출하고 필요한 입력을 제공해 도구를 사용.
  2. 관찰: 도구실행결과, 출력을 다시 모델에 전달. 이 행동 결과를 llm스스로 이해 / 학습까지 하는 경우도 있음.
  3. 추론: 이전 단계의 관찰 결과를 토대로 다음 행동을 결정. 이는현재 상황을 평가 하고 무엇이 최선일지를 결정하는 과정이 포함되어있음.

💡 관련해서 ReAct 논문에 대해 살펴봐도 좋을 듯

Tool&ToolNode 설정

레스토랑 메뉴와 관련된 챗봇으로 실습해보자. 이 챗봇은 RAG + 웹 검색 두가지를 지원한다.

첫번째로 RAG를 진행하는 도구

from langchain_chroma import Chroma
from langchain_ollama  import OllamaEmbeddings
from langchain_core.tools import tool
from typing import List

embeddings_model = OllamaEmbeddings(model="bge-m3") 

# Chroma 인덱스 로드
vector_db = Chroma(
    embedding_function=embeddings_model,   
    collection_name="restaurant_menu",
    persist_directory="./chroma_db",
)

# Tool 정의 
@tool
def search_menu(query: str) -> List[str]:
    """레스토랑 메뉴에서 정보를 검색합니다."""
    docs = vector_db.similarity_search(query, k=2)

    formatted_docs = "\\n\\n---\\n\\n".join(
        [
            f'<Document source="{doc.metadata["source"]}"/>\\n{doc.page_content}\\n</Document>'
            for doc in docs
        ]
    )

    if len(docs) > 0:
        return formatted_docs
    
    return "관련 메뉴 정보를 찾을 수 없습니다."

두번째는 웹 검색을 지원하는 도구

from langchain_community.tools import TavilySearchResults

# Tool 정의 
@tool
def search_web(query: str) -> List[str]:
    """데이터베이스에 존재하지 않는 정보 또는 최신 정보를 인터넷에서 검색합니다."""

    tavily_search = TavilySearchResults(max_results=3)
    docs = tavily_search.invoke(query)

    formatted_docs = "\\n\\n---\\n\\n".join(
        [
            f'<Document href="{doc["url"]}"/>\\n{doc["content"]}\\n</Document>'
            for doc in docs
        ]
    )

    if len(docs) > 0:
        return formatted_docs
    
    return "관련 정보를 찾을 수 없습니다."