검색
검색
회원가입로그인

Code Interpreter 도구 사용해 보기 (Tools) - Assistants API 베타 사용법

page thumbnail

도구

오픈 AI가 서비스하는 Code Interpreter 나 Knowledge Retrieval 을 사용할 수 있게 하거나, 내가 만든 도구를 어시스턴트가 function calling 을 통해 사용할 수 있게 하는 개념. 인공지능이 내부와 외부의 도구를 활용하여 단점을 보완하고 할 수 있는 일들이 엄청 많아지게 된다. 도구를 사용하는 인공지능!

코드 인터프리터

코드인터프리터는 Assistants API가 파이썬을 샌드박스 환경에서 작성하고 실행시킬 수 있는 도구이다. 데이터가 있는 파일을 다룰 수 있고 데이터가 있는 파일이나 그래프의 이미지를 만들 수 있다. 또 코드가 실패했을 때 이를 수정해서 다시 시도할 수 있는 능력도 가지고 있다. (자기 수정 기능은 꽤 유행했던 개념)

코드 인터프리터 활성화 하기

Assistant object에 tools 파라미터에 code_interpreter 를 전달하면 된다. 모델은 사용자의 메시지에서 코드 인터프리터를 실행해야 하는 상황일 때 이를 실행한다. 이런 조건은 지침에 (instructions) 추가할 수 있다. (예: 이 문제를 해결하기 위해서 코드를 짜줘. write code to solve this problem.)

assistant = client.beta.assistants.create(
  instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",
  model="gpt-4-1106-preview",
  tools=[{"type": "code_interpreter"}]
)

예제에서는 개인 수학 교사를 만드는데 수학 질문을 받으면 코드를 실행해서 답하라고 되어 있다.

코드 인터프리터에 파일 제공하기

코드 인터프리터는 파일을 제공받고 이를 활용할 수 있다. 큰 볼륨의 데이터를 제공하기를 원하거나 사용자가 분석을 위해 파일을 올리기를 원할 때 유용하게 사용할 수 있다.

이번에는 직접 코드로 실습을 하면서 해보자. tool.py 를 만들고 다음과 같이 작성한다.

from openai import OpenAI
import time

client = OpenAI(api_key="YOUR_API_KEY")

# 여기에서 rb는 read binary의 뜻
file = client.files.create(
  file=open("math.csv", "rb"),
  purpose="assistants"
)

assistant = client.beta.assistants.create(
  instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",
  model="gpt-4-1106-preview",
  tools=[{"type": "code_interpreter"}],
  file_ids=[file.id]
)

print(assistant)

이렇게 한 후 터미널에서 실행해 보자.

python tool.py

성공적으로 생생되었다면 결과의 assistant 의 id를 따로 복사해 놓자. 이제 이걸 불러서 사용하면 된다.

  • YOUR_API_KEY 에는 여러분의 키를 넣으면 된다.

  • file 은 다음과 같이 수학 계산을 해보기 위해서 간단한 csv 파일을 만들었다.

Number1Number2AdditionSubtractionMultiplicationDivision
167-560.166667
279-5140.285714
3811-5240.375
4913-5360.444444
51015-5500.5

복사해서 엑셀 등에서 붙여넣기 하거나 구글 드라이브에서 다운로드 받자 (math.csv)

참고로 파일은 스레드 레벨에서도 첨부할 수 있다.

thread = client.beta.threads.create(
  messages=[
    {
      "role": "user",
      "content": "I need to solve the equation `3x + 11 = 14`. Can you help me?",
      "file_ids": [file.id]
    }
  ]
)

파일은 최대 512MB의 용량을 가진다. 코드 인터프리터는 csv, pdf, json 등 다양한 파일을 지원한다. 총 지원 파일 리스트

사용해 보기

자 이제 만든 어시스턴트를 사용해 보자.

