| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 |
- AI
- LNP
- 디카
- 만들기
- GROMACS
- Packmol
- 라즈베리파이
- HTML
- Avogadro
- MD
- 깃허브
- 원격 저장소
- 디지털 카메라
- ssh
- CSS
- openai
- mdvwhole
- LLM
- AABBQuery
- 깃
- Maker
- 깃(Git)
- 자바스크립트
- 깃허브(github)
- 초기화
- MDAnalysis
- Agent
- API
- Freud
- 카메라
- Today
- Total
청관재(淸觀齋)
일지 #6 LLM API를 사용하여 백설공주 계모 챗봇 만들기 본문
이제 챗봇을 만들어본다.
책 예제에서는 '어린왕자' 페르소나를 부여해서 챗봇을 만드는데 나는 백설공주에게 독사과를 주는 '계모' 페르소나를 부여해 볼 생각이다. (김수인 아이디어;; ㅋㅋ)
이번 장에서는 OpenAI의 모델과 API만을 사용한다고 한다.
먼저, CLI를 사용한 챗봇 만들기이다.
# 1. CLI를 이용한 챗봇 만들기
다음과 같이 코드를 작성해준다.
from openai import OpenAI
client = OpenAI()
def chatbot_response(user_message: str):
#1 OpenAI의 gpt-5-mini 모델을 사용하여 응답 생성
result = client.responses.create(model="gpt-5-mini", input=user_message)
return result
if __name__ == "__main__":
#2 사용자 메시지를 입력받고 응답을 출력한다.
while True:
#3 사용자에게 메시지 입력받기
user_message = input("메시지: ")
#4 'exit' 입력 시 대화 종료
if user_message.lower() == "exit":
print("대화를 종료합니다.")
break
#5 챗봇 응답받아오기
result = chatbot_response(user_message)
print("챗봇 :" + result.output_text)
- OpenAI의 Response API를 사용하여 사용자의 요청에 답하는 코드이다.
- 챗봇이므로 사용자의 입력이 주어지면 계속해서 답을 한다. (while True 조건 사용)
- input() 메서드를 사용하면 터미널에서 사용자의 입력을 받도록 대기할 수 있다.
- while True로 무한루프를 돌고 있는데, 입력으로 exit이 오면 종료한다. Ctrl + C 로도 종료 가능하지만, 이때는 KeyboardInterrupt 에러가 발생한다. 하지만 이 에러는 try ~ except 구문으로 해결 가능하다.
어디 한 번 실행을 해보도록 하겠다.
실행 결과는 아래와 같다.

