AI API 활용

OpenAI Assistants API로 나만의 GPT 만들기 - 실전 구축 가이드

소개왕 탑백귀 2026. 4. 16. 15:51

OpenAI Assistants API로 나만의 GPT 만들기 - 실전 구축 가이드

2026년 4월 기준 | AI API 활용

요약: OpenAI Assistants API를 사용해서 파일 검색, 코드 실행, 함수 호출이 가능한 나만의 AI 어시스턴트를 만드는 전 과정을 다룹니다. 기본 설정부터 Thread 관리, Tool 연동, 그리고 Streamlit으로 UI를 붙이는 것까지 코드와 함께 단계별로 설명합니다.

Assistants API가 뭔가

ChatGPT에서 "나만의 GPT"를 만들어본 적 있으신가요? Assistants API는 그걸 코드로 하는 겁니다. 다만 훨씬 더 세밀한 제어가 가능합니다.

기존 Chat Completions API는 매 요청마다 대화 히스토리를 직접 관리해야 했습니다. 메시지 목록을 배열로 만들어서 매번 보내고, 토큰 초과하면 앞부분을 잘라내고, 파일을 참조하려면 RAG 파이프라인을 직접 구축해야 했습니다.

Assistants API는 이 모든 걸 서버 쪽에서 관리합니다. Thread(대화 세션)를 만들면 OpenAI 서버가 메시지 히스토리를 저장하고, 파일을 올리면 자동으로 벡터 검색이 가능해지고, 파이썬 코드를 실행하는 샌드박스까지 제공합니다.

Chat Completions API와 뭐가 다른가

항목 Chat Completions Assistants API
대화 관리 클라이언트에서 직접 서버에서 자동
파일 검색 RAG 직접 구축 내장 File Search
코드 실행 불가 Code Interpreter 내장
상태 관리 Stateless Stateful (Thread)
비용 토큰 비용만 토큰 + 스토리지 + 도구 비용

간단한 챗봇이면 Chat Completions로 충분합니다. 하지만 파일을 참조하거나, 계산을 해야 하거나, 대화를 장기간 유지해야 하면 Assistants API가 압도적으로 편합니다.

환경 설정

# 가상환경 생성
python -m venv assistant-env
source assistant-env/bin/activate

# 패키지 설치
pip install openai python-dotenv streamlit

.env 파일을 만들어서 API 키를 설정합니다.

OPENAI_API_KEY=sk-proj-your-key-here

기본 어시스턴트 만들기

가장 먼저 어시스턴트 객체를 생성합니다. 이건 한 번만 하면 됩니다.

from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()
client = OpenAI()

# 어시스턴트 생성 (최초 1회)
assistant = client.beta.assistants.create(
    name="코드 리뷰 도우미",
    instructions="""당신은 시니어 파이썬 개발자입니다.
사용자가 코드를 공유하면:
1. 버그나 잠재적 문제를 찾아서 설명합니다.
2. 개선 방안을 코드와 함께 제시합니다.
3. 파이썬 베스트 프랙티스를 기준으로 리뷰합니다.
한국어로 답변합니다.""",
    model="gpt-4o",
    tools=[{"type": "code_interpreter"}],
)

print(f"Assistant ID: {assistant.id}")
# 이 ID를 저장해두세요. 다음부터 이 ID로 어시스턴트를 불러옵니다.

이제 대화를 시작합니다. Thread를 만들고 메시지를 추가한 뒤 Run을 실행하는 3단계 구조입니다.

import time

# 1. Thread 생성
thread = client.beta.threads.create()

# 2. 메시지 추가
client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="""이 코드를 리뷰해주세요:

def get_user(users, id):
    for user in users:
        if user['id'] == id:
            return user
    return None
""",
)

# 3. Run 실행
run = client.beta.threads.runs.create(
    thread_id=thread.id,
    assistant_id=assistant.id,
)

