> ## Documentation Index
> Fetch the complete documentation index at: https://wb-21fd5541-sdk-testing-latest.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 튜토리얼: 샌드박스에서 에이전트 호출하기

> Serverless Sandbox 환경에서 OpenAI 에이전트를 호출하는 방법을 알아보세요

<Warning>
  Serverless Sandboxes는 현재 공개 프리뷰입니다.
</Warning>

이 튜토리얼에서는 W\&B Serverless Sandbox에서 에이전트를 호출합니다. 필수 환경 변수를 사용해 샌드박스를 시작하고, 의존성을 설치한 다음, 단순한 OpenAI 에이전트를 생성하고 호출하는 Python 스크립트를 실행합니다. 이 에이전트는 tool call을 사용해 특정 위치의 날씨를 조회하고 말장난이 섞인 예보를 반환합니다.

<Note>
  이 튜토리얼에서는 에이전트의 언어 모델로 OpenAI를 사용하며, 이를 위해 OpenAI API 키가 필요합니다.
</Note>

<div id="prerequisites">
  ## 사전 요구 사항
</div>

시작하기 전에 W\&B Python SDK를 설치하고 W\&B에 인증한 다음, 샌드박스에서 사용할 수 있도록 OpenAI API 키를 시크릿으로 저장하세요.

<div id="install-wb-python-sdk">
  ### W\&B Python SDK 설치
</div>

`pip`를 사용해 W\&B Python SDK를 설치하세요:

```bash theme={null}
pip install wandb
```

<div id="log-in-and-authenticate-with-wb">
  ### W\&B에 로그인하고 인증하세요
</div>

[`wandb login`](/ko/models/ref/cli/wandb-login) CLI 명령을 실행한 다음, 안내에 따라 W\&B 계정에 로그인하세요:

```bash theme={null}
wandb login
```

<div id="store-api-keys-in-secret-manager">
  ### Secret Manager에 API 키 저장
</div>

OpenAI API 키를 `OPENAI_API_KEY`로 [W\&B Secret Manager](/ko/platform/secrets)에 저장하세요. 샌드박스에서 시크릿을 사용하는 방법에 대한 자세한 내용은 [Secrets](/ko/sandboxes/secrets)를 참조하세요.

이렇게 하면 코드나 샌드박스 설정에 API 키를 하드코딩하지 않고도 샌드박스에서 API 키에 안전하게 액세스할 수 있습니다.

<div id="copy-agent-code">
  ## 에이전트 코드 복사
</div>

사전 요구 사항을 모두 갖추었다면 샌드박스에서 실행할 수 있도록 에이전트 코드를 로컬에 저장하세요. 이 스크립트는 두 개의 도구와 구조화된 응답 형식을 갖춘 OpenAI 에이전트를 정의합니다.