# 기존 생성 코드는 주석처리
# assistant = client.beta.assistants.create(
#     instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",
#     model="gpt-4-1106-preview",
#     tools=[{"type": "code_interpreter"}],
#     file_ids=[file.id],
# )

# print(assistant)

# YOUR_API_KEY에 여러분의 키 입력
assistant = client.beta.assistants.retrieve("YOUR_API_KEY")

thread = client.beta.threads.create()

message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="첨부된 csv 파일의 첫번째 열의 합을 알려줘.",
)

run = client.beta.threads.runs.create(
    thread_id=thread.id,
    assistant_id=assistant.id,
)

# 코드 인터프리터는 느려서 5초마다 한번씩 확인
while True:
    if run.status == "completed":
        break
    run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
    print("실행 중...")
    time.sleep(5)

messages = client.beta.threads.messages.list(thread_id=thread.id)
print(messages)

전에 했던 것과 마찬가지로 스레드를 생성하고 여기에 메시지를 더한다. 그리고 스레드를 run 한다. run의 상태를 지속적으로 확인한 후 completed 일 경우 메시지에 받은 응답을 확인한다.

나온 결과를 각 메시지별로 인스턴스로 정리해 보겠습니다.

# 각 메시지 인스턴스 생성
message1 = ThreadMessage(
    id='msg_xG6VhzRHgJxHwc6cBoeIYcpS',
    assistant_id='asst_CEVHjX4kiDXKxl6QCmTHZQ8V',
    content=[MessageContentText(text='첫 번째 열의 합계는 15입니다.')],
    created_at=1700375215,
    role='assistant',
    thread_id='thread_zmyzfjGZuFbb2qso8CxaLCQf'
)

message2 = ThreadMessage(
    id='msg_NlW4q6Q6GNu3z5fBOrHDe2gS',
    assistant_id='asst_CEVHjX4kiDXKxl6QCmTHZQ8V',
    content=[MessageContentText(text="CSV 파일이 성공적으로 로드되었으며, 데이터의 구조를 확인할 수 있습니다. 첫 번째 열의 이름은 'Number1'이며, 이 열의 값을 모두 더해 합계를 계산하겠습니다.")],
    created_at=1700375212,
    role='assistant',
    thread_id='thread_zmyzfjGZuFbb2qso8CxaLCQf'
)

message3 = ThreadMessage(
    id='msg_cKpKy8s7dIIopxPFC1ilRzYh',
    assistant_id='asst_CEVHjX4kiDXKxl6QCmTHZQ8V',
    content=[MessageContentText(text='먼저 업로드하신 CSV 파일의 내용을 확인하고, 첫 번째 열의 합을 계산하기 위해 파일을 불러와 보겠습니다.')],
    created_at=1700375206,
    role='assistant',
    thread_id='thread_zmyzfjGZuFbb2qso8CxaLCQf'
)

message4 = ThreadMessage(
    id='msg_3WdaZ5MNhpmVMuCOzCMBqZAC',
    assistant_id=None,
    content=[MessageContentText(text='첨부된 csv 파일의 첫번째 열의 합을 알려줘.')],
    created_at=1700375205,
    role='user',
    thread_id='thread_zmyzfjGZuFbb2qso8CxaLCQf'
)

먼저 질문을 받고 실행 계획을 세우고 코드 인터프리터를 실행하여 결과를 나타내는 것을 확인할 수 있습니다.

  1. 첨부된 csv 파일의 첫번째 열의 합을 알려줘.

  2. 먼저 업로드하신 CSV 파일의 내용을 확인하고, 첫 번째 열의 합을 계산하기 위해 파일을 불러와 보겠습니다.

  3. CSV 파일이 성공적으로 로드되었으며, 데이터의 구조를 확인할 수 있습니다. 첫 번째 열의 이름은 'Number1'이며, 이 열의 값을 모두 더해 합계를 계산하겠습니다.

  4. 첫 번째 열의 합계는 15입니다.

1 + 2+ 3+ 4+ 5 의 결과값이니 15가 맞네요. 똑똑하다 GPT!

