Python 코드 도구

인라인 Python 코드를 에이전트의 도구로 제공하여 에이전트의 기능을 유연하게 확장할 수 있습니다. 이 코드는 에이전트가 제공하는 입력을 수락하고 대화에서 에이전트가 사용하는 결과를 반환할 수 있습니다. 맞춤 로직을 구현하고, 독점 API 또는 데이터베이스에 연결하고, 정밀도가 필요한 작업의 결정론적 결과를 보장할 수 있습니다.

이름 및 설명

도구를 만들 때 도구 이름과 호출할 기본 함수 이름은 스네이크 케이스로 동일한 이름이어야 합니다.

Docstring은 Python 도구를 정의하는 데 중요한 부분입니다. 함수의 docstring은 도구를 사용하는 에이전트에 제공되는 도구 설명으로 사용됩니다. docstring은 프롬프트의 확장으로 생각해야 합니다. 명확하고 설명이 포함된 잘 구성된 docstring은 모델이 도구의 기능, 사용 시점, 제공할 인수를 얼마나 잘 이해하는지에 직접적인 영향을 미칩니다. 이는 안정적이고 정확한 도구 선택을 달성하는 데 핵심입니다.

환경

Python 도구 코드에서 코드를 작성하는 데 도움이 되는 특정 클래스와 함수에 액세스할 수 있습니다. 자세한 내용은 Python 런타임 참조를 확인하세요.

예를 들어, context ToolContext 유형의 ToolContext는 현재 대화 상태의 스냅샷을 제공하는 전역적으로 사용 가능한 변수입니다. 가져오거나 매개변수로 정의할 필요가 없으며 직접 액세스할 수 있습니다. 여기에는 복잡한 로직을 실행하는 데 유용한 정보가 포함되어 있습니다.

다음은 컨텍스트 객체에서 사용할 수 있는 키의 분석입니다.

  • function_call_id: 실행 중인 특정 도구 호출의 고유 ID입니다.
  • user_content: 텍스트와 역할을 포함하여 사용자의 가장 최근 메시지가 포함된 사전입니다. 가장 흔히 사용되는 속성 중 하나입니다.
  • state: 세션 상태를 나타내는 사전입니다. 이를 사용하여 대화에서 여러 차례 지속되어야 하는 변수(사용자 프로필, 장바구니 콘텐츠 등)를 저장하고 검색할 수 있습니다.
  • events: 이 시점까지의 대화 기록에 있는 모든 이벤트의 목록으로, 더 복잡한 컨텍스트 인식으로 도구를 빌드할 수 있습니다.
  • session_id: 전체 대화 세션의 고유 식별자입니다.
  • invocation_id: 대화의 현재 차례에 대한 고유 식별자입니다.
  • agent_name: 도구를 실행하는 에이전트의 이름입니다.

다른 도구를 호출하는 Python 도구

Python 코드 도구를 정의할 때 에이전트 애플리케이션에 정의된 다른 도구를 명시적으로 호출할 수 있습니다. 예를 들어 crm_service_get_cart_information이라는 OpenAPI 도구가 있는 경우 다음 코드를 사용하여 해당 도구를 호출할 수 있습니다.

# Deterministically call another tool from this tool.
# This syntax for OpenAPI spec tool is:
# tools.<tool_name>_<endpoint_name>({tool_args})
res = tools.crm_service_get_cart_information({})

코드 샘플

다음 섹션에서는 샘플을 제공합니다.

마지막 사용자 입력 가져오기

이 샘플은 사용자의 가장 최근 메시지에 액세스하는 기본 기능을 보여줍니다. 도구는 context.user_content 객체를 검사하고 마지막 사용자 차례에서 텍스트를 추출합니다. 이 패턴은 사용자가 방금 말한 내용을 기반으로 작업을 실행해야 하는 모든 도구에 필수적입니다.

from typing import Optional

# Docstrings in tools are important because they are directly
# sent to the model as the description for the tool. You should
# think of docstrings as an extension of prompting. Clear and
# descriptive docstrings will yield higher quality tool
# selection from the model.
def get_last_user_utterance() -> Optional[str]:
  """
  Retrieves the most recent message sent by the user from the conversation history.

  Returns:
    The text of the last user message, or None if no user messages are found.
  """
  # The 'context.user_content' contains the last input data
  # provided by the user.
  # We can filter it to find only the text input from the user.
  user_messages = [
    part.text for part in context.user_content.parts
    if context.user_content.role == "user"
  ]

  if user_messages:
    # The most recent message is the first one in the list.
    return user_messages[0]

  return None

변수 가져오기 및 업데이트

