OpenAI Assistant
기본 개념 정리

1. Assistant
- Assistant는 ‘AI 역할’을 하는 기능으로, 주로 특정 역할·목적에 맞게 설정된 챗봇
- 예: 수학 튜터, 고객 지원 챗봇 등으로 사용할 수 있으며, 해당 역할에 맞는 instructions, 도구, 지식 베이스 설정 가능 / 코드 실행 및 파일 업로드, 번역까지 가능
- Assistant 생성 시 instructions, tools, file_ids 등을 지정해 챗봇 정의
1-1. Instructions
- 역할 : OPENAI에게 맞춤화 설정 입력
1-2. Tools
1) code interpreter
어시스턴트가 코드를 작성하고 실행
2) retrieval
첨부된 문서를 기반으로 답변 기능
3) functions
어시스턴트에게 사용자 정의 함수를 지정

2. Thread
- Thread는 대화의 컨테이너
- 서버 측 저장된 메시지 모음
- Assistant 와 사용자 간의 하나의 단톡방
- 메시지만 저장 가능
- 각 사용자의 하나의 대화 세션을 Thread로 구성
- 그 안에 여러 Message 저장 가능
- thread_id 생성으로 특정 대화 세션 식별 가능
- 해당 id는 하나의 대화방 ID
- 주요 특징:
-“스레드” 하나당 여러 메시지를 담고 있어, 이전 대화를 그대로 이어갈 수 있는 컨텍스트 유지 기능 - metadata를 붙여 “language_preference”, “사용자 구분” 등 다양한 정보 기록
- 생성(create), 조회(retrieve), 업데이트(update), 삭제(delete) 모두 가능
3. Message
- 역할 : Thread 내부의 하나의 대화 단위
role: user 또는 assistant로 구분되어, 누가 보낸 메시지인지 표시
content: 실제 대화 내용
파일 attachment, annotations, metadata 등도 포함가능
메시지를 생성하고 관리할 수 있으며,
목록 조회(list), 단일 조회(retrieve), 수정(update), 삭제(delete) 가능
구성 요소 간 관계 도식화
Assistant(역할) —> Thread(대화 컨테이너) —> Message들(대화 내용, 유저/어시스턴트)
사용자가 입력 메시지(Message, role="user")를 보내면
Thread에 추가되고,
Assistant가 이를 처리해서
답변 메시지(Message, role="assistant")를 생성해
해당 Thread에 쌓입니다.
이후, 대화가 이어지면
동일한 Thread에 Message들이 누적
컨텍스트가 유지됩니다.

