Ver3.0 ๐ค ๋ฉํฐ์์ด์ ํธ๋ก ํ์๋ก ์์ฑ + ๊ฒฐ๋ก ์์ฝ ์๋ํ

๐ค ๋ฉํฐ์์ด์ ํธ๋ก ํ์๋ก ์์ฑ + ๊ฒฐ๋ก ์์ฝ ์๋ํ
AI ์์ด์ ํธ๋ค์ด ํ์ ํด์ ํ์๋ก์ ์๋์ผ๋ก ์์ฑํ๊ณ ์์ฝ๊น์ง! ๐
๐ฏ ์ ๋ฉํฐ์์ด์ ํธ๊ฐ ํ์ํ ๊น?
์์ฆ ํ์๊ฐ ์ ๋ง ๋ง์ง ์์? ๐ ํ๋ฃจ์๋ ๋ช ๋ฒ์ฉ ํ์ํ์, ๋๋ฉดํ์๋ฅผ ํ๋ค ๋ณด๋ฉด ์ ์ ์ค์ํ ๊ฑด ํ์ ๋ด์ฉ์ ์ ๋ฆฌํ๋ ๊ฑฐ์ผ. ๊ทผ๋ฐ ์์งํ ํ์๋ก ์์ฑํ๋ ๊ฑฐ... ์ง์ง ๊ท์ฐฎ์์? ํ์ ์ค์ ํ๊ธฐํ๋๋ผ ์ง์ค๋ ์ ๋๊ณ , ํ์ ๋๋๊ณ ์ ๋ฆฌํ๋ ค๋ฉด ๋ ์๊ฐ ์์ฒญ ๊ฑธ๋ฆฌ๊ณ ๋ง์ด์ผ.๊ทธ๋์ ๋ฑ์ฅํ ๊ฒ ๋ฐ๋ก ๋ฉํฐ์์ด์ ํธ ์์คํ ์ด์ผ! ๐
์๋ฅผ ๋ค์ด:
โข ๐ค ์์ฑ์ธ์ ์์ด์ ํธ: ํ์ ๋ด์ฉ์ ๋ฃ๊ณ ํ ์คํธ๋ก ๋ณํ
โข โ๏ธ ํ์๋ก ์์ฑ ์์ด์ ํธ: ํ ์คํธ๋ฅผ ๊ตฌ์กฐํ๋ ํ์๋ก์ผ๋ก ์ ๋ฆฌ
โข ๐ ์์ฝ ์์ด์ ํธ: ํต์ฌ ๋ด์ฉ๋ง ์ถ๋ ค์ ๊ฒฐ๋ก ๋์ถ
โข ๐ ์ก์ ์์ดํ ์ถ์ถ ์์ด์ ํธ: ํ์ ์กฐ์น์ฌํญ ์ ๋ฆฌ
๐๏ธ ๋ฉํฐ์์ด์ ํธ ์์คํ ๊ตฌ์กฐ ์ดํดํ๊ธฐ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๋ฉํฐ์์ด์ ํธ ์์คํ ์ด ์ด๋ป๊ฒ ๊ตฌ์ฑ๋๋์ง ์์๋ณผ๊น? ๐ค๐ ๊ธฐ๋ณธ ์ํคํ ์ฒ
๋ฉํฐ์์ด์ ํธ ์์คํ ์ ํฌ๊ฒ ์ธ ๊ฐ์ง ๋ ์ด์ด๋ก ๊ตฌ์ฑ๋ผ:ํ์ ์์ฑ์ด๋ ์์์ ๋ฐ์๋ค์ด๋ ๋จ๊ณ์ผ. ์ฌ๊ธฐ์๋ ์ฃผ๋ก ์์ฑ์ธ์ ์์ด์ ํธ๊ฐ ํ์ฝํ์ง.
Whisper API Google Speech-to-Text Azure Speech Services
์ด๋ฐ ์๋น์ค๋ค์ ํ์ฉํด์ ์ค์๊ฐ์ผ๋ก ์์ฑ์ ํ ์คํธ๋ก ๋ณํํด. ์ ํ๋๊ฐ 95% ์ด์์ด๋ผ ๊ฑฐ์ ์๋ฒฝํ๊ฒ ์์๋ฃ๋๋ผ๊ณ ! ๐
์ฌ๊ธฐ๊ฐ ์ง์ง ํต์ฌ์ด์ผ! ์ฌ๋ฌ ์์ด์ ํธ๋ค์ด ํ์ ํ๋ ๊ณต๊ฐ์ด์ง.
โข ํ์๋ก ๊ตฌ์กฐํ ์์ด์ ํธ: ํ ์คํธ๋ฅผ ์๊ฑด๋ณ๋ก ๋ถ๋ฅํ๊ณ ๊ตฌ์กฐํ
โข ํ์ ์๋ณ ์์ด์ ํธ: ๋๊ฐ ๋งํ๋์ง ๊ตฌ๋ถ
โข ๊ฐ์ ๋ถ์ ์์ด์ ํธ: ํ์ ๋ถ์๊ธฐ๋ ์ค์๋ ํ์
โข ํค์๋ ์ถ์ถ ์์ด์ ํธ: ํต์ฌ ๋จ์ด์ ๊ฐ๋ ์ถ์ถ
์ด ์์ด์ ํธ๋ค์ด ์๋ก ์ ๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ์ผ๋ฉด์ ํ์ ํ๋ ๊ฑฐ์ผ. ๋ง์น ๋ฆด๋ ์ด์ฒ๋ผ! ๐โโ๏ธ
์ต์ข ์ ์ผ๋ก ์์ฑ๋ ํ์๋ก๊ณผ ์์ฝ๋ณธ์ ์์ฑํ๋ ๋จ๊ณ์ผ.
โข ๐ ์ ์ฒด ํ์๋ก (์์ธ ๋ฒ์ )
โข ๐ ์์ฝ๋ณธ (ํต์ฌ๋ง ๋ด์ ๋ฒ์ )
โข โ ์ก์ ์์ดํ ๋ฆฌ์คํธ
โข ๐ ํ์ ํต๊ณ ๋ฐ ์ธ์ฌ์ดํธ
ํ์์ ๋ฐ๋ผ PDF, Word, Notion ๋ฑ ๋ค์ํ ํ์์ผ๋ก ๋ด๋ณด๋ผ ์ ์์ด!
๐ ์์ด์ ํธ ๊ฐ ํต์ ๋ฐฉ์
์์ด์ ํธ๋ค์ด ์ด๋ป๊ฒ ์๋ก ์ํตํ ๊น? ์ฃผ๋ก ์ธ ๊ฐ์ง ๋ฐฉ์์ ์ฌ์ฉํด:์ฅ์ : ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ ํจ์จ์ ์ด์ผ โก
๋จ์ : ์ค์ ์ด ์ข ๋ณต์กํ ์ ์์ด ๐ค
์ฅ์ : ๋น ๋ฅธ ๋ฐ์ดํฐ ์ ๊ทผ์ด ๊ฐ๋ฅํด ๐
๋จ์ : ๋์์ฑ ์ ์ด๊ฐ ํ์ํด ๐
์ฅ์ : ์ ์ฐํ๊ณ ํ์ฅ์ฑ์ด ์ข์ ๐
๋จ์ : ๋คํธ์ํฌ ์ค๋ฒํค๋๊ฐ ์์ ์ ์์ด ๐
๐ป ์ค์ ! ๋ฉํฐ์์ด์ ํธ ์์คํ ๊ตฌ์ถํ๊ธฐ
์ด์ ์ง์ ๋ง๋ค์ด๋ณผ๊น? ๐ Python๊ณผ LangChain์ ํ์ฉํ ๊ฐ๋จํ ์์ ๋ฅผ ๋ณด์ฌ์ค๊ฒ!๐ ๏ธ ํ์ํ ๋๊ตฌ๋ค
| ๋๊ตฌ | ์ญํ | ์ถ์ฒ ์ด์ |
|---|---|---|
| LangChain | ์์ด์ ํธ ํ๋ ์์ํฌ | ์์ด์ ํธ ๊ตฌ์ถ์ด ์ ๋ง ์ฌ์! ๐ฏ |
| OpenAI API | LLM ์์ง | GPT-4์ ๊ฐ๋ ฅํ ์ดํด๋ ฅ ๐ช |
| Whisper | ์์ฑ์ธ์ | ๋ฌด๋ฃ์ธ๋ฐ ์ ํ๋ ์ต๊ณ ๐ค |
| FastAPI | ๋ฐฑ์๋ ์๋ฒ | ๋น ๋ฅด๊ณ ๊ฐ๋จํ API ๊ตฌ์ถ โก |
| Redis | ๋ฐ์ดํฐ ๊ณต์ | ์์ด์ ํธ ๊ฐ ๋น ๋ฅธ ํต์ ๐ |
๐ Step 1: ์์ฑ์ธ์ ์์ด์ ํธ ๋ง๋ค๊ธฐ
๋จผ์ ํ์ ์์ฑ์ ํ ์คํธ๋ก ๋ณํํ๋ ์์ด์ ํธ๋ถํฐ ๋ง๋ค์ด๋ณด์!import openai
from pathlib import Path
class TranscriptionAgent:
def __init__(self, api_key):
openai.api_key = api_key
self.name = "์์ฑ์ธ์ ์์ด์ ํธ"
def transcribe_audio(self, audio_file_path):
"""
์์ฑ ํ์ผ์ ํ
์คํธ๋ก ๋ณํ
"""
print(f"๐ค {self.name}: ์์ฑ ํ์ผ ๋ถ์ ์์...")
with open(audio_file_path, "rb") as audio_file:
transcript = openai.Audio.transcribe(
model="whisper-1",
file=audio_file,
language="ko" # ํ๊ตญ์ด ์ง์
)
print(f"โ
{self.name}: ๋ณํ ์๋ฃ!")
return transcript['text']
def transcribe_with_timestamps(self, audio_file_path):
"""
ํ์์คํฌํ์ ํจ๊ป ๋ณํ (ํ์ ๊ตฌ๋ถ์ ์ ์ฉ)
"""
with open(audio_file_path, "rb") as audio_file:
transcript = openai.Audio.transcribe(
model="whisper-1",
file=audio_file,
response_format="verbose_json",
language="ko"
)
# ์ธ๊ทธ๋จผํธ๋ณ๋ก ์ ๋ฆฌ
segments = []
for segment in transcript['segments']:
segments.append({
'start': segment['start'],
'end': segment['end'],
'text': segment['text']
})
return segments
# ์ฌ์ฉ ์์
agent = TranscriptionAgent(api_key="your-api-key")
text = agent.transcribe_audio("meeting_audio.mp3")
print(text)
โ๏ธ Step 2: ํ์๋ก ์์ฑ ์์ด์ ํธ ๋ง๋ค๊ธฐ
์ด์ ํ ์คํธ๋ฅผ ๋ฐ์์ ๊ตฌ์กฐํ๋ ํ์๋ก์ผ๋ก ๋ง๋๋ ์์ด์ ํธ๋ฅผ ๊ตฌ์ถํด๋ณด์!from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List
class MeetingMinutes(BaseModel):
"""ํ์๋ก ๋ฐ์ดํฐ ๋ชจ๋ธ"""
title: str = Field(description="ํ์ ์ ๋ชฉ")
date: str = Field(description="ํ์ ๋ ์ง")
attendees: List[str] = Field(description="์ฐธ์์ ๋ชฉ๋ก")
agenda_items: List[dict] = Field(description="์๊ฑด๋ณ ๋ด์ฉ")
decisions: List[str] = Field(description="๊ฒฐ์ ์ฌํญ")
action_items: List[dict] = Field(description="์ก์
์์ดํ
")
class MinutesWriterAgent:
def __init__(self, api_key):
self.llm = ChatOpenAI(
model="gpt-4",
temperature=0.3, # ์ฐฝ์์ฑ๋ณด๋ค ์ ํ์ฑ ์ค์
openai_api_key=api_key
)
self.name = "ํ์๋ก ์์ฑ ์์ด์ ํธ"
self.parser = PydanticOutputParser(pydantic_object=MeetingMinutes)
def create_minutes(self, transcript_text):
"""
ํ
์คํธ๋ฅผ ๊ตฌ์กฐํ๋ ํ์๋ก์ผ๋ก ๋ณํ
"""
print(f"โ๏ธ {self.name}: ํ์๋ก ์์ฑ ์ค...")
prompt = ChatPromptTemplate.from_messages([
("system", """๋น์ ์ ์ ๋ฌธ ํ์๋ก ์์ฑ์์
๋๋ค.
์ฃผ์ด์ง ํ์ ๋ด์ฉ์ ๋ถ์ํ์ฌ ์ฒด๊ณ์ ์ธ ํ์๋ก์ ์์ฑํ์ธ์.
๋ค์ ํ์์ ๋ฐ๋ผ์ฃผ์ธ์:
{format_instructions}
์ค์ํ ์ :
1. ์๊ฑด๋ณ๋ก ๋ช
ํํ๊ฒ ๊ตฌ๋ถํ์ธ์
2. ๊ฒฐ์ ์ฌํญ๊ณผ ๋
ผ์์ฌํญ์ ๊ตฌ๋ถํ์ธ์
3. ์ก์
์์ดํ
์ ๋ด๋น์์ ๊ธฐํ์ ํฌํจํ์ธ์
4. ๊ฐ๊ด์ ์ด๊ณ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํ์ธ์"""),
("user", "ํ์ ๋ด์ฉ:\n{transcript}")
])
chain = prompt | self.llm
response = chain.invoke({
"transcript": transcript_text,
"format_instructions": self.parser.get_format_instructions()
})
# ํ์ฑ
minutes = self.parser.parse(response.content)
print(f"โ
{self.name}: ํ์๋ก ์์ฑ ์๋ฃ!")
return minutes
def format_to_markdown(self, minutes: MeetingMinutes):
"""
ํ์๋ก์ ๋งํฌ๋ค์ด ํ์์ผ๋ก ๋ณํ
"""
md = f"""# {minutes.title}
## ๐
ํ์ ์ ๋ณด
- **๋ ์ง**: {minutes.date}
- **์ฐธ์์**: {', '.join(minutes.attendees)}
## ๐ ์๊ฑด ๋ฐ ๋
ผ์ ๋ด์ฉ
"""
for i, item in enumerate(minutes.agenda_items, 1):
md += f"\n### {i}. {item['title']}\n"
md += f"{item['content']}\n"
md += "\n## โ
๊ฒฐ์ ์ฌํญ\n"
for i, decision in enumerate(minutes.decisions, 1):
md += f"{i}. {decision}\n"
md += "\n## ๐ฏ ์ก์
์์ดํ
\n"
for item in minutes.action_items:
md += f"- [ ] {item['task']} (๋ด๋น: {item['assignee']}, ๊ธฐํ: {item['deadline']})\n"
return md
# ์ฌ์ฉ ์์
writer = MinutesWriterAgent(api_key="your-api-key")
minutes = writer.create_minutes(transcript_text)
markdown = writer.format_to_markdown(minutes)
print(markdown)
๐ Step 3: ์์ฝ ๋ฐ ๋ถ์ ์์ด์ ํธ ๋ง๋ค๊ธฐ
ํ์๋ก์ด ์์ฑ๋๋ฉด ์ด์ ํต์ฌ๋ง ์ถ๋ ค๋ด๋ ์์ฝ ์์ด์ ํธ๊ฐ ํ์ํด!from langchain.chains.summarize import load_summarize_chain
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.docstore.document import Document
class SummaryAgent:
def __init__(self, api_key):
self.llm = ChatOpenAI(
model="gpt-4",
temperature=0.2,
openai_api_key=api_key
)
self.name = "์์ฝ ๋ถ์ ์์ด์ ํธ"
def create_executive_summary(self, minutes_text):
"""
๊ฒฝ์์ง์ฉ ์์ฝ๋ณธ ์์ฑ (ํต์ฌ๋ง!)
"""
print(f"๐ {self.name}: ๊ฒฝ์์ง ์์ฝ๋ณธ ์์ฑ ์ค...")
prompt = ChatPromptTemplate.from_messages([
("system", """๋น์ ์ ๊ฒฝ์์ง์ ์ํ ์์ฝ ์ ๋ฌธ๊ฐ์
๋๋ค.
ํ์๋ก์ ์ฝ๊ณ ๋ค์ ๋ด์ฉ์ ํฌํจํ ๊ฐ๊ฒฐํ ์์ฝ์ ์์ฑํ์ธ์:
1. ํ์์ ํต์ฌ ๋ชฉ์ (1-2๋ฌธ์ฅ)
2. ์ฃผ์ ๊ฒฐ์ ์ฌํญ (3-5๊ฐ bullet points)
3. ์ค์ํ ์ก์
์์ดํ
(์ฐ์ ์์ ๋์ ๊ฒ๋ง)
4. ๋ฆฌ์คํฌ๋ ์ด์ (์๋ค๋ฉด)
์ ์ฒด ๊ธธ์ด๋ 200๋จ์ด ์ด๋ด๋ก ์์ฑํ์ธ์."""),
("user", "{minutes}")
])
chain = prompt | self.llm
summary = chain.invoke({"minutes": minutes_text})
return summary.content
def extract_key_insights(self, minutes_text):
"""
ํ์์์ ๋์ถ๋ ์ธ์ฌ์ดํธ ์ถ์ถ
"""
print(f"๐ก {self.name}: ์ธ์ฌ์ดํธ ์ถ์ถ ์ค...")
prompt = ChatPromptTemplate.from_messages([
("system", """ํ์ ๋ด์ฉ์ ๋ถ์ํ์ฌ ๋ค์์ ์ถ์ถํ์ธ์:
1. ์๋ก์ด ์์ด๋์ด๋ ์ ์
2. ๋ฐ๊ฒฌ๋ ๋ฌธ์ ์ ์ด๋ ๊ฐ์ ์
3. ํฅํ ๊ธฐํ ์์
4. ์ฃผ์ํด์ผ ํ ๋ฆฌ์คํฌ
๊ฐ ํญ๋ชฉ์ ๋ช
ํํ๊ฒ ๊ตฌ๋ถํ์ฌ ์์ฑํ์ธ์."""),
("user", "{minutes}")
])
chain = prompt | self.llm
insights = chain.invoke({"minutes": minutes_text})
return insights.content
def analyze_sentiment(self, minutes_text):
"""
ํ์ ๋ถ์๊ธฐ ๋ถ์
"""
print(f"๐ {self.name}: ํ์ ๋ถ์๊ธฐ ๋ถ์ ์ค...")
prompt = ChatPromptTemplate.from_messages([
("system", """ํ์ ๋ด์ฉ์ ์ ๋ฐ์ ์ธ ๋ถ์๊ธฐ๋ฅผ ๋ถ์ํ์ธ์:
- ๊ธ์ ์ /๋ถ์ ์ /์ค๋ฆฝ์ ๋ถ์๊ธฐ
- ์ฐธ์์๋ค์ ์ฐธ์ฌ๋
- ์๊ฒฌ ์ผ์น๋
- ์ ๋ฐ์ ์ธ ์๋์ง ๋ ๋ฒจ
๊ฐ๋จํ๊ฒ ์์ฝํด์ฃผ์ธ์."""),
("user", "{minutes}")
])
chain = prompt | self.llm
sentiment = chain.invoke({"minutes": minutes_text})
return sentiment.content
# ์ฌ์ฉ ์์
summary_agent = SummaryAgent(api_key="your-api-key")
executive_summary = summary_agent.create_executive_summary(markdown)
insights = summary_agent.extract_key_insights(markdown)
sentiment = summary_agent.analyze_sentiment(markdown)
print("=== ๊ฒฝ์์ง ์์ฝ ===")
print(executive_summary)
print("\n=== ์ฃผ์ ์ธ์ฌ์ดํธ ===")
print(insights)
print("\n=== ํ์ ๋ถ์๊ธฐ ===")
print(sentiment)
๐ Step 4: ์์ด์ ํธ๋ค์ ์ฐ๊ฒฐํ๊ธฐ
์ด์ ๋ชจ๋ ์์ด์ ํธ๋ฅผ ํ๋๋ก ๋ฌถ์ด์ ์์ ํ ์์คํ ์ ๋ง๋ค์ด๋ณด์!import asyncio
from datetime import datetime
import json
class MeetingMinutesOrchestrator:
"""
๋ชจ๋ ์์ด์ ํธ๋ฅผ ์กฐ์จํ๋ ์ค์ผ์คํธ๋ ์ดํฐ
"""
def __init__(self, api_key):
self.transcription_agent = TranscriptionAgent(api_key)
self.writer_agent = MinutesWriterAgent(api_key)
self.summary_agent = SummaryAgent(api_key)
self.results = {}
async def process_meeting(self, audio_file_path, output_dir="./output"):
"""
์ ์ฒด ํ๋ก์ธ์ค๋ฅผ ์คํ
"""
print("๐ ํ์๋ก ์๋ํ ์์คํ
์์!\n")
start_time = datetime.now()
# Step 1: ์์ฑ ์ธ์
print("=" * 50)
transcript = self.transcription_agent.transcribe_audio(audio_file_path)
self.results['transcript'] = transcript
print(f"๐ ๋ณํ๋ ํ
์คํธ ๊ธธ์ด: {len(transcript)} ๊ธ์\n")
# Step 2: ํ์๋ก ์์ฑ
print("=" * 50)
minutes = self.writer_agent.create_minutes(transcript)
markdown = self.writer_agent.format_to_markdown(minutes)
self.results['minutes'] = markdown
print(f"๐ ํ์๋ก ์์ฑ ์๋ฃ\n")
# Step 3: ์์ฝ ๋ฐ ๋ถ์ (๋ณ๋ ฌ ์ฒ๋ฆฌ)
print("=" * 50)
print("๐ ์์ฝ ๋ฐ ๋ถ์ ์์ (๋ณ๋ ฌ ์ฒ๋ฆฌ)...")
summary_tasks = [
self.summary_agent.create_executive_summary(markdown),
self.summary_agent.extract_key_insights(markdown),
self.summary_agent.analyze_sentiment(markdown)
]
# ๋น๋๊ธฐ๋ก ๋์ ์คํ
exec_summary, insights, sentiment = await asyncio.gather(*summary_tasks)
self.results['executive_summary'] = exec_summary
self.results['insights'] = insights
self.results['sentiment'] = sentiment
# Step 4: ๊ฒฐ๊ณผ ์ ์ฅ
print("\n" + "=" * 50)
print("๐พ ๊ฒฐ๊ณผ ์ ์ฅ ์ค...")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# ์ ์ฒด ํ์๋ก ์ ์ฅ
with open(f"{output_dir}/minutes_{timestamp}.md", "w", encoding="utf-8") as f:
f.write(markdown)
# ์์ฝ๋ณธ ์ ์ฅ
summary_content = f"""# ํ์ ์์ฝ ๋ณด๊ณ ์
## ๐ ๊ฒฝ์์ง ์์ฝ
{exec_summary}
## ๐ก ์ฃผ์ ์ธ์ฌ์ดํธ
{insights}
## ๐ ํ์ ๋ถ์๊ธฐ
{sentiment}
"""
with open(f"{output_dir}/summary_{timestamp}.md", "w", encoding="utf-8") as f:
f.write(summary_content)
# JSON ํ์์ผ๋ก๋ ์ ์ฅ
with open(f"{output_dir}/data_{timestamp}.json", "w", encoding="utf-8") as f:
json.dump(self.results, f, ensure_ascii=False, indent=2)
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
print(f"โ
๋ชจ๋ ์์
์๋ฃ!")
print(f"โฑ๏ธ ์ด ์์ ์๊ฐ: {duration:.2f}์ด")
print(f"๐ ๊ฒฐ๊ณผ ํ์ผ ์์น: {output_dir}/")
return self.results
# ์ค์ ์ฌ์ฉ
async def main():
orchestrator = MeetingMinutesOrchestrator(api_key="your-api-key")
results = await orchestrator.process_meeting(
audio_file_path="meeting_recording.mp3",
output_dir="./meeting_outputs"
)
print("\n๐ ํ์๋ก ์๋ํ ์๋ฃ!")
print("์์ฑ๋ ํ์ผ:")
print("- ์ ์ฒด ํ์๋ก (Markdown)")
print("- ์์ฝ ๋ณด๊ณ ์ (Markdown)")
print("- ๋ฐ์ดํฐ ํ์ผ (JSON)")
# ์คํ
if __name__ == "__main__":
asyncio.run(main())
2. ์บ์ฑ: ๊ฐ์ ํ์๋ฅผ ์ฌ๋ฌ ๋ฒ ์ฒ๋ฆฌํ ์ผ์ด ์๋ค๋ฉด Redis์ ์ค๊ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํด๋๋ฉด ์ข์.
3. ์คํธ๋ฆฌ๋ฐ: ๊ธด ํ์๋ ์ค์๊ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ฉด์ ์ค๊ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐
๐จ ๊ณ ๊ธ ๊ธฐ๋ฅ ์ถ๊ฐํ๊ธฐ
๊ธฐ๋ณธ ์์คํ ์ ์์ฑํ์ผ๋, ์ด์ ๋ ๋ฉ์ง ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐํด๋ณด์! ๐๐ฅ ํ์ ๊ตฌ๋ถ (Speaker Diarization)
ํ์์์ ๋๊ฐ ๋ญ๋ผ๊ณ ํ๋์ง ๊ตฌ๋ถํ๋ ๊ฑด ์ ๋ง ์ค์ํ์ง! ์ด๊ฑธ ํ์ ๊ตฌ๋ถ(Speaker Diarization)์ด๋ผ๊ณ ํด.from pyannote.audio import Pipeline
import torch
class SpeakerDiarizationAgent:
def __init__(self, hf_token):
"""
Hugging Face์ pyannote ๋ชจ๋ธ ์ฌ์ฉ
"""
self.pipeline = Pipeline.from_pretrained(
"pyannote/speaker-diarization",
use_auth_token=hf_token
)
self.name = "ํ์ ๊ตฌ๋ถ ์์ด์ ํธ"
def identify_speakers(self, audio_file_path):
"""
์ค๋์ค์์ ํ์๋ฅผ ๊ตฌ๋ถ
"""
print(f"๐ฅ {self.name}: ํ์ ๋ถ์ ์ค...")
# ํ์ ๊ตฌ๋ถ ์คํ
diarization = self.pipeline(audio_file_path)
# ๊ฒฐ๊ณผ ์ ๋ฆฌ
speakers_timeline = []
for turn, _, speaker in diarization.itertracks(yield_label=True):
speakers_timeline.append({
'start': turn.start,
'end': turn.end,
'speaker': speaker,
'duration': turn.end - turn.start
})
print(f"โ
{len(set([s['speaker'] for s in speakers_timeline]))}๋ช
์ ํ์ ๊ฐ์ง!")
return speakers_timeline
def merge_with_transcript(self, transcript_segments, speaker_timeline):
"""
์์ฑ ์ธ์ ๊ฒฐ๊ณผ์ ํ์ ์ ๋ณด๋ฅผ ๊ฒฐํฉ
"""
merged = []
for segment in transcript_segments:
# ํด๋น ์๊ฐ๋์ ํ์ ์ฐพ๊ธฐ
segment_time = (segment['start'] + segment['end']) / 2
speaker = "Unknown"
for sp in speaker_timeline:
if sp['start'] <= segment_time <= sp['end']:
speaker = sp['speaker']
break
merged.append({
'start': segment['start'],
'end': segment['end'],
'speaker': speaker,
'text': segment['text']
})
return merged
def format_conversation(self, merged_data):
"""
๋ํ ํ์์ผ๋ก ํฌ๋งทํ
"""
conversation = []
current_speaker = None
current_text = []
for item in merged_data:
if item['speaker'] != current_speaker:
if current_speaker is not None:
conversation.append({
'speaker': current_speaker,
'text': ' '.join(current_text)
})
current_speaker = item['speaker']
current_text = [item['text']]
else:
current_text.append(item['text'])
# ๋ง์ง๋ง ํ์ ์ถ๊ฐ
if current_speaker is not None:
conversation.append({
'speaker': current_speaker,
'text': ' '.join(current_text)
})
return conversation
# ์ฌ์ฉ ์์
diarization_agent = SpeakerDiarizationAgent(hf_token="your-hf-token")
speaker_timeline = diarization_agent.identify_speakers("meeting.mp3")
merged = diarization_agent.merge_with_transcript(transcript_segments, speaker_timeline)
conversation = diarization_agent.format_conversation(merged)
โข ๋๊ฐ ์ด๋ค ์๊ฒฌ์ ๋๋์ง ๋ช ํํ๊ฒ ๊ธฐ๋ก
โข ๊ฐ ์ฐธ์์์ ๋ฐ์ธ ์๊ฐ ํต๊ณ
โข ํน์ ์ฌ๋์ ๋ฐ์ธ๋ง ๋ชจ์์ ๋ณด๊ธฐ
โข ํ์ ์ฐธ์ฌ๋ ๋ถ์
ํนํ ์ฌ๋ฌ ๋ถ์๊ฐ ์ฐธ์ฌํ๋ ํ์์์ ์ ๋ง ์ ์ฉํด! ๐ฏ
๐ ์ค์๊ฐ ์ฒ๋ฆฌ (Real-time Processing)
ํ์๊ฐ ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ๋ง๊ณ , ์ค์๊ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ฉด ์ด๋จ๊น? ๐import asyncio
from queue import Queue
import threading
class RealtimeProcessor:
def __init__(self, api_key):
self.transcription_queue = Queue()
self.processing_queue = Queue()
self.results_queue = Queue()
self.transcription_agent = TranscriptionAgent(api_key)
self.writer_agent = MinutesWriterAgent(api_key)
self.is_running = False
def start_streaming(self, audio_stream):
"""
์ค์๊ฐ ์ค๋์ค ์คํธ๋ฆผ ์ฒ๋ฆฌ ์์
"""
self.is_running = True
# 3๊ฐ์ ์์ปค ์ค๋ ๋ ์์
threading.Thread(target=self._transcription_worker, daemon=True).start()
threading.Thread(target=self._processing_worker, daemon=True).start()
threading.Thread(target=self._results_worker, daemon=True).start()
print("๐๏ธ ์ค์๊ฐ ์ฒ๋ฆฌ ์์!")
# ์ค๋์ค ์ฒญํฌ๋ฅผ ํ์ ์ถ๊ฐ
for audio_chunk in audio_stream:
if not self.is_running:
break
self.transcription_queue.put(audio_chunk)
def _transcription_worker(self):
"""
์์ฑ ์ธ์ ์์ปค
"""
while self.is_running:
if not self.transcription_queue.empty():
audio_chunk = self.transcription_queue.get()
# ์์ฑ์ ํ
์คํธ๋ก ๋ณํ
text = self.transcription_agent.transcribe_audio(audio_chunk)
# ๋ค์ ๋จ๊ณ๋ก ์ ๋ฌ
self.processing_queue.put(text)
def _processing_worker(self):
"""
ํ
์คํธ ์ฒ๋ฆฌ ์์ปค
"""
accumulated_text = []
while self.is_running:
if not self.processing_queue.empty():
text = self.processing_queue.get()
accumulated_text.append(text)
# ์ผ์ ๋ ์์ด๋ฉด ์ฒ๋ฆฌ
if len(accumulated_text) >= 5: # 5๊ฐ ์ฒญํฌ๋ง๋ค
combined_text = ' '.join(accumulated_text)
# ์ค๊ฐ ์์ฝ ์์ฑ
summary = self._create_interim_summary(combined_text)
self.results_queue.put({
'type': 'interim_summary',
'content': summary,
'timestamp': datetime.now()
})
accumulated_text = []
def _results_worker(self):
"""
๊ฒฐ๊ณผ ์ถ๋ ฅ ์์ปค
"""
while self.is_running:
if not self.results_queue.empty():
result = self.results_queue.get()
# ์ค์๊ฐ์ผ๋ก ๊ฒฐ๊ณผ ํ์
print(f"\nโก ์ค์๊ฐ ์
๋ฐ์ดํธ [{result['timestamp']}]")
print(f"๐ {result['content']}\n")
def _create_interim_summary(self, text):
"""
์ค๊ฐ ์์ฝ ์์ฑ
"""
prompt = f"""๋ค์ ํ์ ๋ด์ฉ์ ํต์ฌ์ 2-3๋ฌธ์ฅ์ผ๋ก ์์ฝํ์ธ์:
{text}
์์ฝ:"""
# ๊ฐ๋จํ ์์ฝ ์์ฑ
response = self.writer_agent.llm.predict(prompt)
return response
def stop(self):
"""
์ฒ๋ฆฌ ์ค์ง
"""
self.is_running = False
print("โน๏ธ ์ค์๊ฐ ์ฒ๋ฆฌ ์ค์ง")
# ์ฌ์ฉ ์์
processor = RealtimeProcessor(api_key="your-api-key")
# ๊ฐ์์ ์ค๋์ค ์คํธ๋ฆผ (์ค์ ๋ก๋ ๋ง์ดํฌ๋ ํ์ํ์ ํ๋ซํผ์์ ๋ฐ์์ด)
def audio_stream_generator():
# ์ค์ ๋ก๋ ์ค์๊ฐ ์ค๋์ค๋ฅผ ์ฒญํฌ๋ก ๋๋ ์ yield
for i in range(10):
yield f"audio_chunk_{i}.mp3"
time.sleep(5) # 5์ด๋ง๋ค ์ฒญํฌ ์์ฑ
# ์คํ
processor.start_streaming(audio_stream_generator())
2. ๋ฉ๋ชจ๋ฆฌ ํจ์จ: ์ ์ฒด ์ค๋์ค๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ์ง ์์๋ ๋ผ
3. ๋น ๋ฅธ ๊ฒฐ๊ณผ: ํ์ ๋๋์๋ง์ ํ์๋ก์ด ์ค๋น๋ผ ์์ด
4. ์ค๋ฅ ์กฐ๊ธฐ ๋ฐ๊ฒฌ: ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ๋ฐ๋ก ์ ์ ์์ด ๐ฏ
๐ฑ ๋ค์ํ ํ๋ซํผ ์ฐ๋
Zoom, Google Meet, Teams ๊ฐ์ ํ๋ซํผ๊ณผ ์ฐ๋ํ๋ฉด ๋ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์์ด!Zoom์ API๋ฅผ ํตํด ๋ นํ ํ์ผ์ ์๋์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์์ด:
import requests
from datetime import datetime, timedelta
class ZoomIntegration:
def __init__(self, api_key, api_secret):
self.api_key = api_key
self.api_secret = api_secret
self.base_url = "https://api.zoom.us/v2"
def get_recent_recordings(self, days=7):
"""
์ต๊ทผ ๋
นํ ํ์ผ ๊ฐ์ ธ์ค๊ธฐ
"""
from_date = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
to_date = datetime.now().strftime("%Y-%m-%d")
endpoint = f"{self.base_url}/users/me/recordings"
params = {
'from': from_date,
'to': to_date
}
response = requests.get(
endpoint,
headers=self._get_headers(),
params=params
)
return response.json()['meetings']
def download_recording(self, recording_url, output_path):
"""
๋
นํ ํ์ผ ๋ค์ด๋ก๋
"""
response = requests.get(
recording_url,
headers=self._get_headers(),
stream=True
)
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
return output_path
def _get_headers(self):
# JWT ํ ํฐ ์์ฑ ๋ก์ง
# (์ค์ ๋ก๋ Zoom SDK ์ฌ์ฉ ๊ถ์ฅ)
return {
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
}
ํ์๋ก์ด ์์ฑ๋๋ฉด ์๋์ผ๋ก ์ฐธ์์๋ค์๊ฒ ์ด๋ฉ์ผ์ ๋ณด๋ด์!
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
class EmailNotifier:
def __init__(self, smtp_server, smtp_port, email, password):
self.smtp_server = smtp_server
self.smtp_port = smtp_port
self.email = email
self.password = password
def send_minutes(self, recipients, subject, minutes_file, summary):
"""
ํ์๋ก์ ์ด๋ฉ์ผ๋ก ๋ฐ์ก
"""
msg = MIMEMultipart()
msg['From'] = self.email
msg['To'] = ', '.join(recipients)
msg['Subject'] = subject
# ์ด๋ฉ์ผ ๋ณธ๋ฌธ
body = f"""
์๋
ํ์ธ์,
ํ์๋ก์ด ์๋์ผ๋ก ์์ฑ๋์์ต๋๋ค.
=== ํ์ ์์ฝ ===
{summary}
์์ธํ ๋ด์ฉ์ ์ฒจ๋ถ ํ์ผ์ ํ์ธํด์ฃผ์ธ์.
๊ฐ์ฌํฉ๋๋ค.
"""
msg.attach(MIMEText(body, 'plain'))
# ํ์๋ก ํ์ผ ์ฒจ๋ถ
with open(minutes_file, 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename= {minutes_file}'
)
msg.attach(part)
# ์ด๋ฉ์ผ ๋ฐ์ก
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
server.starttls()
server.login(self.email, self.password)
server.send_message(msg)
print(f"๐ง ์ด๋ฉ์ผ ๋ฐ์ก ์๋ฃ: {', '.join(recipients)}")
# ์ฌ์ฉ ์์
notifier = EmailNotifier(
smtp_server="smtp.gmail.com",
smtp_port=587,
email="your-email@gmail.com",
password="your-password"
)
notifier.send_minutes(
recipients=["team@company.com"],
subject="[์๋์์ฑ] 2024๋
1์ ํ๋ก์ ํธ ํ์๋ก",
minutes_file="minutes_20240115.md",
summary=executive_summary
)
๋ง์ ํ์ด Notion์ ์ฌ์ฉํ๋๊น, ํ์๋ก์ ์๋์ผ๋ก Notion์ ์ฌ๋ฆฌ๋ฉด ํธํ๊ฒ ์ง?
from notion_client import Client
class NotionIntegration:
def __init__(self, token):
self.client = Client(auth=token)
def create_meeting_page(self, database_id, minutes_data):
"""
Notion ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ์๋ก ํ์ด์ง ์์ฑ
"""
# ํ์ด์ง ์์ฑ ์ค์
properties = {
"์ ๋ชฉ": {
"title": [
{
"text": {
"content": minutes_data['title']
}
}
]
},
"๋ ์ง": {
"date": {
"start": minutes_data['date']
}
},
"์ฐธ์์": {
"multi_select": [
{"name": attendee}
for attendee in minutes_data['attendees']
]
},
"์ํ": {
"select": {
"name": "์๋ฃ"
}
}
}
# ํ์ด์ง ๋ด์ฉ (๋ธ๋ก)
children = []
# ์์ฝ ์น์
children.append({
"object": "block",
"type": "heading_2",
"heading_2": {
"rich_text": [{"type": "text", "text": {"content": "๐ ํ์ ์์ฝ"}}]
}
})
children.append({
"object": "block",
"type": "paragraph",
"paragraph": {
"rich_text": [{"type": "text", "text": {"content": minutes_data['summary']}}]
}
})
# ์๊ฑด ์น์
children.append({
"object": "block",
"type": "heading_2",
"heading_2": {
"rich_text": [{"type": "text", "text": {"content": "๐ ์๊ฑด ๋ฐ ๋
ผ์์ฌํญ"}}]
}
})
for item in minutes_data['agenda_items']:
children.append({
"object": "block",
"type": "heading_3",
"heading_3": {
"rich_text": [{"type": "text", "text": {"content": item['title']}}]
}
})
children.append({
"object": "block",
"type": "paragraph",
"paragraph": {
"rich_text": [{"type": "text", "text": {"content": item['content']}}]
}
})
# ์ก์
์์ดํ
์น์
children.append({
"object": "block",
"type": "heading_2",
"heading_2": {
"rich_text": [{"type": "text", "text": {"content": "โ
์ก์
์์ดํ
"}}]
}
})
for action in minutes_data['action_items']:
children.append({
"object": "block",
"type": "to_do",
"to_do": {
"rich_text": [{
"type": "text",
"text": {
"content": f"{action['task']} (๋ด๋น: {action['assignee']}, ๊ธฐํ: {action['deadline']})"
}
}],
"checked": False
}
})
# ํ์ด์ง ์์ฑ
page = self.client.pages.create(
parent={"database_id": database_id},
properties=properties,
children=children
)
print(f"โ
Notion ํ์ด์ง ์์ฑ ์๋ฃ: {page['url']}")
return page['url']
# ์ฌ์ฉ ์์
notion = NotionIntegration(token="your-notion-token")
page_url = notion.create_meeting_page(
database_id="your-database-id",
minutes_data=minutes_data
)
print(f"๐ ํ์๋ก ๋งํฌ: {page_url}")
๐ฏ ์ค์ ํ์ฉ ์ฌ๋ก
์ด๋ก ์ ์ถฉ๋ถํ ๋ดค์ผ๋, ์ด์ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉ๋๋์ง ์ดํด๋ณด์! ๐๐ผ ์ฌ๋ก 1: ์คํํธ์ A์ฌ์ ์ผ์ผ ์คํ ๋์
๐ฐ ๊ธฐ์กด ๋ฌธ์ ์
โข ๋งค์ผ ํ์๋ก์ ์์ฑํ๋ ๊ฒ ๋ถ๋ด
โข ์ฐธ์ ๋ชปํ ์ฌ๋์ ๋ด์ฉ์ ๋์นจ
โข ์ก์ ์์ดํ ์ด ์ ๋๋ก ์ถ์ ๋์ง ์์
โจ ๋์ ํ ๋ณํ
๋ฉํฐ์์ด์ ํธ ์์คํ ์ ๋์ ํ ํ:
1. ํ์ ์ข ๋ฃ ํ 3๋ถ ๋ด์ ์๋์ผ๋ก ํ์๋ก ์์ฑ
2. Slack์ ์๋์ผ๋ก ์์ฝ๋ณธ ํฌ์คํ
3. ์ก์ ์์ดํ ์ Jira ํฐ์ผ์ผ๋ก ์๋ ์์ฑ
4. ์ฐธ์ ๋ชปํ ํ์๋ 5๋ถ ์์ ๋ด์ฉ ํ์ ๊ฐ๋ฅ
๐ ์ฑ๊ณผ
โข ํ์๋ก ์์ฑ ์๊ฐ: 30๋ถ โ 0๋ถ (100% ์ ๊ฐ)
โข ์ก์ ์์ดํ ์๋ฃ์จ: 65% โ 92% (27%p ์์น)
โข ํ์ ๋ง์กฑ๋: 3.2/5 โ 4.7/5
๐ญ ์ฌ๋ก 2: ์ ์กฐ์ B์ฌ์ ํ์ง ํ์
๐ฐ ๊ธฐ์กด ๋ฌธ์ ์
โข 2์๊ฐ์ง๋ฆฌ ํ์๋ก ์์ฑ์ 1์๊ฐ ์ด์ ์์
โข ์ ๋ฌธ ์ฉ์ด๊ฐ ๋ง์ ์ ํํ ๊ธฐ๋ก์ด ์ด๋ ค์
โข ๋ถ์๋ณ ์ก์ ์์ดํ ์ถ์ ์ด ๋ณต์กํจ
โจ ๋ง์ถค ์๋ฃจ์
1. ์ ์กฐ ์ ๋ฌธ ์ฉ์ด ์ฌ์ ์ ์์ด์ ํธ์ ํ์ต์ํด
2. ๋ถ์๋ณ๋ก ์๋ ๋ถ๋ฅ๋๋ ์ก์ ์์ดํ ์์ฑ
3. ์ด์ ํ์๋ก๊ณผ ๋น๊ตํ์ฌ ์งํ ์ํฉ ์๋ ์ถ์
4. ํ์ง ์งํ ๋ณํ๋ฅผ ๊ทธ๋ํ๋ก ์๊ฐํ
๐ ์ฑ๊ณผ
โข ํ์๋ก ์์ฑ ์๊ฐ: 60๋ถ โ 5๋ถ (92% ์ ๊ฐ)
โข ์ฉ์ด ์ ํ๋: 78% โ 96%
โข ์ก์ ์์ดํ ์ถ์ ๋ฅ : 100% (์ด์ ์๋ ์๋ ๊ด๋ฆฌ๋ก ๋๋ฝ ๋ฐ์)
๐ ์ฌ๋ก 3: ๋ํ C์ ์ฐ๊ตฌ ๋ฏธํ
๐ฐ ๊ธฐ์กด ๋ฌธ์ ์
โข ํ์๋ค์ด ๋์๊ฐ๋ฉฐ ํ์๋ก ์์ฑ (์๊ฐ ๋ญ๋น)
โข ์ ๋ฌธ์ ์ธ ๋ด์ฉ์ด๋ผ ์ ํํ ๊ธฐ๋ก์ด ์ค์
โข ๋ ผ๋ฌธ ์์ฑ ์ ํ์ ๋ด์ฉ์ ๋ค์ ์ฐพ๊ธฐ ์ด๋ ค์
โจ ํนํ ๊ธฐ๋ฅ
1. ๋ ผ๋ฌธ ์ธ์ฉ ํ์์ผ๋ก ์๋ ์ ๋ฆฌ
2. ์ฐ๊ตฌ ์์ด๋์ด์ ์คํ ๊ฒฐ๊ณผ๋ฅผ ์๋ ๋ถ๋ฅ
3. ์ด์ ๋ฏธํ ๊ณผ์ ์ฐ๊ด์ฑ ์๋ ๋งํฌ
4. ์ฐธ๊ณ ๋ฌธํ ์๋ ์ถ์ถ ๋ฐ ์ ๋ฆฌ
๐ ์ฑ๊ณผ
โข ํ์๋ค์ ์ฐ๊ตฌ ์๊ฐ ํ๋ณด: ์ฃผ๋น 2์๊ฐ ์ฆ๊ฐ
โข ๋ ผ๋ฌธ ์์ฑ ์ ์๋ฃ ์ฐพ๋ ์๊ฐ: 50% ๊ฐ์
โข ์ฐ๊ตฌ ์์ด๋์ด ์ถ์ ๋ฐ ๋ฐ์ ๊ณผ์ ๋ช ํํ
1. ๋๋ฉ์ธ ํนํ: ๊ฐ ๋ถ์ผ์ ํน์ฑ์ ๋ง๊ฒ ์ปค์คํฐ๋ง์ด์ง
2. ๊ธฐ์กด ์ํฌํ๋ก์ฐ ํตํฉ: ์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ๋๊ตฌ๋ค๊ณผ ์ฐ๋
3. ์ ์ง์ ๋์ : ์์ ํ๋ถํฐ ์์ํด์ ํ๋
4. ์ง์์ ๊ฐ์ : ํผ๋๋ฐฑ์ ๋ฐ์ ๊ณ์ ๊ฐ์
์ฒ์๋ถํฐ ์๋ฒฝํ ํ์๋ ์์ด. ์์ํ๊ณ ๊ฐ์ ํ๋ฉด ๋ผ! ๐
โ ๏ธ ์ฃผ์์ฌํญ๊ณผ ํ๊ณ์
์๋ฒฝํ ์์คํ ์ ์์ด. ๋ฉํฐ์์ด์ ํธ ์์คํ ๋ ๋ช ๊ฐ์ง ์ฃผ์ํ ์ ์ด ์์ด! ๐ค๐ ๋ณด์ ๋ฐ ํ๋ผ์ด๋ฒ์
1. ๋ฐ์ดํฐ ์ํธํ: ์ ์ก ์ค์ธ ๋ฐ์ดํฐ์ ์ ์ฅ๋ ๋ฐ์ดํฐ ๋ชจ๋ ์ํธํ
2. ์ ๊ทผ ๊ถํ ๊ด๋ฆฌ: ํ์๋ก์ ๋ํ ์ ๊ทผ ๊ถํ์ ์ฒ ์ ํ ๊ด๋ฆฌ
3. ๋ฐ์ดํฐ ๋ณด๊ด ์ ์ฑ : ์ผ์ ๊ธฐ๊ฐ ํ ์๋ ์ญ์ ์ค์
4. ์ธ๋ถ API ์ฌ์ฉ ์ฃผ์: OpenAI ๊ฐ์ ์ธ๋ถ ์๋น์ค ์ฌ์ฉ ์ ๋ฐ์ดํฐ ์ ์ฑ ํ์ธ
๐ก ํด๊ฒฐ์ฑ : ๋ฏผ๊ฐํ ํ์๋ ์จํ๋ ๋ฏธ์ค ๋ชจ๋ธ(์: Llama 2, Mistral)์ ์ฌ์ฉํ๊ฑฐ๋, Azure OpenAI ๊ฐ์ ์ํฐํ๋ผ์ด์ฆ๊ธ ์๋น์ค๋ฅผ ํ์ฉํ๋ ๊ฒ ์ข์!
๐ฏ ์ ํ๋ ๋ฌธ์
โข ์ ๋ฌธ ์ฉ์ด ์ค์ธ์: ํน์ํ ์ ๊ณ ์ฉ์ด๋ ์๋ชป ์ธ์ํ ์ ์์ด
โข ๋งฅ๋ฝ ์ดํด ์คํจ: ๋๋ด์ด๋ ๋น์ ๋ฅผ ๋ฌธ์ ๊ทธ๋๋ก ๋ฐ์๋ค์ผ ์ ์์ด
โข ํ์ ํผ๋: ๋ชฉ์๋ฆฌ๊ฐ ๋น์ทํ ์ฌ๋๋ค์ ๊ตฌ๋ถ ๋ชปํ ์ ์์ด
โข ๋ฐฐ๊ฒฝ ์์: ์์์ด ๋ง์ผ๋ฉด ์ธ์๋ฅ ์ด ๋จ์ด์ ธ
๐ก ๋์ ๋ฐฉ๋ฒ:
1. ์ค์ํ ํ์๋ ์ฌ๋์ด ํ ๋ฒ ๋ ๊ฒํ
2. ์ ๋ฌธ ์ฉ์ด ์ฌ์ ์ ์ง์์ ์ผ๋ก ์ ๋ฐ์ดํธ
3. ํ์ ํ๊ฒฝ์ ๊ฐ์ (์ข์ ๋ง์ดํฌ, ์กฐ์ฉํ ๊ณต๊ฐ)
4. ์์ด์ ํธ๊ฐ ๋ถํ์คํ ๋ถ๋ถ์ ํ์ํ๋๋ก ์ค์
๐ฐ ๋น์ฉ ๊ด๋ฆฌ
โข GPT-4: ์ ๋ ฅ ํ ํฐ๋น $0.03, ์ถ๋ ฅ ํ ํฐ๋น $0.06
โข Whisper: ๋ถ๋น $0.006
1์๊ฐ์ง๋ฆฌ ํ์๋ฅผ ์ฒ๋ฆฌํ๋ฉด ๋๋ต:
โข ์์ฑ ์ธ์: $0.36
โข ํ์๋ก ์์ฑ: $2-5 (๊ธธ์ด์ ๋ฐ๋ผ)
โข ์์ฝ ๋ฐ ๋ถ์: $1-3
โข ์ด ์ฝ $3-8 ์ ๋
ํ๋ฃจ์ 10๊ฐ ํ์๋ฅผ ์ฒ๋ฆฌํ๋ฉด ํ ๋ฌ์ $900-2,400 ์ ๋ ๋์ฌ ์ ์์ด! ๐ฑ
๐ก ๋น์ฉ ์ ๊ฐ ํ:
1. ์บ์ฑ ํ์ฉ: ๊ฐ์ ๋ด์ฉ์ ์ฌ์ฒ๋ฆฌํ์ง ์๊ธฐ
2. ๋ชจ๋ธ ์ ํ: ๊ฐ๋จํ ์์ ์ GPT-3.5 ์ฌ์ฉ
3. ๋ฐฐ์น ์ฒ๋ฆฌ: ์ฌ๋ฌ ํ์๋ฅผ ๋ชจ์์ ํ ๋ฒ์ ์ฒ๋ฆฌ
4. ์คํ์์ค ๋ชจ๋ธ ๊ณ ๋ ค: Llama 2, Mistral ๋ฑ ๋ฌด๋ฃ ๋ชจ๋ธ ํ์ฉ
๐ง ๊ธฐ์ ์ ํ๊ณ
โข ๋์ ์ฒ๋ฆฌ ํ๊ณ: ๋๋ฌด ๋ง์ ํ์๋ฅผ ๋์์ ์ฒ๋ฆฌํ๋ฉด ์ฑ๋ฅ ์ ํ
โข ๋คํธ์ํฌ ์์กด์ฑ: ์ธํฐ๋ท ์ฐ๊ฒฐ์ด ํ์ (ํด๋ผ์ฐ๋ API ์ฌ์ฉ ์)
โข ์ธ์ด ์ ํ: ํ๊ตญ์ด๋ ์์ด๋ณด๋ค ์ ํ๋๊ฐ ์ฝ๊ฐ ๋ฎ์ ์ ์์ด
์ด๋ฐ ํ๊ณ๋ค์ ์ดํดํ๊ณ ์์คํ ์ ์ค๊ณํด์ผ ํด! ๐ฏ
๐ ๋ฏธ๋ ์ ๋ง๊ณผ ๋ฐ์ ๋ฐฉํฅ
๋ฉํฐ์์ด์ ํธ ์์คํ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด! ์์ผ๋ก ์ด๋ป๊ฒ ๋ณํ ๊น? ๐ฎ๐ค ๋ ๋๋ํ ์์ด์ ํธ
โข ์์ฑ๋ฟ๋ง ์๋๋ผ ํ๋ฉด ๊ณต์ , ํ์ , ์ ์ค์ฒ๊น์ง ๋ถ์
โข ๋ฐํ ์๋ฃ์ ๋ฐ์ธ ๋ด์ฉ์ ์ฐ๊ฒฐํ์ฌ ๋ ์ ํํ ํ์๋ก ์์ฑ
โข ์ฐธ์์๋ค์ ๋ฐ์์ ๋ถ์ํ์ฌ ์ค์๋ ํ๋จ
2. ์ปจํ ์คํธ ๊ธฐ์ต
โข ์ด์ ํ์ ๋ด์ฉ์ ๊ธฐ์ตํ๊ณ ์ฐ๊ฒฐ
โข ํ๋ก์ ํธ ์ ์ฒด ๋งฅ๋ฝ์์ ํ์ ๋ด์ฉ ํด์
โข ์ฅ๊ธฐ์ ์ธ ํธ๋ ๋์ ํจํด ํ์
3. ๋ฅ๋์ ์ ์
โข ํ์ ์ค ๊ด๋ จ ์๋ฃ๋ฅผ ์๋์ผ๋ก ์ฐพ์์ ์ ์
โข ๋น์ทํ ๊ณผ๊ฑฐ ์ฌ๋ก๋ฅผ ์ฐธ๊ณ ๋ก ์ ๊ณต
โข ์ ์ฌ์ ๋ฌธ์ ์ ์ ๋ฏธ๋ฆฌ ๊ฒฝ๊ณ
๐ ํ๋ซํผ ํตํฉ
โข Zoom/Teams โ ์๋ ๋ นํ ๋ฐ ์ ์ฌ
โข ํ์๋ก ์์คํ โ ์๋ ์์ฑ ๋ฐ ๋ถ์
โข Jira/Asana โ ์ก์ ์์ดํ ์๋ ์์ฑ
โข Slack/Teams โ ์์ฝ๋ณธ ์๋ ๊ณต์
โข Notion/Confluence โ ๋ฌธ์ ์๋ ์ ๋ฐ์ดํธ
โข Calendar โ ํ์ ํ์ ์๋ ์ค์ผ์ค๋ง
ํด๋ฆญ ํ ๋ฒ ์์ด ๋ชจ๋ ๊ฒ ์๋์ผ๋ก! ๐ฏ
๐จ ๊ฐ์ธํ์ ์ปค์คํฐ๋ง์ด์ง
โข ๋ด๊ฐ ์ ํธํ๋ ํ์๋ก ํ์์ผ๋ก ์๋ ์์ฑ
โข ๋ด ์ญํ ์ ๋ง๋ ์ ๋ณด๋ง ํ์ด๋ผ์ดํธ
โข ๋ด ์ ๋ฌด ์คํ์ผ์ ๋ง์ถ ์ก์ ์์ดํ ์ ์
โข ๋ด๊ฐ ๊ด์ฌ ์๋ ์ฃผ์ ๋ ๋ ์์ธํ ์ ๋ฆฌ
๋ง์น ์ค๋ซ๋์ ํจ๊ป ์ผํ ๋น์์ฒ๋ผ! ๐
๐ ์์ธก๊ณผ ์ธ์ฌ์ดํธ
โข ํ๋ก์ ํธ ์งํ ์ํฉ์ ๋ถ์ํ์ฌ ์ง์ฐ ์ํ ์์ธก
โข ํ ๋ถ์๊ธฐ๋ฅผ ๋ถ์ํ์ฌ ๊ฐ๋ฑ ๊ฐ๋ฅ์ฑ ์ฌ์ ๊ฐ์ง
โข ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก ์ต์ ์ ์์ฌ๊ฒฐ์ ์ ์
โข ๋ฆฌ์์ค ๋ถ์กฑ์ด๋ ๋ณ๋ชฉ ํ์ ๋ฏธ๋ฆฌ ๊ฒฝ๊ณ
ํ์๋ก ์์คํ ์ด ์ง์ง ์ ๋ต์ ๋๊ตฌ๊ฐ ๋๋ ๊ฑฐ์ง! ๐
1. ๋ฐ์ดํฐ ์ถ์ : ์ง๊ธ๋ถํฐ ์์ธ ๋ฐ์ดํฐ๊ฐ ๋ฏธ๋์ ์์ฐ
2. ํ์ต ๊ณก์ : ๋นจ๋ฆฌ ์์ํ ์๋ก ๋นจ๋ฆฌ ์ต์ํด์ ธ
3. ๊ฒฝ์ ์ฐ์: ๋จ๋ค๋ณด๋ค ๋จผ์ ๋์ ํ๋ฉด ์์๊ฐ ์ ์์ด
4. ๋น์ฉ ์ ๊ฐ: ์ง๊ธ ํฌ์ํ ์๊ฐ์ด ๋์ค์ ๋ช ๋ฐฐ๋ก ๋์์
์๋ฒฝํ์ง ์์๋ ๊ด์ฐฎ์. ์์ํ๊ณ ๊ฐ์ ํ๋ฉด ๋ผ! ๐ช
๐ ๏ธ ์ง์ ๋ง๋ค์ด๋ณด๊ธฐ: ์์ ํ ์์
์, ์ด์ ์ง๊ธ๊น์ง ๋ฐฐ์ด ๊ฑธ ๋ชจ๋ ํฉ์ณ์ ์์ ํ ์์คํ ์ ๋ง๋ค์ด๋ณด์! ๐๐ฆ ํ๋ก์ ํธ ๊ตฌ์กฐ
meeting-minutes-automation/
โ
โโโ agents/
โ โโโ __init__.py
โ โโโ transcription_agent.py # ์์ฑ์ธ์
โ โโโ diarization_agent.py # ํ์ ๊ตฌ๋ถ
โ โโโ writer_agent.py # ํ์๋ก ์์ฑ
โ โโโ summary_agent.py # ์์ฝ ๋ถ์
โ โโโ action_agent.py # ์ก์
์์ดํ
์ถ์ถ
โ
โโโ integrations/
โ โโโ __init__.py
โ โโโ zoom_integration.py # Zoom ์ฐ๋
โ โโโ notion_integration.py # Notion ์ฐ๋
โ โโโ email_notifier.py # ์ด๋ฉ์ผ ๋ฐ์ก
โ โโโ slack_notifier.py # Slack ์๋ฆผ
โ
โโโ utils/
โ โโโ __init__.py
โ โโโ audio_processor.py # ์ค๋์ค ์ฒ๋ฆฌ
โ โโโ text_processor.py # ํ
์คํธ ์ฒ๋ฆฌ
โ โโโ file_manager.py # ํ์ผ ๊ด๋ฆฌ
โ
โโโ config/
โ โโโ config.yaml # ์ค์ ํ์ผ
โ โโโ prompts.yaml # ํ๋กฌํํธ ํ
ํ๋ฆฟ
โ
โโโ main.py # ๋ฉ์ธ ์คํ ํ์ผ
โโโ requirements.txt # ์์กด์ฑ ํจํค์ง
โโโ README.md # ๋ฌธ์
โ๏ธ ์ค์ ํ์ผ (config.yaml)
# config/config.yaml
# API ํค ์ค์
api_keys:
openai: "your-openai-key"
huggingface: "your-hf-key"
notion: "your-notion-key"
zoom: "your-zoom-key"
# ๋ชจ๋ธ ์ค์
models:
transcription: "whisper-1"
llm: "gpt-4"
llm_temperature: 0.3
# ์ฒ๋ฆฌ ์ต์
processing:
chunk_size: 300 # ์ด ๋จ์
parallel_processing: true
max_workers: 4
# ์ถ๋ ฅ ์ค์
output:
formats: ["markdown", "pdf", "json"]
save_audio: false
save_transcript: true
# ์๋ฆผ ์ค์
notifications:
email:
enabled: true
recipients: ["team@company.com"]
slack:
enabled: true
channel: "#meeting-minutes"
notion:
enabled: true
database_id: "your-database-id"
# ๋ณด์ ์ค์
security:
encrypt_files: true
auto_delete_after_days: 90
access_control: true
๐ฏ ๋ฉ์ธ ์คํ ํ์ผ (main.py)
#!/usr/bin/env python3
"""
ํ์๋ก ์๋ํ ์์คํ
- ๋ฉ์ธ ์คํ ํ์ผ
"""
import asyncio
import argparse
from pathlib import Path
import yaml
from datetime import datetime
from agents.transcription_agent import TranscriptionAgent
from agents.diarization_agent import SpeakerDiarizationAgent
from agents.writer_agent import MinutesWriterAgent
from agents.summary_agent import SummaryAgent
from agents.action_agent import ActionItemAgent
from integrations.email_notifier import EmailNotifier
from integrations.slack_notifier import SlackNotifier
from integrations.notion_integration import NotionIntegration
from utils.audio_processor import AudioProcessor
from utils.file_manager import FileManager
class MeetingMinutesSystem:
"""
๋ฉํฐ์์ด์ ํธ ํ์๋ก ์๋ํ ์์คํ
"""
def __init__(self, config_path="config/config.yaml"):
# ์ค์ ๋ก๋
with open(config_path, 'r', encoding='utf-8') as f:
self.config = yaml.safe_load(f)
# ์์ด์ ํธ ์ด๊ธฐํ
self._init_agents()
# ํตํฉ ์๋น์ค ์ด๊ธฐํ
self._init_integrations()
# ์ ํธ๋ฆฌํฐ ์ด๊ธฐํ
self.audio_processor = AudioProcessor()
self.file_manager = FileManager(self.config['output'])
print("โ
์์คํ
์ด๊ธฐํ ์๋ฃ!")
def _init_agents(self):
"""์์ด์ ํธ ์ด๊ธฐํ"""
api_key = self.config['api_keys']['openai']
hf_token = self.config['api_keys']['huggingface']
self.transcription_agent = TranscriptionAgent(api_key)
self.diarization_agent = SpeakerDiarizationAgent(hf_token)
self.writer_agent = MinutesWriterAgent(api_key)
self.summary_agent = SummaryAgent(api_key)
self.action_agent = ActionItemAgent(api_key)
print("๐ค ์์ด์ ํธ ์ด๊ธฐํ ์๋ฃ")
def _init_integrations(self):
"""ํตํฉ ์๋น์ค ์ด๊ธฐํ"""
if self.config['notifications']['email']['enabled']:
self.email_notifier = EmailNotifier(self.config)
if self.config['notifications']['slack']['enabled']:
self.slack_notifier = SlackNotifier(self.config)
if self.config['notifications']['notion']['enabled']:
self.notion = NotionIntegration(
self.config['api_keys']['notion']
)
print("๐ ํตํฉ ์๋น์ค ์ด๊ธฐํ ์๋ฃ")
async def process_meeting(self, audio_file, meeting_info=None):
"""
ํ์ ์ฒ๋ฆฌ ๋ฉ์ธ ํจ์
Args:
audio_file: ์ค๋์ค ํ์ผ ๊ฒฝ๋ก
meeting_info: ํ์ ์ ๋ณด (์ ๋ชฉ, ๋ ์ง, ์ฐธ์์ ๋ฑ)
"""
print("\n" + "="*60)
print("๐ ํ์๋ก ์๋ํ ์์!")
print("="*60 + "\n")
start_time = datetime.now()
results = {}
try:
# Step 1: ์ค๋์ค ์ ์ฒ๋ฆฌ
print("๐ Step 1: ์ค๋์ค ํ์ผ ์ฒ๋ฆฌ")
processed_audio = self.audio_processor.preprocess(audio_file)
print("โ
์ค๋์ค ์ ์ฒ๋ฆฌ ์๋ฃ\n")
# Step 2: ์์ฑ ์ธ์ + ํ์ ๊ตฌ๋ถ (๋ณ๋ ฌ ์ฒ๋ฆฌ)
print("๐ค Step 2: ์์ฑ ์ธ์ ๋ฐ ํ์ ๊ตฌ๋ถ")
transcript_task = self.transcription_agent.transcribe_with_timestamps(
processed_audio
)
speaker_task = self.diarization_agent.identify_speakers(
processed_audio
)
transcript, speakers = await asyncio.gather(
transcript_task,
speaker_task
)
# ์์ฑ ์ธ์ ๊ฒฐ๊ณผ์ ํ์ ์ ๋ณด ๋ณํฉ
merged_transcript = self.diarization_agent.merge_with_transcript(
transcript,
speakers
)
conversation = self.diarization_agent.format_conversation(
merged_transcript
)
results['transcript'] = conversation
print("โ
์์ฑ ์ธ์ ๋ฐ ํ์ ๊ตฌ๋ถ ์๋ฃ\n")
# Step 3: ํ์๋ก ์์ฑ
print("โ๏ธ Step 3: ํ์๋ก ์์ฑ")
minutes = await self.writer_agent.create_minutes(
conversation,
meeting_info
)
markdown = self.writer_agent.format_to_markdown(minutes)
results['minutes'] = markdown
print("โ
ํ์๋ก ์์ฑ ์๋ฃ\n")
# Step 4: ๋ถ์ ์์
(๋ณ๋ ฌ ์ฒ๋ฆฌ)
print("๐ Step 4: ์์ฝ ๋ฐ ๋ถ์")
analysis_tasks = [
self.summary_agent.create_executive_summary(markdown),
self.summary_agent.extract_key_insights(markdown),
self.summary_agent.analyze_sentiment(markdown),
self.action_agent.extract_action_items(markdown)
]
exec_summary, insights, sentiment, action_items = await asyncio.gather(
*analysis_tasks
)
results['executive_summary'] = exec_summary
results['insights'] = insights
results['sentiment'] = sentiment
results['action_items'] = action_items
print("โ
๋ถ์ ์๋ฃ\n")
# Step 5: ํ์ผ ์ ์ฅ
print("๐พ Step 5: ๊ฒฐ๊ณผ ์ ์ฅ")
saved_files = self.file_manager.save_results(results, meeting_info)
print(f"โ
ํ์ผ ์ ์ฅ ์๋ฃ: {len(saved_files)}๊ฐ ํ์ผ\n")
# Step 6: ์๋ฆผ ๋ฐ์ก (๋ณ๋ ฌ ์ฒ๋ฆฌ)
print("๐ข Step 6: ์๋ฆผ ๋ฐ์ก")
notification_tasks = []
if self.config['notifications']['email']['enabled']:
notification_tasks.append(
self.email_notifier.send_minutes(
results,
saved_files['markdown']
)
)
if self.config['notifications']['slack']['enabled']:
notification_tasks.append(
self.slack_notifier.post_summary(
exec_summary,
saved_files['markdown']
)
)
if self.config['notifications']['notion']['enabled']:
notification_tasks.append(
self.notion.create_meeting_page(
self.config['notifications']['notion']['database_id'],
results
)
)
if notification_tasks:
await asyncio.gather(*notification_tasks)
print("โ
์๋ฆผ ๋ฐ์ก ์๋ฃ\n")
# ์๋ฃ ๋ฉ์์ง
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
print("="*60)
print("๐ ๋ชจ๋ ์์
์๋ฃ!")
print(f"โฑ๏ธ ์ด ์์ ์๊ฐ: {duration:.2f}์ด")
print(f"๐ ์ ์ฅ๋ ํ์ผ: {saved_files}")
print("="*60)
return results
except Exception as e:
print(f"โ ์ค๋ฅ ๋ฐ์: {str(e)}")
raise
async def process_realtime(self, audio_stream):
"""
์ค์๊ฐ ์ฒ๋ฆฌ (์คํธ๋ฆฌ๋ฐ)
"""
print("๐๏ธ ์ค์๊ฐ ์ฒ๋ฆฌ ๋ชจ๋ ์์")
# ์ค์๊ฐ ์ฒ๋ฆฌ ๋ก์ง
# (์ด์ ์์ ์ RealtimeProcessor ํ์ฉ)
pass
def main():
"""๋ฉ์ธ ํจ์"""
parser = argparse.ArgumentParser(
description="ํ์๋ก ์๋ํ ์์คํ
"
)
parser.add_argument(
'audio_file',
help="์ฒ๋ฆฌํ ์ค๋์ค ํ์ผ ๊ฒฝ๋ก"
)
parser.add_argument(
'--title',
help="ํ์ ์ ๋ชฉ",
default="ํ์"
)
parser.add_argument(
'--date',
help="ํ์ ๋ ์ง (YYYY-MM-DD)",
default=datetime.now().strftime("%Y-%m-%d")
)
parser.add_argument(
'--attendees',
help="์ฐธ์์ (์ผํ๋ก ๊ตฌ๋ถ)",
default=""
)
parser.add_argument(
'--config',
help="์ค์ ํ์ผ ๊ฒฝ๋ก",
default="config/config.yaml"
)
args = parser.parse_args()
# ํ์ ์ ๋ณด ๊ตฌ์ฑ
meeting_info = {
'title': args.title,
'date': args.date,
'attendees': [a.strip() for a in args.attendees.split(',') if a.strip()]
}
# ์์คํ
์ด๊ธฐํ ๋ฐ ์คํ
system = MeetingMinutesSystem(config_path=args.config)
# ๋น๋๊ธฐ ์คํ
asyncio.run(system.process_meeting(args.audio_file, meeting_info))
if __name__ == "__main__":
main()
๐ ์คํ ๋ฐฉ๋ฒ
# ๊ฐ์ํ๊ฒฝ ์์ฑ
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# ํจํค์ง ์ค์น
pip install -r requirements.txt
# ์ค์ ํ์ผ ์์
# config/config.yaml ํ์ผ์์ API ํค ๋ฑ์ ์ค์
# ๊ฐ์ฅ ๊ฐ๋จํ ์คํ
python main.py meeting_recording.mp3
# ํ์ ์ ๋ณด์ ํจ๊ป ์คํ
python main.py meeting_recording.mp3 \
--title "2024 Q1 ์ ๋ต ํ์" \
--date "2024-01-15" \
--attendees "๊น์ฒ ์, ์ด์ํฌ, ๋ฐ๋ฏผ์"
# ์ปค์คํ
์ค์ ํ์ผ ์ฌ์ฉ
python main.py meeting_recording.mp3 \
--config my_config.yaml
์คํ์ด ์๋ฃ๋๋ฉด ๋ค์ ํ์ผ๋ค์ด ์์ฑ๋ผ:
output/
โโโ minutes_20240115_143022.md # ์ ์ฒด ํ์๋ก
โโโ summary_20240115_143022.md # ์์ฝ๋ณธ
โโโ data_20240115_143022.json # JSON ๋ฐ์ดํฐ
โโโ transcript_20240115_143022.txt # ์๋ณธ ํ
์คํธ
2. ์ค์ ์ต์ ํ: ํ์ ๋์ฆ์ ๋ง๊ฒ config.yaml์ ์กฐ์ ํด
3. ํ๋กฌํํธ ํ๋: prompts.yaml์์ ํ๋กฌํํธ๋ฅผ ์์ ํ๋ฉด ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ ธ
4. ๋ก๊ทธ ํ์ธ: ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ๋ก๊ทธ๋ฅผ ๊ผญ ํ์ธํด๋ด
5. ์ ์ง์ ๋์ : ํ ํ์์ ๋จผ์ ์จ๋ณด๊ณ ํ๋ํด ๋๊ฐ๋ ๊ฒ ์ข์! ๐
๐ก ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ ์ฐพ๊ธฐ
ํน์ ์ง์ ๊ตฌ์ถํ๊ธฐ๊ฐ ๋ถ๋ด์ค๋ฝ๋ค๋ฉด? ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐๐ง ๊ฐ๋ฐ ์ง์
โข ๋ฉํฐ์์ด์ ํธ ์์คํ ๊ตฌ์ถ ๋ํ
โข ๊ธฐ์กด ์์คํ ์ AI ๊ธฐ๋ฅ ์ถ๊ฐ
โข ์ปค์คํ ์์ด์ ํธ ๊ฐ๋ฐ
โข ์ฑ๋ฅ ์ต์ ํ ๋ฐ ํ๋
๐ ๊ต์ก ๋ฐ ์ปจ์คํ
โข 1:1 ๋ง์ถค ๊ต์ก
โข ํ ์ํฌ์ต ์งํ
โข ์ํคํ ์ฒ ์ค๊ณ ์ปจ์คํ
โข ๋์ ์ ๋ต ์๋ฆฝ
๐ ๏ธ ์ ์ง๋ณด์
โข ์์คํ ๋ชจ๋ํฐ๋ง
โข ๋ฒ๊ทธ ์์ ๋ฐ ์ ๋ฐ์ดํธ
โข ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
โข ๊ธฐ์ ์ง์
ํนํ AI ์๋ํ๋ ์์ด์ ํธ ๊ฐ๋ฐ ๊ฒฝํ์ด ์๋ ์ ๋ฌธ๊ฐ๋ค์ด ๋ง์์, ํ๋ก์ ํธ์ ๋ฑ ๋ง๋ ์ฌ๋์ ์ฐพ์ ์ ์์ ๊ฑฐ์ผ! ๐ช
๐ ํ์ต ๋ฆฌ์์ค
๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ์๋ฃ๋ค์ ์ถ์ฒํด! ๐| ์นดํ ๊ณ ๋ฆฌ | ๋ฆฌ์์ค | ๋์ด๋ |
|---|---|---|
| ๊ณต์ ๋ฌธ์ | LangChain Documentation OpenAI API Reference Hugging Face Docs |
โญโญโญ |
| ์จ๋ผ์ธ ๊ฐ์ | Coursera: AI Agents Udemy: LangChain Masterclass DeepLearning.AI: Building Systems with ChatGPT |
โญโญ |
| ๋ธ๋ก๊ทธ/์ํฐํด | LangChain Blog OpenAI Blog Towards Data Science |
โญโญ |
| ์ปค๋ฎค๋ํฐ | LangChain Discord r/LangChain (Reddit) Stack Overflow |
โญ |
| GitHub | LangChain Examples Awesome LangChain OpenAI Cookbook |
โญโญโญ |
โข Python ๊ธฐ์ด ๋ณต์ต
โข OpenAI API ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
โข ๊ฐ๋จํ ์ฑ๋ด ๋ง๋ค์ด๋ณด๊ธฐ
2๋จ๊ณ (2-3์ฃผ)
โข LangChain ๊ธฐ์ด
โข ํ๋กฌํํธ ์์ง๋์ด๋ง
โข ์ฒด์ธ๊ณผ ์์ด์ ํธ ๊ฐ๋ ์ดํด
3๋จ๊ณ (3-4์ฃผ)
โข ๋ฉํฐ์์ด์ ํธ ์์คํ ๊ตฌ์ถ
โข ์ค์ ํ๋ก์ ํธ ์งํ
โข ์ฑ๋ฅ ์ต์ ํ
4๋จ๊ณ (์ง์์ )
โข ์ต์ ๊ธฐ์ ํธ๋ ๋ ํ๋ก์ฐ
โข ์ปค๋ฎค๋ํฐ ์ฐธ์ฌ
โข ์์ ๋ง์ ํ๋ก์ ํธ ๋ฐ์ ์ํค๊ธฐ
๊พธ์คํ ํ๋ค ๋ณด๋ฉด ์ด๋์ ์ ๋ฌธ๊ฐ๊ฐ ๋์ด ์์ ๊ฑฐ์ผ! ๐
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์์ด! ๐๋ฉํฐ์์ด์ ํธ ์์คํ ์ ํ์ฉํ ํ์๋ก ์๋ํ์ ๋ํด ์ ๋ง ๋ง์ ๊ฑธ ๋ค๋ค์ง? ์์ฑ ์ธ์๋ถํฐ ์์ํด์ ํ์ ๊ตฌ๋ถ, ํ์๋ก ์์ฑ, ์์ฝ ๋ถ์, ๊ทธ๋ฆฌ๊ณ ์ค์ ๊ตฌํ๊น์ง! ๐ฏ
์ฌ๋ฌ AI ์์ด์ ํธ๊ฐ ๊ฐ์์ ์ ๋ฌธ ๋ถ์ผ์์ ํ์ ํ๋ฉด, ํผ์์๋ ๋ถ๊ฐ๋ฅํ ๋ณต์กํ ์์ ๋ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด.
2. ์ค์ฉ์ ์ธ ๊ฐ์น
ํ์๋ก ์์ฑ ์๊ฐ์ 80% ์ด์ ์ ๊ฐํ๊ณ , ๋ ์ ํํ๊ณ ์ฒด๊ณ์ ์ธ ๋ฌธ์๋ฅผ ๋ง๋ค ์ ์์ด. ์ด๊ฑด ๋จ์ํ ์๊ฐ ์ ์ฝ์ ๋์ด์ ์ ๋ฌด ํจ์จ์ฑ ์ ์ฒด๋ฅผ ๋์ด๋ ๊ฑฐ์ผ.
3. ํ์ฅ ๊ฐ๋ฅ์ฑ
ํ์๋ก ์๋ํ๋ ์์์ผ ๋ฟ์ด์ผ. ๊ฐ์ ์๋ฆฌ๋ก ๊ณ ๊ฐ ์๋ด ๋ถ์, ์ธํฐ๋ทฐ ์ ๋ฆฌ, ๊ฐ์ ๋ ธํธ ์์ฑ ๋ฑ ๋ค์ํ ๋ถ์ผ์ ์ ์ฉํ ์ ์์ด.
4. ์ง์์ ์ธ ๋ฐ์
AI ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. ์ง๊ธ ์์ํ๋ฉด ๋ฏธ๋์ ๋ ํฐ ํํ์ ๋๋ฆด ์ ์์ง!
๐ ๋ค์ ๋จ๊ณ
์ด์ ๋ญ ํด์ผ ํ ๊น?โข ๊ฐ๋จํ ์์ฑ ์ธ์๋ถํฐ ์์
โข ์งง์ ํ์๋ก ํ ์คํธ
โข ๊ฒฐ๊ณผ๋ฅผ ๋ถ์ํ๊ณ ๊ฐ์ ์ ์ฐพ๊ธฐ
โข ๊ธฐ๋ฅ์ ํ๋์ฉ ์ถ๊ฐ
โข ํ ํผ๋๋ฐฑ ์๋ ด
โข ๋ค๋ฅธ ๋๊ตฌ๋ค๊ณผ ์ฐ๋
โข ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
โข ๋น์ฉ ์ต์ ํ
โข ์ฌ์ฉ์ ๊ฒฝํ ๊ฐ์
โข ํ์ ํ์ฐ
โข ๋ค๋ฅธ ๋ถ์๋ก ํ๋
โข ๊ฒฝํ ๊ณต์ (๋ธ๋ก๊ทธ, ์ปค๋ฎค๋ํฐ ๋ฑ)
๊ถ๊ธํ ์ ์ด ์๊ฑฐ๋ ๋์์ด ํ์ํ๋ฉด ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ๋ค์ ์ฐพ์๋ด. ํจ๊ปํ๋ฉด ๋ ๋นจ๋ฆฌ, ๋ ๋ฉ๋ฆฌ ๊ฐ ์ ์์ด!
์ฌ๋ฌ๋ถ์ ์ฑ๊ณต์ ์ธ AI ์๋ํ ์ฌ์ ์ ์์ํ ๊ฒ! ํ์ดํ ! ๐๐ช
๐ ๋๊น์ง ์ฝ์ด์ค์ ๊ณ ๋ง์!
์ด์ ์ฌ๋ฌ๋ถ๋ ๋ฉํฐ์์ด์ ํธ ์ ๋ฌธ๊ฐ! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.