먼저 해당 프로젝트는 취미로 시작한 AI와 함께하는 우주만들기 및 이야기 생성 (https://story.edamyrepo.com)과 관련있습니다.
최근 ChatGPT를 활용해서 여러가지 분석용 도구 (코드들) 생성에 도움을 받고 있다보니, 자연스럽게 취미생활로 생각이 확장되기 시작했습니다. 작년부터 이를 활용했는데, 실제로 생성된 코드들이 원하는 목표를 달성하기를 기대하기는 힘들어도, 그 코드들을 템플릿으로 삼아서 제가 원하는 형태로 수정한다음에 물리적인 부분을 재확인하는 형태로 ChatGPT와 함께 일을 하는 편입니다. 무엇보다도 대화형태의 프롬프트를 통해 스스로의 논리를 재확인하는 효과가 사실 크지요.
그런데 이를 취미활동으로 확장하여, 일종의 simulated world생성에 사용해볼까 하는 생각이 들어 오늘 OpenAI쪽 서비스를 알아보니 이러한 API작용은 Platform서비스를 이용하여야 하고 쿼리당 추가 비용이 드는 형태라는것을 알게 되었습니다. 사실, 가능하다면, Python구문을 이용해 AWS Lightsail서버에서 워드프레스쪽으로 바로 포스팅되는 부분을 만들까 싶었습니다. 일정시간마다 업데이트가 발생하면 실시간 효과도 반영되어서 뭔가 실시간으로 세상이 업데이트 되는 느낌이 되지 않을까 생각이 들었습니다만, 일단 비용의 문제로 이러한 부분은 넘어가기로 결정했습니다.
조금 조사를 해보니, 노트북 수준에서 큰 모델을 돌리긴 힘들어도 Ollama를 통해 Llama나 Mixtral (혹은 Mistral)모델을 돌릴 수 있겠더군요. 처음은 Llama.cpp를 설치해서 하려고 했는데, 이후 Ollama가 훨씬 간편한것을 보고 homebrew를 통해 설치하고 기본적인 설정을 진행해 보았습니다. 일반적인 설치 및 사용은 다음과 같습니다.
>> brew install ollama
>> python -m pip install ollama-cpp-python
>> ollama pull mixtral
>> ollama serve
이 상태에서 Python을 통해서 다음과 같은 이야기가 있는 세계를 만들기 위해 노력합니다. 세계에 대한 기본적인 설정은 다음 페이지 를 참조해주세요 ( https://story.edamyrepo.com/유사우주-프로젝트-설정집/ ) 여기 지식공방에서는 환경설정과 이를 활용한 기술적인 부분에 대해서 업데이트를 할 예정이고, 해당 세상에 대한 이야기는 새롭게 만든 서브블로그를 활용해서 업데이트 할 예정입니다.
ChatGPT의 도움을 통해 만들어놓은 기본적인 문명생성에 대한 코드는 다음과 같습니다. 처음 시도하는 부분이기 때문에, 차츰 차츰 코드를 더 다듬어 갈 예정입니다. 아주 간단하게 사용하는 방법은 다음과 같습니다.
>> ollama run mistral "$(cat settings.txt) $(cat settings_name.txt). 세 가지 문명의 발전을 만들고 싶습니다. 각 문명의 발전에 대해서 생성해보세요. “
(만약 대화문이 영문으로 생성된다면, 한글로 번역해달라고 요청하면 됩니다. )
ChatGPT의 도움을 받아 Python으로 작성한 코드는 다음과 같습니다. 세계관의 각종 설정들은 유사우주 프로젝트 페이지를 참조하시길 바랍니다.
import json
import requests
import time
import random
import os
# 설정 파일 불러오기
def load_text(filename):
"""파일에서 텍스트 데이터를 불러오는 함수"""
try:
with open(filename, "r", encoding="utf-8") as file:
return file.read().strip()
except FileNotFoundError:
return ""
# 기본 설정 로드
universe_settings = load_text("settings.txt")
civilization_names = load_text("settings_name.txt")
base_civilization_settings = load_text("settings_base_civilization.txt")
other_civilization_settings = load_text("setting_other_civilizations.txt")
# 랜덤 문명 이름 생성 (알파벳 + 한글 병기)
def generate_civilization_name():
prefixes = ["Zor", "Xan", "Tal", "Vex", "Kri", "Ulu", "Mor", "Yen", "Phi"]
suffixes = ["dor", "kar", "thar", "ion", "zar", "mir", "lan", "quo"]
english_name = random.choice(prefixes) + random.choice(suffixes)
korean_phonetics = {
"Zor": "조르", "Xan": "잔", "Tal": "탈", "Vex": "벡스", "Kri": "크리",
"Ulu": "울루", "Mor": "모르", "Yen": "옌", "Phi": "피",
"dor": "도르", "kar": "카르", "thar": "타르", "ion": "이온",
"zar": "자르", "mir": "미르", "lan": "란", "quo": "쿼"
}
korean_name = korean_phonetics.get(english_name[:3], "") + korean_phonetics.get(english_name[3:], "")
return f"{english_name} ({korean_name})"
# JSON 저장 함수
def save_to_json(data, filename):
if os.path.exists(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
existing_data = json.load(f)
except json.JSONDecodeError:
existing_data = []
else:
existing_data = []
existing_data.append(data)
with open(filename, "w", encoding="utf-8") as f:
json.dump(existing_data, f, indent=4, ensure_ascii=False)
# Ollama API 호출 함수 (Mistral 사용, 한 글자씩 나오는 문제 해결)
def generate_civilization(planet, index):
civilization_name = generate_civilization_name()
prompt = f"""
행성 이름: {planet["name"]}
환경: {planet["star_type"]}, {planet["atmosphere"]}, {planet["gravity"]}, {planet["temperature"]}, {planet["biosphere"]}
문명 #{index}의 정보를 한국어로 생성하되 명확히 하기 위해 괄호로 영문도 함께 표기해주세요.
- 문명 이름: {civilization_name}
{other_civilization_settings}
**반드시 다음 사항을 지켜주세요:**
- 모든 정보는 새로운 세계관에서 독창적으로 생성되어야 합니다. 저작권에 대해 문제가 있을 부분들은 철저히 검사할 필요가 있습니다.
- 모든 내용은 한글로 작성하되, 명사 및 어려운 단어 혹은 동의어로 혼동되기 쉽다면 괄호로 영문표기를 병용해주세요. 즉 영어(English)처럼 기술해주세요. 현실에서 정치 및 종교적으로 중요한 단어들을 이름으로 표기하지 말아 주세요.
"""
response = requests.post(
"http://localhost:11434/api/chat",
json={"model": "mistral", "messages": [{"role": "user", "content": prompt}], "stream": True},
timeout=300
)
response.raise_for_status()
# 스트리밍 응답을 한 문장으로 조립
response_text = ""
for line in response.iter_lines():
if line:
try:
json_data = json.loads(line.decode("utf-8"))
if "message" in json_data and "content" in json_data["message"]:
response_text += json_data["message"]["content"]
except json.JSONDecodeError:
continue
# 로그
print(f"\n문명 #{index} API 응답\n{response_text}\n")
# JSON 파일에 저장
save_to_json({"id": index, "planet": planet["name"], "name": civilization_name, "description": response_text.strip()}, "universe_data.json")
# save_to_json({"id": index, "planet": planet["name"], "name": civilization_name, "description": korean_text.strip()}, "universe_data_kr.json")
# 실행
if __name__ == "__main__":
universe = {"planets": []}
num_planets = random.randint(8, 12)
for i in range(num_planets):
planet = {
"name": f"행성 {i+1}",
"star_type": random.choice(["적색왜성", "백색왜성", "황색주계열성"]),
"atmosphere": random.choice(["산소-질소", "이산화탄소", "메탄"]),
"gravity": random.choice(["낮음", "표준", "높음"]),
"temperature": random.choice(["한랭", "온화", "고온"]),
"biosphere": random.choice(["무생명", "단세포 생명", "다세포 생명"])
}
num_civilizations = random.randint(1, 15)
planet["civilizations"] = [
generate_civilization(planet, j + 1) for j in range(num_civilizations)
]
universe["planets"].append(planet)
save_to_json(universe, "universe_data.json")
print("✅ 모든 행성과 문명 생성 완료. 데이터가 개별 JSON 파일에 저장되었습니다.")
다만 저작권과 관련된 자료를 배제해달라는 요청에도 불구하고, 이를 명시적으로 지키지 않는 형태가 제법 있습니다. 예로들어 내용은 다른데 이름을 모르도르나 사우론이라고 넣은 부분이 있습니다. 이는, 생각보다 결정적인 문제로서, 일종의 Simulated World를 생각하면서 실시간으로 업데이트되는 웹블로그 형태의 세계관을 만드는데 완전한 자동화를 하기는 힘들다는 생각이 드네요. 물론 걸음마 단계의 지금으로서는, 사람의 개입이 필수적으로 이루어지기 마련이므로, 그 속에서 필요한 경우 특별한 내용을 배제하는 형태로 해야 된다고 생각합니다. 문제는 제가 알지못하는 방대한 작품들을 어떻게 체계적으로 확인할수 있는가에 대한 부분이고, 현재는 ChatGPT를 활용해서 저작권에 문제가 있을만한 부분을 따로 확인중에 있습니다.
Leave a Reply