Gemini와 LlamaIndex로 리서치 에이전트 만들기

Gemini와 LlamaIndex로 리서치 에이전트 만들기

LlamaIndex는 LLM을 여러분의 데이터에 연결해 지식 에이전트를 만드는 프레임워크예요. 이 예시에서는 Research Agent를 위한 다중 에이전트 워크플로를 구축하는 방법을 보여줘요.

출처: 원문

본문

LlamaIndex는 LLM을 데이터에 연결해 지식 에이전트를 만드는 프레임워크예요. 이 예시는 Research Agent를 위한 다중 에이전트 워크플로를 구축하는 방법을 보여줘요. LlamaIndex에서 Workflows는 에이전트와 다중 에이전트 시스템의 구성 요소예요.

Gemini API 키가 필요해요. 아직 없으면 Google AI Studio에서 발급받을 수 있어요. 먼저 필요한 모든 LlamaIndex 라이브러리를 설치하세요. LlamaIndex는 내부적으로 google-genai 패키지를 사용해요.

pip install llama-index llama-index-utils-workflow llama-index-llms-google-genai llama-index-tools-google

LlamaIndex에서 Gemini 설정

모든 LlamaIndex 에이전트의 엔진은 추론과 텍스트 처리를 담당하는 LLM이에요. 이 예시는 Gemini 3 Flash를 사용해요. API 키를 환경 변수로 설정했는지 확인하세요.

import os
from llama_index.llms.google_genai import GoogleGenAI

# Set your API key in the environment elsewhere, or with os.environ['GEMINI_API_KEY'] = '...'
assert 'GEMINI_API_KEY' in os.environ

llm = GoogleGenAI(model="gemini-3.8-flash")

도구 만들기

에이전트는 도구를 사용해 외부 세계와 상호작용해요. 예를 들어 웹을 검색하거나 정보를 저장하죠. LlamaIndex의 도구는 일반 Python 함수일 수도 있고, 기존 ToolSpecs에서 가져올 수도 있어요. Gemini에는 Google 검색을 위한 내장 도구가 있는데, 여기서 사용해요.

from google.genai import types

google_search_tool = types.Tool(
    google_search=types.GoogleSearch()
)

llm_with_search = GoogleGenAI(
    model="gemini-3.8-flash",
    generation_config=types.GenerateContentConfig(tools=[google_search_tool])
)

검색이 필요한 쿼리로 LLM 인스턴스를 테스트해 보세요. 이 가이드는 실행 중인 이벤트 루프(python -m asyncio 또는 Google Colab)를 가정해요.

response = await llm_with_search.acomplete("What's the weather like today in Biarritz?")
print(response)

Research Agent는 Python 함수를 도구로 사용할 거예요. 이 작업을 수행하는 시스템을 구축하는 방법은 여러 가지가 있는데, 이 예시에서는 다음을 사용해요:

  1. search_web은 Gemini + Google 검색으로 주어진 주제에 대한 웹 정보를 검색해요.
  2. record_notes는 웹에서 찾은 연구 내용을 다른 도구가 사용할 수 있게 상태에 저장해요.
  3. write_report는 ResearchAgent가 찾은 정보로 보고서를 작성해요.
  4. review_report는 보고서를 검토하고 피드백을 제공해요.

Context 클래스는 에이전트/도구 사이의 상태를 전달하며, 각 에이전트는 시스템의 현재 상태에 접근할 수 있어요.

from llama_index.core.workflow import Context

async def search_web(ctx: Context, query: str) -> str:
    """Useful for searching the web about a specific query or topic"""
    response = await llm_with_search.acomplete(f"""Please research given this query or topic,
    and return the result\n<query_or_topic>{query}</query_or_topic>""")
    return response

async def record_notes(ctx: Context, notes: str, notes_title: str) -> str:
    """Useful for recording notes on a given topic."""
    current_state = await ctx.store.get("state")
    if "research_notes" not in current_state:
        current_state["research_notes"] = {}
    current_state["research_notes"][notes_title] = notes
    await ctx.store.set("state", current_state)
    return "Notes recorded."

async def write_report(ctx: Context, report_content: str) -> str:
    """Useful for writing a report on a given topic."""
    current_state = await ctx.store.get("state")
    current_state["report_content"] = report_content
    await ctx.store.set("state", current_state)
    return "Report written."

async def review_report(ctx: Context, review: str) -> str:
    """Useful for reviewing a report and providing feedback."""
    current_state = await ctx.store.get("state")
    current_state["review"] = review
    await ctx.store.set("state", current_state)
    return "Report reviewed."

다중 에이전트 어시스턴트 만들기

다중 에이전트 시스템을 만들려면 에이전트와 그 상호작용을 정의해야 해요. 이 시스템에는 세 가지 에이전트가 있어요:

  1. ResearchAgent는 주어진 주제에 대한 웹 정보를 검색해요.
  2. WriteAgent는 ResearchAgent가 찾은 정보로 보고서를 작성해요.
  3. ReviewAgent는 보고서를 검토하고 피드백을 제공해요.

이 예시는 AgentWorkflow 클래스를 사용해 에이전트를 순서대로 실행하는 다중 에이전트 시스템을 만들어요. 각 에이전트는 무엇을 해야 하는지 알려주는 system_prompt와 다른 에이전트와 협력하는 방법을 제안받아요.

선택적으로 can_handoff_to로 다른 에이전트와 대화할 수 있는 대상을 지정해 다중 에이전트 시스템을 도울 수 있어요 (지정하지 않으면 스스로 알아서 하려고 해요).

