Ver3.0 ๐ค Gemini API๋ก ์๋์์ฝยท์๋๋ถ๋ฅ ์์ด์ ํธ ๊ตฌ์ถํ๊ธฐ

๐ค Gemini API๋ก ์๋์์ฝยท์๋๋ถ๋ฅ ์์ด์ ํธ ๊ตฌ์ถํ๊ธฐ
AI๊ฐ ์์์ ์ ๋ฆฌํ๊ณ ๋ถ๋ฅํด์ฃผ๋ ๋๋ํ ๋น์ ๋ง๋ค๊ธฐ ๐
๐ฏ ์ ์ง๊ธ Gemini API์ธ๊ฐ?
์๋
! ์ค๋์ ์ ๋ง ์ค์ฉ์ ์ด๋ฉด์๋ ์ฌ๋ฏธ์๋ ์ฃผ์ ๋ฅผ ๊ฐ์ง๊ณ ์์ด. ๋ฐ๋ก Gemini API๋ฅผ ํ์ฉํ ์๋์์ฝยท์๋๋ถ๋ฅ ์์ด์ ํธ ๊ตฌ์ถ์ด์ผ. ๐
์์ฆ ์ ๋ณด์ ํ์ ์์์ ์ด๊ณ ์์์? ๋งค์ผ ์์์ง๋ ๋ด์ค ๊ธฐ์ฌ, ์
๋ฌด ๋ฌธ์, ๊ณ ๊ฐ ๋ฌธ์, SNS ํผ๋... ์ด ๋ชจ๋ ๊ฑธ ์ผ์ผ์ด ์ฝ๊ณ ์ ๋ฆฌํ๊ณ ๋ถ๋ฅํ๋ ๊ฑด ์ ๋ง ์๊ฐ ๋ญ๋น์ผ. ๊ทธ๋์ ๋ฑ์ฅํ ๊ฒ ๋ฐ๋ก AI ์์ด์ ํธ์ธ๋ฐ, ํนํ ๊ตฌ๊ธ์ Gemini API๋ 2024๋
๋ค์ด์ ์์ฒญ๋ ๋ฐ์ ์ ์ด๋ค์ด.
Gemini API์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ ๋ญ๊น? ๋ฐ๋ก ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ ๋ฅ๋ ฅ์ด์ผ. ํ
์คํธ๋ ๋ฌผ๋ก ์ด๊ณ ์ด๋ฏธ์ง, ๋์์, ์ค๋์ค๊น์ง ํ ๋ฒ์ ์ฒ๋ฆฌํ ์ ์๋ค๋ ๊ฑฐ์ง. ๊ฒ๋ค๊ฐ OpenAI์ GPT ์๋ฆฌ์ฆ์ ๋นํด ๊ฐ๊ฒฉ๋ ํฉ๋ฆฌ์ ์ด๊ณ , ๊ตฌ๊ธ ์ํ๊ณ์์ ํตํฉ๋ ๋งค๋๋ฌ์. ๐จ
๐ง Gemini API ์์ํ๊ธฐ: ๊ธฐ๋ณธ ์ค์
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํด๋ณผ๊น? ๋จผ์ Gemini API๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ๊ธฐ๋ณธ ์ค์ ๋ถํฐ ์์๋ณด์. ์๊ฐ๋ณด๋ค ๊ฐ๋จํ๋๊น ๊ฑฑ์ ํ์ง ๋ง! ๐
1๋จ๊ณ: API ํค ๋ฐ๊ธ๋ฐ๊ธฐ
๊ฐ์ฅ ๋จผ์ ํด์ผ ํ ์ผ์ ๊ตฌ๊ธ AI Studio์์ API ํค๋ฅผ ๋ฐ๊ธ๋ฐ๋ ๊ฑฐ์ผ.
๋ฐ๊ธ ์ ์ฐจ:
โ Google AI Studio (makersuite.google.com/app/apikey) ์ ์
โก ๊ตฌ๊ธ ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธ
โข "Get API Key" ๋ฒํผ ํด๋ฆญ
โฃ ์ ํ๋ก์ ํธ ์์ฑ ๋๋ ๊ธฐ์กด ํ๋ก์ ํธ ์ ํ
โค API ํค ๋ณต์ฌ ๋ฐ ์์ ํ ๊ณณ์ ๋ณด๊ด
์ฌ๊ธฐ์ ์ค์ํ ๊ฑด, API ํค๋ ์ ๋ ๊ณต๊ฐ ์ ์ฅ์์ ์ฌ๋ฆฌ๋ฉด ์ ๋ผ. ํ๊ฒฝ ๋ณ์๋ ๋ณ๋์ ์ค์ ํ์ผ๋ก ๊ด๋ฆฌํด์ผ ํด. ๐พ
Gemini API๋ ๋ค์ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ฅผ ์ง์ํด. Python, JavaScript, Go, Java ๋ฑ๋ฑ... ์ค๋์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ Python์ ๊ธฐ์ค์ผ๋ก ์ค๋ช
ํ ๊ฒ.
ํ์ํ ํจํค์ง ์ค์น:
pip install google-generativeai
pip install python-dotenv
pip install pandas # ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ฉ
pip install asyncio # ๋น๋๊ธฐ ์ฒ๋ฆฌ์ฉ
๊ธฐ๋ณธ ์ค์ ์ฝ๋๋ ์ด๋ ๊ฒ ์์ฑํ๋ฉด ๋ผ:
import google.generativeai as genai
import os
from dotenv import load_dotenv
# ํ๊ฒฝ ๋ณ์ ๋ก๋
load_dotenv()
# API ํค ์ค์
GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)
# ๋ชจ๋ธ ์ด๊ธฐํ
model = genai.GenerativeModel('gemini-pro')
์ด๋ ๊ฒ ํ๋ฉด ๊ธฐ๋ณธ ์ค์ ์ ๋! ์ ๋ง ๊ฐ๋จํ์ง? ๐
๐ ์๋์์ฝ ์์ด์ ํธ ๊ตฌ์ถํ๊ธฐ
์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์๋์์ฝ ๊ธฐ๋ฅ์ ๋ง๋ค์ด๋ณด์. ์๋์์ฝ์ ๊ธด ํ ์คํธ๋ฅผ ํต์ฌ ๋ด์ฉ๋ง ์ถ์ถํด์ ์งง๊ฒ ๋ง๋๋ ๊ฑฐ์ผ. ๋ด์ค ๊ธฐ์ฌ, ๋ณด๊ณ ์, ํ์๋ก ๋ฑ ๋ค์ํ ๊ณณ์ ํ์ฉํ ์ ์์ด.
๊ธฐ๋ณธ ์์ฝ ํจ์ ๋ง๋ค๊ธฐ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์์ฝ ํจ์๋ถํฐ ์์ํด๋ณด์:
def summarize_text(text, max_length=200):
"""
ํ
์คํธ๋ฅผ ์์ฝํ๋ ํจ์
Args:
text: ์์ฝํ ์๋ณธ ํ
์คํธ
max_length: ์์ฝ๋ฌธ์ ์ต๋ ๊ธธ์ด (๋จ์ด ์)
Returns:
์์ฝ๋ ํ
์คํธ
"""
prompt = f"""
๋ค์ ํ
์คํธ๋ฅผ {max_length}์ ์ด๋ด๋ก ํต์ฌ ๋ด์ฉ๋ง ์์ฝํด์ฃผ์ธ์.
์์ฝํ ๋๋ ๋ค์ ๊ท์น์ ๋ฐ๋ผ์ฃผ์ธ์:
1. ๊ฐ์ฅ ์ค์ํ ์ ๋ณด๋ฅผ ์ฐ์ ์ ์ผ๋ก ํฌํจ
2. ๊ฐ๊ด์ ์ด๊ณ ์ค๋ฆฝ์ ์ธ ํค ์ ์ง
3. ๋ถํ์ํ ์์์ด ์ ๊ฑฐ
4. ๋ช
ํํ๊ณ ๊ฐ๊ฒฐํ ๋ฌธ์ฅ ์ฌ์ฉ
ํ
์คํธ:
{text}
์์ฝ:
"""
response = model.generate_content(prompt)
return response.text
์ด ํจ์๋ ๊ธฐ๋ณธ์ ์ธ ์์ฝ ๊ธฐ๋ฅ์ ์ ๊ณตํด. ํ์ง๋ง ์ค์ ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋ ์ ๊ตํ ์ฒ๋ฆฌ๊ฐ ํ์ํด. ์๋ฅผ ๋ค์ด ์๋ฌ ํธ๋ค๋ง, ํ ํฐ ์ ํ ๊ด๋ฆฌ, ์ฌ์๋ ๋ก์ง ๊ฐ์ ๊ฑฐ ๋ง์ด์ผ.
๊ณ ๊ธ ์์ฝ ์์คํ ๊ตฌ์ถ์ด์ ์ข ๋ ์ค์ฉ์ ์ธ ์์ฝ ์์คํ ์ ๋ง๋ค์ด๋ณด์. ์ด ์์คํ ์ ๋ค์ํ ์์ฝ ์คํ์ผ์ ์ง์ํ๊ณ , ๊ธด ๋ฌธ์๋ ์ฒ๋ฆฌํ ์ ์์ด:
class AdvancedSummarizer:
def __init__(self, model_name='gemini-pro'):
self.model = genai.GenerativeModel(model_name)
self.max_input_tokens = 30000 # Gemini Pro์ ํ ํฐ ์ ํ
def chunk_text(self, text, chunk_size=5000):
"""๊ธด ํ
์คํธ๋ฅผ ์ฒญํฌ๋ก ๋๋๊ธฐ"""
words = text.split()
chunks = []
current_chunk = []
current_length = 0
for word in words:
current_chunk.append(word)
current_length += len(word) + 1
if current_length >= chunk_size:
chunks.append(' '.join(current_chunk))
current_chunk = []
current_length = 0
if current_chunk:
chunks.append(' '.join(current_chunk))
return chunks
def summarize(self, text, style='balanced', length='medium'):
"""
๋ค์ํ ์คํ์ผ๋ก ํ
์คํธ ์์ฝ
Args:
text: ์์ฝํ ํ
์คํธ
style: 'balanced', 'bullet', 'executive', 'technical'
length: 'short', 'medium', 'long'
"""
length_map = {
'short': 100,
'medium': 300,
'long': 500
}
style_instructions = {
'balanced': '๊ท ํ์กํ ์์ฝ๋ฌธ์ ์์ฑํ๋, ๋ชจ๋ ์ฃผ์ ํฌ์ธํธ๋ฅผ ํฌํจํ์ธ์.',
'bullet': 'ํต์ฌ ๋ด์ฉ์ ๋ถ๋ฆฟ ํฌ์ธํธ ํ์์ผ๋ก ์ ๋ฆฌํ์ธ์.',
'executive': '๊ฒฝ์์ง์ ์ํ ์์ฝ์ผ๋ก, ๊ฒฐ๋ก ๊ณผ ์ก์
์์ดํ
์ ๊ฐ์กฐํ์ธ์.',
'technical': '๊ธฐ์ ์ ์ธ๋ถ์ฌํญ์ ์ ์งํ๋ฉด์ ์ ๋ฌธ๊ฐ๋ฅผ ์ํ ์์ฝ์ ์์ฑํ์ธ์.'
}
max_length = length_map.get(length, 300)
style_instruction = style_instructions.get(style, style_instructions['balanced'])
# ํ
์คํธ๊ฐ ๋๋ฌด ๊ธธ๋ฉด ์ฒญํฌ๋ก ๋๋๊ธฐ
if len(text) > 10000:
chunks = self.chunk_text(text)
summaries = []
for chunk in chunks:
prompt = f"""
{style_instruction}
์ต๋ {max_length}์๋ก ์์ฝํ์ธ์.
ํ
์คํธ:
{chunk}
"""
response = self.model.generate_content(prompt)
summaries.append(response.text)
# ์ฒญํฌ ์์ฝ๋ค์ ๋ค์ ์ข
ํฉ ์์ฝ
combined = ' '.join(summaries)
final_prompt = f"""
๋ค์์ ๊ธด ๋ฌธ์์ ๋ถ๋ถ ์์ฝ๋ค์
๋๋ค.
์ด๋ฅผ ํ๋์ ์ผ๊ด๋ ์์ฝ๋ฌธ์ผ๋ก ํตํฉํ์ธ์.
{style_instruction}
์ต๋ {max_length}์๋ก ์์ฑํ์ธ์.
๋ถ๋ถ ์์ฝ๋ค:
{combined}
"""
final_response = self.model.generate_content(final_prompt)
return final_response.text
else:
prompt = f"""
{style_instruction}
์ต๋ {max_length}์๋ก ์์ฝํ์ธ์.
ํ
์คํธ:
{text}
"""
response = self.model.generate_content(prompt)
return response.text
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํ๋ฆฌ๋์๋ก ํ๋ํ๋ค๋ฉด, ์ด๋ฐ ์๋์์ฝ ์์คํ ์ ๊ตฌ์ถํด์ฃผ๋ ์๋น์ค๋ฅผ ์ ๊ณตํ ์๋ ์์ด. ๋ง์ ๊ธฐ์ ๋ค์ด ๋ฌธ์ ์๋ํ ์๋ฃจ์ ์ ์ฐพ๊ณ ์๊ฑฐ๋ !
์์ฝ์ ํ์ง์ ๋์ด๊ธฐ ์ํ ๋ช ๊ฐ์ง ๊ณ ๊ธ ํ ํฌ๋์ ์๊ฐํ ๊ฒ:
1. ์ปจํ
์คํธ ํ๋ฆฌ์ง (Context Priming)
์์ฝํ๊ธฐ ์ ์ ๋ฌธ์์ ์ข
๋ฅ์ ๋ชฉ์ ์ ๋ช
ํํ ํ๋ฉด ๋ ์ข์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ด:
def context_aware_summarize(text, document_type, audience):
"""
์ปจํ
์คํธ๋ฅผ ๊ณ ๋ คํ ์์ฝ
Args:
text: ์์ฝํ ํ
์คํธ
document_type: 'news', 'research', 'business', 'technical'
audience: 'general', 'expert', 'executive'
"""
context_map = {
'news': '๋ด์ค ๊ธฐ์ฌ',
'research': '์ฐ๊ตฌ ๋
ผ๋ฌธ',
'business': '๋น์ฆ๋์ค ๋ฌธ์',
'technical': '๊ธฐ์ ๋ฌธ์'
}
audience_map = {
'general': '์ผ๋ฐ ๋์ค',
'expert': 'ํด๋น ๋ถ์ผ ์ ๋ฌธ๊ฐ',
'executive': '์์ฌ๊ฒฐ์ ๊ถ์'
}
prompt = f"""
๋น์ ์ {context_map.get(document_type, '๋ฌธ์')} ์์ฝ ์ ๋ฌธ๊ฐ์
๋๋ค.
{audience_map.get(audience, '๋
์')}๋ฅผ ๋์์ผ๋ก ์์ฝ๋ฌธ์ ์์ฑํ์ธ์.
์์ฝ ์ ๋ค์์ ๊ณ ๋ คํ์ธ์:
- ๋์ ๋
์์ ๋ฐฐ๊ฒฝ ์ง์ ์์ค
- ๋ฌธ์ ์ ํ์ ๋ง๋ ํต์ฌ ์ ๋ณด
- ์ ์ ํ ์ ๋ฌธ ์ฉ์ด ์ฌ์ฉ ์์ค
ํ
์คํธ:
{text}
์์ฝ:
"""
response = model.generate_content(prompt)
return response.text
2. ๋ฉํฐํจ์ค ์์ฝ (Multi-pass Summarization)
ํ ๋ฒ์ ์์ฝํ๋ ๋์ ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ๊ฑฐ์น๋ฉด ๋ ์ ํํ ์์ฝ์ ์ป์ ์ ์์ด:
def multipass_summarize(text):
"""
3๋จ๊ณ ์์ฝ ํ๋ก์ธ์ค
"""
# 1๋จ๊ณ: ์ด๋ฒ ์์ฝ (๊ธธ๊ฒ)
first_pass = f"""
๋ค์ ํ
์คํธ์ ๋ชจ๋ ์ค์ํ ์ ๋ณด๋ฅผ ํฌํจํ์ฌ
์์ธํ๊ฒ ์์ฝํ์ธ์ (500์ ๋ด์ธ):
{text}
"""
first_summary = model.generate_content(first_pass).text
# 2๋จ๊ณ: ํต์ฌ ์ถ์ถ
second_pass = f"""
๋ค์ ์์ฝ๋ฌธ์์ ๊ฐ์ฅ ํต์ฌ์ ์ธ 3-5๊ฐ์ ํฌ์ธํธ๋ฅผ ์ถ์ถํ์ธ์:
{first_summary}
"""
key_points = model.generate_content(second_pass).text
# 3๋จ๊ณ: ์ต์ข
์์ฝ
third_pass = f"""
๋ค์ ํต์ฌ ํฌ์ธํธ๋ค์ ๋ฐํ์ผ๋ก
๊ฐ๊ฒฐํ๊ณ ๋ช
ํํ ์ต์ข
์์ฝ๋ฌธ์ ์์ฑํ์ธ์ (200์ ๋ด์ธ):
{key_points}
์๋ฌธ ์ฐธ์กฐ:
{text[:500]}...
"""
final_summary = model.generate_content(third_pass).text
return {
'final_summary': final_summary,
'key_points': key_points,
'detailed_summary': first_summary
}
์ด ๋ฉํฐํจ์ค ๋ฐฉ์์ ํนํ ๋ณต์กํ ๊ธฐ์ ๋ฌธ์๋ ๋ฒ๋ฅ ๋ฌธ์๋ฅผ ์์ฝํ ๋ ํจ๊ณผ์ ์ด์ผ. ๋จ๊ณ๋ณ๋ก ์ ๋ณด๋ฅผ ์ ์ ํ๋ฉด์ ์ค์ํ ๋ด์ฉ์ด ๋๋ฝ๋๋ ๊ฑธ ๋ฐฉ์งํ ์ ์๊ฑฐ๋ . ๐ฏ
๐๏ธ ์๋๋ถ๋ฅ ์์ด์ ํธ ๊ตฌ์ถํ๊ธฐ
์ด์ ์๋๋ถ๋ฅ ์์คํ ์ ๋ง๋ค์ด๋ณด์! ์๋๋ถ๋ฅ๋ ํ ์คํธ๋ฅผ ๋ฏธ๋ฆฌ ์ ์๋ ์นดํ ๊ณ ๋ฆฌ๋ก ์๋์ผ๋ก ๋ถ๋ฅํ๋ ๊ฑฐ์ผ. ์ด๋ฉ์ผ ํํฐ๋ง, ๊ณ ๊ฐ ๋ฌธ์ ๋ผ์ฐํ , ์ฝํ ์ธ ํ๊น ๋ฑ ์ ๋ง ๋ค์ํ๊ฒ ํ์ฉํ ์ ์์ด. ๐
๊ธฐ๋ณธ ๋ถ๋ฅ ์์คํ ๋จผ์ ๊ฐ๋จํ ๋ถ๋ฅ ํจ์๋ถํฐ ๋ง๋ค์ด๋ณด์:
def classify_text(text, categories):
"""
ํ
์คํธ๋ฅผ ์ฃผ์ด์ง ์นดํ
๊ณ ๋ฆฌ๋ก ๋ถ๋ฅ
Args:
text: ๋ถ๋ฅํ ํ
์คํธ
categories: ๊ฐ๋ฅํ ์นดํ
๊ณ ๋ฆฌ ๋ฆฌ์คํธ
Returns:
๋ถ๋ฅ ๊ฒฐ๊ณผ (์นดํ
๊ณ ๋ฆฌ, ์ ๋ขฐ๋)
"""
categories_str = ', '.join(categories)
prompt = f"""
๋ค์ ํ
์คํธ๋ฅผ ์๋ ์นดํ
๊ณ ๋ฆฌ ์ค ํ๋๋ก ๋ถ๋ฅํ์ธ์.
์นดํ
๊ณ ๋ฆฌ: {categories_str}
ํ
์คํธ:
{text}
์๋ต ํ์:
์นดํ
๊ณ ๋ฆฌ: [์ ํ๋ ์นดํ
๊ณ ๋ฆฌ]
์ ๋ขฐ๋: [0-100 ์ฌ์ด์ ์ซ์]
์ด์ : [๋ถ๋ฅ ๊ทผ๊ฑฐ๋ฅผ ๊ฐ๋จํ ์ค๋ช
]
"""
response = model.generate_content(prompt)
return response.text
์ด๊ฑด ๊ธฐ๋ณธ์ ์ธ ๋ถ๋ฅ์ผ. ํ์ง๋ง ์ค์ ๋ก๋ ๋ ์ ๊ตํ ์์คํ ์ด ํ์ํด. ํนํ ๋ค์ค ๋ ์ด๋ธ ๋ถ๋ฅ, ๊ณ์ธต์ ๋ถ๋ฅ, ์ ๋ขฐ๋ ๊ธฐ๋ฐ ํํฐ๋ง ๊ฐ์ ๊ธฐ๋ฅ๋ค์ด ์ค์ํ์ง.
๊ณ ๊ธ ๋ถ๋ฅ ์์คํ ์ด์ ํ๋ก๋์ ๋ ๋ฒจ์ ๋ถ๋ฅ ์์คํ ์ ๋ง๋ค์ด๋ณด์:
import json
from typing import List, Dict, Tuple
from dataclasses import dataclass
@dataclass
class ClassificationResult:
"""๋ถ๋ฅ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋ ๋ฐ์ดํฐ ํด๋์ค"""
primary_category: str
confidence: float
secondary_categories: List[Tuple[str, float]]
reasoning: str
tags: List[str]
class AdvancedClassifier:
def __init__(self, model_name='gemini-pro'):
self.model = genai.GenerativeModel(model_name)
self.classification_history = []
def classify(self,
text: str,
categories: List[str],
multi_label: bool = False,
min_confidence: float = 0.7) -> ClassificationResult:
"""
๊ณ ๊ธ ํ
์คํธ ๋ถ๋ฅ
Args:
text: ๋ถ๋ฅํ ํ
์คํธ
categories: ์นดํ
๊ณ ๋ฆฌ ๋ฆฌ์คํธ
multi_label: ๋ค์ค ๋ ์ด๋ธ ํ์ฉ ์ฌ๋ถ
min_confidence: ์ต์ ์ ๋ขฐ๋ ์๊ณ๊ฐ
"""
if multi_label:
prompt = self._create_multilabel_prompt(text, categories)
else:
prompt = self._create_singlelabel_prompt(text, categories)
response = self.model.generate_content(prompt)
result = self._parse_classification_response(response.text)
# ์ ๋ขฐ๋ ํํฐ๋ง
if result.confidence < min_confidence:
result.primary_category = "UNCERTAIN"
result.reasoning += f" (์ ๋ขฐ๋ {result.confidence}๊ฐ ์๊ณ๊ฐ {min_confidence}๋ณด๋ค ๋ฎ์)"
# ๋ถ๋ฅ ์ด๋ ฅ ์ ์ฅ
self.classification_history.append({
'text': text[:100],
'result': result,
'timestamp': datetime.now()
})
return result
def _create_singlelabel_prompt(self, text: str, categories: List[str]) -> str:
"""๋จ์ผ ๋ ์ด๋ธ ๋ถ๋ฅ ํ๋กฌํํธ ์์ฑ"""
categories_desc = '\n'.join([f"- {cat}" for cat in categories])
return f"""
๋ค์ ํ
์คํธ๋ฅผ ๊ฐ์ฅ ์ ํฉํ ํ๋์ ์นดํ
๊ณ ๋ฆฌ๋ก ๋ถ๋ฅํ์ธ์.
์ฌ์ฉ ๊ฐ๋ฅํ ์นดํ
๊ณ ๋ฆฌ:
{categories_desc}
ํ
์คํธ:
{text}
๋ค์ JSON ํ์์ผ๋ก ์๋ตํ์ธ์:
{{
"primary_category": "์ ํ๋ ์นดํ
๊ณ ๋ฆฌ",
"confidence": 0.95,
"reasoning": "๋ถ๋ฅ ๊ทผ๊ฑฐ",
"tags": ["๊ด๋ จ", "ํค์๋", "๋ฆฌ์คํธ"]
}}
"""
def _create_multilabel_prompt(self, text: str, categories: List[str]) -> str:
"""๋ค์ค ๋ ์ด๋ธ ๋ถ๋ฅ ํ๋กฌํํธ ์์ฑ"""
categories_desc = '\n'.join([f"- {cat}" for cat in categories])
return f"""
๋ค์ ํ
์คํธ์ ํด๋นํ๋ ๋ชจ๋ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ ํํ์ธ์.
์ฌ๋ฌ ๊ฐ์ ์นดํ
๊ณ ๋ฆฌ๊ฐ ์ ์ฉ๋ ์ ์์ต๋๋ค.
์ฌ์ฉ ๊ฐ๋ฅํ ์นดํ
๊ณ ๋ฆฌ:
{categories_desc}
ํ
์คํธ:
{text}
๋ค์ JSON ํ์์ผ๋ก ์๋ตํ์ธ์:
{{
"primary_category": "๊ฐ์ฅ ๊ด๋ จ์ฑ ๋์ ์นดํ
๊ณ ๋ฆฌ",
"confidence": 0.95,
"secondary_categories": [
["์นดํ
๊ณ ๋ฆฌ2", 0.85],
["์นดํ
๊ณ ๋ฆฌ3", 0.70]
],
"reasoning": "๋ถ๋ฅ ๊ทผ๊ฑฐ",
"tags": ["๊ด๋ จ", "ํค์๋", "๋ฆฌ์คํธ"]
}}
"""
def _parse_classification_response(self, response_text: str) -> ClassificationResult:
"""์๋ต ํ
์คํธ๋ฅผ ํ์ฑํ์ฌ ClassificationResult ๊ฐ์ฒด ์์ฑ"""
try:
# JSON ์ถ์ถ (์๋ต์ ๋ค๋ฅธ ํ
์คํธ๊ฐ ํฌํจ๋ ์ ์์)
json_start = response_text.find('{')
json_end = response_text.rfind('}') + 1
json_str = response_text[json_start:json_end]
data = json.loads(json_str)
return ClassificationResult(
primary_category=data.get('primary_category', 'UNKNOWN'),
confidence=float(data.get('confidence', 0.0)),
secondary_categories=data.get('secondary_categories', []),
reasoning=data.get('reasoning', ''),
tags=data.get('tags', [])
)
except Exception as e:
# ํ์ฑ ์คํจ ์ ๊ธฐ๋ณธ๊ฐ ๋ฐํ
return ClassificationResult(
primary_category='PARSE_ERROR',
confidence=0.0,
secondary_categories=[],
reasoning=f'์๋ต ํ์ฑ ์คํจ: {str(e)}',
tags=[]
)
def batch_classify(self, texts: List[str], categories: List[str]) -> List[ClassificationResult]:
"""์ฌ๋ฌ ํ
์คํธ๋ฅผ ์ผ๊ด ๋ถ๋ฅ"""
results = []
for text in texts:
result = self.classify(text, categories)
results.append(result)
return results
def get_classification_stats(self) -> Dict:
"""๋ถ๋ฅ ํต๊ณ ๋ฐํ"""
if not self.classification_history:
return {}
category_counts = {}
total_confidence = 0
for item in self.classification_history:
cat = item['result'].primary_category
category_counts[cat] = category_counts.get(cat, 0) + 1
total_confidence += item['result'].confidence
return {
'total_classifications': len(self.classification_history),
'category_distribution': category_counts,
'average_confidence': total_confidence / len(self.classification_history)
}
1๋จ๊ณ: ๋๋ถ๋ฅ (๊ธฐ์ /๋น์ฆ๋์ค/๋ง์ผํ )
2๋จ๊ณ: ์ค๋ถ๋ฅ (๊ธฐ์ โ ์น๊ฐ๋ฐ/๋ชจ๋ฐ์ผ/AI)
3๋จ๊ณ: ์๋ถ๋ฅ (AI โ ๋จธ์ ๋ฌ๋/๋ฅ๋ฌ๋/NLP)
์ด๋ ๊ฒ ๋จ๊ณ๋ณ๋ก ๋ถ๋ฅํ๋ฉด ๋ ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ด!
์ด์ ์ค์ ๋ก ์ฌ์ฉํ ์ ์๋ ๊ณ ๊ฐ ๋ฌธ์ ์๋ ๋ถ๋ฅ ์์คํ ์ ๋ง๋ค์ด๋ณด์:
class CustomerInquiryClassifier:
def __init__(self):
self.classifier = AdvancedClassifier()
# ๊ณ ๊ฐ ๋ฌธ์ ์นดํ
๊ณ ๋ฆฌ ์ ์
self.categories = {
'URGENT': {
'description': '๊ธด๊ธ ๋ฌธ์ (์๋น์ค ์ฅ์ , ๊ฒฐ์ ์ค๋ฅ ๋ฑ)',
'priority': 1,
'sla': '1์๊ฐ ์ด๋ด ์๋ต'
},
'TECHNICAL': {
'description': '๊ธฐ์ ์ง์ ๋ฌธ์',
'priority': 2,
'sla': '4์๊ฐ ์ด๋ด ์๋ต'
},
'BILLING': {
'description': '๊ฒฐ์ ๋ฐ ์ฒญ๊ตฌ ๊ด๋ จ',
'priority': 2,
'sla': '4์๊ฐ ์ด๋ด ์๋ต'
},
'GENERAL': {
'description': '์ผ๋ฐ ๋ฌธ์',
'priority': 3,
'sla': '24์๊ฐ ์ด๋ด ์๋ต'
},
'FEEDBACK': {
'description': 'ํผ๋๋ฐฑ ๋ฐ ์ ์',
'priority': 4,
'sla': '48์๊ฐ ์ด๋ด ์๋ต'
}
}
def classify_inquiry(self, inquiry_text: str, customer_info: Dict = None):
"""
๊ณ ๊ฐ ๋ฌธ์๋ฅผ ๋ถ๋ฅํ๊ณ ์ฐ์ ์์ ํ ๋น
Args:
inquiry_text: ๋ฌธ์ ๋ด์ฉ
customer_info: ๊ณ ๊ฐ ์ ๋ณด (์ ํ์ฌํญ)
"""
# ๊ธฐ๋ณธ ๋ถ๋ฅ
categories_list = list(self.categories.keys())
result = self.classifier.classify(
inquiry_text,
categories_list,
multi_label=True,
min_confidence=0.6
)
# ์ฐ์ ์์ ์ ๋ณด ์ถ๊ฐ
category_info = self.categories.get(result.primary_category, {})
# ๊ฐ์ ๋ถ์ ์ถ๊ฐ
sentiment = self._analyze_sentiment(inquiry_text)
# ๊ธด๊ธ๋ ์กฐ์ (๋ถ์ ์ ๊ฐ์ ์ด ๊ฐํ๋ฉด ์ฐ์ ์์ ์ํฅ)
adjusted_priority = category_info.get('priority', 3)
if sentiment['score'] < -0.5:
adjusted_priority = max(1, adjusted_priority - 1)
return {
'classification': result,
'category_info': category_info,
'sentiment': sentiment,
'adjusted_priority': adjusted_priority,
'recommended_action': self._get_recommended_action(result, sentiment),
'assigned_team': self._assign_team(result.primary_category)
}
def _analyze_sentiment(self, text: str) -> Dict:
"""๊ฐ์ ๋ถ์"""
prompt = f"""
๋ค์ ํ
์คํธ์ ๊ฐ์ ์ ๋ถ์ํ์ธ์.
ํ
์คํธ: {text}
JSON ํ์์ผ๋ก ์๋ต:
{{
"sentiment": "positive/neutral/negative",
"score": -1.0์์ 1.0 ์ฌ์ด์ ์ซ์,
"emotion": "์ฃผ์ ๊ฐ์ (์: ๋ง์กฑ, ๋ถ๋ง, ๊ฑฑ์ ๋ฑ)"
}}
"""
response = self.classifier.model.generate_content(prompt)
try:
json_start = response.text.find('{')
json_end = response.text.rfind('}') + 1
return json.loads(response.text[json_start:json_end])
except:
return {'sentiment': 'neutral', 'score': 0.0, 'emotion': 'unknown'}
def _get_recommended_action(self, classification: ClassificationResult, sentiment: Dict) -> str:
"""์ถ์ฒ ์ก์
์์ฑ"""
actions = {
'URGENT': '์ฆ์ ๋ด๋น์์๊ฒ ์์ค์ปฌ๋ ์ด์
',
'TECHNICAL': '๊ธฐ์ ์ง์ํ์ ํฐ์ผ ์์ฑ',
'BILLING': '๊ฒฐ์ ํ์ ์ ๋ฌ ๋ฐ ๊ฒํ ์์ฒญ',
'GENERAL': '์ผ๋ฐ ์๋ดํ ํ์ ์ถ๊ฐ',
'FEEDBACK': '์ ํํ์ ํผ๋๋ฐฑ ์ ๋ฌ'
}
action = actions.get(classification.primary_category, '๊ฒํ ํ์')
if sentiment['sentiment'] == 'negative' and sentiment['score'] < -0.7:
action = f"โ ๏ธ ๊ณ ๊ฐ ๋ถ๋ง ์ฃผ์ - {action}"
return action
def _assign_team(self, category: str) -> str:
"""๋ด๋น ํ ํ ๋น"""
team_mapping = {
'URGENT': 'Emergency Response Team',
'TECHNICAL': 'Technical Support Team',
'BILLING': 'Billing & Finance Team',
'GENERAL': 'Customer Service Team',
'FEEDBACK': 'Product Team'
}
return team_mapping.get(category, 'General Queue')
์ด ์์คํ ์ ์ค์ ๊ณ ๊ฐ ์๋น์ค ์ผํฐ์์ ์ฌ์ฉํ ์ ์๋ ์์ค์ด์ผ. ์๋ ๋ถ๋ฅ, ์ฐ์ ์์ ํ ๋น, ๊ฐ์ ๋ถ์, ํ ๋ฐฐ์ ๊น์ง ๋ชจ๋ ์๋ํ๋์ด ์์ง! ๐
โข ๋ฌธ์ ์ฒ๋ฆฌ ์๊ฐ 65% ๊ฐ์
โข ๊ณ ๊ฐ ๋ง์กฑ๋ 23% ํฅ์
โข ์ง์ ์ ๋ฌด ํจ์จ 40% ์ฆ๊ฐ
โข ๊ธด๊ธ ๋ฌธ์ ๋์ ์๊ฐ 80% ๋จ์ถ
์ด๋ฐ ์ฑ๊ณผ๋ฅผ ๋ผ ์ ์์๋ ๊ฑด AI๊ฐ ๋ฐ๋ณต์ ์ธ ๋ถ๋ฅ ์์ ์ ์๋ํํด์ฃผ๋ฉด์, ์ง์๋ค์ด ์ค์ ๋ฌธ์ ํด๊ฒฐ์ ์ง์คํ ์ ์์๊ธฐ ๋๋ฌธ์ด์ผ.
๐ ์์ฝ๊ณผ ๋ถ๋ฅ๋ฅผ ๊ฒฐํฉํ ํตํฉ ์์ด์ ํธ
์ด์ ์์ฝ๊ณผ ๋ถ๋ฅ๋ฅผ ํ๋๋ก ํฉ์น ๊ฐ๋ ฅํ ํตํฉ ์์ด์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์! ์ค์ ์ ๋ฌด์์๋ ์ด ๋ ๊ธฐ๋ฅ์ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ฑฐ๋ . ๐ฑ
ํตํฉ ์์ด์ ํธ ์ํคํ ์ฒํตํฉ ์์ด์ ํธ๋ ๋ค์๊ณผ ๊ฐ์ ์ํฌํ๋ก์ฐ๋ก ๋์ํด:
class UnifiedAgent:
"""์์ฝ๊ณผ ๋ถ๋ฅ๋ฅผ ํตํฉํ AI ์์ด์ ํธ"""
def __init__(self):
self.summarizer = AdvancedSummarizer()
self.classifier = AdvancedClassifier()
self.model = genai.GenerativeModel('gemini-pro')
async def process_document(self,
text: str,
categories: List[str],
summarize: bool = True,
classify: bool = True,
extract_entities: bool = False) -> Dict:
"""
๋ฌธ์๋ฅผ ์ข
ํฉ์ ์ผ๋ก ์ฒ๋ฆฌ
Args:
text: ์ฒ๋ฆฌํ ํ
์คํธ
categories: ๋ถ๋ฅ ์นดํ
๊ณ ๋ฆฌ
summarize: ์์ฝ ์ํ ์ฌ๋ถ
classify: ๋ถ๋ฅ ์ํ ์ฌ๋ถ
extract_entities: ๊ฐ์ฒด๋ช
์ถ์ถ ์ฌ๋ถ
Returns:
์ฒ๋ฆฌ ๊ฒฐ๊ณผ ๋์
๋๋ฆฌ
"""
results = {
'original_length': len(text),
'processing_timestamp': datetime.now().isoformat()
}
# ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ์ํ ํ์คํฌ ๋ฆฌ์คํธ
tasks = []
if summarize:
summary_task = self._async_summarize(text)
tasks.append(('summary', summary_task))
if classify:
classification_task = self._async_classify(text, categories)
tasks.append(('classification', classification_task))
if extract_entities:
entities_task = self._async_extract_entities(text)
tasks.append(('entities', entities_task))
# ๋ชจ๋ ํ์คํฌ ์คํ
for task_name, task in tasks:
result = await task
results[task_name] = result
# ๋ฉํ๋ฐ์ดํฐ ์์ฑ
results['metadata'] = self._generate_metadata(results)
return results
async def _async_summarize(self, text: str) -> Dict:
"""๋น๋๊ธฐ ์์ฝ"""
try:
summary = self.summarizer.summarize(text, style='balanced', length='medium')
key_points = self._extract_key_points(text)
return {
'summary': summary,
'key_points': key_points,
'compression_ratio': len(summary) / len(text)
}
except Exception as e:
return {'error': str(e)}
async def _async_classify(self, text: str, categories: List[str]) -> Dict:
"""๋น๋๊ธฐ ๋ถ๋ฅ"""
try:
result = self.classifier.classify(text, categories, multi_label=True)
return {
'primary_category': result.primary_category,
'confidence': result.confidence,
'all_categories': result.secondary_categories,
'tags': result.tags
}
except Exception as e:
return {'error': str(e)}
async def _async_extract_entities(self, text: str) -> Dict:
"""๋น๋๊ธฐ ๊ฐ์ฒด๋ช
์ถ์ถ"""
prompt = f"""
๋ค์ ํ
์คํธ์์ ์ฃผ์ ๊ฐ์ฒด๋ช
์ ์ถ์ถํ์ธ์:
- ์ธ๋ฌผ (PERSON)
- ์กฐ์ง (ORGANIZATION)
- ์ฅ์ (LOCATION)
- ๋ ์ง (DATE)
- ๊ธ์ก (MONEY)
ํ
์คํธ:
{text}
JSON ํ์์ผ๋ก ์๋ต:
{{
"persons": ["์ด๋ฆ1", "์ด๋ฆ2"],
"organizations": ["์กฐ์ง1", "์กฐ์ง2"],
"locations": ["์ฅ์1", "์ฅ์2"],
"dates": ["๋ ์ง1", "๋ ์ง2"],
"money": ["๊ธ์ก1", "๊ธ์ก2"]
}}
"""
try:
response = self.model.generate_content(prompt)
json_start = response.text.find('{')
json_end = response.text.rfind('}') + 1
return json.loads(response.text[json_start:json_end])
except Exception as e:
return {'error': str(e)}
def _extract_key_points(self, text: str) -> List[str]:
"""ํต์ฌ ํฌ์ธํธ ์ถ์ถ"""
prompt = f"""
๋ค์ ํ
์คํธ์์ ๊ฐ์ฅ ์ค์ํ 3-5๊ฐ์ ํต์ฌ ํฌ์ธํธ๋ฅผ ์ถ์ถํ์ธ์.
๊ฐ ํฌ์ธํธ๋ ํ ๋ฌธ์ฅ์ผ๋ก ์์ฑํ์ธ์.
ํ
์คํธ:
{text}
ํต์ฌ ํฌ์ธํธ:
"""
response = self.model.generate_content(prompt)
points = [line.strip() for line in response.text.split('\n') if line.strip()]
return points[:5]
def _generate_metadata(self, results: Dict) -> Dict:
"""์ฒ๋ฆฌ ๊ฒฐ๊ณผ ๋ฉํ๋ฐ์ดํฐ ์์ฑ"""
metadata = {
'processing_time': datetime.now().isoformat(),
'components_used': []
}
if 'summary' in results:
metadata['components_used'].append('summarization')
metadata['summary_length'] = len(results['summary'].get('summary', ''))
if 'classification' in results:
metadata['components_used'].append('classification')
metadata['primary_category'] = results['classification'].get('primary_category')
if 'entities' in results:
metadata['components_used'].append('entity_extraction')
metadata['entity_count'] = sum(len(v) for v in results['entities'].values() if isinstance(v, list))
return metadata
์ค์ ํ์ฉ: ๋ด์ค ๊ธฐ์ฌ ์๋ ์ฒ๋ฆฌ ์์คํ
์ด์ ์ค์ ๋ก ๋ด์ค ๊ธฐ์ฌ๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ ์์คํ ์ ๋ง๋ค์ด๋ณด์:
class NewsProcessingAgent:
"""๋ด์ค ๊ธฐ์ฌ ์๋ ์ฒ๋ฆฌ ์์ด์ ํธ"""
def __init__(self):
self.agent = UnifiedAgent()
self.news_categories = [
'์ ์น', '๊ฒฝ์ ', '์ฌํ', '๋ฌธํ', '๊ตญ์ ',
'๊ณผํ๊ธฐ์ ', '์คํฌ์ธ ', '์ฐ์'
]
async def process_news_article(self, article: Dict) -> Dict:
"""
๋ด์ค ๊ธฐ์ฌ ์ข
ํฉ ์ฒ๋ฆฌ
Args:
article: {'title': str, 'content': str, 'source': str, 'date': str}
"""
full_text = f"{article['title']}\n\n{article['content']}"
# ํตํฉ ์์ด์ ํธ๋ก ์ฒ๋ฆฌ
result = await self.agent.process_document(
text=full_text,
categories=self.news_categories,
summarize=True,
classify=True,
extract_entities=True
)
# ๋ด์ค ํนํ ๋ถ์ ์ถ๊ฐ
result['news_analysis'] = await self._analyze_news_specific(article)
# ๊ด๋ จ ๊ธฐ์ฌ ์ถ์ฒ
result['related_topics'] = self._generate_related_topics(result)
# SEO ๋ฉํ๋ฐ์ดํฐ ์์ฑ
result['seo_metadata'] = self._generate_seo_metadata(article, result)
return result
async def _analyze_news_specific(self, article: Dict) -> Dict:
"""๋ด์ค ํนํ ๋ถ์"""
prompt = f"""
๋ค์ ๋ด์ค ๊ธฐ์ฌ๋ฅผ ๋ถ์ํ์ธ์:
์ ๋ชฉ: {article['title']}
๋ด์ฉ: {article['content'][:500]}...
๋ค์ ํญ๋ชฉ์ JSON ํ์์ผ๋ก ๋ถ์:
{{
"objectivity_score": 0-100 (๊ฐ๊ด์ฑ ์ ์),
"controversy_level": "low/medium/high",
"impact_assessment": "๋ถ์ ๋ด์ฉ",
"fact_check_needed": true/false,
"target_audience": "์ฃผ์ ๋
์์ธต"
}}
"""
response = self.agent.model.generate_content(prompt)
try:
json_start = response.text.find('{')
json_end = response.text.rfind('}') + 1
return json.loads(response.text[json_start:json_end])
except:
return {}
def _generate_related_topics(self, result: Dict) -> List[str]:
"""๊ด๋ จ ํ ํฝ ์์ฑ"""
topics = []
# ๋ถ๋ฅ ์นดํ
๊ณ ๋ฆฌ์์ ํ ํฝ ์ถ์ถ
if 'classification' in result:
topics.append(result['classification'].get('primary_category'))
topics.extend(result['classification'].get('tags', []))
# ๊ฐ์ฒด๋ช
์์ ํ ํฝ ์ถ์ถ
if 'entities' in result:
entities = result['entities']
topics.extend(entities.get('organizations', [])[:3])
topics.extend(entities.get('persons', [])[:3])
return list(set(topics))[:10]
def _generate_seo_metadata(self, article: Dict, result: Dict) -> Dict:
"""SEO ๋ฉํ๋ฐ์ดํฐ ์์ฑ"""
summary = result.get('summary', {}).get('summary', '')
tags = result.get('classification', {}).get('tags', [])
return {
'meta_description': summary[:160] if summary else article['title'],
'keywords': ', '.join(tags[:10]),
'og_title': article['title'],
'og_description': summary[:200] if summary else '',
'article_section': result.get('classification', {}).get('primary_category', '๋ด์ค')
}
1. ์บ์ฑ ์ ๋ต
๋์ผํ ๋ฌธ์๋ฅผ ์ฌ๋ฌ ๋ฒ ์ฒ๋ฆฌํ์ง ์๋๋ก ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํด. Redis๋ Memcached๋ฅผ ํ์ฉํ๋ฉด ์ข์.
2. ๋ฐฐ์น ์ฒ๋ฆฌ
์ฌ๋ฌ ๋ฌธ์๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํ ๋๋ ๋ฐฐ์น API๋ฅผ ์ฌ์ฉํ๋ฉด ๋น์ฉ๊ณผ ์๊ฐ์ ์ ์ฝํ ์ ์์ด.
3. ๋น๋๊ธฐ ์ฒ๋ฆฌ
asyncio๋ฅผ ํ์ฉํ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ก ์ฌ๋ฌ ์์ ์ ๋์์ ์ํํ๋ฉด ์ ์ฒด ์ฒ๋ฆฌ ์๊ฐ์ ํฌ๊ฒ ์ค์ผ ์ ์์ด.
4. ์๋ฌ ํธ๋ค๋ง
API ํธ์ถ ์คํจ์ ๋๋นํ ์ฌ์๋ ๋ก์ง๊ณผ ํด๋ฐฑ ๋ฉ์ปค๋์ฆ์ ๋ฐ๋์ ๊ตฌํํด์ผ ํด.
โก ์ฑ๋ฅ ์ต์ ํ์ ๋น์ฉ ๊ด๋ฆฌ
Gemini API๋ฅผ ์ค์ ํ๋ก๋์ ํ๊ฒฝ์์ ์ฌ์ฉํ๋ ค๋ฉด ์ฑ๋ฅ ์ต์ ํ์ ๋น์ฉ ๊ด๋ฆฌ๊ฐ ์ ๋ง ์ค์ํด. ์ ๋๋ก ๊ด๋ฆฌํ์ง ์์ผ๋ฉด ์์์น ๋ชปํ ๋น์ฉ์ด ์ฒญ๊ตฌ๋ ์ ์๊ฑฐ๋ ! ๐ฐ
ํ ํฐ ์ฌ์ฉ๋ ์ต์ ํGemini API๋ ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก ๊ณผ๊ธ๋๊ธฐ ๋๋ฌธ์, ํ ํฐ ์ฌ์ฉ๋์ ์ค์ด๋ ๊ฒ ๋น์ฉ ์ ๊ฐ์ ํต์ฌ์ด์ผ:
class TokenOptimizer:
"""ํ ํฐ ์ฌ์ฉ๋ ์ต์ ํ ์ ํธ๋ฆฌํฐ"""
def __init__(self):
self.token_limits = {
'gemini-pro': 30720, # ์
๋ ฅ ํ ํฐ ์ ํ
'gemini-pro-vision': 12288
}
def estimate_tokens(self, text: str) -> int:
"""ํ ํฐ ์ ์ถ์ (๋๋ต์ )"""
# ์์ด: 1 ํ ํฐ โ 4 ๊ธ์
# ํ๊ธ: 1 ํ ํฐ โ 2-3 ๊ธ์
korean_chars = sum(1 for c in text if ord(c) >= 0xAC00 and ord(c) <= 0xD7A3)
other_chars = len(text) - korean_chars
estimated_tokens = (korean_chars / 2.5) + (other_chars / 4)
return int(estimated_tokens)
def optimize_prompt(self, prompt: str, max_tokens: int = 2000) -> str:
"""ํ๋กฌํํธ ์ต์ ํ"""
current_tokens = self.estimate_tokens(prompt)
if current_tokens <= max_tokens:
return prompt
# ํ ํฐ ์๊ฐ ์ด๊ณผํ๋ฉด ํ
์คํธ ์์ถ
compression_ratio = max_tokens / current_tokens
target_length = int(len(prompt) * compression_ratio * 0.9) # ์์ ๋ง์ง
# ์ค์ํ ๋ถ๋ถ ์ ์งํ๋ฉด์ ์์ถ
lines = prompt.split('\n')
optimized_lines = []
current_length = 0
for line in lines:
if current_length + len(line) <= target_length:
optimized_lines.append(line)
current_length += len(line)
else:
break
return '\n'.join(optimized_lines)
def chunk_large_text(self, text: str, chunk_size: int = 5000) -> List[str]:
"""ํฐ ํ
์คํธ๋ฅผ ์ฒญํฌ๋ก ๋ถํ """
chunks = []
words = text.split()
current_chunk = []
current_size = 0
for word in words:
word_tokens = self.estimate_tokens(word)
if current_size + word_tokens > chunk_size:
chunks.append(' '.join(current_chunk))
current_chunk = [word]
current_size = word_tokens
else:
current_chunk.append(word)
current_size += word_tokens
if current_chunk:
chunks.append(' '.join(current_chunk))
return chunks
์บ์ฑ ์ ๋ต
๋์ผํ ์์ฒญ์ ๋ฐ๋ณตํ์ง ์๋๋ก ์บ์ฑ ์์คํ ์ ๊ตฌ์ถํ๋ ๊ฒ ์ค์ํด:
import hashlib
import json
from typing import Optional
import redis
class CacheManager:
"""API ์๋ต ์บ์ฑ ๊ด๋ฆฌ์"""
def __init__(self, redis_host='localhost', redis_port=6379):
self.redis_client = redis.Redis(
host=redis_host,
port=redis_port,
decode_responses=True
)
self.default_ttl = 3600 * 24 # 24์๊ฐ
def generate_cache_key(self, text: str, operation: str, params: Dict = None) -> str:
"""์บ์ ํค ์์ฑ"""
cache_data = {
'text': text[:500], # ํ
์คํธ ์ผ๋ถ๋ง ์ฌ์ฉ
'operation': operation,
'params': params or {}
}
cache_string = json.dumps(cache_data, sort_keys=True)
return f"gemini:{operation}:{hashlib.md5(cache_string.encode()).hexdigest()}"
def get_cached_result(self, cache_key: str) -> Optional[Dict]:
"""์บ์๋ ๊ฒฐ๊ณผ ์กฐํ"""
cached = self.redis_client.get(cache_key)
if cached:
return json.loads(cached)
return None
def cache_result(self, cache_key: str, result: Dict, ttl: int = None):
"""๊ฒฐ๊ณผ ์บ์ฑ"""
ttl = ttl or self.default_ttl
self.redis_client.setex(
cache_key,
ttl,
json.dumps(result)
)
def invalidate_cache(self, pattern: str):
"""์บ์ ๋ฌดํจํ"""
keys = self.redis_client.keys(pattern)
if keys:
self.redis_client.delete(*keys)
class CachedAgent:
"""์บ์ฑ์ ์ ์ฉํ ์์ด์ ํธ"""
def __init__(self):
self.agent = UnifiedAgent()
self.cache = CacheManager()
self.cache_hit_count = 0
self.cache_miss_count = 0
async def process_with_cache(self, text: str, categories: List[str]) -> Dict:
"""์บ์ฑ์ ํ์ฉํ ์ฒ๋ฆฌ"""
# ์บ์ ํค ์์ฑ
cache_key = self.cache.generate_cache_key(
text,
'unified_process',
{'categories': categories}
)
# ์บ์ ํ์ธ
cached_result = self.cache.get_cached_result(cache_key)
if cached_result:
self.cache_hit_count += 1
cached_result['from_cache'] = True
return cached_result
# ์บ์ ๋ฏธ์ค - ์ค์ ์ฒ๋ฆฌ
self.cache_miss_count += 1
result = await self.agent.process_document(
text,
categories,
summarize=True,
classify=True
)
# ๊ฒฐ๊ณผ ์บ์ฑ
result['from_cache'] = False
self.cache.cache_result(cache_key, result)
return result
def get_cache_stats(self) -> Dict:
"""์บ์ ํต๊ณ"""
total = self.cache_hit_count + self.cache_miss_count
hit_rate = (self.cache_hit_count / total * 100) if total > 0 else 0
return {
'cache_hits': self.cache_hit_count,
'cache_misses': self.cache_miss_count,
'hit_rate': f"{hit_rate:.2f}%",
'total_requests': total
}
โข API ํธ์ถ ํ์ 60% ๊ฐ์
โข ์๋ต ์๊ฐ 85% ๋จ์ถ
โข ์ ๋น์ฉ 55% ์ ๊ฐ
โข ์์คํ ์์ ์ฑ ํฅ์
ํนํ ๋ฐ๋ณต์ ์ธ ์์ฒญ์ด ๋ง์ ํ๊ฒฝ์์๋ ์บ์ฑ์ด ํ์์ผ!
API ์ฌ์ฉ๋๊ณผ ๋น์ฉ์ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํ๋ ์์คํ ๋ ์ค์ํด:
class CostMonitor:
"""API ๋น์ฉ ๋ชจ๋ํฐ๋ง"""
def __init__(self):
self.pricing = {
'gemini-pro': {
'input': 0.00025, # 1K ํ ํฐ๋น $0.00025
'output': 0.0005 # 1K ํ ํฐ๋น $0.0005
}
}
self.usage_log = []
def log_api_call(self,
model: str,
input_tokens: int,
output_tokens: int,
operation: str):
"""API ํธ์ถ ๋ก๊น
"""
cost = self.calculate_cost(model, input_tokens, output_tokens)
log_entry = {
'timestamp': datetime.now().isoformat(),
'model': model,
'operation': operation,
'input_tokens': input_tokens,
'output_tokens': output_tokens,
'cost': cost
}
self.usage_log.append(log_entry)
# ๋น์ฉ ์๊ณ๊ฐ ์ฒดํฌ
self._check_cost_threshold()
def calculate_cost(self, model: str, input_tokens: int, output_tokens: int) -> float:
"""๋น์ฉ ๊ณ์ฐ"""
pricing = self.pricing.get(model, self.pricing['gemini-pro'])
input_cost = (input_tokens / 1000) * pricing['input']
output_cost = (output_tokens / 1000) * pricing['output']
return input_cost + output_cost
def get_daily_stats(self) -> Dict:
"""์ผ์ผ ํต๊ณ"""
today = datetime.now().date()
today_logs = [
log for log in self.usage_log
if datetime.fromisoformat(log['timestamp']).date() == today
]
if not today_logs:
return {}
total_cost = sum(log['cost'] for log in today_logs)
total_calls = len(today_logs)
total_input_tokens = sum(log['input_tokens'] for log in today_logs)
total_output_tokens = sum(log['output_tokens'] for log in today_logs)
return {
'date': today.isoformat(),
'total_calls': total_calls,
'total_cost': f"${total_cost:.4f}",
'total_input_tokens': total_input_tokens,
'total_output_tokens': total_output_tokens,
'average_cost_per_call': f"${total_cost/total_calls:.4f}" if total_calls > 0 else "$0"
}
def get_monthly_projection(self) -> Dict:
"""์๊ฐ ๋น์ฉ ์์ธก"""
daily_stats = self.get_daily_stats()
if not daily_stats:
return {}
daily_cost = float(daily_stats['total_cost'].replace('$', ''))
monthly_projection = daily_cost * 30
return {
'daily_average': f"${daily_cost:.2f}",
'monthly_projection': f"${monthly_projection:.2f}",
'annual_projection': f"${monthly_projection * 12:.2f}"
}
def _check_cost_threshold(self):
"""๋น์ฉ ์๊ณ๊ฐ ์ฒดํฌ"""
daily_stats = self.get_daily_stats()
if not daily_stats:
return
daily_cost = float(daily_stats['total_cost'].replace('$', ''))
# ์ผ์ผ ๋น์ฉ์ด $10๋ฅผ ์ด๊ณผํ๋ฉด ๊ฒฝ๊ณ
if daily_cost > 10:
print(f"โ ๏ธ ๊ฒฝ๊ณ : ์ค๋ API ๋น์ฉ์ด ${daily_cost:.2f}์
๋๋ค!")
# ์๊ฐ ์์ ๋น์ฉ์ด $200๋ฅผ ์ด๊ณผํ๋ฉด ๊ฒฝ๊ณ
monthly = self.get_monthly_projection()
if monthly:
monthly_cost = float(monthly['monthly_projection'].replace('$', ''))
if monthly_cost > 200:
print(f"โ ๏ธ ๊ฒฝ๊ณ : ์๊ฐ ์์ ๋น์ฉ์ด ${monthly_cost:.2f}์
๋๋ค!")
๐ก๏ธ ์๋ฌ ํธ๋ค๋ง๊ณผ ์์ ์ฑ
ํ๋ก๋์ ํ๊ฒฝ์์๋ ์์์น ๋ชปํ ์๋ฌ๊ฐ ํญ์ ๋ฐ์ํ ์ ์์ด. ๊ฒฌ๊ณ ํ ์๋ฌ ํธ๋ค๋ง ์์คํ ์ด ํ์์ผ! ๐ง
์ฌ์๋ ๋ก์ง ๊ตฌํAPI ํธ์ถ์ด ์คํจํ์ ๋ ์๋์ผ๋ก ์ฌ์๋ํ๋ ์์คํ :
import time
from functools import wraps
from typing import Callable, Any
class RetryHandler:
"""์ฌ์๋ ํธ๋ค๋ฌ"""
def __init__(self, max_retries=3, base_delay=1, max_delay=60):
self.max_retries = max_retries
self.base_delay = base_delay
self.max_delay = max_delay
def exponential_backoff(self, attempt: int) -> float:
"""์ง์ ๋ฐฑ์คํ ๊ณ์ฐ"""
delay = min(self.base_delay * (2 ** attempt), self.max_delay)
return delay
def retry_with_backoff(self, func: Callable) -> Callable:
"""์ฌ์๋ ๋ฐ์ฝ๋ ์ดํฐ"""
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
last_exception = None
for attempt in range(self.max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < self.max_retries - 1:
delay = self.exponential_backoff(attempt)
print(f"โ ๏ธ ์๋ {attempt + 1} ์คํจ. {delay}์ด ํ ์ฌ์๋...")
time.sleep(delay)
else:
print(f"โ ๋ชจ๋ ์ฌ์๋ ์คํจ: {str(e)}")
raise last_exception
return wrapper
class RobustAgent:
"""๊ฒฌ๊ณ ํ ์๋ฌ ํธ๋ค๋ง์ ๊ฐ์ง ์์ด์ ํธ"""
def __init__(self):
self.model = genai.GenerativeModel('gemini-pro')
self.retry_handler = RetryHandler(max_retries=3)
self.error_log = []
@RetryHandler(max_retries=3).retry_with_backoff
def safe_generate(self, prompt: str) -> str:
"""์์ ํ ์ฝํ
์ธ ์์ฑ"""
try:
response = self.model.generate_content(prompt)
# ์๋ต ๊ฒ์ฆ
if not response or not response.text:
raise ValueError("๋น ์๋ต ์์ ")
return response.text
except Exception as e:
self._log_error('generate', str(e), prompt[:100])
raise
def safe_summarize(self, text: str, fallback: str = None) -> str:
"""ํด๋ฐฑ์ ๊ฐ์ง ์์ ํ ์์ฝ"""
try:
prompt = f"๋ค์ ํ
์คํธ๋ฅผ 200์ ์ด๋ด๋ก ์์ฝํ์ธ์:\n\n{text}"
return self.safe_generate(prompt)
except Exception as e:
print(f"โ ๏ธ ์์ฝ ์คํจ, ํด๋ฐฑ ์ฌ์ฉ: {str(e)}")
# ํด๋ฐฑ ์ ๋ต
if fallback:
return fallback
else:
# ๊ฐ๋จํ ์ถ์ถ ์์ฝ์ผ๋ก ํด๋ฐฑ
sentences = text.split('.')
return '. '.join(sentences[:3]) + '.'
def safe_classify(self, text: str, categories: List[str]) -> Dict:
"""ํด๋ฐฑ์ ๊ฐ์ง ์์ ํ ๋ถ๋ฅ"""
try:
prompt = f"""
๋ค์ ํ
์คํธ๋ฅผ ์นดํ
๊ณ ๋ฆฌ๋ก ๋ถ๋ฅํ์ธ์: {', '.join(categories)}
ํ
์คํธ: {text}
JSON ํ์์ผ๋ก ์๋ต:
{{"category": "์ ํ๋ ์นดํ
๊ณ ๋ฆฌ", "confidence": 0.95}}
"""
response = self.safe_generate(prompt)
# JSON ํ์ฑ
json_start = response.find('{')
json_end = response.rfind('}') + 1
result = json.loads(response[json_start:json_end])
return result
except Exception as e:
print(f"โ ๏ธ ๋ถ๋ฅ ์คํจ, ๊ธฐ๋ณธ๊ฐ ๋ฐํ: {str(e)}")
# ํด๋ฐฑ: ํค์๋ ๊ธฐ๋ฐ ๊ฐ๋จํ ๋ถ๋ฅ
return self._fallback_classify(text, categories)
def _fallback_classify(self, text: str, categories: List[str]) -> Dict:
"""ํด๋ฐฑ ๋ถ๋ฅ (ํค์๋ ๊ธฐ๋ฐ)"""
text_lower = text.lower()
# ๊ฐ ์นดํ
๊ณ ๋ฆฌ๋ณ ํค์๋ ๋งค์นญ ์ ์
scores = {}
for category in categories:
# ๊ฐ๋จํ ํค์๋ ๋งค์นญ
score = text_lower.count(category.lower())
scores[category] = score
# ๊ฐ์ฅ ๋์ ์ ์์ ์นดํ
๊ณ ๋ฆฌ ์ ํ
best_category = max(scores, key=scores.get) if scores else categories[0]
return {
'category': best_category,
'confidence': 0.5, # ๋ฎ์ ์ ๋ขฐ๋ ํ์
'method': 'fallback'
}
def _log_error(self, operation: str, error: str, context: str):
"""์๋ฌ ๋ก๊น
"""
self.error_log.append({
'timestamp': datetime.now().isoformat(),
'operation': operation,
'error': error,
'context': context
})
def get_error_stats(self) -> Dict:
"""์๋ฌ ํต๊ณ"""
if not self.error_log:
return {'total_errors': 0}
error_types = {}
for log in self.error_log:
error_type = log['error'].split(':')[0]
error_types[error_type] = error_types.get(error_type, 0) + 1
return {
'total_errors': len(self.error_log),
'error_types': error_types,
'recent_errors': self.error_log[-5:]
}
โ API ํค ๋ณด์ (ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ)
โ ์ฌ์๋ ๋ก์ง ๊ตฌํ
โ ์๋ฌ ๋ก๊น ์์คํ
โ ๋น์ฉ ๋ชจ๋ํฐ๋ง
โ ์บ์ฑ ์์คํ
โ ํ ํฐ ์ ํ ๊ด๋ฆฌ
โ ํด๋ฐฑ ๋ฉ์ปค๋์ฆ
โ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
โ ์ฌ์ฉ์ ํผ๋๋ฐฑ ์์ง
โ ์ ๊ธฐ์ ์ธ ๋ชจ๋ธ ์ ๋ฐ์ดํธ
๐จ ์ค์ ํ๋ก์ ํธ: ์ฝํ ์ธ ํ๋ ์ด์ ํ๋ซํผ
์ด์ ์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ชจ๋ ๊ฑธ ํ์ฉํด์ ์ค์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์! ๋ฐ๋ก AI ๊ธฐ๋ฐ ์ฝํ ์ธ ํ๋ ์ด์ ํ๋ซํผ์ด์ผ. ๐ฐ
ํ๋ก์ ํธ ๊ฐ์
์ด ํ๋ซํผ์ ๋ค์ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด:
โข ๋ค์ํ ์์ค์์ ์ฝํ
์ธ ์์ง
โข ์๋ ์์ฝ ๋ฐ ๋ถ๋ฅ
โข ์ฌ์ฉ์ ๊ด์ฌ์ฌ ๊ธฐ๋ฐ ์ถ์ฒ
โข ํธ๋ ๋ ๋ถ์
โข ๊ฐ์ธํ๋ ๋ด์ค๋ ํฐ ์์ฑ
class ContentCurationPlatform:
"""AI ๊ธฐ๋ฐ ์ฝํ
์ธ ํ๋ ์ด์
ํ๋ซํผ"""
def __init__(self):
self.agent = UnifiedAgent()
self.classifier = AdvancedClassifier()
self.cache = CacheManager()
self.cost_monitor = CostMonitor()
# ์ฝํ
์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค (์ค์ ๋ก๋ PostgreSQL ๋ฑ ์ฌ์ฉ)
self.content_db = []
self.user_preferences = {}
async def ingest_content(self, content: Dict) -> Dict:
"""
์ฝํ
์ธ ์์ง ๋ฐ ์ฒ๋ฆฌ
Args:
content: {
'title': str,
'body': str,
'source': str,
'url': str,
'published_date': str
}
"""
# 1. ์ค๋ณต ์ฒดํฌ
if self._is_duplicate(content):
return {'status': 'duplicate', 'content_id': None}
# 2. ์ฝํ
์ธ ์ฒ๋ฆฌ
full_text = f"{content['title']}\n\n{content['body']}"
processed = await self.agent.process_document(
text=full_text,
categories=[
'๊ธฐ์ ', '๋น์ฆ๋์ค', '๊ณผํ', '๋ฌธํ', '์ ์น',
'๊ฒฝ์ ', '์คํฌ์ธ ', '๊ฑด๊ฐ', '๊ต์ก', '์ํฐํ
์ธ๋จผํธ'
],
summarize=True,
classify=True,
extract_entities=True
)
# 3. ํ์ง ์ ์ ๊ณ์ฐ
quality_score = self._calculate_quality_score(content, processed)
# 4. ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ
content_entry = {
'id': self._generate_id(),
'original': content,
'processed': processed,
'quality_score': quality_score,
'ingested_at': datetime.now().isoformat(),
'view_count': 0,
'engagement_score': 0
}
self.content_db.append(content_entry)
# 5. ๋น์ฉ ๋ก๊น
self.cost_monitor.log_api_call(
'gemini-pro',
input_tokens=len(full_text) // 4,
output_tokens=len(str(processed)) // 4,
operation='content_ingestion'
)
return {
'status': 'success',
'content_id': content_entry['id'],
'summary': processed.get('summary', {}).get('summary'),
'category': processed.get('classification', {}).get('primary_category')
}
def _is_duplicate(self, content: Dict) -> bool:
"""์ค๋ณต ์ฝํ
์ธ ์ฒดํฌ"""
# ์ ๋ชฉ ๊ธฐ๋ฐ ๊ฐ๋จํ ์ค๋ณต ์ฒดํฌ
title = content['title'].lower()
for item in self.content_db:
if item['original']['title'].lower() == title:
return True
return False
def _calculate_quality_score(self, content: Dict, processed: Dict) -> float:
"""์ฝํ
์ธ ํ์ง ์ ์ ๊ณ์ฐ"""
score = 0.5 # ๊ธฐ๋ณธ ์ ์
# ์์ฝ ํ์ง
if 'summary' in processed:
summary = processed['summary']
if summary.get('compression_ratio', 0) < 0.5:
score += 0.1
# ๋ถ๋ฅ ์ ๋ขฐ๋
if 'classification' in processed:
confidence = processed['classification'].get('confidence', 0)
score += confidence * 0.2
# ๊ฐ์ฒด๋ช
์ถ์ถ ํ์ง
if 'entities' in processed:
entities = processed['entities']
entity_count = sum(len(v) for v in entities.values() if isinstance(v, list))
score += min(entity_count * 0.05, 0.2)
return min(score, 1.0)
def get_personalized_feed(self, user_id: str, limit: int = 10) -> List[Dict]:
"""๊ฐ์ธํ๋ ํผ๋ ์์ฑ"""
user_prefs = self.user_preferences.get(user_id, {})
preferred_categories = user_prefs.get('categories', [])
# ์ฝํ
์ธ ํํฐ๋ง ๋ฐ ์ ์ ๊ณ์ฐ
scored_content = []
for content in self.content_db:
score = self._calculate_relevance_score(content, user_prefs)
scored_content.append((score, content))
# ์ ์์ ์ ๋ ฌ
scored_content.sort(reverse=True, key=lambda x: x[0])
# ์์ N๊ฐ ๋ฐํ
return [
{
'id': content['id'],
'title': content['original']['title'],
'summary': content['processed'].get('summary', {}).get('summary'),
'category': content['processed'].get('classification', {}).get('primary_category'),
'relevance_score': score,
'quality_score': content['quality_score']
}
for score, content in scored_content[:limit]
]
def _calculate_relevance_score(self, content: Dict, user_prefs: Dict) -> float:
"""์ฌ์ฉ์ ๊ด๋ จ์ฑ ์ ์ ๊ณ์ฐ"""
score = content['quality_score']
# ์นดํ
๊ณ ๋ฆฌ ๋งค์นญ
content_category = content['processed'].get('classification', {}).get('primary_category')
preferred_categories = user_prefs.get('categories', [])
if content_category in preferred_categories:
score += 0.3
# ์ต์ ์ฑ ๋ณด๋์ค
ingested_time = datetime.fromisoformat(content['ingested_at'])
hours_old = (datetime.now() - ingested_time).total_seconds() / 3600
if hours_old < 24:
score += 0.2 * (1 - hours_old / 24)
# ์ธ๊ธฐ๋ ๋ณด๋์ค
engagement = content.get('engagement_score', 0)
score += min(engagement * 0.1, 0.2)
return score
async def generate_newsletter(self, user_id: str) -> str:
"""๊ฐ์ธํ๋ ๋ด์ค๋ ํฐ ์์ฑ"""
feed = self.get_personalized_feed(user_id, limit=5)
if not feed:
return "์ค๋์ ์๋ก์ด ์ฝํ
์ธ ๊ฐ ์์ต๋๋ค."
# ๋ด์ค๋ ํฐ ํ๋กฌํํธ ์์ฑ
content_summaries = '\n\n'.join([
f"**{item['title']}**\n{item['summary']}\n์นดํ
๊ณ ๋ฆฌ: {item['category']}"
for item in feed
])
prompt = f"""
๋ค์ ์ฝํ
์ธ ๋ค์ ๋ฐํ์ผ๋ก ๋งค๋ ฅ์ ์ธ ๋ด์ค๋ ํฐ๋ฅผ ์์ฑํ์ธ์.
์น๊ทผํ๊ณ ํฅ๋ฏธ๋ก์ด ํค์ผ๋ก ์์ฑํ๋, ๊ฐ ์ฝํ
์ธ ์ ํต์ฌ์ ์ ์ ๋ฌํ์ธ์.
์ฝํ
์ธ :
{content_summaries}
๋ด์ค๋ ํฐ ํ์:
- ์ธ์ฌ๋ง๋ก ์์
- ๊ฐ ์ฝํ
์ธ ๋ฅผ ๊ฐ๋ตํ ์๊ฐ
- ๋ง๋ฌด๋ฆฌ ์ธ์ฌ
"""
newsletter = await self.agent.model.generate_content(prompt)
return newsletter.text
def get_trending_topics(self, time_window_hours: int = 24) -> List[Dict]:
"""ํธ๋ ๋ฉ ํ ํฝ ๋ถ์"""
cutoff_time = datetime.now() - timedelta(hours=time_window_hours)
# ์ต๊ทผ ์ฝํ
์ธ ํํฐ๋ง
recent_content = [
c for c in self.content_db
if datetime.fromisoformat(c['ingested_at']) > cutoff_time
]
# ์นดํ
๊ณ ๋ฆฌ๋ณ ์ง๊ณ
category_counts = {}
category_engagement = {}
for content in recent_content:
category = content['processed'].get('classification', {}).get('primary_category', 'Unknown')
category_counts[category] = category_counts.get(category, 0) + 1
category_engagement[category] = category_engagement.get(category, 0) + content.get('engagement_score', 0)
# ํธ๋ ๋ฉ ์ ์ ๊ณ์ฐ
trending = []
for category in category_counts:
score = (category_counts[category] * 0.6) + (category_engagement.get(category, 0) * 0.4)
trending.append({
'category': category,
'count': category_counts[category],
'engagement': category_engagement.get(category, 0),
'trending_score': score
})
trending.sort(reverse=True, key=lambda x: x['trending_score'])
return trending[:10]
def _generate_id(self) -> str:
"""๊ณ ์ ID ์์ฑ"""
import uuid
return str(uuid.uuid4())
1. ๊ธฐ์ ๋ด๋ถ ์ง์ ๊ด๋ฆฌ
ํ์ฌ ๋ด ๋ค์ํ ๋ฌธ์์ ๋ฆฌํฌํธ๋ฅผ ์๋์ผ๋ก ๋ถ๋ฅํ๊ณ ์์ฝํด์ ์ง์๋ค์๊ฒ ์ ๊ณต
2. ๋ฏธ๋์ด ๋ชจ๋ํฐ๋ง
ํน์ ํค์๋๋ ์ฃผ์ ์ ๋ํ ๋ด์ค๋ฅผ ์๋์ผ๋ก ์์งํ๊ณ ๋ถ์
3. ๊ฐ์ธ ํ์ต ๋๊ตฌ
๊ด์ฌ ๋ถ์ผ์ ์ต์ ์ ๋ณด๋ฅผ ์๋์ผ๋ก ํ๋ ์ด์ ํด์ ํ์ต ์๋ฃ๋ก ํ์ฉ
4. ๋ง์ผํ ์ธ์ฌ์ดํธ
์ ๊ณ ํธ๋ ๋์ ๊ฒฝ์์ฌ ๋ํฅ์ ์๋์ผ๋ก ๋ถ์ํ๊ณ ๋ฆฌํฌํธ ์์ฑ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ด๋ฐ ํ๋ ์ด์ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ฉด, ๋ง์ ๊ธฐ์ ๊ณผ ๊ฐ์ธ๋ค์ด ๊ด์ฌ์ ๊ฐ์ง ๊ฑฐ์ผ!
๐ฎ ๊ณ ๊ธ ๊ธฐ๋ฅ: ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ
Gemini API์ ์ง์ง ๊ฐ์ ์ ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ์ผ! ํ ์คํธ๋ฟ๋ง ์๋๋ผ ์ด๋ฏธ์ง, ๋์์๊น์ง ํจ๊ป ์ฒ๋ฆฌํ ์ ์๊ฑฐ๋ . ๐ฌ
์ด๋ฏธ์ง์ ํ ์คํธ ํตํฉ ๋ถ์์ด๋ฏธ์ง๊ฐ ํฌํจ๋ ๋ฌธ์๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์:
import PIL.Image
import io
import base64
class MultimodalAgent:
"""๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ ์์ด์ ํธ"""
def __init__(self):
self.vision_model = genai.GenerativeModel('gemini-pro-vision')
self.text_model = genai.GenerativeModel('gemini-pro')
def analyze_image_with_text(self, image_path: str, text_context: str = None) -> Dict:
"""
์ด๋ฏธ์ง์ ํ
์คํธ๋ฅผ ํจ๊ป ๋ถ์
Args:
image_path: ์ด๋ฏธ์ง ํ์ผ ๊ฒฝ๋ก
text_context: ์ถ๊ฐ ํ
์คํธ ์ปจํ
์คํธ
"""
# ์ด๋ฏธ์ง ๋ก๋
image = PIL.Image.open(image_path)
# ํ๋กฌํํธ ์์ฑ
if text_context:
prompt = f"""
๋ค์ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ํ๊ณ , ์ ๊ณต๋ ํ
์คํธ ์ปจํ
์คํธ์ ์ฐ๊ด์ง์ด ์ค๋ช
ํ์ธ์.
ํ
์คํธ ์ปจํ
์คํธ:
{text_context}
๋ค์ ํญ๋ชฉ์ ํฌํจํ์ฌ ๋ถ์ํ์ธ์:
1. ์ด๋ฏธ์ง์ ๋ฌด์์ด ๋ณด์ด๋๊ฐ?
2. ํ
์คํธ์ ์ด๋ฏธ์ง์ ๊ด๋ จ์ฑ
3. ์ฃผ์ ์ธ์ฌ์ดํธ
4. ์ถ์ฒ ์ก์
"""
else:
prompt = """
์ด ์ด๋ฏธ์ง๋ฅผ ์์ธํ ๋ถ์ํ๊ณ ๋ค์์ ์ค๋ช
ํ์ธ์:
1. ์ฃผ์ ๊ฐ์ฒด์ ์ฅ๋ฉด
2. ์ด๋ฏธ์ง์ ๋ชฉ์ ์ด๋ ๋ฉ์์ง
3. ์ฃผ๋ชฉํ ๋งํ ์ธ๋ถ์ฌํญ
4. ๊ฐ๋ฅํ ํ์ฉ ๋ฐฉ์
"""
# ์ด๋ฏธ์ง ๋ถ์
response = self.vision_model.generate_content([prompt, image])
return {
'analysis': response.text,
'image_path': image_path,
'has_context': text_context is not None
}
def extract_text_from_image(self, image_path: str) -> str:
"""์ด๋ฏธ์ง์์ ํ
์คํธ ์ถ์ถ (OCR)"""
image = PIL.Image.open(image_path)
prompt = """
์ด ์ด๋ฏธ์ง์ ์๋ ๋ชจ๋ ํ
์คํธ๋ฅผ ์ถ์ถํ์ธ์.
ํ
์คํธ์ ์๋ ํ์๊ณผ ๊ตฌ์กฐ๋ฅผ ์ต๋ํ ์ ์งํ์ธ์.
"""
response = self.vision_model.generate_content([prompt, image])
return response.text
def analyze_document_with_images(self,
text: str,
image_paths: List[str]) -> Dict:
"""
ํ
์คํธ์ ์ฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ํฌํจํ ๋ฌธ์ ์ข
ํฉ ๋ถ์
"""
results = {
'text_analysis': {},
'image_analyses': [],
'integrated_summary': ''
}
# 1. ํ
์คํธ ๋ถ์
text_prompt = f"""
๋ค์ ๋ฌธ์๋ฅผ ๋ถ์ํ์ธ์:
{text}
ํต์ฌ ๋ด์ฉ์ ์์ฝํ๊ณ ์ฃผ์ ํฌ์ธํธ๋ฅผ ์ถ์ถํ์ธ์.
"""
text_response = self.text_model.generate_content(text_prompt)
results['text_analysis'] = {
'summary': text_response.text,
'length': len(text)
}
# 2. ๊ฐ ์ด๋ฏธ์ง ๋ถ์
for idx, image_path in enumerate(image_paths):
image_analysis = self.analyze_image_with_text(image_path, text[:500])
results['image_analyses'].append({
'image_index': idx,
'path': image_path,
'analysis': image_analysis['analysis']
})
# 3. ํตํฉ ์์ฝ ์์ฑ
integrated_prompt = f"""
๋ค์์ ๋ฌธ์์ ํ
์คํธ ๋ถ์๊ณผ ์ด๋ฏธ์ง ๋ถ์ ๊ฒฐ๊ณผ์
๋๋ค.
์ด๋ฅผ ํตํฉํ์ฌ ๋ฌธ์ ์ ์ฒด์ ๋ํ ์ข
ํฉ์ ์ธ ์์ฝ์ ์์ฑํ์ธ์.
ํ
์คํธ ์์ฝ:
{results['text_analysis']['summary']}
์ด๋ฏธ์ง ๋ถ์๋ค:
{chr(10).join([f"์ด๋ฏธ์ง {i+1}: {img['analysis'][:200]}..." for i, img in enumerate(results['image_analyses'])])}
ํตํฉ ์์ฝ์ ์์ฑํ์ธ์ (300์ ์ด๋ด):
"""
integrated_response = self.text_model.generate_content(integrated_prompt)
results['integrated_summary'] = integrated_response.text
return results
def classify_image(self, image_path: str, categories: List[str]) -> Dict:
"""์ด๋ฏธ์ง ๋ถ๋ฅ"""
image = PIL.Image.open(image_path)
categories_str = ', '.join(categories)
prompt = f"""
์ด ์ด๋ฏธ์ง๋ฅผ ๋ค์ ์นดํ
๊ณ ๋ฆฌ ์ค ํ๋๋ก ๋ถ๋ฅํ์ธ์: {categories_str}
JSON ํ์์ผ๋ก ์๋ต:
{{
"category": "์ ํ๋ ์นดํ
๊ณ ๋ฆฌ",
"confidence": 0.95,
"reasoning": "๋ถ๋ฅ ๊ทผ๊ฑฐ"
}}
"""
response = self.vision_model.generate_content([prompt, image])
try:
json_start = response.text.find('{')
json_end = response.text.rfind('}') + 1
return json.loads(response.text[json_start:json_end])
except:
return {
'category': 'Unknown',
'confidence': 0.0,
'reasoning': 'Parse error'
}
์ค์ ์์ : ์์
๋ฏธ๋์ด ์ฝํ
์ธ ๋ถ์
์์ ๋ฏธ๋์ด ํฌ์คํธ๋ฅผ ์๋์ผ๋ก ๋ถ์ํ๋ ์์คํ ์ ๋ง๋ค์ด๋ณด์:
class SocialMediaAnalyzer:
"""์์
๋ฏธ๋์ด ์ฝํ
์ธ ๋ถ์๊ธฐ"""
def __init__(self):
self.multimodal = MultimodalAgent()
self.text_model = genai.GenerativeModel('gemini-pro')
def analyze_post(self, post: Dict) -> Dict:
"""
์์
๋ฏธ๋์ด ํฌ์คํธ ์ข
ํฉ ๋ถ์
Args:
post: {
'text': str,
'images': List[str], # ์ด๋ฏธ์ง ๊ฒฝ๋ก๋ค
'platform': str, # 'instagram', 'twitter', 'facebook' ๋ฑ
'author': str,
'timestamp': str
}
"""
analysis = {
'post_id': post.get('id', 'unknown'),
'platform': post['platform'],
'timestamp': post['timestamp']
}
# 1. ํ
์คํธ ๊ฐ์ ๋ถ์
if post.get('text'):
analysis['sentiment'] = self._analyze_sentiment(post['text'])
analysis['topics'] = self._extract_topics(post['text'])
analysis['hashtags'] = self._extract_hashtags(post['text'])
# 2. ์ด๋ฏธ์ง ๋ถ์
if post.get('images'):
analysis['image_analysis'] = []
for img_path in post['images']:
img_result = self.multimodal.analyze_image_with_text(
img_path,
post.get('text', '')
)
analysis['image_analysis'].append(img_result)
# 3. ์ฝํ
์ธ ํ์ง ํ๊ฐ
analysis['quality_metrics'] = self._evaluate_quality(post, analysis)
# 4. ์ฐธ์ฌ๋ ์์ธก
analysis['engagement_prediction'] = self._predict_engagement(analysis)
# 5. ์ถ์ฒ ์ฌํญ
analysis['recommendations'] = self._generate_recommendations(analysis)
return analysis
def _analyze_sentiment(self, text: str) -> Dict:
"""๊ฐ์ ๋ถ์"""
prompt = f"""
๋ค์ ์์
๋ฏธ๋์ด ํฌ์คํธ์ ๊ฐ์ ์ ๋ถ์ํ์ธ์:
"{text}"
JSON ํ์์ผ๋ก ์๋ต:
{{
"overall_sentiment": "positive/neutral/negative",
"sentiment_score": -1.0์์ 1.0 ์ฌ์ด,
"emotions": ["๊ธฐ์จ", "ํฅ๋ถ" ๋ฑ ๊ฐ์ ๋ฆฌ์คํธ],
"tone": "์บ์ฃผ์ผ/์ ๋ฌธ์ /์ ๋จธ๋ฌ์ค ๋ฑ"
}}
"""
response = self.text_model.generate_content(prompt)
try:
json_start = response.text.find('{')
json_end = response.text.rfind('}') + 1
return json.loads(response.text[json_start:json_end])
except:
return {'overall_sentiment': 'neutral', 'sentiment_score': 0.0}
def _extract_topics(self, text: str) -> List[str]:
"""์ฃผ์ ์ถ์ถ"""
prompt = f"""
๋ค์ ํ
์คํธ์์ ์ฃผ์ ์ฃผ์ /ํ ํฝ์ 3-5๊ฐ ์ถ์ถํ์ธ์:
"{text}"
์ฃผ์ ๋ง ์ผํ๋ก ๊ตฌ๋ถํ์ฌ ๋์ดํ์ธ์.
"""
response = self.text_model.generate_content(prompt)
topics = [t.strip() for t in response.text.split(',')]
return topics[:5]
def _extract_hashtags(self, text: str) -> List[str]:
"""ํด์ํ๊ทธ ์ถ์ถ"""
import re
hashtags = re.findall(r'#\w+', text)
return hashtags
def _evaluate_quality(self, post: Dict, analysis: Dict) -> Dict:
"""์ฝํ
์ธ ํ์ง ํ๊ฐ"""
quality = {
'text_quality': 0.5,
'visual_quality': 0.5,
'engagement_potential': 0.5,
'overall_score': 0.5
}
# ํ
์คํธ ํ์ง
text = post.get('text', '')
if text:
# ๊ธธ์ด ์ ์ ์ฑ
if 50 < len(text) < 300:
quality['text_quality'] += 0.2
# ํด์ํ๊ทธ ์ฌ์ฉ
if analysis.get('hashtags'):
quality['text_quality'] += 0.1
# ๊ฐ์ ๋ช
ํ์ฑ
sentiment = analysis.get('sentiment', {})
if abs(sentiment.get('sentiment_score', 0)) > 0.5:
quality['text_quality'] += 0.2
# ๋น์ฃผ์ผ ํ์ง
if post.get('images'):
quality['visual_quality'] += 0.3
if len(post['images']) > 1:
quality['visual_quality'] += 0.2
# ์ ์ฒด ์ ์
quality['overall_score'] = (
quality['text_quality'] * 0.4 +
quality['visual_quality'] * 0.3 +
quality['engagement_potential'] * 0.3
)
return quality
def _predict_engagement(self, analysis: Dict) -> Dict:
"""์ฐธ์ฌ๋ ์์ธก"""
# ๊ฐ๋จํ ํด๋ฆฌ์คํฑ ๊ธฐ๋ฐ ์์ธก
score = 0.5
# ๊ฐ์ ์ด ๊ฐํ ์๋ก ์ฐธ์ฌ๋ ๋์
sentiment = analysis.get('sentiment', {})
sentiment_score = abs(sentiment.get('sentiment_score', 0))
score += sentiment_score * 0.2
# ์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด ์ฐธ์ฌ๋ ๋์
if analysis.get('image_analysis'):
score += 0.2
# ํด์ํ๊ทธ๊ฐ ์ ์ ํ๋ฉด ์ฐธ์ฌ๋ ๋์
hashtag_count = len(analysis.get('hashtags', []))
if 2 <= hashtag_count <= 5:
score += 0.1
return {
'predicted_score': min(score, 1.0),
'confidence': 0.7,
'factors': {
'sentiment_impact': sentiment_score * 0.2,
'visual_impact': 0.2 if analysis.get('image_analysis') else 0,
'hashtag_impact': 0.1 if 2 <= hashtag_count <= 5 else 0
}
}
def _generate_recommendations(self, analysis: Dict) -> List[str]:
"""๊ฐ์ ์ถ์ฒ์ฌํญ ์์ฑ"""
recommendations = []
# ํ
์คํธ ๊ด๋ จ
if not analysis.get('hashtags'):
recommendations.append("๊ด๋ จ ํด์ํ๊ทธ๋ฅผ 2-3๊ฐ ์ถ๊ฐํ์ธ์")
sentiment = analysis.get('sentiment', {})
if abs(sentiment.get('sentiment_score', 0)) < 0.3:
recommendations.append("๋ ๋ช
ํํ ๊ฐ์ ์ด๋ ์๊ฒฌ์ ํํํ์ธ์")
# ์ด๋ฏธ์ง ๊ด๋ จ
if not analysis.get('image_analysis'):
recommendations.append("์๊ฐ์ ์์(์ด๋ฏธ์ง/๋์์)๋ฅผ ์ถ๊ฐํ์ธ์")
# ํ์ง ๊ด๋ จ
quality = analysis.get('quality_metrics', {})
if quality.get('overall_score', 0) < 0.6:
recommendations.append("์ฝํ
์ธ ์ ์ ๋ฐ์ ์ธ ํ์ง์ ๊ฐ์ ํ์ธ์")
return recommendations
1. ์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ
์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ ์ ํ ์กฐ์ ํ๊ณ ํ์ง์ ์ต์ ํํด์ API ํธ์ถ ๋น์ฉ์ ์ค์ฌ.
2. ์ปจํ ์คํธ ์ ๊ณต
์ด๋ฏธ์ง๋ง ๋ณด๋ด๋ ๊ฒ๋ณด๋ค ๊ด๋ จ ํ ์คํธ ์ปจํ ์คํธ๋ฅผ ํจ๊ป ์ ๊ณตํ๋ฉด ๋ ์ ํํ ๋ถ์์ ์ป์ ์ ์์ด.
3. ๋ฐฐ์น ์ฒ๋ฆฌ
์ฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํ ๋๋ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ํ์ฉํด์ ์๊ฐ์ ์ ์ฝํด.
4. ๊ฒฐ๊ณผ ๊ฒ์ฆ
AI์ ์ด๋ฏธ์ง ๋ถ์ ๊ฒฐ๊ณผ๋ ํญ์ ๊ฒ์ฆ์ด ํ์ํด. ํนํ ์ค์ํ ์์ฌ๊ฒฐ์ ์ ์ฌ์ฉํ ๋๋ ๋์ฑ ๊ทธ๋.
๐ ๋ชจ๋ํฐ๋ง๊ณผ ๊ฐ์
์์ด์ ํธ๋ฅผ ๋ฐฐํฌํ ํ์๋ ์ง์์ ์ธ ๋ชจ๋ํฐ๋ง๊ณผ ๊ฐ์ ์ด ํ์ํด. ์ค์ ์ฌ์ฉ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํด์ ์์คํ ์ ๊ณ์ ๋ฐ์ ์์ผ์ผ ํ๊ฑฐ๋ ! ๐
์ฑ๋ฅ ๋ฉํธ๋ฆญ ์ถ์ ์์คํ ์ ์ฑ๋ฅ์ ์ถ์ ํ๋ ๋์๋ณด๋๋ฅผ ๋ง๋ค์ด๋ณด์:
class PerformanceMonitor:
"""์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ์์คํ
"""
def __init__(self):
self.metrics = {
'api_calls': [],
'response_times': [],
'error_rates': [],
'user_satisfaction': []
}
def log_api_call(self,
operation: str,
response_time: float,
success: bool,
token_usage: Dict):
"""API ํธ์ถ ๋ก๊น
"""
self.metrics['api_calls'].append({
'timestamp': datetime.now(),
'operation': operation,
'response_time': response_time,
'success': success,
'token_usage': token_usage
})
self.metrics['response_times'].append(response_time)
if not success:
self.metrics['error_rates'].append({
'timestamp': datetime.now(),
'operation': operation
})
def get_performance_summary(self, time_window_hours: int = 24) -> Dict:
"""์ฑ๋ฅ ์์ฝ ํต๊ณ"""
cutoff = datetime.now() - timedelta(hours=time_window_hours)
recent_calls = [
call for call in self.metrics['api_calls']
if call['timestamp'] > cutoff
]
if not recent_calls:
return {}
total_calls = len(recent_calls)
successful_calls = sum(1 for call in recent_calls if call['success'])
failed_calls = total_calls - successful_calls
response_times = [call['response_time'] for call in recent_calls]
avg_response_time = sum(response_times) / len(response_times)
return {
'time_window': f'{time_window_hours}์๊ฐ',
'total_calls': total_calls,
'successful_calls': successful_calls,
'failed_calls': failed_calls,
'success_rate': f'{(successful_calls/total_calls*100):.2f}%',
'average_response_time': f'{avg_response_time:.2f}์ด',
'min_response_time': f'{min(response_times):.2f}์ด',
'max_response_time': f'{max(response_times):.2f}์ด'
}
def get_operation_breakdown(self) -> Dict:
"""์์
๋ณ ํต๊ณ"""
operation_stats = {}
for call in self.metrics['api_calls']:
op = call['operation']
if op not in operation_stats:
operation_stats[op] = {
'count': 0,
'total_time': 0,
'successes': 0,
'failures': 0
}
operation_stats[op]['count'] += 1
operation_stats[op]['total_time'] += call['response_time']
if call['success']:
operation_stats[op]['successes'] += 1
else:
operation_stats[op]['failures'] += 1
# ํ๊ท ๊ณ์ฐ
for op in operation_stats:
stats = operation_stats[op]
stats['avg_time'] = stats['total_time'] / stats['count']
stats['success_rate'] = (stats['successes'] / stats['count'] * 100)
return operation_stats
def detect_anomalies(self) -> List[Dict]:
"""์ด์ ์งํ ๊ฐ์ง"""
anomalies = []
# ์ต๊ทผ 1์๊ฐ ๋ฐ์ดํฐ
recent_hour = datetime.now() - timedelta(hours=1)
recent_calls = [
call for call in self.metrics['api_calls']
if call['timestamp'] > recent_hour
]
if not recent_calls:
return anomalies
# ์๋ต ์๊ฐ ์ด์
response_times = [call['response_time'] for call in recent_calls]
avg_time = sum(response_times) / len(response_times)
for call in recent_calls[-10:]: # ์ต๊ทผ 10๊ฐ ์ฒดํฌ
if call['response_time'] > avg_time * 2:
anomalies.append({
'type': 'slow_response',
'timestamp': call['timestamp'],
'operation': call['operation'],
'response_time': call['response_time'],
'threshold': avg_time * 2
})
# ์๋ฌ์จ ์ด์
recent_errors = [call for call in recent_calls if not call['success']]
error_rate = len(recent_errors) / len(recent_calls)
if error_rate > 0.1: # 10% ์ด์
anomalies.append({
'type': 'high_error_rate',
'timestamp': datetime.now(),
'error_rate': f'{error_rate*100:.2f}%',
'threshold': '10%'
})
return anomalies
def generate_report(self) -> str:
"""์ข
ํฉ ๋ฆฌํฌํธ ์์ฑ"""
summary = self.get_performance_summary(24)
operations = self.get_operation_breakdown()
anomalies = self.detect_anomalies()
report = f"""
=== ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๋ฆฌํฌํธ ===
์์ฑ ์๊ฐ: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
[์ ์ฒด ์์ฝ (24์๊ฐ)]
- ์ด API ํธ์ถ: {summary.get('total_calls', 0)}ํ
- ์ฑ๊ณต๋ฅ : {summary.get('success_rate', 'N/A')}
- ํ๊ท ์๋ต ์๊ฐ: {summary.get('average_response_time', 'N/A')}
[์์
๋ณ ํต๊ณ]
"""
for op, stats in operations.items():
report += f"\n{op}:"
report += f"\n - ํธ์ถ ํ์: {stats['count']}ํ"
report += f"\n - ํ๊ท ์๊ฐ: {stats['avg_time']:.2f}์ด"
report += f"\n - ์ฑ๊ณต๋ฅ : {stats['success_rate']:.2f}%"
if anomalies:
report += "\n\n[โ ๏ธ ๊ฐ์ง๋ ์ด์ ์งํ]"
for anomaly in anomalies:
report += f"\n- {anomaly['type']}: {anomaly}"
else:
report += "\n\n[โ
์ด์ ์งํ ์์]"
return report
์ฌ์ฉ์ ํผ๋๋ฐฑ ์์ง
์ฌ์ฉ์ ํผ๋๋ฐฑ์ ์์งํ๊ณ ๋ถ์ํ๋ ์์คํ ๋ ์ค์ํด:
class FeedbackCollector:
"""์ฌ์ฉ์ ํผ๋๋ฐฑ ์์ง ๋ฐ ๋ถ์"""
def __init__(self):
self.feedback_db = []
self.text_model = genai.GenerativeModel('gemini-pro')
def collect_feedback(self,
user_id: str,
operation: str,
result_id: str,
rating: int,
comment: str = None):
"""ํผ๋๋ฐฑ ์์ง"""
feedback = {
'id': self._generate_id(),
'user_id': user_id,
'operation': operation,
'result_id': result_id,
'rating': rating, # 1-5
'comment': comment,
'timestamp': datetime.now().isoformat()
}
self.feedback_db.append(feedback)
# ๋ถ์ ์ ํผ๋๋ฐฑ ์ฆ์ ์๋ฆผ
if rating <= 2:
self._alert_negative_feedback(feedback)
def analyze_feedback_trends(self) -> Dict:
"""ํผ๋๋ฐฑ ํธ๋ ๋ ๋ถ์"""
if not self.feedback_db:
return {}
# ํ๊ท ํ์
avg_rating = sum(f['rating'] for f in self.feedback_db) / len(self.feedback_db)
# ์์
๋ณ ํ์
operation_ratings = {}
for feedback in self.feedback_db:
op = feedback['operation']
if op not in operation_ratings:
operation_ratings[op] = []
operation_ratings[op].append(feedback['rating'])
operation_avg = {
op: sum(ratings) / len(ratings)
for op, ratings in operation_ratings.items()
}
# ์ต๊ทผ ํธ๋ ๋ (์ต๊ทผ 30์ผ)
recent_cutoff = datetime.now() - timedelta(days=30)
recent_feedback = [
f for f in self.feedback_db
if datetime.fromisoformat(f['timestamp']) > recent_cutoff
]
recent_avg = sum(f['rating'] for f in recent_feedback) / len(recent_feedback) if recent_feedback else 0
return {
'overall_average': f'{avg_rating:.2f}',
'recent_average': f'{recent_avg:.2f}',
'total_feedback': len(self.feedback_db),
'operation_breakdown': operation_avg,
'trend': 'improving' if recent_avg > avg_rating else 'declining'
}
def extract_improvement_suggestions(self) -> List[str]:
"""๊ฐ์ ์ ์ ์ถ์ถ"""
# ๋ถ์ ์ ํผ๋๋ฐฑ์ ์ฝ๋ฉํธ ์์ง
negative_feedback = [
f for f in self.feedback_db
if f['rating'] <= 2 and f.get('comment')
]
if not negative_feedback:
return []
comments = '\n'.join([f['comment'] for f in negative_feedback])
prompt = f"""
๋ค์์ ์ฌ์ฉ์๋ค์ ๋ถ์ ์ ํผ๋๋ฐฑ ์ฝ๋ฉํธ๋ค์
๋๋ค.
์ด๋ฅผ ๋ถ์ํ์ฌ ์์คํ
๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์์ฌํญ์ 3-5๊ฐ ์ถ์ถํ์ธ์.
ํผ๋๋ฐฑ:
{comments}
๊ฐ์ ์ ์์ฌํญ์ ๋ฒํธ๋ฅผ ๋งค๊ฒจ ๋์ดํ์ธ์.
"""
response = self.text_model.generate_content(prompt)
suggestions = [
line.strip()
for line in response.text.split('\n')
if line.strip() and any(c.isdigit() for c in line[:3])
]
return suggestions[:5]
def _alert_negative_feedback(self, feedback: Dict):
"""๋ถ์ ์ ํผ๋๋ฐฑ ์๋ฆผ"""
print(f"""
โ ๏ธ ๋ถ์ ์ ํผ๋๋ฐฑ ๊ฐ์ง!
์ฌ์ฉ์: {feedback['user_id']}
์์
: {feedback['operation']}
ํ์ : {feedback['rating']}/5
์ฝ๋ฉํธ: {feedback.get('comment', 'N/A')}
""")
def _generate_id(self) -> str:
import uuid
return str(uuid.uuid4())
1. ์ธก์ (Measure)
์ฑ๋ฅ ๋ฉํธ๋ฆญ, ์ฌ์ฉ์ ํผ๋๋ฐฑ, ๋น์ฉ ๋ฐ์ดํฐ ์์ง
2. ๋ถ์ (Analyze)
ํธ๋ ๋ ํ์ , ๋ฌธ์ ์ ์๋ณ, ๊ฐ์ ๊ธฐํ ๋ฐ๊ฒฌ
3. ๊ฐ์ (Improve)
ํ๋กฌํํธ ์ต์ ํ, ๋ก์ง ๊ฐ์ , ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
4. ๊ฒ์ฆ (Validate)
A/B ํ ์คํธ, ์ฑ๋ฅ ๋น๊ต, ์ฌ์ฉ์ ๋ง์กฑ๋ ํ์ธ
์ด ์ฌ์ดํด์ 2-4์ฃผ๋ง๋ค ๋ฐ๋ณตํ๋ฉด์ ์ง์์ ์ผ๋ก ์์คํ ์ ๊ฐ์ ํด ๋๊ฐ๋ ๊ฒ ์ค์ํด!
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ ๋ง ๊ธด ์ฌ์ ์ด์์ง? ๐ ์ฐ๋ฆฌ๋ Gemini API๋ฅผ ํ์ฉํด์ ์๋์์ฝ๊ณผ ์๋๋ถ๋ฅ ์์ด์ ํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์ฒ์๋ถํฐ ๋๊น์ง ์ดํด๋ดค์ด.
ํต์ฌ ๋ด์ฉ์ ์ ๋ฆฌํ๋ฉด:
โ
๊ธฐ๋ณธ ์ค์
โข API ํค ๋ฐ๊ธ ๋ฐ ๋ณด์ ๊ด๋ฆฌ
โข ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถ
โข ๊ธฐ๋ณธ ๋ชจ๋ธ ์ด๊ธฐํ
โ
์๋์์ฝ
โข ๊ธฐ๋ณธ ์์ฝ๋ถํฐ ๊ณ ๊ธ ๋ฉํฐํจ์ค ์์ฝ๊น์ง
โข ๋ค์ํ ์คํ์ผ๊ณผ ๊ธธ์ด ์ง์
โข ๊ธด ๋ฌธ์ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฒญํฌ ๋ถํ
โ
์๋๋ถ๋ฅ
โข ๋จ์ผ/๋ค์ค ๋ ์ด๋ธ ๋ถ๋ฅ
โข ๊ณ์ธต์ ๋ถ๋ฅ ์์คํ
โข ์ ๋ขฐ๋ ๊ธฐ๋ฐ ํํฐ๋ง
โ
ํตํฉ ์์ด์ ํธ
โข ์์ฝ๊ณผ ๋ถ๋ฅ์ ๊ฒฐํฉ
โข ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ (ํ
์คํธ + ์ด๋ฏธ์ง)
โข ์ค์ ํ๋ก์ ํธ ์์
โ
์ต์ ํ ๋ฐ ์ด์
โข ์ฑ๋ฅ ์ต์ ํ์ ๋น์ฉ ๊ด๋ฆฌ
โข ์๋ฌ ํธ๋ค๋ง๊ณผ ์์ ์ฑ
โข ๋ชจ๋ํฐ๋ง๊ณผ ์ง์์ ๊ฐ์
์ด์ ๋น์ ๋ ์ค์ ๋ก ์ฌ์ฉํ ์ ์๋ AI ์์ด์ ํธ๋ฅผ ๋ง๋ค ์ ์์ด! ๐
์์์ ์๊ฒ ํด๋ ๊ด์ฐฎ์. ๊ฐ๋จํ ์์ฝ ๊ธฐ๋ฅ๋ถํฐ ์์ํด์, ์ ์ฐจ ๋ถ๋ฅ, ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ ๋ฑ์ ์ถ๊ฐํด ๋๊ฐ๋ฉด ๋ผ. ์ค์ํ ๊ฑด ์ค์ ๋ก ๋ง๋ค์ด๋ณด๊ณ , ์ฌ์ฉ์ ํผ๋๋ฐฑ์ ๋ฐ์์ ๊ณ์ ๊ฐ์ ํด ๋๊ฐ๋ ๊ฒ์ด์ผ.
ํน์ ์ด๋ฐ AI ์๋ํ ์์คํ
์ ์ง์ ๊ตฌ์ถํ๊ณ ์ถ์๋ฐ ๋์์ด ํ์ํ๋ค๋ฉด? ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ๋ค์ ๋์์ ๋ฐ์ ์๋ ์์ด. ๋๋ ๋ฐ๋๋ก, ๋น์ ์ด ์ด ๋ถ์ผ์ ์ ๋ฌธ๊ฐ๋ผ๋ฉด ์ฌ๋ฅ๋ท์์ ๋น์ ์ ์ฌ๋ฅ์ ๊ณต์ ํ ์๋ ์๊ณ ! ๐
AI ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. Gemini API๋ ๊ณ์ ์
๋ฐ์ดํธ๋๋ฉด์ ์๋ก์ด ๊ธฐ๋ฅ๋ค์ด ์ถ๊ฐ๋๊ณ ์์ง. ๊ทธ๋ฌ๋ ๊ณต์ ๋ฌธ์๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ํ์ธํ๊ณ , ์ปค๋ฎค๋ํฐ์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ์ ๋ณด๋ฅผ ๊ตํํ๋ ๊ฒ๋ ์์ง ๋ง!
์, ์ด์ ๋น์ ์ฐจ๋ก์ผ! ์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๋ฉ์ง AI ์์ด์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์. ํ์ดํ
! ๐ชโจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.