app.py 수정
@app.route("/sendMessage", methods=["POST"])
def send_message():
global thread_id # 전역 변수 thread_id를 사용
try:
# 클라이언트가 보낸 JSON 데이터를 파싱
data = request.get_json()
# JSON에서 'message' 키의 값을 가져옴 (사용자 입력 메시지)
message = data.get('message', '')
# 메시지가 비어있으면 에러를 반환
if not message:
return jsonify({"error": "메시지가 없습니다."}), 400
# 터미널에 받은 메시지를 출력 (디버깅용)
print(f"받은 메시지: {message}")
# 스레드가 없으면 새로 생성 (첫 번째 메시지인 경우)
if thread_id is None:
# 새로운 대화 스레드를 생성
thread = client.beta.threads.create()
# 생성된 스레드의 ID를 저장
thread_id = thread.id
print(f"새 스레드 생성: {thread_id}")
# 사용자 메시지를 스레드에 추가
client.beta.threads.messages.create(
thread_id=thread_id, # 대화 스레드 ID
role="user", # 메시지 역할 (사용자)
content=message # 메시지 내용
)
# Assistant를 실행하여 응답을 생성
run = client.beta.threads.runs.create(
thread_id=thread_id, # 대화 스레드 ID
assistant_id=ASSISTANT_ID # OpenAI 대시보드에서 생성한 Assistant ID
)
# Assistant가 응답을 완료할 때까지 기다림
while True:
# 현재 실행 상태를 확인
run_status = client.beta.threads.runs.retrieve(
thread_id=thread_id,
run_id=run.id
)
# 실행이 완료되면 루프를 종료
if run_status.status == "completed":
break
# 실행이 실패하면 에러를 반환
elif run_status.status == "failed":
return jsonify({"error": "Assistant 실행 실패"}), 500
# Assistant의 응답 메시지를 가져옴
messages = client.beta.threads.messages.list(thread_id=thread_id)
# 가장 최근 메시지(Assistant의 응답)를 가져옴
assistant_message = messages.data[0]
# 응답 내용을 추출
ai_response = assistant_message.content[0].text.value
# 클라이언트에게 성공 응답을 JSON 형태로 반환
return jsonify({
"success": True,
"message": cleaned_response,
"original_message": message
})
except Exception as e:
# 에러가 발생하면 터미널에 에러 메시지를 출력
print(f"에러 발생: {str(e)}")
# 클라이언트에게 에러 응답을 반환
return jsonify({"error": f"서버 에러: {str(e)}"}), 500
실무적으로 어떻게 쓰일까요?
- Assistant 생성
assistant = client.beta.assistants.create(
name="data_science_tutor",
instructions="This assistant is a data science tutor.",
tools=[{"type": "retrieval"}, {"type": "code_interpreter"}],
model="gpt-4-1106-preview",
file_ids=[...]
)
- Thread 생성
thread = client.beta.threads.create()
사용자 메시지 추가
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="ELI5: What is neural network?"
)
- Thread 실행(run)
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id
)
- 답변 메시지 받아오기
run이 완료되었는지 주기적으로 polling하고, 완료되면 Thread의 메시지 목록을 조회