실행 결과를 보면 맨 처음에 이름을 알려줬음에도 불구하고 다음 턴에서 이름을 기억하지 못하는 것을 알 수 있다.
이를 해결하기 위해 리스트로 이전 대화를 전달하는 방식으로 코드를 작성하면 된다.
다음으로는 이전 응답에 이어서 대화를 할 수 있도록 챗봇을 수정해보겠다.
# 2. 챗봇이 이전 대화를 기억할 수 있게 하기
AI가 이전에 했던 대화를 기억하게 하는 것은 이미 이전에 구현해보았다.
바로 아래 포스트에서 확인할 수 있다.
2026.10.22 - [무한도전!!/Agent-ai 마스터하기!!] - 일지 #4 Anthropic API 사용법 / 스트리밍(streaming) 처리
일지 #4 Anthropic API 사용법 / 스트리밍(streaming) 처리
클로드는 매우 자연스러운 한국어 대화를 생성한다.클로드를 사용하려면 anthropic 패키지를 설치해야 한다. pip install anthropic==0.49.0 위 명령으로 의존성 패키지를 설치해준다.(꼭 .venv 환경이 활성
www.cheonggwan.com
아무튼 리스트로 이전 대화의 내용을 LLM에 함게 전달해서 이전 대화를 기억할 수 있도록 한다.
기본적으로 LLM API에서 대화 이력은 이전 대화 내용을 함께 전달하는 방식으로 동작한다.
OpenAI에서는 이를 조금 더 편하게 사용할 수 있는 기능, '대화 상태 관리(conversation state)' 가 들어있고 이는 대화 상태를 자동으로 관리해주는 기능이다.
API 사용 시에 이전 응답의 id를 넣어주면 OpenAI에서 알아서 대화 이력을 추적하여 LLM에 넣어주는 방식이다.
이제 코드를 수정해 보겠다.
from openai import OpenAI
client = OpenAI()
#1 previous_response_id 파라미터 추가
def chatbot_response(user_message: str, previous_response_id=None):
# OpenAI의 gpt-5-mini 모델을 사용하여 응답 생성
result = client.responses.create(
#2 previous_response_id 파라미터에 이전 대화의 id 값을 넣어준다.
model="gpt-5-mini", input=user_message, previous_response_id=previous_response_id
)
return result
if __name__ == "__main__":
previous_response_id = None
# 사용자 메시지를 입력받고 응답을 출력한다.
while True:
# 사용자에게 메시지 입력받기
user_message = input("메시지: ")
# 'exit' 입력 시 대화 종료
if user_message.lower() == "exit":
print("대화를 종료합니다.")
break
#3 이전 대화의 id 값을 추가로 넘겨준다. / 챗봇 응답 받아오기
result = chatbot_response(user_message, previous_response_id)
#4 이전 대화의 id를 response_id에 할당
previous_response_id = result.id
print("챗봇 :" + result.output_text)
- chatbot_response() 함수에 previous_response_id 파라미터가 추가되었다. 이전 대화의 id 값을 넘겨주면 된다. 이전 대화가 없을 때는 None을 넣으면 된다.
- response.create() 함수에는 previous_response_id에 이전 대화의 id 값을 넘겨준다.
- chatbot_response() 를 사용하는 곳에서 previous_response_id를 추가한다. 첫 대화에는 None이 들어간다.
- 대화가 끝난 후 result.id에 대화의 id 값이 들어있다. 해당 값을 previous_response_id에 할당한다.
대화 상태를 사용하는 Response 객체는 기본적으로 30일 동안 보관된다.
즉, OpenAI의 대화 상태 관리 기능을 사용하더라도 30일이 지난 대화의 기록은 기억할 수 없다.
이전 대화를 배열로 넘기지 않고 previous_response_id 만 넘기기 때문에 토큰이 절약된다고 생각할 수 있지만 그렇지 않다.
previous_response_id를 사용하여 OpenAI 쪽에서 알아서 대화 이력을 넣어주는 형태이므로 이전 대화에서 사용되었던 내용이 입력 토큰으로 사용된다.
이제 실행해보도록 하겠다.

API를 사용한 대화 내용은 아래 URL에서 확인할 수 있다.
Completion API의 경우, 로그를 먼저 활성화해야 한다.
https://platform.openai.com/logs?api=responses
OpenAI Platform
platform.openai.com

Responses API는 이렇게 직접 사용할 수도 있고, Agent SDK를 사용하여 간접적으로 호출할 수도 있다.
Agent SDK는 기본적으로 Responses API를 사용한다.
# 3. 백설공주 계모 페르소나 추가하기
LLM API는 코딩도 중요하지만 프롬프트를 잘 작성하는 것 또한 매우 중요하다.
교재에서는 어린왕자 페르소나를 부여했지만 나는 기존 챗봇에 프롬프트를 추가하여 백설공주에게 독사과를 먹인 계모처럼 응답하는 챗봇을 만들어 볼 것이다.
아래처럼 코드를 작성해주었다.
from openai import OpenAI
client = OpenAI()
#1 백설공주 계모 페르소나 프롬프트
SNOWWHITE_STEPMOTHER_PERSONA = """
당신은 백설공주에게 독사과를 먹인 '계모(왕비)'입니다. 다음 특성을 따라주세요:
1. 항상 이 세상에서 가장 예쁜 사람이 될지 궁리하는 관점으로 세상을 바라봅니다.
2. "어떻게 하면 백설공주를 없애고 내가 제일 예쁜 사람이 될 수 있지?"라는 질문을 자주 하며 욕심이 많습니다.
3. 온갖 방법을 사용해서라도 목적을 달성하려 합니다.
4. 요술거울에게 "거울아, 거울아. 누가 제일 예쁘니?"라는 질문을 가끔 합니다.
5. 이웃나라에서 왔으며 아름다운 미모로 백설공주의 아빠(왕)와 결혼에 성공하였습니다.
6. 자신의 "아름다움"과 "권력"에 대한 교훈을 중요시합니다.
7. "벌써 조금 늙어버린 것 같아!"라는 걱정을 자주한다.
8. 거만하고 오만한 말투를 사용합니다.
9. 직설적으로 백설공주를 처치할 방법을 사냥꾼과 의논합니다.
항상 간결하게 답변하세요. 길어야 두세 분장으로 응답하고, 계모(왕비)의 악독함과 욕심을 담아내세요.
어떠한 주제도 자신이 제일 아름다운 사람이 되기 위한 방법으로 설명하세요.
"""
def chatbot_response(user_message: str, previous_response_id=None):
result = client.responses.create(
model="gpt-5-mini",
reasoning={"effort": "low"}, # low, medium, high
#2 instruction에 백설공주 계모 페르소나 프롬프트 입력
instructions=SNOWWHITE_STEPMOTHER_PERSONA,
input=user_message,
previous_response_id=previous_response_id
)
return result
if __name__ == "__main__":
# 여기서 사용자 메시지를 입력받고 응답을 출력한다.
previous_response_id = None
while True:
user_message = input("메시지: ")
if user_message.lower() == "exit":
print("대화를 종료합니다.")
break
result = chatbot_response(user_message, previous_response_id)
previous_response_id = result.id
print("백설공주 계모 : ", result.output_text)
- LLM에게 백설공주 계모처럼 응답하도록 페르소나를 입히는 데 사용할 프롬프트를 작성한다. 실시간으로 프롬프트에 참고 데이터를 추가하거나 참고 데이터가 어디 있는지 알려주는 기법을 검색 증강 생성(Retrieval Augmented Generation) 줄여서 RAG라고 부른다.
- AI의 말투 지정, 즉 페르소나 프롬프트는 보통 시스템 프롬프트에 넣는다. Response API에는 시스템 프롬프트를 추가하는 부분이 없는데, instructions가 그 역할을 한다.
instructions에 페르소나 프롬프트를 입력하면 AI의 말투가 변경된다. user_message 위에 붙여서 넣어도 되지만, 그렇게 되면 페르소나 프롬프트가 매번 들어가 토큰 낭비가 생긴다.
gpt5 부터는 reasoning 파라미터로 추론을 사용할 수 있다. 추론을 어느 정도 사용할지는 effort 키에 값을 넣으면 되며 low, medium, high 3종류를 지원한다.
이제 코드를 실행해서 테스트를 진행해보자.

ㅋㅋ 페르소나가 아주 잘 반영이 된 것 같다.
역시 safety 때문에 백설공주를 제거하는 방법은 알려주지 않는듯하다.
# 4. 웹 인터페이스 추가하기
마지막으로 웹 인터페이스를 추가하겠다.
챗봇은 보통 터미널이 아니라 웹 인터페이스를 많이 사용한다.
FastAPI를 사용하면 웹 인터페이스를 손쉽게 만들 수 있다.
파이썬 생태계에서는 Flask가 꽤 오랫동안 웹 프레임워크로 사용되어 왔고, 지금도 많이 사용하지만 FlaskAPI를 사용하는 것이 성능적으로나 파이썬의 최신 기능을 더 잘 사용할 수 있는 등의 이득이 많다.
먼저 터미널에서 파이썬 가상 환경을 활성화한 뒤, 아래 명령어를 사용하여 FastAPI를 설치해준다.
pip install fastapi==0.115.14 uvicorn==0.35.0
이제 코드를 작성해준다.
from openai import OpenAI
from fastapi import FastAPI, Form
from fastapi.responses import HTMLResponse
import uvicorn
# 1 FastAPI 애플리케이션 인스턴스를 생성
app = FastAPI()
client = OpenAI()
# 백설공주 계모 페르소나
SNOWWHITE_STEPMOTHER_PERSONA = """
당신은 백설공주에게 독사과를 먹인 '계모(왕비)'입니다. 다음 특성을 따라주세요:
1. 항상 이 세상에서 가장 예쁜 사람이 될지 궁리하는 관점으로 세상을 바라봅니다.
2. "어떻게 하면 백설공주를 없애고 내가 제일 예쁜 사람이 될 수 있지?"라는 질문을 자주 하며 욕심이 많습니다.
3. 온갖 방법을 사용해서라도 목적을 달성하려 합니다.
4. 요술거울에게 "거울아, 거울아. 누가 제일 예쁘니?"라는 질문을 가끔 합니다.
5. 이웃나라에서 왔으며 아름다운 미모로 백설공주의 아빠(왕)와 결혼에 성공하였습니다.
6. 자신의 "아름다움"과 "권력"에 대한 교훈을 중요시합니다.
7. "벌써 조금 늙어버린 것 같아!"라는 걱정을 자주한다.
8. 거만하고 오만한 말투를 사용합니다.
9. 직설적으로 백설공주를 처치할 방법을 사냥꾼과 의논합니다.
항상 간결하게 답변하세요. 길어야 두세 분장으로 응답하고, 계모(왕비)의 악독함과 욕심을 담아내세요.
어떠한 주제도 자신이 제일 아름다운 사람이 되기 위한 방법으로 설명하세요.
"""
# 2 사용자와 백설공주 계모의 대화 내용을 저장할 리스트
messages = []
previous_response_id = None
def chatbot_response(user_message: str, previous_response_id=None):
result = client.responses.create(
model="gpt-5-mini",
reasoning={"effort": "low"}, # low, medium, high
#2 instruction에 백설공주 계모 페르소나 프롬프트 입력
instructions=SNOWWHITE_STEPMOTHER_PERSONA,
input=user_message,
previous_response_id=previous_response_id
)
return result
# 3 루트 엔드포인트 - 챗봇 UI를 렌더링
@app.get("/", response_class=HTMLResponse)
async def read_root():
chat_history = ""
# 4 대화 기록을 역할에 따라 구분해 HTML 문자열을 구성
for msg in messages:
if msg["role"] == "user":
chat_history += f"<p><b>당신:</b> {msg['content']}</p>"
else:
chat_history += f"<p><b>백설공주 계모:</b> {msg['content']}</p>"
# 5 간단한 HTML 템플릿을 반환
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>백설공주 계모 챗봇</title>
<meta charset="utf-8">
</head>
<body>
<h1>백설공주 계모 챗봇</h1>
<div>
{chat_history}
</div>
<form action="/chat" method="post">
<input type="text" name="message" placeholder="메시지를 입력하세요..." required>
<button type="submit">전송</button>
</form>
</body>
</html>
"""
return HTMLResponse(content=html_content)
# 6 /chat 엔트포인트 - 사용자 입력을 처리
@app.post("/chat", response_class=HTMLResponse)
async def chat(message: str = Form(...)):
global previous_response_id, messages
# 사용자 메시지 저장
messages.append({"role": "user", "content": message})
result = chatbot_response(message, previous_response_id)
previous_response_id = result.id
# 응답 저장
messages.append({"role": "snowwhite_stepmother", "content": result.output_text})
# 7 최신 대화가 반영된 페이지를 다시 표시
return await read_root()
# 8 애플리케이션을 uvicorn을 사용하여 실행
if __name__ == "__main__":
uvicorn.run(
"chatbot_snowwhite_stepmother_web_ui:app", host="127.0.0.1", port=8000, reload=True
)
- FastAPI 웹 애플리케이션의 기본 인스턴스를 생성한다.
- 사용자와 챗봇 간의 대화 내용을 저장하기 위한 전역 변수이다. 각 메시지는 역할과 내용을 포함한 딕셔너리 형태로 저장된다.
- 웹 애플리케이션의 메인 페이지(/)에 접속했을 때 챗봇 인터페이스를 HTML 형태로 제공하는 API 엔드포인트이다.
- 저장된 메시지 리스트를 순회하면서 사용자('user')와 백설공주 계모('snowwhite-stepmother')의 메시지를 구분하여 HTML 형식으로 변환한다.
- 대화 기록과 사용자 입력 폼을 포함한 HTML 페이지를 생성하여 클라이언트에게 반환한다.
- 사용자가 메시지를 입력하고 전송 버튼을 클릭했을 때 호출되는 엔드포인트로, 사용자 입력을 처리하고 챗봇의 응답을 생성한다. async 구문이 앞에 붙어 있느느 함수는 비동기 호출을 지원하는 함수다. previous_response_id, messages 변수에는 global 키워드가 붙어 있다. 전역 변수의 값을 변경하는 경우 global 키워드가 필요하다.
- read_root() 함수의 결괏값이며 해당 값은 최신 대화 내용을 포함한 페이지이다. 해당 페이지를 다시 렌더링하여 반환한다.
- 웹 서버 uvicorn을 사용하여 FastAPI 애플리케이션을 로컬 호스트(127.0.0.1)의 8000번 포트에서 실행하며, 코드 변경 시 자동으로 재시작(reload=True) 되도록 설정한다. FastAPI를 기동하려면 uvicorn 같은 ASGI(Asynchronous Server Gateway Interface) 서버 구현체가 반드시 필요하다.
실행을 해봤더니 실행이 안된다...;;;
아래 명령으로 설치하라길래 해준다.
pip install python-multipart
이제 다시 돌려보겠다....

예~!!
이제 링크에 접속해준다.

이렇게 홈페이지가 뜬다!!
이제 홈페이지에 메시지를 입력해 봐야겠다.

ㅎㅎ 신기하다. 이제 대화를 더 해봐야겠다.

이렇게 도덕적 관념이 뛰어난 백설공주 계모 챗봇을 만들어 보았다~~
오늘은 여기까지!!
'무한도전!! > Agent-ai 마스터하기!!' 카테고리의 다른 글
| 일지 #5 비동기 처리 및 오류 핸들링 (async / await / tenacity) (0) | 2026.10.26 |
|---|---|
| 일지 #4 Anthropic API 사용법 / 스트리밍(streaming) 처리 (0) | 2026.10.22 |
| 일지 #3 OpenAI API 사용법 (챗 컴플리션 API / 리스폰스 API) (0) | 2026.10.19 |
| 일지 #2 깃 환경 초기화 / 로컬 저장소와 원격 저장소(GitHub) 연결하기 / LLM과 랭체인(LangChain) 개발 (0) | 2026.10.18 |
| 일지 #1 깃/깃허브로 문서 관리하기 (0) | 2026.10.13 |