# 4. 완료 대기
while run.status in ["queued", "in_progress"]:
    time.sleep(1)
    run = client.beta.threads.runs.retrieve(
        thread_id=thread.id, run_id=run.id
    )

# 5. 응답 확인
messages = client.beta.threads.messages.list(thread_id=thread.id)
for msg in messages.data:
    if msg.role == "assistant":
        print(msg.content[0].text.value)
        break

주의: Thread ID와 Assistant ID를 잘 보관하세요. Thread ID를 잃어버리면 이전 대화 내용에 접근할 수 없고, Assistant ID를 잃어버리면 설정을 다시 해야 합니다. 보통 데이터베이스에 사용자별로 저장합니다.

Assistants API의 가장 강력한 기능 중 하나가 File Search입니다. PDF, 텍스트, 마크다운 파일을 올리면 자동으로 벡터 인덱싱해서 검색할 수 있게 됩니다. RAG를 직접 구축할 필요가 없습니다.

# Vector Store 생성
vector_store = client.beta.vector_stores.create(
    name="프로젝트 문서"
)

# 파일 업로드
with open("project_spec.pdf", "rb") as f:
    file = client.files.create(file=f, purpose="assistants")

# Vector Store에 파일 추가
client.beta.vector_stores.files.create(
    vector_store_id=vector_store.id,
    file_id=file.id,
)

# 어시스턴트에 File Search 도구와 Vector Store 연결
client.beta.assistants.update(
    assistant_id=assistant.id,
    tools=[
        {"type": "code_interpreter"},
        {"type": "file_search"},
    ],
    tool_resources={
        "file_search": {
            "vector_store_ids": [vector_store.id]
        }
    },
)

이렇게 하면 어시스턴트에게 "프로젝트 스펙에서 인증 관련 요구사항이 뭐야?"라고 물으면, PDF에서 관련 내용을 찾아서 답변합니다. 인용(citation)까지 자동으로 달아줍니다.

코드 인터프리터 연동

Code Interpreter는 어시스턴트가 파이썬 코드를 직접 실행할 수 있게 해주는 도구입니다. 데이터 분석, 차트 생성, 수학 계산 등에 활용됩니다.

# CSV 파일과 함께 분석 요청
with open("sales_data.csv", "rb") as f:
    file = client.files.create(file=f, purpose="assistants")

client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="이 CSV 데이터를 분석해서 월별 매출 추이 차트를 그려주세요.",
    attachments=[{
        "file_id": file.id,
        "tools": [{"type": "code_interpreter"}]
    }],
)

어시스턴트는 pandas로 CSV를 로드하고, matplotlib으로 차트를 생성하고, 그 이미지를 응답에 포함시킵니다. 사용자 입장에서는 파일 올리고 물어보기만 하면 됩니다.

Function Calling 구현

외부 API를 호출하거나 데이터베이스를 조회해야 할 때 Function Calling을 사용합니다.

# Function 정의와 함께 어시스턴트 업데이트
client.beta.assistants.update(
    assistant_id=assistant.id,
    tools=[
        {"type": "code_interpreter"},
        {"type": "file_search"},
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "현재 날씨 정보를 가져옵니다",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {
                            "type": "string",
                            "description": "도시 이름 (예: Seoul)",
                        }
                    },
                    "required": ["city"],
                },
            },
        },
    ],
)

# Run 실행 후 Function Call 처리
import json

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

while True:
    run = client.beta.threads.runs.retrieve(
        thread_id=thread.id, run_id=run.id
    )

    if run.status == "requires_action":
        tool_calls = run.required_action.submit_tool_outputs.tool_calls
        outputs = []

        for call in tool_calls:
            if call.function.name == "get_weather":
                args = json.loads(call.function.arguments)
                # 실제로는 날씨 API를 호출
                result = {"temp": "18°C", "condition": "맑음"}
                outputs.append({
                    "tool_call_id": call.id,
                    "output": json.dumps(result, ensure_ascii=False),
                })

        run = client.beta.threads.runs.submit_tool_outputs(
            thread_id=thread.id, run_id=run.id,
            tool_outputs=outputs,
        )
    elif run.status == "completed":
        break

    time.sleep(1)

