인라인 Python 코드를 에이전트의 도구로 제공하여 에이전트의 기능을 유연하게 확장할 수 있습니다. 이 코드는 에이전트가 제공한 입력을 수락하고 에이전트가 대화에서 사용하는 결과를 반환할 수 있습니다. 맞춤 로직을 구현하고, 독점 API 또는 데이터베이스에 연결하고, 정밀도가 필요한 작업의 결정론적 결과를 보장할 수 있습니다.
이름 및 설명
도구를 만들 때 도구 이름과 호출할 기본 함수 이름은 스네이크 케이스로 동일해야 합니다.
독스트링은 Python 도구를 정의하는 데 중요한 부분입니다. 함수의 docstring은 도구를 사용하는 에이전트에게 제공되는 도구 설명으로 사용됩니다. 독스트링은 프롬프트의 확장으로 생각해야 합니다. 명확하고 설명적이며 구조화된 docstring은 모델이 도구의 기능, 사용 시기, 제공할 인수를 얼마나 잘 이해하는지에 직접적인 영향을 미칩니다. 이는 안정적이고 정확한 도구 선택을 달성하는 데 핵심적인 요소입니다.
환경
Python 도구 코드에서는 코드를 작성하는 데 도움이 되는 특정 클래스와 함수에 액세스할 수 있습니다. 자세한 내용은 Python 런타임 참조를 참고하세요.
예를 들어 context 객체는 현재 대화 상태의 스냅샷을 제공하는 전역적으로 사용 가능한 변수입니다.
가져오거나 매개변수로 정의할 필요가 없으며 직접 액세스할 수 있습니다.
여기에는 복잡한 로직을 실행하는 데 유용한 정보가 포함되어 있습니다.
컨텍스트 객체에서 사용할 수 있는 키는 다음과 같습니다.
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 기반 도구를 사용하는 대신 유연하게 사용할 수 있습니다. 이 샘플에서는 환경에서 사용할 수 있는 표준 요청 라이브러리를 사용하여 공개 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