인라인 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