from llama_index.core.agent.workflow import (
    AgentInput,
    AgentOutput,
    ToolCall,
    ToolCallResult,
    AgentStream,
)
from llama_index.core.agent.workflow import FunctionAgent, ReActAgent

research_agent = FunctionAgent(
    name="ResearchAgent",
    description="Useful for searching the web for information on a given topic and recording notes on the topic.",
    system_prompt=(
        "You are the ResearchAgent that can search the web for information on a given topic and record notes on the topic. "
        "Once notes are recorded and you are satisfied, you should hand off control to the WriteAgent to write a report on the topic."
    ),
    llm=llm,
    tools=[search_web, record_notes],
    can_handoff_to=["WriteAgent"],
)

write_agent = FunctionAgent(
    name="WriteAgent",
    description="Useful for writing a report on a given topic.",
    system_prompt=(
        "You are the WriteAgent that can write a report on a given topic. "
        "Your report should be in a markdown format. The content should be grounded in the research notes. "
        "Once the report is written, you should get feedback at least once from the ReviewAgent."
    ),
    llm=llm,
    tools=[write_report],
    can_handoff_to=["ReviewAgent", "ResearchAgent"],
)

review_agent = FunctionAgent(
    name="ReviewAgent",
    description="Useful for reviewing a report and providing feedback.",
    system_prompt=(
        "You are the ReviewAgent that can review a report and provide feedback. "
        "Your feedback should either approve the current report or request changes for the WriteAgent to implement."
    ),
    llm=llm,
    tools=[review_report],
    can_handoff_to=["ResearchAgent","WriteAgent"],
)

에이전트가 정의됐으니 AgentWorkflow를 만들고 실행할 수 있어요.

from llama_index.core.agent.workflow import AgentWorkflow

agent_workflow = AgentWorkflow(
    agents=[research_agent, write_agent, review_agent],
    root_agent=research_agent.name,
    initial_state={
        "research_notes": {},
        "report_content": "Not written yet.",
        "review": "Review required.",
    },
)

워크플로 실행 중에는 이벤트, 도구 호출, 업데이트를 콘솔로 스트리밍할 수 있어요.

from llama_index.core.agent.workflow import (
    AgentInput,
    AgentOutput,
    ToolCall,
    ToolCallResult,
    AgentStream,
)

research_topic = """Write me a report on the history of the web.
Briefly describe the history of the world wide web, including
the development of the internet and the development of the web,
including 21st century developments"""

handler = agent_workflow.run(
    user_msg=research_topic
)

current_agent = None
current_tool_calls = ""
async for event in handler.stream_events():
    if (
        hasattr(event, "current_agent_name")
        and event.current_agent_name != current_agent
    ):
        current_agent = event.current_agent_name
        print(f"\n{'='*50}")
        print(f"🤖 Agent: {current_agent}")
        print(f"{'='*50}\n")
    elif isinstance(event, AgentOutput):
        if event.response.content:
            print("📤 Output:", event.response.content)
        if event.tool_calls:
            print(
                "🛠️  Planning to use tools:",
                [call.tool_name for call in event.tool_calls],
            )
    elif isinstance(event, ToolCallResult):
        print(f"🔧 Tool Result ({event.tool_name}):")
        print(f"  Arguments: {event.tool_kwargs}")
        print(f"  Output: {event.tool_output}")
    elif isinstance(event, ToolCall):
        print(f"🔨 Calling Tool: {event.tool_name}")
        print(f"  With arguments: {event.tool_kwargs}")

워크플로가 완료된 후에는 보고서의 최종 출력과 리뷰 에이전트의 최종 리뷰 상태를 출력할 수 있어요.

state = await handler.ctx.store.get("state")
print("Report Content:\n", state["report_content"])
print("\n------------\nFinal Review:\n", state["review"])

커스텀 워크플로로 더 나아가기

AgentWorkflow는 다중 에이전트 시스템을 시작하기 좋은 방법이에요. 하지만 더 많은 제어가 필요하다면 어떨까요? 워크플로를 처음부터 직접 만들 수 있어요. 직접 워크플로를 만들고 싶은 이유를 몇 가지 들어볼게요:

  • 프로세스에 대한 더 많은 제어: 에이전트가 취하는 정확한 경로를 결정할 수 있어요. 루프 만들기, 특정 지점에서 결정 내리기, 에이전트가 다른 작업을 병렬로 수행하게 하기 등이 포함돼요.
  • 복잡한 데이터 사용: 평범한 텍스트를 넘어서요. 커스텀 워크플로는 입력과 출력에 JSON 객체나 커스텀 클래스 같은 더 구조화된 데이터를 쓸 수 있어요.
  • 다양한 미디어 작업: 텍스트뿐 아니라 이미지, 오디오, 비디오도 이해·처리하는 에이전트를 만들 수 있어요.
  • 더 스마트한 계획: 에이전트가 작업을 시작하기 전에 상세 계획을 먼저 만드는 워크플로를 설계할 수 있어요. 여러 단계가 필요한 복잡한 작업에 유용해요.
  • 자기 수정 활성화: 자신의 작업을 검토할 수 있는 에이전트를 만들 수 있어요. 출력이 충분히 좋지 않으면 에이전트가 다시 시도해 결과가 완벽해질 때까지 개선 루프를 만들어요.

LlamaIndex Workflows에 대한 자세한 내용은 LlamaIndex Workflows 문서를 참고하세요.

더 알아보기 (Learn more)