Streamlit으로 UI 붙이기

CLI에서만 쓰기 아쉬우니 Streamlit으로 간단한 채팅 UI를 만들어 봅시다.

import streamlit as st
from openai import OpenAI
import time

client = OpenAI()
ASSISTANT_ID = "asst_xxxxxxxxxxxx"  # 위에서 만든 ID

st.title("나만의 AI 어시스턴트")

# Thread 생성 (세션당 1회)
if "thread_id" not in st.session_state:
    thread = client.beta.threads.create()
    st.session_state.thread_id = thread.id
    st.session_state.messages = []

# 이전 메시지 표시
for msg in st.session_state.messages:
    with st.chat_message(msg["role"]):
        st.write(msg["content"])

# 사용자 입력
if prompt := st.chat_input("메시지를 입력하세요"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.write(prompt)

    # 어시스턴트에게 전송
    client.beta.threads.messages.create(
        thread_id=st.session_state.thread_id,
        role="user", content=prompt,
    )

    run = client.beta.threads.runs.create(
        thread_id=st.session_state.thread_id,
        assistant_id=ASSISTANT_ID,
    )

    with st.spinner("생각 중..."):
        while run.status in ["queued", "in_progress"]:
            time.sleep(1)
            run = client.beta.threads.runs.retrieve(
                thread_id=st.session_state.thread_id,
                run_id=run.id,
            )

    messages = client.beta.threads.messages.list(
        thread_id=st.session_state.thread_id
    )
    response = messages.data[0].content[0].text.value

    st.session_state.messages.append({"role": "assistant", "content": response})
    with st.chat_message("assistant"):
        st.write(response)

실전 운영 팁

1. Thread 재사용으로 비용 절약

같은 사용자면 새 Thread를 만들지 말고 기존 Thread에 메시지를 추가하세요. Thread를 새로 만들면 이전 맥락이 사라지고, File Search도 다시 벡터 검색을 해야 해서 비용이 늘어납니다.

2. Run의 상태를 꼼꼼히 체크

Run은 queued → in_progress → completed 외에도 failed, expired, cancelled 상태가 있습니다. 프로덕션 코드에서는 반드시 에러 상태를 처리해야 합니다.

3. Streaming으로 UX 개선

# polling 대신 streaming 사용
with client.beta.threads.runs.stream(
    thread_id=thread.id,
    assistant_id=assistant.id,
) as stream:
    for text in stream.text_deltas:
        print(text, end="", flush=True)

4. 비용 모니터링

File Search는 파일 저장 비용($0.10/GB/일)과 검색 비용이 별도로 발생합니다. Code Interpreter 세션도 시간당 비용이 있습니다. OpenAI 대시보드에서 정기적으로 비용을 확인하세요.

마무리

Assistants API의 진짜 가치는 복잡한 인프라를 직접 구축하지 않아도 된다는 점입니다. RAG 파이프라인, 코드 실행 샌드박스, 대화 히스토리 관리를 OpenAI가 다 해줍니다.

물론 단점도 있습니다. 벤더 종속(OpenAI 서버에 데이터가 저장됨), 세밀한 벡터 검색 제어 어려움, 그리고 비용이 Chat Completions보다 높다는 점입니다. 민감한 데이터를 다루거나 검색 로직을 세밀하게 조절해야 하면 직접 RAG를 구축하는 게 낫습니다.

프로토타입이나 사내 도구를 빠르게 만들어야 한다면 Assistants API가 정답입니다. 직접 구축하면 2주, Assistants API를 쓰면 하루면 됩니다.

관련 글 추천:

  • AI Function Calling 실전 활용법 - GPT & Claude 비교 구현
  • 파이썬으로 Claude API 연동하기 - 자동 요약기 만들기
  • RAG 쉽게 이해하기 - 내 문서로 AI 챗봇 만드는 핵심 기술