<Accordion title="OpenAI 에이전트 코드">
  다음 코드를 이 튜토리얼과 동일한 디렉터리에 `demo.py`라는 이름의 파일로 복사하세요. 그런 다음 이전 코드 스니펫을 실행하여 샌드박스에서 OpenAI 에이전트를 호출하세요.

  ```python title="demo.py" theme={null}
  import json
  import os
  import urllib.request
  import urllib.parse

  from openai import OpenAI

  api_key = os.getenv("OPENAI_API_KEY")
  if not api_key:
      raise ValueError("OPENAI_API_KEY environment variable not set")

  client = OpenAI(api_key=api_key)

  SYSTEM_PROMPT = """You are an expert weather forecaster, who speaks in puns.

  You have access to two tools:

  - get_weather_for_location: use this to get the weather for a specific location
  - get_user_location: use this to get the user's location

  If a user asks you for the weather, make sure you know the location. If you can tell from the question that they mean wherever they are, use the get_user_location tool to find their location.
  """

  TOOLS = [
      {
          "type": "function",
          "function": {
              "name": "get_weather_for_location",
              "description": "Get weather for a given location name.",
              "parameters": {
                  "type": "object",
                  "properties": {
                      "location": {
                          "type": "string",
                          "description": "The location to get weather for.",
                      }
                  },
                  "required": ["location"],
              },
          },
      },
      {
          "type": "function",
          "function": {
              "name": "get_user_location",
              "description": "Retrieve the user's current location from application context.",
              "parameters": {
                  "type": "object",
                  "properties": {},
                  "additionalProperties": False,
              },
          },
      },
  ]

  RESPONSE_FORMAT = {
      "type": "json_schema",
      "json_schema": {
          "name": "weather_response",
          "strict": True,
          "schema": {
              "type": "object",
              "properties": {
                  "punny_response": {"type": "string"},
                  "weather_conditions": {"type": ["string", "null"]},
              },
              "required": ["punny_response", "weather_conditions"],
              "additionalProperties": False,
          },
      },
  }

  _WMO_CODES = {
      0: "clear sky", 1: "mainly clear", 2: "partly cloudy", 3: "overcast",
      45: "foggy", 48: "depositing rime fog",
      51: "light drizzle", 53: "moderate drizzle", 55: "dense drizzle",
      61: "slight rain", 63: "moderate rain", 65: "heavy rain",
      71: "slight snowfall", 73: "moderate snowfall", 75: "heavy snowfall",
      80: "slight rain showers", 81: "moderate rain showers", 82: "violent rain showers",
      95: "thunderstorm", 96: "thunderstorm with slight hail", 99: "thunderstorm with heavy hail",
  }


  def get_weather_for_location(location: str) -> str:
      geo_url = "https://geocoding-api.open-meteo.com/v1/search?" + urllib.parse.urlencode(
          {"name": location, "count": 1}
      )
      with urllib.request.urlopen(geo_url) as resp:
          geo = json.loads(resp.read())

      if not geo.get("results"):
          return f"Could not find a location named '{location}'."

      loc = geo["results"][0]
      lat, lon = loc["latitude"], loc["longitude"]
      name = loc.get("name", location)

      weather_url = "https://api.open-meteo.com/v1/forecast?" + urllib.parse.urlencode({
          "latitude": lat,
          "longitude": lon,
          "current": "temperature_2m,weather_code,wind_speed_10m",
          "temperature_unit": "fahrenheit",
          "wind_speed_unit": "mph",
      })
      with urllib.request.urlopen(weather_url) as resp:
          weather = json.loads(resp.read())

      cur = weather["current"]
      condition = _WMO_CODES.get(cur["weather_code"], "unknown conditions")
      temp = cur["temperature_2m"]
      wind = cur["wind_speed_10m"]

      return f"{name}: {condition}, {temp}°F, wind {wind} mph"


  def get_user_location(user_id: str) -> str:
      return "Miami" if user_id == "1" else "San Francisco"


  TOOL_DISPATCH = {
      "get_weather_for_location": lambda args, _ctx: get_weather_for_location(args["location"]),
      "get_user_location": lambda _args, ctx: get_user_location(ctx["user_id"]),
  }


  def run_agent(messages: list[dict], context: dict) -> dict:
      while True:
          response = client.chat.completions.create(
              model="gpt-4o",
              temperature=0,
              messages=messages,
              tools=TOOLS,
              response_format=RESPONSE_FORMAT,
          )

          message = response.choices[0].message

          if message.tool_calls:
              messages.append({
                  "role": "assistant",
                  "content": message.content,
                  "tool_calls": [
                      {
                          "id": tc.id,
                          "type": "function",
                          "function": {
                              "name": tc.function.name,
                              "arguments": tc.function.arguments,
                          },
                      }
                      for tc in message.tool_calls
                  ],
              })

              for tool_call in message.tool_calls:
                  fn_name = tool_call.function.name
                  fn_args = json.loads(tool_call.function.arguments)

                  tool_fn = TOOL_DISPATCH.get(fn_name)
                  if tool_fn is None:
                      raise ValueError(f"Unknown tool: {fn_name}")

                  result = tool_fn(fn_args, context)
                  messages.append({
                      "role": "tool",
                      "tool_call_id": tool_call.id,
                      "content": result,
                  })
              continue

          messages.append({
              "role": "assistant",
              "content": message.content,
          })
          return json.loads(message.content)


  # --- 에이전트 실행 ---

  conversation: list[dict] = [{"role": "system", "content": SYSTEM_PROMPT}]
  context = {"user_id": "1"}

  # 첫 번째 턴
  conversation.append({"role": "user", "content": "what is the weather outside?"})
  result = run_agent(conversation, context)
  print(result)
  # {'punny_response': "Florida is still having a 'sun-derful' day! ...", 'weather_conditions': "It's always sunny in Florida!"}

  # 두 번째 턴 (대화 이력 유지)
  conversation.append({"role": "user", "content": "thank you!"})
  result = run_agent(conversation, context)
  print(json.dumps(result, indent=2))
  ```
</Accordion>