언어 모델은 세션 상태를 직접 수정할 수 없습니다. 이는 상태 변경이 제어되고 예측 가능한 방식으로 처리되도록 하기 위한 것입니다. 상태는 Python 도구 또는 콜백이라는 두 가지 메커니즘을 통해서만 수정할 수 있습니다.

이 샘플은 Python 도구가 상태를 관리하는 방법을 보여줍니다. 도구는 먼저 context.state에서 현재 customer_profile을 읽습니다. 그런 다음 비즈니스 로직 (포인트 추가)을 실행하고 업데이트된 프로필을 context.state에 다시 씁니다. 그러면 이 새 상태를 세션의 나머지 부분에서 에이전트와 다른 도구에서 사용할 수 있습니다.

from pydantic import BaseModel, Field
from typing import Optional, Dict, Any

# Using Pydantic defines the expected structure of your state variables. This makes your code more reliable and easier to
# debug.
class CustomerProfile(BaseModel):
  email: Optional[str] = None
  loyalty_points: int = Field(default=0, ge=0) # Must be >= 0
  plan: str = "Standard"

# Docstrings in tools are important because they are directly
# sent to the model as the description for the tool. You should
# think of docstrings as an extension of prompting. Clear and
# descriptive docstrings will yield higher quality tool
# selection from the model.
def update_customer_loyalty_points(points_to_add: int) -> Dict[str, Any]:
  """
  Adds loyalty points to the customer's profile and returns the updated profile.

  Args:
    points_to_add: The number of loyalty points to add to the existing total.

  Returns:
    A dictionary containing the customer's updated profile information.
  """
  # 1. Get the current profile data from the session state.
  # The .get() method safely returns an empty dict if
  # 'customer_profile' doesn't exist.
  current_profile_data = context.state.get("customer_profile", {})

  # 2. Load the data into a Pydantic model for validation and easy access.
  profile = CustomerProfile(**current_profile_data)

  # 3. Perform the business logic.
  # Print statements can be used for debugging and will show
  # up in the tracing details.
  profile.loyalty_points += points_to_add
  print(f"Updated loyalty points to: {profile.loyalty_points}")

  # 4. Save the updated data back into the session state.
  # .model_dump() converts the Pydantic model back to a
  # dictionary.
  context.state["customer_profile"] = profile.model_dump()

  return context.state["customer_profile"]

외부 네트워크 요청

Python 도구는 외부 네트워크 요청을 할 수 있으며, 이는 실시간 데이터를 가져오거나 OpenAPI 사양이 없는 서드 파티 서비스와 통합하는 데 유용합니다. 이를 통해 OpenAPI 기반 도구를 사용하는 유연한 대안을 제공합니다.

Python 코드 도구의 주요 제한사항은 다음과 같습니다.

  • 비공개 네트워크 액세스 (PNA) 없음: 서비스 디렉터리가 구성되어 있더라도 Python 코드 도구는 비공개 IP 주소에 직접 액세스하거나 비공개 DNS 도메인을 확인할 수 없습니다. 비공개 네트워크 내의 리소스에 액세스해야 하는 경우 대신 비공개 네트워크 액세스를 지원하는 도구 유형 (예: OpenAPI 또는 MCP 도구)을 사용해야 합니다.
  • 공개 인터넷 액세스: Python 코드 도구는 인터넷에서 공개적으로 사용할 수 있는 엔드포인트에만 액세스할 수 있습니다.

이 샘플은 환경에서 사용할 수 있는 표준 요청 라이브러리를 사용하여 공개 API를 호출하고 임의의 사실을 가져옵니다.

from typing import Optional

# Docstrings in tools are important because they are directly sent to the model as the
# description for the tool. You should think of docstrings as an extension of prompting.
# Clear and descriptive docstrings will yield higher quality tool selection from the model.

def get_random_fact() -> Optional[str]:
  """
  Fetches a random fact from a public API.

  Returns:
      A string containing the fact on success, or None if an error occurs.
  """
  # The 'ces_requests' library is inspired by 'requests', a  standard and powerful way in Python
  # to make HTTP network calls to any external API, just like you would with `curl` or a web browser.
  url = "https://uselessfacts.jsph.pl/api/v2/facts/random"

  try:
      # This example calls a public API that is completely open and requires no authentication
      # (like an API key). Many other APIs for services like weather or e-commerce require you
      # to send credentials, often as an API key in the request headers or parameters.
      res = ces_requests.get(url=url, json=request_body, headers=headers)

      # Example POST request
      #res = ces_requests.post(url=url, json=request_body, headers=headers)

      # This is a standard practice with 'ces_requests' to check if the call was successful. It will
      # raise an error for statuses like 404 or 500.
      res.raise_for_status()

      fact_data = res.json()
      return fact_data.get("text")

  except:
      return None