Thread
🔑 핵심 개념
OpenAI Assistants API는 대화를 Thread 단위로 묶습니다.
“대화 회수(컨텍스트 유지)” 기능을 넣으려면 같은 Thread ID를 재사용해야 합니다.
🛠️ 회수(대화 이어가기) 기능 추가 방법
- Thread를 한 번만 생성하고 저장
Flask 서버 실행 시 또는 첫 요청 시 thread_id를 생성하고,
이후 요청부터는 같은 thread_id를 사용하도록 관리해야 합니다.
Flask 전역 변수에 저장 (예시)
THREAD_ID = None
@app.route("/chat", methods=["POST"])
def chat():
global THREAD_ID
user_message = request.json.get("message")
# 최초 1회만 Thread 생성
if THREAD_ID is None:
thread = client.beta.threads.create()
THREAD_ID = thread.id
# Thread 재사용
client.beta.threads.messages.create(
thread_id=THREAD_ID,
role="user",
content=user_message
)
run = client.beta.threads.runs.create(
thread_id=THREAD_ID,
assistant_id=ASSISTANT_ID
)
...
- React 쪽에서 “대화 세션 유지” 기능 넣기
React에서 새로고침하면 기존 대화 삭제됨
thread_id를 백엔드에서 세션 쿠키나 DB에 저장
또는 프론트 localStorage에 thread_id 보관
→ “대화 회수”가 가능합니다.
- 구현하면서 고려할 점
대화 초기화 버튼 → 새로운 thread를 만들어 처음부터 대화 시작하기
대화 내역 불러오기 → /messages API 호출해서 지금까지 대화 보여주기
메타데이터 저장 → 사용자가 누구인지, 어떤 문맥인지 thread metadata에 기록할 수 있음
✅ 정리
지금 상태에선 “매번 새로운 Thread 생성”만 있어서 회수 기능이 없음.
Thread ID를 저장 & 재사용하도록 Flask API를 고쳐야 함.
즉, client.beta.threads.create()를 매번 하지 않고, 한 번만 생성해서 계속 같은 thread_id 사용.
React에서는 이 thread_id를 쿠키/세션/localStorage로 보관해서 API 호출할 때 같이 보내야 함.
Assistant API
포트폴리오 프로젝트 구성
포폴 프로젝트/
│
├─ backend/ # Flask 서버
│ ├─ app.py # 챗봇 API
│ └─ .env # OPENAI_API_KEY, ASSISTANT_ID
│
├─ frontend/ # React/Next.js 프론트
│ └─ Chat.jsx # 챗 UI
│
├─ resume.pdf # 이력서/자소서 PDF
└─ package.json, requirements.txt
1️⃣ Flask 백엔드 (backend/app.py)
from flask import Flask, request, jsonify
from flask_cors import CORS
import os
from dotenv import load_dotenv
from openai import OpenAI
import time
import { v4 as uuidv4 } from "uuid"; # uuid 선언
# 환경 변수 로드
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
ASSISTANT_ID = os.getenv("ASSISTANT_ID") # 이력서 기반 Assistant ID
app = Flask(__name__)
CORS(app)
client = OpenAI(api_key=api_key)
# 전역 Thread ID (대화 회수용)
THREAD_ID = None
@app.route('/sendMessage', methods=['POST'])
def send_message():
global THREAD_ID
data = request.json
username = data.get("username")
message = data.get("message")
thread_id_from_front = data.get("thread_id")
# 기존 Thread 사용 또는 새 Thread 생성
if thread_id_from_front:
THREAD_ID = thread_id_from_front
elif THREAD_ID is None:
thread = client.beta.threads.create()
THREAD_ID = thread.id
# 사용자 메시지 추가
client.beta.threads.messages.create(
thread_id=THREAD_ID,
role="user",
content=message
)
# Assistant 실행
run = client.beta.threads.runs.create(
thread_id=THREAD_ID,
assistant_id=ASSISTANT_ID
)
# 답변 완료될 때까지 Polling
while True:
status = client.beta.threads.runs.retrieve(thread_id=THREAD_ID, run_id=run.id)
if status.status == "completed":
break
time.sleep(1)
# 마지막 Assistant 메시지 추출
messages = client.beta.threads.messages.list(thread_id=THREAD_ID)
bot_reply = ""
for msg in reversed(messages.data):
if msg.role == "assistant":
bot_reply = msg.content[0].text.value
break
return jsonify({
"username": username,
"user_message": message,
"bot_reply": bot_reply,
"thread_id": THREAD_ID
}), 200
if __name__ == '__main__':
app.run(debug=True)
2️⃣ React 프론트 (frontend/Chat.jsx)
const [threadId, setThreadId] = useState(null);
// sendMessage 수정
const res = await fetch("http://localhost:5000/sendMessage", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "Ju-hyeok", message: text, thread_id: threadId }),
});
const data = await res.json();
setThreadId(data.thread_id);
setMessages((m) => [
...m,
{ id: crypto.randomUUID(), user: "bot", name: "J", text: data.bot_reply, time: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) }
]);
브라우조 오류로 uuidv4로 변경
- v4는 랜덤 기반 UUID 생성 방식
- crypto.randomUUID()와 비슷한 역할이지만,
- 구버전 브라우저에서도 안전하게 사용 가능
import { v4 as uuidv4 } from "uuid";
const myMsg = {
id: uuidv4(),
user: "me",
name: "우주혁",
text,
time: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
};
SSL 인증서 오류 발생
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain
- openai 클라이언트가 서버에 연결 시
- 자체 서명(self-signed) 인증서 포함한 SSL을 신뢰하지 못해서 연결 실패
- Python에서는 기본적으로 공인 인증서만 신뢰
'알토르' 카테고리의 다른 글
| Assistant + Retrieval 기반으로 바꾸기 - code (0) | 2026.05.14 |
|---|---|
| 알토르 3주차 - Browser Storage (0) | 2026.05.13 |
| 알토르 3주차 OpenAI API 연동 (0) | 2026.05.11 |
| 알토르 3주차 - Fetch / Axios (0) | 2026.05.10 |
| 알토르 3주차 API Endpoint (0) | 2026.05.10 |