`dspy.ReAct` 로 프로그램에 도구 주기

dspy.ReAct 로 프로그램에 도구 주기 (Give the program tools with dspy.ReAct)

우리 하이쿠 작성기는 꽤 유능하지만, 이름을 대는 장소에 대한 정보가 더 있으면 좋겠어요. "가을의 Bodega Bay"를 배경으로 한 하이쿠를 요청하면 LM은 날씨에 대해 그럴듯하게 추측하지만 가끔 틀리기도 합니다. 프로그램에 도구(tool) 를 주면, 시를 쓰기 전에 먼저 조사하고 근거를 확보할 수 있게 돼요.

출처: 문서

본문

도구는 그냥 파이썬 함수입니다

DSPy 도구는 타입 힌트를 단 매개변수와 docstring을 가진 표준 파이썬 함수예요. DSPy는 함수의 이름, 매개변수, docstring을 읽어 LM에 보낼 지시문을 조립합니다.

예를 들어, wikipedia 라이브러리를 사용해 에이전트가 위키백과를 검색하게 하는 도구를 정의해 볼게요(따라 하고 있다면 pip install wikipedia 를 실행하세요):

import wikipedia

def wikipedia_search(query: str) -> list[str]:
    """Search Wikipedia for the given query and return a list of page titles."""
    return wikipedia.search(query)

dspy.ReAct 모듈은 이것을 다음과 같이 제시해요:

When selecting the next_tool_name and its next_tool_args, the tool must be one of:
        
(1) wikipedia_search, whose description is <desc>Search Wikipedia for the given query and return a list of page titles.</desc>. It takes arguments {'query': {'type': 'string'}}.
(2) finish, whose description is <desc>Marks the task as complete. That is, signals that all information for producing the outputs, i.e. `haiku`, are now available to be extracted.</desc>. It takes arguments {}.

DSPy가 함수 이름, docstring, 매개변수를 어떻게 모델에게 제시하는지 주목해 보세요. 시그니처와 마찬가지로 도구를 작성할 때도 이름 짓기에 신경 써야 해요. wikipedia_search 와 매개변수 query 는 그 기능과 역할을 분명히 설명하는 좋은 이름입니다.

그런데 우리가 정의하지 않은 도구가 하나 섞여 있어요. finish 는 dspy.ReAct 가 사용하는 특수 도구로, 모델이 작업을 끝냈다고 판단했을 때 호출합니다. dspy.ReAct 가 이것을 우리 대신 채워 넣어 줍니다.

위키백과를 다루는 하이쿠 봇 정의하기

위키백과를 검색하는 것 외에도, 에이전트가 주어진 페이지를 가져올 수 있는 도구가 하나 더 필요해요:

def get_wikipedia_page(title: str) -> str:
    """Get the content of a Wikipedia page given its title."""
    return wikipedia.page(title).content

이 두 도구와 기존 HaikuBot 시그니처가 있으면 에이전트를 정의할 준비가 끝납니다:

haiku_bot = dspy.ReAct(HaikuBot, tools=[wikipedia_search, get_wikipedia_page])
result = haiku_bot(location="Camp Meeker", mood="pensive", season="summer")
print(result.haiku)

haiku_bot 을 호출하면:

  1. "Camp Meeker"를 위키백과에서 검색했고, "Camp Meeker, California"를 포함해 아홉 개의 관련 결과가 나왔어요.
  2. "Camp Meeker, California" 페이지 내용을 가져왔습니다.
  3. finish 를 호출한 뒤 출력을 종합했습니다.

위 코드의 결과:

Redwood shadows breathe  
Summer creek whispers old tales  
Quiet mind lingers

result.reasoning 을 출력하면:

I gathered context about Camp Meeker, California—its redwood forest, Dutch Bill Creek, and the quiet summer atmosphere—from the Wikipedia page. I focused on a pensive mood and the summer season, weaving in imagery of towering trees and a gently flowing creek. The haiku follows the traditional 5‑7‑5 syllable structure, evoking the stillness and reflection of a summer day among the redwoods.

ReAct는 에이전트 루프를 관리합니다

ReAct는 테스트 시점(test-time / inference time) 루프 전략이에요. 모델에게 도구 집합과 작업을 넘겨주면, dspy.ReAct 모듈은 모델이 먼저 추론하고 도구를 사용해 행동하도록 지시합니다. 모델이 finish 를 호출하면 DSPy는 루프를 멈추고 선언된 출력 필드를 만들기 위해 마지막 합성(synthesis) 패스를 한 번 실행해요.

모델이 몇 번의 루프를 돌지는 스스로 결정하지만, max_iters 매개변수로 그 횟수를 제한할 수 있어요:

haiku_bot = dspy.ReAct(HaikuBot, tools=[wikipedia_search, get_wikipedia_page], max_iters=4)

이 프로그램을 테스트하면서 우리는 모델이 도구 호출을 최대 8번이나 하는 실행(버몬트의 시럽 농업을 조사하던 경우)과, 조사를 아예 건너뛰는 실행을 모두 목격했어요.

ReAct 궤적(trajectory) 살펴보기

ReAct가 반환하는 Prediction 인스턴스는 trajectory 필드를 지닙니다. 이 필드는 각 생각(thought), 도구 호출, 관찰(observation, 도구가 반환한 값)을 순서대로 기록하는 사전(dict)이에요. 에이전트가 예상 밖의 행동을 하면 trajectory가 제일 먼저 읽어야 할 것입니다.

다음처럼 출력할 수 있어요:

for step, value in result.trajectory.items():
    print(f"{step}: {value}")

도구 작성 패턴과 MCP 통합은 도구와 MCP를, ReAct 루프의 실행 모델과 trajectory 디버깅은 ReAct와 ReActV2를 참고하세요.

!!! note "실험적: ReActV2" dspy.ReActV2 는 현재 ReAct 구현을 대체할 실험적인 구조화 히스토리 버전이에요. DSPy 3.5에서 dspy.ReAct 가 될 예정이며, 네이티브 다중 턴(멀티턴)·병렬 도구 호출과 개선된 프롬프트 캐시 재사용을 가져옵니다. dspy.ReActV2 이름은 3.5 내내 deprecated 호환 별칭으로 남아 있다가 3.6에서 제거될 예정이에요. 자세한 내용은 ReAct에서 ReActV2로의 전환을 참고하세요.

다음: 모듈 조합하기 →

더 알아보기 (Learn more)