코드 인터프리터가 생성한 파일 또는 이미지 읽기

코드 인터프리터는 이미지 다이어그램이나, CSV, PDF 파일등을 결과로 내놓을 수 있다. 다음은 가능한 파일의 유형이다.

  1. 이미지

  2. 데이터 파일 (예 : Assistant가 생성한 데이터를 가지고 있는 csv)

이렇게 만들어진 결과는 file_id 필드의 값을 활용해서 조회하거나 다운로드 받을 수 있다.

아까 우리의 어시스턴트에 결과를 csv 파일로 생성해 달라고 하자.

첨부된 csv 파일의 첫번째 열의 합을 알려줘. 결과는 csv 파일로 출력해줘.

다음 코드는 나온 결과를 좀 보기 쉽게 정리한 것입니다.

# 파일 경로 어노테이션
file_path_annotation = TextAnnotationFilePath(
    end_index=137,
    file_path=TextAnnotationFilePathFilePath(file_id='file-WUeQ8rOTtHfJOZQIkDCO7YZx'),
    start_index=96,
    text='sandbox:/mnt/data/sum_of_first_column.csv',
    type='file_path'
)

# 메시지 텍스트
message_text = Text(
    annotations=[file_path_annotation],
    value='첫 번째 열의 합이 계산되어 새 CSV 파일로 저장되었습니다. 아래 링크에서 결과 파일을 다운로드할 수 있습니다.\n\n[sum_of_first_column.csv 다운로드](sandbox:/mnt/data/sum_of_first_column.csv)'
)

# ThreadMessage 객체
thread_message = ThreadMessage(
    id='msg_bjYcK10aMQc07mHEh9mEBhLQ',
    assistant_id='asst_CEVHjX4kiDXKxl6QCmTHZQ8V',
    content=[MessageContentText(text=message_text, type='text')],
    created_at=1700379797,
    file_ids=['file-UeQ8rOTtHfJOZQIkDCO7YZx'],
    metadata={},
    object='thread.message',
    role='assistant',
    run_id='run_kD8NN3rBnzstZCcNPKsPpbk6',
    thread_id='thread_ihc9wMtj4jpWfIMAf1Wu64II'
)

결과를 보면 다음과 같이 출력된 것을 알 수 있습니다. 여기에서 보면 file_ids 에 생성된 파일의 아이디가 있는 걸 알 수 있습니다.

이제 이 아이디를 Files API를 활용해 다운로드 받아 보겠습니다. 위의 코드들을 잠시 주석처리하고 다음 코드를 실행해 봅시다.

image_data = client.files.content("YOUR_FILE_ID")
image_data_bytes = image_data.read()

with open("./my-math.csv", "wb") as file:
    file.write(image_data_bytes)

이렇게 하면 폴더에 my-math.csv 가 생성된 것을 확인할 수 있을 것입니다.

그리고 만약 코드 인터프리터가 결과 파일을 링크로 제공을 했다면 message의 annotations (주석)에서 상세 정보를 확인할 수 있습니다.

file_path_annotation = TextAnnotationFilePath(
    end_index=137,
    file_path=TextAnnotationFilePathFilePath(file_id='file-WUeQ8rOTtHfJOZQIkDCO7YZx'),
    start_index=96,
    text='sandbox:/mnt/data/sum_of_first_column.csv',
    type='file_path'
)

코드 인터프리터의 입력과 출력 로그 보기

run에 steps의 list 를 지정함으로써 코드 인터프리터의 로그를 볼 수 있습니다.

마지막에 다음 코드를 추가해서 실행한 결과의 로그값을 확인해 봅시다.

run_steps = client.beta.threads.runs.steps.list(thread_id=thread.id, run_id=run.id)

print(run_steps)

정리한 코드

sync_cursor_page_run_step = {
    "data": [
        # ... 이전 RunStep 객체들 ...
        {
            "id": "step_3B5ONOk5kijxTMy1MyfMisSc",
            "assistant_id": "asst_CEVHjX4kiDXKxl6QCmTHZQ8V",
            # ... 기타 RunStep 속성들 ...
            "step_details": {
                "tool_calls": [
                    {
                        "id": "call_bx6BusL2mvFaxAWmKV6WqmXB",
                        "code_interpreter": {
                            "input": "# Create a new dataframe with the sum\nsum_df = pd.DataFrame([first_column_sum], columns=['Sum of First Column'])\n\n# Define the path for the new CSV file\noutput_file_path = '/mnt/data/sum_of_first_column.csv'\n\n# Save the sum to a new CSV file\nsum_df.to_csv(output_file_path, index=False)\n\noutput_file_path",
                            "outputs": [
                                {
                                    "logs": "'/mnt/data/sum_of_first_column.csv'",
                                    "type": "logs"
                                }
                            ]
                        },
                        "type": "code_interpreter"
                    }
                ],
                "type": "tool_calls"
            },
            # ... 기타 RunStep 속성들 ...
        },
        # ... 나머지 RunStep 객체들 ...
    ],
    "object": "list",
    "first_id": "step_4brrrOz4ycy0uaIGMQe62Pva",
    "last_id": "step_a9Q0QYxj4SZN59Vk2uthUmsq",
    "has_more": False
}

여기에서 code_interpreter 항목에서 inputs 와 outputs 를 확인할 수 있습니다.

input :

Create a new dataframe with the sum

sum_df = pd.DataFrame([first_column_sum], columns=['Sum of First Column'])

# Define the path for the new CSV file\noutput_file_path = '/mnt/data/sum_of_first_column.csv'

# Save the sum to a new CSV file\nsum_df.to_csv(output_file_path, index=False)

output_file_path

outputs :

"outputs": [
    {
        "logs": "'/mnt/data/sum_of_first_column.csv'",
        "type": "logs"
    }
]

코드 인터프리터의 내용을 보면 데이터 처리 라이브러리인 Pandas의 DataFrame으로 데이터를 올리고 처리하는 것을 알 수 있습니다.

여기까지 Tool 중 대표적인 코드 인터프리터에 대해 알아 보았습니다. 다음은 다른 툴인 retrieval에 대해 알아보겠습니다.

기타

가격은 세션 당 0.03달러입니다. 세션은 한시간동안 유지됩니다. 코드인터프리터 등 OpenAI가 제공하는 도구의 비용은 여기에서 확인할 수 있습니다.

전체 코드

from openai import OpenAI
import time

client = OpenAI(api_key="YOUR_API_KEY")

# file = client.files.create(file=open("math.csv", "rb"), purpose="assistants")

# assistant = client.beta.assistants.create(
#     instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",
#     model="gpt-4-1106-preview",
#     tools=[{"type": "code_interpreter"}],
#     file_ids=[file.id],
# )

# print(assistant)

assistant = client.beta.assistants.retrieve("YOUR_ASSISTANT_ID")

thread = client.beta.threads.create()

message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="첨부된 csv 파일의 첫번째 열의 합을 알려줘. 결과는 csv 파일로 출력해줘.",
)

run = client.beta.threads.runs.create(
    thread_id=thread.id,
    assistant_id=assistant.id,
)

print("run을 실행합니다.")
while True:
    if run.status == "completed":
        break
    run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
    print("실행 중...")
    time.sleep(5)

messages = client.beta.threads.messages.list(thread_id=thread.id)
print(messages)
print("\n\n")
# file_id = messages.data[0].file_ids[0]
# image_data = client.files.content(file_id)
# image_data_bytes = image_data.read()

# with open("./my-math.csv", "wb") as file:
#     file.write(image_data_bytes)

run_steps = client.beta.threads.runs.steps.list(thread_id=thread.id, run_id=run.id)
print(run_steps)
조회수 : 593
공유하기
카카오로 공유하기
페이스북 공유하기
트위터로 공유하기
url 복사하기