Ver3.0 ๐ค LangChain์ผ๋ก ๋ฐ์ดํฐ ์ ๋ฆฌยท์์ฝ ์๋ํ ์๋ฒฝ ๊ฐ์ด๋: AI๊ฐ ๋น์ ์ ์ ๋ฌด๋ฅผ ํ์ ํ๋ ๋ฐฉ๋ฒ

๐ค LangChain์ผ๋ก ๋ฐ์ดํฐ ์ ๋ฆฌยท์์ฝ ์๋ํ ์๋ฒฝ ๊ฐ์ด๋: AI๊ฐ ๋น์ ์ ์ ๋ฌด๋ฅผ ํ์ ํ๋ ๋ฐฉ๋ฒ
๋ณต์กํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ์ด์ AI์๊ฒ ๋งก๊ธฐ๊ณ ์ปคํผ ํ ์์ ์ฌ์ ๋ฅผ ์ฆ๊ฒจ๋ณด์ธ์ โ
์๋ ! ์ค๋์ ์ ๋ง ํฅ๋ฏธ๋ก์ด ์ฃผ์ ๋ฅผ ๊ฐ์ง๊ณ ์์ด. ๋ฐ๋ก LangChain์ ํ์ฉํ ๋ฐ์ดํฐ ์ ๋ฆฌ์ ์์ฝ ์๋ํ์ธ๋ฐ, ์ด๊ฒ ์ ์ค์ํ๋๊ณ ? ๐ค
์์ํด๋ด. ๋งค์ผ ์์นจ ์ถ๊ทผํ๋ฉด ์๋ฐฑ ํ์ด์ง์ ๋ณด๊ณ ์, ๋์๋ ์ด๋ฉ์ผ, ๊ณ ๊ฐ ํผ๋๋ฐฑ, ์์ฅ ์กฐ์ฌ ์๋ฃ๋ค์ด ์์ฌ์์ด. ์ด๊ฑธ ์ผ์ผ์ด ์ฝ๊ณ ์ ๋ฆฌํ๊ณ ์์ฝํ๋ ค๋ฉด... ์๋ง ํด๊ทผ ์๊ฐ์ด ์์ ์ ๋๊ธธ ๊ฑฐ์ผ. ๐ฑ
ํ์ง๋ง LangChain์ ํ์ฉํ๋ฉด? ์ด ๋ชจ๋ ์์ ์ ์๋ํํ ์ ์์ด! AI๊ฐ ๋น์ ์ ๋๋ํ ๋น์๊ฐ ๋์ด ๋ฐ์ดํฐ๋ฅผ ์ ๋ฆฌํ๊ณ , ํต์ฌ๋ง ์์ ๋ฝ์๋ด๊ณ , ์ฌ์ง์ด ์ธ์ฌ์ดํธ๊น์ง ์ ๊ณตํด์ค. ๋ง์น ์ฌ๋ฅ๋ท์์ ์ต๊ณ ์ ๋ฐ์ดํฐ ๋ถ์ ์ ๋ฌธ๊ฐ๋ฅผ ๊ณ ์ฉํ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ! ๐
๐ฏ LangChain์ด ๋ญ๊ธธ๋? ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ํํค์ณ๋ณด์
์, ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํ๊ธฐ ์ ์ LangChain์ด ์ ํํ ๋ญ์ง๋ถํฐ ์์์ผ๊ฒ ์ง? ๐ง
LangChain์ ์ ์ฒด
LangChain์ ๋๊ท๋ชจ ์ธ์ด ๋ชจ๋ธ(LLM)์ ํ์ฉํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฝ๊ฒ ๊ฐ๋ฐํ ์ ์๋๋ก ๋์์ฃผ๋ ์คํ์์ค ํ๋ ์์ํฌ์ผ. 2022๋ 10์์ ์ฒ์ ๊ณต๊ฐ๋์๊ณ , ํ์ฌ๋ Python๊ณผ JavaScript/TypeScript ๋ฒ์ ์ด ๋ชจ๋ ์ ๊ณต๋๊ณ ์์ด.
์ฝ๊ฒ ๋งํ๋ฉด, ChatGPT ๊ฐ์ AI ๋ชจ๋ธ์ ๋ด ํ๋ก๊ทธ๋จ์ ์ฐ๊ฒฐํด์ ์ค์ ์ ๋ฌด์ ํ์ฉํ ์ ์๊ฒ ๋ง๋ค์ด์ฃผ๋ ๋๊ตฌ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ! ๐ ๏ธ
์ LangChain์ ์จ์ผ ํ ๊น?
GPT API๋ฅผ ์ง์ ํธ์ถํด์ ์ธ ์๋ ์๋๋ฐ, ์ ๊ตณ์ด LangChain์ ์ธ๊น? ๊ทธ ์ด์ ๋ ๋ช ํํด:
1. ์ฒด์ธ(Chain) ๊ตฌ์กฐ โ๏ธ
์ฌ๋ฌ ์์
์ ์์ฐจ์ ์ผ๋ก ์ฐ๊ฒฐํ ์ ์์ด. ์๋ฅผ ๋ค์ด "๋ฌธ์ ์ฝ๊ธฐ โ ์์ฝํ๊ธฐ โ ๋ฒ์ญํ๊ธฐ โ ์ด๋ฉ์ผ ์์ฑํ๊ธฐ"๋ฅผ ํ๋์ ํ๋ฆ์ผ๋ก ๋ง๋ค ์ ์์ง.
2. ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๐ง
๋ํ ๋ด์ฉ์ด๋ ์ด์ ์์
๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์ตํ ์ ์์ด. ๋ง์น ์ฌ๋์ฒ๋ผ ๋งฅ๋ฝ์ ์ดํดํ๋ฉด์ ์์
์ ์งํํ๋ ๊ฑฐ์ผ.
3. ๋ค์ํ ๋ฐ์ดํฐ ์์ค ์ฐ๊ฒฐ ๐
PDF, ์นํ์ด์ง, ๋ฐ์ดํฐ๋ฒ ์ด์ค, API ๋ฑ ๋ค์ํ ๊ณณ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ ์ฒ๋ฆฌํ ์ ์์ด.
4. ์์ด์ ํธ(Agent) ๊ธฐ๋ฅ ๐ค
AI๊ฐ ์ค์ค๋ก ํ๋จํด์ ํ์ํ ๋๊ตฌ๋ฅผ ์ ํํ๊ณ ์์
์ ์ํํ ์ ์์ด. ์ง์ง ์๋ํ์ ํต์ฌ์ด์ง!
๐ ๋ฐ์ดํฐ ์ ๋ฆฌยท์์ฝ ์๋ํ, ์ด๋ป๊ฒ ์์ํ ๊น?
์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก LangChain์ ํ์ฉํ ๋ฐ์ดํฐ ์๋ํ ์์คํ ์ ๋ง๋ค์ด๋ณด์! ๋จ๊ณ๋ณ๋ก ์ฐจ๊ทผ์ฐจ๊ทผ ์ค๋ช ํ ๊ฒ. ๐
Step 1: ํ๊ฒฝ ์ค์ ํ๊ธฐ
๋จผ์ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ธํ ํด์ผ ํด. Python์ ์ฌ์ฉํ๋ค๊ณ ๊ฐ์ ํ๊ณ ์งํํ ๊ฒ.
ํ์ํ ํจํค์ง ์ค์น:
pip install langchain
pip install openai
pip install chromadb
pip install tiktoken
pip install pypdf
pip install python-dotenv
๊ฐ ํจํค์ง๊ฐ ํ๋ ์ผ์ ๊ฐ๋จํ ์ค๋ช ํ๋ฉด:
langchain: ํต์ฌ ํ๋ ์์ํฌ
openai: OpenAI API ์ฐ๊ฒฐ์ฉ
chromadb: ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค (๋ฌธ์ ๊ฒ์์ฉ)
tiktoken: ํ ํฐ ๊ณ์ฐ์ฉ
pypdf: PDF ํ์ผ ์ฒ๋ฆฌ์ฉ
python-dotenv: ํ๊ฒฝ๋ณ์ ๊ด๋ฆฌ์ฉ
Step 2: API ํค ์ค์ ํ๊ธฐ
OpenAI API๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด API ํค๊ฐ ํ์ํด. .env ํ์ผ์ ๋ง๋ค์ด์ ๊ด๋ฆฌํ๋ ๊ฒ ์์ ํด:
# .env ํ์ผ
OPENAI_API_KEY=your-api-key-here
๊ทธ๋ฆฌ๊ณ Python ์ฝ๋์์ ์ด๋ ๊ฒ ๋ถ๋ฌ์:
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
Step 3: ๊ธฐ๋ณธ ๋ฌธ์ ๋ก๋ ๋ง๋ค๊ธฐ
์ด์ ์ค์ ๋ก ๋ฌธ์๋ฅผ ์ฝ์ด์ค๋ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์. LangChain์ ๋ค์ํ ๋ฌธ์ ๋ก๋๋ฅผ ์ ๊ณตํด:
from langchain.document_loaders import PyPDFLoader, TextLoader, CSVLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# PDF ํ์ผ ๋ก๋
pdf_loader = PyPDFLoader("report.pdf")
documents = pdf_loader.load()
# ํ
์คํธ๋ฅผ ์ ์ ํ ํฌ๊ธฐ๋ก ๋ถํ
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # ๊ฐ ์ฒญํฌ์ ํฌ๊ธฐ
chunk_overlap=200, # ์ฒญํฌ ๊ฐ ๊ฒน์น๋ ๋ถ๋ถ
length_function=len
)
texts = text_splitter.split_documents(documents)
๐ก ์ ๋ฌธ์๋ฅผ ์ชผ๊ฐ์ผ ํ ๊น?
LLM์ ํ ๋ฒ์ ์ฒ๋ฆฌํ ์ ์๋ ํ ์คํธ ์์ ์ ํ์ด ์์ด. ๊ทธ๋์ ํฐ ๋ฌธ์๋ฅผ ์์ ์กฐ๊ฐ(chunk)์ผ๋ก ๋๋ ์ ์ฒ๋ฆฌํ๋ ๊ฑฐ์ผ. chunk_overlap์ ์ค์ ํ๋ฉด ๋ฌธ๋งฅ์ด ๋๊ธฐ์ง ์๋๋ก ์๋ค ๋ด์ฉ์ด ์กฐ๊ธ์ฉ ๊ฒน์น๊ฒ ๋ง๋ค ์ ์์ด! ๐งฉ
๐ ์ค์ ! ๋ฐ์ดํฐ ์์ฝ ์์คํ ๊ตฌ์ถํ๊ธฐ
์ด์ ์ง์ง ์ฌ๋ฏธ์๋ ๋ถ๋ถ์ด์ผ! ์ค์ ๋ก ๋ฐ์ดํฐ๋ฅผ ์์ฝํ๋ ์์คํ ์ ๋ง๋ค์ด๋ณด์. ๐จ
๋ฐฉ๋ฒ 1: ๊ฐ๋จํ ์์ฝ ์ฒด์ธ
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ์ Summarization Chain์ ์ฌ์ฉํ๋ ๊ฑฐ์ผ:
from langchain.chains.summarize import load_summarize_chain
from langchain.chat_models import ChatOpenAI
# LLM ๋ชจ๋ธ ์ด๊ธฐํ
llm = ChatOpenAI(
temperature=0, # ์ฐฝ์์ฑ ์์ค (0=์ผ๊ด์ , 1=์ฐฝ์์ )
model_name="gpt-3.5-turbo"
)
# ์์ฝ ์ฒด์ธ ์์ฑ
chain = load_summarize_chain(
llm,
chain_type="map_reduce" # ๋๋ "stuff", "refine"
)
# ์์ฝ ์คํ
summary = chain.run(texts)
print(summary)
์ฒด์ธ ํ์ ๋ณ ํน์ง
| ํ์ | ํน์ง | ์ ํฉํ ๊ฒฝ์ฐ |
|---|---|---|
| stuff | ๋ชจ๋ ๋ฌธ์๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌ | ๋ฌธ์๊ฐ ์งง์ ๋ (๊ฐ์ฅ ๋น ๋ฆ) |
| map_reduce | ๊ฐ ์ฒญํฌ๋ฅผ ๊ฐ๋ณ ์์ฝ ํ ํตํฉ | ๊ธด ๋ฌธ์ ์ฒ๋ฆฌ (๋ณ๋ ฌ ์ฒ๋ฆฌ ๊ฐ๋ฅ) |
| refine | ์์ฐจ์ ์ผ๋ก ์์ฝ์ ๊ฐ์ | ์ ํ๋๊ฐ ์ค์ํ ๋ |
๋ฐฉ๋ฒ 2: ์ปค์คํ ํ๋กฌํํธ๋ก ์ ๊ตํ๊ฒ ์ ์ดํ๊ธฐ
๊ธฐ๋ณธ ์์ฝ์ด ๋ง์์ ์ ๋ ๋ค๋ฉด? ์ง์ ํ๋กฌํํธ๋ฅผ ์์ฑํด์ ์ํ๋ ํ์์ผ๋ก ์์ฝํ ์ ์์ด! โ๏ธ
from langchain.prompts import PromptTemplate
# ์ปค์คํ
ํ๋กฌํํธ ํ
ํ๋ฆฟ
prompt_template = """๋ค์ ํ
์คํธ๋ฅผ 3๊ฐ์ง ๊ด์ ์์ ์์ฝํด์ฃผ์ธ์:
1. ํต์ฌ ๋ด์ฉ (3-5๋ฌธ์ฅ)
2. ์ฃผ์ ์์น ๋ฐ ๋ฐ์ดํฐ
3. ์คํ ๊ฐ๋ฅํ ์ธ์ฌ์ดํธ
ํ
์คํธ:
{text}
์์ฝ:"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["text"]
)
# ํ๋กฌํํธ๋ฅผ ์ ์ฉํ ์ฒด์ธ
chain = load_summarize_chain(
llm,
chain_type="stuff",
prompt=PROMPT
)
summary = chain.run(texts)
์ด๋ ๊ฒ ํ๋ฉด ๋จ์ํ ์์ฝ๋ง ํ๋ ๊ฒ ์๋๋ผ, ๊ตฌ์กฐํ๋ ํ์์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ์ ์์ด! ๋ณด๊ณ ์ ์์ฑํ ๋ ์ ๋ง ์ ์ฉํ์ง. ๐
๋ฐฉ๋ฒ 3: ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ํ์ฉํ ์ค๋งํธ ์์ฝ
์ด๊ฑด ์ข ๋ ๊ณ ๊ธ ๊ธฐ์ ์ด์ผ. ๋ฌธ์ ์ ์ฒด๋ฅผ ๋ฒกํฐ๋ก ๋ณํํด์ ์ ์ฅํ๊ณ , ์ง๋ฌธ์ ๊ด๋ จ๋ ๋ถ๋ถ๋ง ์ฐพ์์ ์์ฝํ๋ ๋ฐฉ์์ด์ง:
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
# ์๋ฒ ๋ฉ ๋ชจ๋ธ ์ด๊ธฐํ
embeddings = OpenAIEmbeddings()
# ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ
vectordb = Chroma.from_documents(
documents=texts,
embedding=embeddings,
persist_directory="./chroma_db"
)
# ๊ฒ์ ๊ธฐ๋ฐ QA ์ฒด์ธ ์์ฑ
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectordb.as_retriever(
search_kwargs={"k": 3} # ์์ 3๊ฐ ๊ด๋ จ ๋ฌธ์ ๊ฒ์
)
)
# ์ง๋ฌธํ๊ธฐ
question = "์ด ๋ณด๊ณ ์์ ์ฃผ์ ์ฌ๋ฌด ์งํ๋ ๋ฌด์์ธ๊ฐ์?"
answer = qa_chain.run(question)
๐ฏ ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฅ์
์๋ฐฑ, ์์ฒ ํ์ด์ง์ ๋ฌธ์๊ฐ ์์ด๋ ๊ด๋ จ๋ ๋ถ๋ถ๋ง ์ ํํ๊ฒ ์ฐพ์๋ผ ์ ์์ด. ๋ง์น ์ฌ๋ฅ๋ท์์ ๋ฑ ํ์ํ ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ! ๊ฒ์ ์๋๋ ์์ฒญ ๋น ๋ฅด๊ณ , ์๋ฏธ์ ์ผ๋ก ์ ์ฌํ ๋ด์ฉ๊น์ง ์ฐพ์์ค. ๐
๐ ์๋ํ ํ์ดํ๋ผ์ธ ๊ตฌ์ถํ๊ธฐ
์ด์ ์ง์ง ์๋ํ์ ํต์ฌ์ผ๋ก ๋ค์ด๊ฐ๋ณด์! ๋จ์ํ ํ ๋ฒ ์์ฝํ๋ ๊ฒ ์๋๋ผ, ์ ๊ธฐ์ ์ผ๋ก ์๋ ์คํ๋๋ ์์คํ ์ ๋ง๋๋ ๊ฑฐ์ผ. โ๏ธ
์์ ์๋ํ ์์คํ ์ค๊ณ
์ค๋ฌด์์ ์ฌ์ฉํ ์ ์๋ ์์ ํ ์๋ํ ์์คํ ์ ์ด๋ฐ ๊ตฌ์กฐ๋ก ๋ง๋ค์ด:
import schedule
import time
from datetime import datetime
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
class DataSummarizationBot:
def __init__(self, llm, source_folder, output_folder):
self.llm = llm
self.source_folder = source_folder
self.output_folder = output_folder
self.processed_files = set()
def scan_new_files(self):
"""์๋ก์ด ํ์ผ ์ค์บ"""
import os
all_files = set(os.listdir(self.source_folder))
new_files = all_files - self.processed_files
return new_files
def process_document(self, file_path):
"""๋ฌธ์ ์ฒ๋ฆฌ ๋ฐ ์์ฝ"""
# ํ์ผ ๋ก๋
loader = PyPDFLoader(file_path)
documents = loader.load()
# ํ
์คํธ ๋ถํ
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
texts = text_splitter.split_documents(documents)
# ์์ฝ ์คํ
chain = load_summarize_chain(self.llm, chain_type="map_reduce")
summary = chain.run(texts)
return summary
def save_summary(self, filename, summary):
"""์์ฝ ๊ฒฐ๊ณผ ์ ์ฅ"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"{self.output_folder}/{filename}_{timestamp}_summary.txt"
with open(output_file, 'w', encoding='utf-8') as f:
f.write(f"์์ฝ ์์ฑ ์๊ฐ: {timestamp}\n")
f.write(f"์๋ณธ ํ์ผ: {filename}\n")
f.write("-" * 50 + "\n\n")
f.write(summary)
print(f"โ
์์ฝ ์๋ฃ: {output_file}")
def run_automation(self):
"""์๋ํ ์คํ"""
print(f"๐ ์ ํ์ผ ์ค์บ ์ค... ({datetime.now()})")
new_files = self.scan_new_files()
if not new_files:
print("์๋ก์ด ํ์ผ์ด ์์ต๋๋ค.")
return
for file in new_files:
try:
file_path = f"{self.source_folder}/{file}"
print(f"๐ ์ฒ๋ฆฌ ์ค: {file}")
summary = self.process_document(file_path)
self.save_summary(file, summary)
self.processed_files.add(file)
except Exception as e:
print(f"โ ์ค๋ฅ ๋ฐ์ ({file}): {str(e)}")
# ๋ด ์ด๊ธฐํ
bot = DataSummarizationBot(
llm=llm,
source_folder="./input_documents",
output_folder="./summaries"
)
# ์ค์ผ์ค ์ค์ (๋งค์ผ ์ค์ 9์ ์คํ)
schedule.every().day.at("09:00").do(bot.run_automation)
# ๋๋ 30๋ถ๋ง๋ค ์คํ
# schedule.every(30).minutes.do(bot.run_automation)
print("๐ค ์๋ํ ๋ด ์์!")
while True:
schedule.run_pending()
time.sleep(60) # 1๋ถ๋ง๋ค ์ฒดํฌ
๐ ์ด ์ฝ๋์ ๋ฉ์ง ์ ๋ค:
1. ์๋ ํ์ผ ๊ฐ์ง
์๋ก์ด ํ์ผ์ด ํด๋์ ์ถ๊ฐ๋๋ฉด ์๋์ผ๋ก ๊ฐ์งํด์ ์ฒ๋ฆฌํด.
2. ์ค๋ณต ์ฒ๋ฆฌ ๋ฐฉ์ง
์ด๋ฏธ ์ฒ๋ฆฌํ ํ์ผ์ ๋ค์ ์ฒ๋ฆฌํ์ง ์์. ํจ์จ์ ์ด์ง!
3. ํ์์คํฌํ ๊ธฐ๋ก
์ธ์ ์์ฝ์ด ์์ฑ๋์๋์ง ์ ํํ ๊ธฐ๋ก๋ผ.
4. ์๋ฌ ํธ๋ค๋ง
ํ๋์ ํ์ผ์์ ์ค๋ฅ๊ฐ ๋๋ ๋ค๋ฅธ ํ์ผ ์ฒ๋ฆฌ๋ ๊ณ์ ์งํ๋ผ.
์ด๋ฉ์ผ ์๋ ๋ฐ์ก ๊ธฐ๋ฅ ์ถ๊ฐํ๊ธฐ
์์ฝ๋ง ํ๊ณ ๋? ์๋์ง! ๊ฒฐ๊ณผ๋ฅผ ์๋์ผ๋ก ์ด๋ฉ์ผ๋ก ๋ณด๋ด๋ฉด ๋ ์๋ฒฝํด: ๐ง
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_summary_email(summary, recipient_email):
"""์์ฝ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ฉ์ผ๋ก ์ ์ก"""
sender_email = "your-email@gmail.com"
password = "your-app-password"
# ์ด๋ฉ์ผ ๊ตฌ์ฑ
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = recipient_email
message["Subject"] = f"๐ ์ผ์ผ ๋ฐ์ดํฐ ์์ฝ ๋ฆฌํฌํธ - {datetime.now().strftime('%Y-%m-%d')}"
# HTML ํ์์ ๋ณธ๋ฌธ
html_body = f"""
<html>
<body style="font-family: Arial, sans-serif;">
<h2 style="color: #4a5568;">๐ ๋ฐ์ดํฐ ์์ฝ ๋ฆฌํฌํธ</h2>
<p>์๋
ํ์ธ์! ์ค๋์ ๋ฐ์ดํฐ ์์ฝ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฌ๋๋ฆฝ๋๋ค.</p>
<div style="background-color: #f8f9fb; padding: 20px; border-radius: 8px;">
<pre style="white-space: pre-wrap;">{summary}</pre>
</div>
<hr>
<p style="color: #6b7280; font-size: 0.9em;">
์ด ๋ฆฌํฌํธ๋ LangChain ์๋ํ ์์คํ
์ ์ํด ์์ฑ๋์์ต๋๋ค.
</p>
</body>
</html>
"""
message.attach(MIMEText(html_body, "html"))
# ์ด๋ฉ์ผ ์ ์ก
try:
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender_email, password)
server.send_message(message)
print("โ
์ด๋ฉ์ผ ์ ์ก ์๋ฃ!")
except Exception as e:
print(f"โ ์ด๋ฉ์ผ ์ ์ก ์คํจ: {str(e)}")
์ด์ save_summary ํจ์ ๋์ send_summary_email(summary, "boss@company.com")๋ง ์ถ๊ฐํ๋ฉด ๋! ๋งค์ผ ์์นจ ์์ฌ์ ๋ฉ์ผํจ์ ๊น๋ํ ์์ฝ ๋ฆฌํฌํธ๊ฐ ๋์ฐฉํ ๊ฑฐ์ผ. ๐
๐จ ๊ณ ๊ธ ๊ธฐ๋ฅ: ๋ค์ํ ํ์์ผ๋ก ์ถ๋ ฅํ๊ธฐ
์์ฝ ๊ฒฐ๊ณผ๋ฅผ ํ ์คํธ๋ก๋ง ๋ฐ๋ ๊ฑด ์ข ์ฌ์ฌํ์ง? ๋ค์ํ ํ์์ผ๋ก ์ถ๋ ฅํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์! ๐ผ๏ธ
1. ๋งํฌ๋ค์ด ํ์์ผ๋ก ๊ตฌ์กฐํํ๊ธฐ
markdown_prompt = """๋ค์ ๋ฌธ์๋ฅผ ๋งํฌ๋ค์ด ํ์์ผ๋ก ์์ฝํด์ฃผ์ธ์:
# ์ ๋ชฉ
## ์ฃผ์ ๋ด์ฉ
- ํต์ฌ ํฌ์ธํธ 1
- ํต์ฌ ํฌ์ธํธ 2
## ์์น ๋ฐ์ดํฐ
| ํญ๋ชฉ | ๊ฐ |
|------|-----|
| ... | ... |
## ๊ฒฐ๋ก ๋ฐ ์ ์ธ
๋ฌธ์:
{text}
"""
MARKDOWN_PROMPT = PromptTemplate(
template=markdown_prompt,
input_variables=["text"]
)
2. JSON ํ์์ผ๋ก ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ ์ถ์ถ
ํ๋ก๊ทธ๋จ์์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ JSON ํ์์ผ๋ก ๋ฐ์ ์๋ ์์ด:
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
# ์ถ๋ ฅ ์คํค๋ง ์ ์
response_schemas = [
ResponseSchema(name="title", description="๋ฌธ์์ ์ ๋ชฉ"),
ResponseSchema(name="summary", description="3-5๋ฌธ์ฅ์ ์์ฝ"),
ResponseSchema(name="key_points", description="์ฃผ์ ํฌ์ธํธ ๋ฆฌ์คํธ"),
ResponseSchema(name="numbers", description="์ค์ํ ์์น ๋ฐ์ดํฐ"),
ResponseSchema(name="action_items", description="์คํ ๊ฐ๋ฅํ ์ก์
์์ดํ
")
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
format_instructions = output_parser.get_format_instructions()
json_prompt = PromptTemplate(
template="๋ค์ ๋ฌธ์๋ฅผ ๋ถ์ํ๊ณ JSON ํ์์ผ๋ก ์ถ๋ ฅํด์ฃผ์ธ์.\n{format_instructions}\n\n๋ฌธ์:\n{text}",
input_variables=["text"],
partial_variables={"format_instructions": format_instructions}
)
chain = LLMChain(llm=llm, prompt=json_prompt)
result = chain.run(text=document_text)
parsed_result = output_parser.parse(result)
print(parsed_result)
# {'title': '...', 'summary': '...', 'key_points': [...], ...}
๐ก JSON ์ถ๋ ฅ์ ํ์ฉ
JSON ํ์์ผ๋ก ๋ฐ์ผ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ฑฐ๋, ๋์๋ณด๋์ ํ์ํ๊ฑฐ๋, ๋ค๋ฅธ ์์คํ ๊ณผ ์ฐ๋ํ๊ธฐ๊ฐ ํจ์ฌ ์ฌ์์ ธ. API๋ฅผ ๋ง๋ค์ด์ ์น ์๋น์ค๋ก ์ ๊ณตํ ์๋ ์๊ณ ! ๐
3. ์์ ๋ฆฌํฌํธ ์๋ ์์ฑ
๋น์ฆ๋์ค ํ๊ฒฝ์์๋ ์์ ๋ฆฌํฌํธ๊ฐ ํ์์ง. ์ด๊ฒ๋ ์๋ํํ ์ ์์ด:
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
def create_excel_report(summary_data, filename):
"""์์
๋ฆฌํฌํธ ์์ฑ"""
wb = Workbook()
ws = wb.active
ws.title = "์์ฝ ๋ฆฌํฌํธ"
# ํค๋ ์คํ์ผ
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
header_font = Font(bold=True, color="FFFFFF")
# ์ ๋ชฉ
ws['A1'] = "๋ฐ์ดํฐ ์์ฝ ๋ฆฌํฌํธ"
ws['A1'].font = Font(size=16, bold=True)
ws.merge_cells('A1:D1')
# ์์ฑ ์ผ์
ws['A2'] = f"์์ฑ ์ผ์: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
# ์์ฝ ๋ด์ฉ
ws['A4'] = "์์ฝ"
ws['A4'].font = header_font
ws['A4'].fill = header_fill
ws['A5'] = summary_data['summary']
ws.merge_cells('A5:D5')
# ์ฃผ์ ํฌ์ธํธ
ws['A7'] = "์ฃผ์ ํฌ์ธํธ"
ws['A7'].font = header_font
ws['A7'].fill = header_fill
row = 8
for point in summary_data['key_points']:
ws[f'A{row}'] = f"โข {point}"
row += 1
# ์ด ๋๋น ์กฐ์
ws.column_dimensions['A'].width = 50
wb.save(filename)
print(f"โ
์์
๋ฆฌํฌํธ ์์ฑ ์๋ฃ: {filename}")
๐ง ์ค์ ํ: ์ฑ๋ฅ ์ต์ ํ์ ๋น์ฉ ์ ๊ฐ
LangChain์ ์ค๋ฌด์์ ์ฌ์ฉํ๋ค ๋ณด๋ฉด ์ฑ๋ฅ๊ณผ ๋น์ฉ ๋ฌธ์ ์ ๋ถ๋ชํ๊ฒ ๋ผ. ์ฌ๊ธฐ ๋ช ๊ฐ์ง ๊ฟํ์ ๊ณต์ ํ ๊ฒ! ๐ฐ
1. ํ ํฐ ์ฌ์ฉ๋ ์ต์ ํ
OpenAI API๋ ํ ํฐ ๋จ์๋ก ๊ณผ๊ธ๋ผ. ๋ถํ์ํ ํ ํฐ ์ฌ์ฉ์ ์ค์ด๋ ๊ฒ ์ค์ํด:
import tiktoken
def count_tokens(text, model="gpt-3.5-turbo"):
"""ํ ํฐ ์ ๊ณ์ฐ"""
encoding = tiktoken.encoding_for_model(model)
return len(encoding.encode(text))
def optimize_text_length(text, max_tokens=3000):
"""ํ
์คํธ ๊ธธ์ด ์ต์ ํ"""
current_tokens = count_tokens(text)
if current_tokens <= max_tokens:
return text
# ํ ํฐ ์์ ๋ง๊ฒ ํ
์คํธ ์๋ฅด๊ธฐ
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
tokens = encoding.encode(text)
truncated_tokens = tokens[:max_tokens]
return encoding.decode(truncated_tokens)
# ์ฌ์ฉ ์์
optimized_text = optimize_text_length(long_document)
print(f"์๋ณธ: {count_tokens(long_document)} ํ ํฐ")
print(f"์ต์ ํ: {count_tokens(optimized_text)} ํ ํฐ")
๐ก ๋น์ฉ ์ ๊ฐ ์ ๋ต
1. ์ ์ ํ ๋ชจ๋ธ ์ ํ
๊ฐ๋จํ ์์ฝ์ gpt-3.5-turbo๋ก๋ ์ถฉ๋ถํด. GPT-4๋ ๋ณต์กํ ๋ถ์์ด ํ์ํ ๋๋ง ์ฌ์ฉํ์.
2. ์บ์ฑ ํ์ฉ
๊ฐ์ ๋ฌธ์๋ฅผ ์ฌ๋ฌ ๋ฒ ์ฒ๋ฆฌํ์ง ์๋๋ก ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํด๋ฌ.
3. ๋ฐฐ์น ์ฒ๋ฆฌ
์ฌ๋ฌ ๋ฌธ์๋ฅผ ํ ๋ฒ์ ๋ชจ์์ ์ฒ๋ฆฌํ๋ฉด ํจ์จ์ ์ด์ผ.
4. ์คํธ๋ฆฌ๋ฐ ์ฌ์ฉ
๊ธด ์๋ต์ ์คํธ๋ฆฌ๋ฐ์ผ๋ก ๋ฐ์ผ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ์ข์์ ธ.
2. ์บ์ฑ ์์คํ ๊ตฌํ
import hashlib
import json
import os
class SummaryCache:
def __init__(self, cache_dir="./cache"):
self.cache_dir = cache_dir
os.makedirs(cache_dir, exist_ok=True)
def get_cache_key(self, text):
"""ํ
์คํธ์ ํด์๊ฐ์ ์บ์ ํค๋ก ์ฌ์ฉ"""
return hashlib.md5(text.encode()).hexdigest()
def get(self, text):
"""์บ์์์ ์์ฝ ๊ฐ์ ธ์ค๊ธฐ"""
cache_key = self.get_cache_key(text)
cache_file = f"{self.cache_dir}/{cache_key}.json"
if os.path.exists(cache_file):
with open(cache_file, 'r', encoding='utf-8') as f:
cached_data = json.load(f)
print("โ
์บ์์์ ๋ถ๋ฌ์ด!")
return cached_data['summary']
return None
def set(self, text, summary):
"""์์ฝ ๊ฒฐ๊ณผ๋ฅผ ์บ์์ ์ ์ฅ"""
cache_key = self.get_cache_key(text)
cache_file = f"{self.cache_dir}/{cache_key}.json"
cache_data = {
'timestamp': datetime.now().isoformat(),
'summary': summary
}
with open(cache_file, 'w', encoding='utf-8') as f:
json.dump(cache_data, f, ensure_ascii=False, indent=2)
# ์ฌ์ฉ ์์
cache = SummaryCache()
def summarize_with_cache(text):
# ์บ์ ํ์ธ
cached_summary = cache.get(text)
if cached_summary:
return cached_summary
# ์บ์์ ์์ผ๋ฉด ์๋ก ์์ฑ
summary = chain.run(text)
cache.set(text, summary)
return summary
3. ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ก ์๋ ํฅ์
์ฌ๋ฌ ๋ฌธ์๋ฅผ ๋์์ ์ฒ๋ฆฌํ๋ฉด ์๊ฐ์ ํฌ๊ฒ ์ค์ผ ์ ์์ด:
from concurrent.futures import ThreadPoolExecutor, as_completed
def process_multiple_documents(file_paths, max_workers=5):
"""์ฌ๋ฌ ๋ฌธ์๋ฅผ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌ"""
results = {}
def process_single(file_path):
try:
summary = process_document(file_path)
return file_path, summary
except Exception as e:
return file_path, f"์ค๋ฅ: {str(e)}"
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(process_single, fp): fp for fp in file_paths}
for future in as_completed(futures):
file_path, result = future.result()
results[file_path] = result
print(f"โ
์๋ฃ: {file_path}")
return results
# ์ฌ์ฉ ์์
files = ["doc1.pdf", "doc2.pdf", "doc3.pdf", "doc4.pdf"]
summaries = process_multiple_documents(files)
for file, summary in summaries.items():
print(f"\n๐ {file}:")
print(summary[:200] + "...")
๐ ์ค์ ํ์ฉ ์ฌ๋ก: ์ด๋ ๊ฒ ์ฐ๋ฉด ๋๋ค!
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋, ์ด์ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๊ตฌ์ฒด์ ์ธ ์ฌ๋ก๋ฅผ ์ดํด๋ณด์! ๐ฏ
์ฌ๋ก 1: ๊ณ ๊ฐ ํผ๋๋ฐฑ ์๋ ๋ถ์ ์์คํ
๋งค์ผ ์๋ฐฑ ๊ฐ์ ๊ณ ๊ฐ ๋ฆฌ๋ทฐ๊ฐ ์์ด๋ ์๋น์ค๊ฐ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์:
class CustomerFeedbackAnalyzer:
def __init__(self, llm):
self.llm = llm
# ๊ฐ์ฑ ๋ถ์ ํ๋กฌํํธ
self.sentiment_prompt = PromptTemplate(
template="""๋ค์ ๊ณ ๊ฐ ํผ๋๋ฐฑ๋ค์ ๋ถ์ํด์ JSON ํ์์ผ๋ก ์ถ๋ ฅํด์ฃผ์ธ์:
ํผ๋๋ฐฑ:
{feedback}
๋ค์ ํ์์ผ๋ก ์ถ๋ ฅ:
{{
"overall_sentiment": "๊ธ์ /๋ถ์ /์ค๋ฆฝ",
"positive_points": ["๊ธ์ ์ ์๊ฒฌ1", "๊ธ์ ์ ์๊ฒฌ2"],
"negative_points": ["๋ถ์ ์ ์๊ฒฌ1", "๋ถ์ ์ ์๊ฒฌ2"],
"common_issues": ["๊ณตํต ์ด์1", "๊ณตํต ์ด์2"],
"action_items": ["๊ฐ์ ์ฌํญ1", "๊ฐ์ ์ฌํญ2"]
}}
""",
input_variables=["feedback"]
)
def analyze_batch(self, feedbacks):
"""ํผ๋๋ฐฑ ๋ฐฐ์น ๋ถ์"""
combined_feedback = "\n\n".join([f"[ํผ๋๋ฐฑ {i+1}]\n{fb}" for i, fb in enumerate(feedbacks)])
chain = LLMChain(llm=self.llm, prompt=self.sentiment_prompt)
result = chain.run(feedback=combined_feedback)
return json.loads(result)
def generate_daily_report(self, feedbacks):
"""์ผ์ผ ๋ฆฌํฌํธ ์์ฑ"""
analysis = self.analyze_batch(feedbacks)
report = f"""
๐ ๊ณ ๊ฐ ํผ๋๋ฐฑ ์ผ์ผ ๋ฆฌํฌํธ
{'='*50}
๐ ์ ์ฒด ๊ฐ์ฑ: {analysis['overall_sentiment']}
์ด ํผ๋๋ฐฑ ์: {len(feedbacks)}๊ฐ
โ
๊ธ์ ์ ์๊ฒฌ:
{chr(10).join([f" โข {point}" for point in analysis['positive_points']])}
โ ๏ธ ๋ถ์ ์ ์๊ฒฌ:
{chr(10).join([f" โข {point}" for point in analysis['negative_points']])}
๐ ๊ณตํต ์ด์:
{chr(10).join([f" โข {issue}" for issue in analysis['common_issues']])}
๐ก ๊ฐ์ ์ ์:
{chr(10).join([f" โข {item}" for item in analysis['action_items']])}
"""
return report
# ์ฌ์ฉ ์์
analyzer = CustomerFeedbackAnalyzer(llm)
daily_feedbacks = [
"๋ฐฐ์ก์ด ์ ๋ง ๋นจ๋์ด์! ๋ค๋ง ํฌ์ฅ์ด ์กฐ๊ธ ์์ฌ์ ์ต๋๋ค.",
"์ ํ ํ์ง์ ์ข์๋ฐ ๊ฐ๊ฒฉ์ด ๋น์ผ ๊ฒ ๊ฐ์์.",
"๊ณ ๊ฐ์ผํฐ ์๋๊ฐ ์น์ ํ์ต๋๋ค. ๋ง์กฑ์ค๋ฌ์์!",
# ... ๋ ๋ง์ ํผ๋๋ฐฑ
]
report = analyzer.generate_daily_report(daily_feedbacks)
print(report)
์ฌ๋ก 2: ๋ด์ค ๊ธฐ์ฌ ์๋ ํ๋ ์ด์
์ฌ๋ฌ ๋ด์ค ์ฌ์ดํธ์์ ๊ธฐ์ฌ๋ฅผ ์์งํด์ ์์ฝํ๊ณ ์นดํ ๊ณ ๋ฆฌ๋ณ๋ก ๋ถ๋ฅํ๋ ์์คํ :
from langchain.document_loaders import WebBaseLoader
class NewsAggregator:
def __init__(self, llm):
self.llm = llm
def fetch_articles(self, urls):
"""์น์์ ๊ธฐ์ฌ ์์ง"""
articles = []
for url in urls:
try:
loader = WebBaseLoader(url)
docs = loader.load()
articles.append({
'url': url,
'content': docs[0].page_content
})
except Exception as e:
print(f"โ ์ค๋ฅ ({url}): {str(e)}")
return articles
def summarize_and_categorize(self, article):
"""๊ธฐ์ฌ ์์ฝ ๋ฐ ์นดํ
๊ณ ๋ฆฌ ๋ถ๋ฅ"""
prompt = PromptTemplate(
template="""๋ค์ ๋ด์ค ๊ธฐ์ฌ๋ฅผ ๋ถ์ํด์ฃผ์ธ์:
๊ธฐ์ฌ:
{content}
๋ค์ ํ์์ผ๋ก ์ถ๋ ฅ:
{{
"category": "์ ์น/๊ฒฝ์ /์ฌํ/๋ฌธํ/IT/์คํฌ์ธ ์ค ํ๋",
"headline": "ํ ์ค ํค๋๋ผ์ธ",
"summary": "3๋ฌธ์ฅ ์์ฝ",
"keywords": ["ํค์๋1", "ํค์๋2", "ํค์๋3"]
}}
""",
input_variables=["content"]
)
chain = LLMChain(llm=self.llm, prompt=prompt)
result = chain.run(content=article['content'][:2000]) # ํ ํฐ ์ ์ฝ
return json.loads(result)
def create_newsletter(self, articles):
"""๋ด์ค๋ ํฐ ์์ฑ"""
categorized = {}
for article in articles:
analysis = self.summarize_and_categorize(article)
category = analysis['category']
if category not in categorized:
categorized[category] = []
categorized[category].append({
'url': article['url'],
'headline': analysis['headline'],
'summary': analysis['summary'],
'keywords': analysis['keywords']
})
# HTML ๋ด์ค๋ ํฐ ์์ฑ
newsletter = "<html><body style='font-family: Arial;'>"
newsletter += f"<h1>๐ฐ ์ค๋์ ๋ด์ค ๋ค์ด์ ์คํธ - {datetime.now().strftime('%Y-%m-%d')}</h1>"
for category, items in categorized.items():
newsletter += f"<h2>๐ {category}</h2>"
for item in items:
newsletter += f"""
<div style='margin: 20px 0; padding: 15px; background: #f8f9fb; border-radius: 8px;'>
<h3>{item['headline']}</h3>
<p>{item['summary']}</p>
<p><small>๐ท๏ธ {', '.join(item['keywords'])}</small></p>
<a href='{item['url']}'>์๋ฌธ ๋ณด๊ธฐ โ</a>
</div>
"""
newsletter += "</body></html>"
return newsletter
# ์ฌ์ฉ ์์
aggregator = NewsAggregator(llm)
news_urls = [
"https://example.com/news1",
"https://example.com/news2",
# ... ๋ ๋ง์ URL
]
articles = aggregator.fetch_articles(news_urls)
newsletter = aggregator.create_newsletter(articles)
# ์ด๋ฉ์ผ๋ก ๋ฐ์กํ๊ฑฐ๋ ์น์ ๊ฒ์
with open("newsletter.html", "w", encoding="utf-8") as f:
f.write(newsletter)
์ฌ๋ก 3: ํ์๋ก ์๋ ์์ฑ ์์คํ
์์ฑ ํ์๋ฅผ ํ ์คํธ๋ก ๋ณํํ ํ ์๋์ผ๋ก ํ์๋ก์ ์์ฑํ๋ ์์คํ :
class MeetingMinutesGenerator:
def __init__(self, llm):
self.llm = llm
def generate_minutes(self, transcript):
"""ํ์๋ก ์์ฑ"""
prompt = PromptTemplate(
template="""๋ค์ ํ์ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๊ณต์ ํ์๋ก์ ์์ฑํด์ฃผ์ธ์:
ํ์ ๋ด์ฉ:
{transcript}
๋ค์ ํ์์ผ๋ก ์์ฑ:
# ํ์๋ก
## 1. ํ์ ๊ฐ์
- ์ฐธ์์:
- ์๊ฑด:
## 2. ์ฃผ์ ๋
ผ์ ์ฌํญ
(ํต์ฌ ๋ด์ฉ์ ์ ๋ฆฌ)
## 3. ๊ฒฐ์ ์ฌํญ
(๊ตฌ์ฒด์ ์ธ ๊ฒฐ์ ๋ด์ฉ)
## 4. ์ก์
์์ดํ
- [ ] ๋ด๋น์: ํ ์ผ (๊ธฐํ)
## 5. ๋ค์ ํ์
- ์ผ์:
- ์๊ฑด:
""",
input_variables=["transcript"]
)
chain = LLMChain(llm=self.llm, prompt=prompt)
minutes = chain.run(transcript=transcript)
return minutes
def extract_action_items(self, minutes):
"""์ก์
์์ดํ
์ถ์ถ"""
prompt = PromptTemplate(
template="""๋ค์ ํ์๋ก์์ ์ก์
์์ดํ
๋ง ์ถ์ถํด์ JSON ํ์์ผ๋ก ์ถ๋ ฅํด์ฃผ์ธ์:
ํ์๋ก:
{minutes}
ํ์:
{{
"action_items": [
{{
"task": "ํ ์ผ",
"assignee": "๋ด๋น์",
"deadline": "๊ธฐํ",
"priority": "๋์/์ค๊ฐ/๋ฎ์"
}}
]
}}
""",
input_variables=["minutes"]
)
chain = LLMChain(llm=self.llm, prompt=prompt)
result = chain.run(minutes=minutes)
return json.loads(result)
def send_to_participants(self, minutes, action_items, emails):
"""์ฐธ์์๋ค์๊ฒ ํ์๋ก ๋ฐ์ก"""
for email in emails:
# ๊ฐ์ธ๋ณ ์ก์
์์ดํ
ํํฐ๋ง
personal_actions = [
item for item in action_items['action_items']
if email in item['assignee']
]
# ์ด๋ฉ์ผ ๋ณธ๋ฌธ ์์ฑ
body = f"""
<h2>ํ์๋ก</h2>
{minutes}
<h2>๐ฏ ๋น์ ์ ์ก์
์์ดํ
</h2>
"""
for action in personal_actions:
priority_emoji = "๐ด" if action['priority'] == "๋์" else "๐ก" if action['priority'] == "์ค๊ฐ" else "๐ข"
body += f"""
<div style='margin: 10px 0; padding: 10px; background: #f0f4f8; border-radius: 5px;'>
{priority_emoji} <strong>{action['task']}</strong><br>
๐
๊ธฐํ: {action['deadline']}
</div>
"""
# ์ด๋ฉ์ผ ๋ฐ์ก (์ด์ ์ ๋ง๋ send_email ํจ์ ํ์ฉ)
send_summary_email(body, email)
# ์ฌ์ฉ ์์
generator = MeetingMinutesGenerator(llm)
# ํ์ ์์ฑ์ ํ
์คํธ๋ก ๋ณํํ ๊ฒฐ๊ณผ
transcript = """
[๊น๋๋ฆฌ] ์ค๋์ ์ ์ ํ ์ถ์ ์ผ์ ์ ๋ํด ๋
ผ์ํ๊ฒ ์ต๋๋ค.
[๋ฐ๊ณผ์ฅ] ๊ฐ๋ฐ์ 90% ์๋ฃ๋์๊ณ , ๋ค์ ์ฃผ๊น์ง QA๋ฅผ ๋ง์น ์์ ์
๋๋ค.
[์ด๋ถ์ฅ] ์ข์ต๋๋ค. ๋ง์ผํ
ํ์ ์ค๋น ์ํฉ์ด ์ด๋ค๊ฐ์?
[์ตํ์ฅ] ํ๋ณด ์๋ฃ๋ ์์ฑ๋์๊ณ , ๊ด๊ณ ์บ ํ์ธ์ ๋ค์ ๋ฌ 1์ผ๋ถํฐ ์์ํฉ๋๋ค.
...
"""
minutes = generator.generate_minutes(transcript)
action_items = generator.extract_action_items(minutes)
print(minutes)
print("\n์ก์
์์ดํ
:")
print(json.dumps(action_items, indent=2, ensure_ascii=False))
๐ ์ด๋ฐ ์์คํ ์ ์ฅ์
ํ์๊ฐ ๋๋์๋ง์ ๋ฐ๋ก ์ ๋ฆฌ๋ ํ์๋ก์ด ๋์. ๋๊ฐ ๋ฌด์์ ์ธ์ ๊น์ง ํด์ผ ํ๋์ง ๋ช ํํ๊ฒ ์ ๋ฆฌ๋๊ณ , ๊ฐ์์๊ฒ ์๋์ผ๋ก ์ ๋ฌ๋ผ. ํ์๋ก ์์ฑํ๋๋ผ 30๋ถ์ฉ ์ฐ๋ ์๊ฐ์ ์์ ํ ์ ์ฝํ ์ ์์ง! โฐ
๐จ ์ฃผ์์ฌํญ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
LangChain์ ์ค๋ฌด์ ์ ์ฉํ ๋ ๊ผญ ์์์ผ ํ ์ฃผ์์ฌํญ๋ค์ ์ ๋ฆฌํด๋ดค์ด. โ ๏ธ
1. ๋ฐ์ดํฐ ๋ณด์๊ณผ ํ๋ผ์ด๋ฒ์
๐ ๋ฏผ๊ฐํ ์ ๋ณด ์ฒ๋ฆฌ ์ ์ฃผ์์ฌํญ
๊ฐ์ธ์ ๋ณด ๋ง์คํน
๊ณ ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋๋ ๋ฐ๋์ ๊ฐ์ธ์ ๋ณด๋ฅผ ๋ง์คํนํด์ผ ํด:
import re
def mask_sensitive_data(text):
"""๋ฏผ๊ฐํ ์ ๋ณด ๋ง์คํน"""
# ์ด๋ฉ์ผ ๋ง์คํน
text = re.sub(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
'***@***.***', text)
# ์ ํ๋ฒํธ ๋ง์คํน
text = re.sub(r'\d{2,3}-\d{3,4}-\d{4}', '***-****-****', text)
# ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ ๋ง์คํน
text = re.sub(r'\d{6}-\d{7}', '******-*******', text)
return text
์จํ๋ ๋ฏธ์ค LLM ๊ณ ๋ ค
์ ๋ง ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ผ๋ฉด OpenAI ๋์ ์์ฒด ์๋ฒ์์ ๋์๊ฐ๋ ์คํ์์ค LLM์ ์ฌ์ฉํ๋ ๊ฒ๋ ๋ฐฉ๋ฒ์ด์ผ. LangChain์ ๋ค์ํ LLM์ ์ง์ํ๊ฑฐ๋ !
2. ์๋ฌ ํธ๋ค๋ง๊ณผ ์ฌ์๋ ๋ก์ง
API ํธ์ถ์ ์ธ์ ๋ ์คํจํ ์ ์์ด. ๊ฒฌ๊ณ ํ ์๋ฌ ํธ๋ค๋ง์ด ํ์์ผ:
from tenacity import retry, stop_after_attempt, wait_exponential
class RobustSummarizer:
def __init__(self, llm):
self.llm = llm
@retry(
stop=stop_after_attempt(3), # ์ต๋ 3๋ฒ ์ฌ์๋
wait=wait_exponential(multiplier=1, min=4, max=10) # ์ง์ ๋ฐฑ์คํ
)
def summarize_with_retry(self, text):
"""์ฌ์๋ ๋ก์ง์ด ์๋ ์์ฝ"""
try:
chain = load_summarize_chain(self.llm, chain_type="stuff")
return chain.run(text)
except Exception as e:
print(f"โ ๏ธ ์ค๋ฅ ๋ฐ์: {str(e)}")
raise # ์ฌ์๋๋ฅผ ์ํด ์์ธ๋ฅผ ๋ค์ ๋ฐ์
def safe_summarize(self, text, fallback="์์ฝ ์์ฑ ์คํจ"):
"""์์ ํ ์์ฝ (์คํจ ์ ๋์ฒด ํ
์คํธ ๋ฐํ)"""
try:
return self.summarize_with_retry(text)
except Exception as e:
print(f"โ ์ต์ข
์คํจ: {str(e)}")
return fallback
3. ํ๋กฌํํธ ์ธ์ ์ ๋ฐฉ์ง
์ฌ์ฉ์ ์ ๋ ฅ์ ๊ทธ๋๋ก ํ๋กฌํํธ์ ๋ฃ์ผ๋ฉด ์ํํ ์ ์์ด:
def sanitize_input(user_input):
"""์ฌ์ฉ์ ์
๋ ฅ ์ ์ """
# ํ๋กฌํํธ ์ธ์ ์
์๋ ํจํด ์ ๊ฑฐ
dangerous_patterns = [
r'ignore previous instructions',
r'disregard all',
r'forget everything',
# ... ๋ ๋ง์ ํจํด
]
for pattern in dangerous_patterns:
user_input = re.sub(pattern, '', user_input, flags=re.IGNORECASE)
# ๊ธธ์ด ์ ํ
max_length = 5000
if len(user_input) > max_length:
user_input = user_input[:max_length]
return user_input
# ์ฌ์ฉ ์์
user_text = sanitize_input(request.get('text'))
summary = chain.run(user_text)
4. ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น
ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋ชจ๋ ๊ฒ์ ๋ก๊น ํ๊ณ ๋ชจ๋ํฐ๋งํด์ผ ํด:
import logging
from datetime import datetime
# ๋ก๊น
์ค์
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('langchain_automation.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
class MonitoredSummarizer:
def __init__(self, llm):
self.llm = llm
self.stats = {
'total_requests': 0,
'successful': 0,
'failed': 0,
'total_tokens': 0
}
def summarize(self, text):
"""๋ชจ๋ํฐ๋ง์ด ํฌํจ๋ ์์ฝ"""
start_time = datetime.now()
self.stats['total_requests'] += 1
try:
logger.info(f"์์ฝ ์์ - ํ
์คํธ ๊ธธ์ด: {len(text)}")
# ํ ํฐ ์ ๊ณ์ฐ
tokens = count_tokens(text)
self.stats['total_tokens'] += tokens
# ์์ฝ ์คํ
summary = chain.run(text)
# ์ฑ๊ณต ๊ธฐ๋ก
self.stats['successful'] += 1
elapsed = (datetime.now() - start_time).total_seconds()
logger.info(f"์์ฝ ์๋ฃ - ์์ ์๊ฐ: {elapsed:.2f}์ด, ํ ํฐ: {tokens}")
return summary
except Exception as e:
self.stats['failed'] += 1
logger.error(f"์์ฝ ์คํจ: {str(e)}")
raise
def get_stats(self):
"""ํต๊ณ ์กฐํ"""
success_rate = (self.stats['successful'] / self.stats['total_requests'] * 100) if self.stats['total_requests'] > 0 else 0
return f"""
๐ ์์คํ
ํต๊ณ
{'='*40}
์ด ์์ฒญ: {self.stats['total_requests']}
์ฑ๊ณต: {self.stats['successful']}
์คํจ: {self.stats['failed']}
์ฑ๊ณต๋ฅ : {success_rate:.1f}%
์ด ํ ํฐ ์ฌ์ฉ: {self.stats['total_tokens']:,}
"""
๐ ๋ ๋์๊ฐ๊ธฐ: ๊ณ ๊ธ ํ์ฉ ํจํด
๊ธฐ๋ณธ์ ๋์ด์ ๋ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ์์๋ณด์! ๐
1. ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ: ์ด๋ฏธ์ง์ ํ ์คํธ ํจ๊ป ๋ถ์
GPT-4 Vision์ ํ์ฉํ๋ฉด ์ด๋ฏธ์ง๋ ํจ๊ป ๋ถ์ํ ์ ์์ด:
from langchain.chat_models import ChatOpenAI
from langchain.schema.messages import HumanMessage
def analyze_document_with_images(text, image_paths):
"""ํ
์คํธ์ ์ด๋ฏธ์ง๋ฅผ ํจ๊ป ๋ถ์"""
llm = ChatOpenAI(model="gpt-4-vision-preview", max_tokens=1024)
# ์ด๋ฏธ์ง๋ฅผ base64๋ก ์ธ์ฝ๋ฉ
import base64
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
# ๋ฉ์์ง ๊ตฌ์ฑ
content = [
{"type": "text", "text": f"๋ค์ ๋ฌธ์์ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ํด์ฃผ์ธ์:\n\n{text}"}
]
for img_path in image_paths:
base64_image = encode_image(img_path)
content.append({
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
})
message = HumanMessage(content=content)
response = llm([message])
return response.content
# ์ฌ์ฉ ์์
result = analyze_document_with_images(
text="์ด ๋ณด๊ณ ์๋ 2024๋
1๋ถ๊ธฐ ์ค์ ์ ๋ค๋ฃน๋๋ค.",
image_paths=["chart1.png", "graph2.png"]
)
print(result)
2. ์์ด์ ํธ ํ์ฉ: AI๊ฐ ์ค์ค๋ก ํ๋จํ๊ณ ์คํ
์์ด์ ํธ๋ LangChain์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ผ. AI๊ฐ ์ค์ค๋ก ํ์ํ ๋๊ตฌ๋ฅผ ์ ํํด์ ์์ ์ ์ํํด:
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.utilities import GoogleSearchAPIWrapper
# ๋๊ตฌ ์ ์
search = GoogleSearchAPIWrapper()
tools = [
Tool(
name="Search",
func=search.run,
description="์ต์ ์ ๋ณด๋ฅผ ๊ฒ์ํ ๋ ์ ์ฉํฉ๋๋ค."
),
Tool(
name="Calculator",
func=lambda x: eval(x),
description="์ํ ๊ณ์ฐ์ด ํ์ํ ๋ ์ฌ์ฉํฉ๋๋ค."
)
]
# ์์ด์ ํธ ์ด๊ธฐํ
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# ์์ด์ ํธ ์คํ
result = agent.run("""
2024๋
AI ์์ฅ ๊ท๋ชจ๋ฅผ ์กฐ์ฌํ๊ณ ,
2025๋
์์ ์ฑ์ฅ๋ฅ ์ ์ ์ฉํด์
2025๋
์์ฅ ๊ท๋ชจ๋ฅผ ๊ณ์ฐํด์ฃผ์ธ์.
""")
print(result)
๐ค ์์ด์ ํธ์ ์ฌ๊ณ ๊ณผ์
์์ด์ ํธ๋ ์ด๋ ๊ฒ ์๊ฐํด:
1. "AI ์์ฅ ๊ท๋ชจ๋ฅผ ์์์ผ ํ๋ ๊ฒ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด์ผ๊ฒ ๋ค"
2. "๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์ผ๋ ์ด์ ๊ณ์ฐ์ด ํ์ํ๋ค"
3. "๊ณ์ฐ๊ธฐ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด์ 2025๋
์์์น๋ฅผ ๊ณ์ฐํ์"
4. "๊ฒฐ๊ณผ๋ฅผ ์ ๋ฆฌํด์ ๋ต๋ณํ์"
์์ ์๋ํ์ ์ ์ ์ด์ง! ๐ฏ
3. ์ปค์คํ ์ฒด์ธ ๋ง๋ค๊ธฐ
๋ณต์กํ ์ํฌํ๋ก์ฐ๋ ์ปค์คํ ์ฒด์ธ์ผ๋ก ๋ง๋ค ์ ์์ด:
from langchain.chains import SequentialChain
class DocumentProcessingPipeline:
def __init__(self, llm):
self.llm = llm
# 1๋จ๊ณ: ๋ฌธ์ ๋ถ๋ฅ
self.classify_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
template="๋ค์ ๋ฌธ์์ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ๋ถ๋ฅํ์ธ์: {document}\n์นดํ
๊ณ ๋ฆฌ:",
input_variables=["document"]
),
output_key="category"
)
# 2๋จ๊ณ: ์์ฝ
self.summarize_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
template="๋ค์ {category} ๋ฌธ์๋ฅผ ์์ฝํ์ธ์: {document}\n์์ฝ:",
input_variables=["document", "category"]
),
output_key="summary"
)
# 3๋จ๊ณ: ์ก์
์์ดํ
์ถ์ถ
self.action_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
template="๋ค์ ์์ฝ์์ ์ก์
์์ดํ
์ ์ถ์ถํ์ธ์: {summary}\n์ก์
์์ดํ
:",
input_variables=["summary"]
),
output_key="actions"
)
# ์ฒด์ธ ์ฐ๊ฒฐ
self.pipeline = SequentialChain(
chains=[self.classify_chain, self.summarize_chain, self.action_chain],
input_variables=["document"],
output_variables=["category", "summary", "actions"],
verbose=True
)
def process(self, document):
"""์ ์ฒด ํ์ดํ๋ผ์ธ ์คํ"""
return self.pipeline({"document": document})
# ์ฌ์ฉ ์์
pipeline = DocumentProcessingPipeline(llm)
result = pipeline.process(long_document)
print(f"์นดํ
๊ณ ๋ฆฌ: {result['category']}")
print(f"์์ฝ: {result['summary']}")
print(f"์ก์
์์ดํ
: {result['actions']}")
๐ผ ์ฌ๋ฅ๋ท์์ LangChain ์ ๋ฌธ๊ฐ ๋๊ธฐ
์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ด๋ฏธ LangChain ์ค๊ธ์๋ ๋ ๊ฑฐ์ผ! ์ด์ ์ด ์ง์์ ์ด๋ป๊ฒ ํ์ฉํ ์ ์์๊น? ๐ก
์ฌ๋ฅ๋ท์์ ์ ๊ณตํ ์ ์๋ ์๋น์ค๋ค
1. ๋ฐ์ดํฐ ์๋ํ ์์คํ
๊ตฌ์ถ ๐ค
๊ธฐ์
์ ๋ฐ๋ณต์ ์ธ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์
์ ์๋ํํด์ฃผ๋ ์์คํ
์ ๋ง๋ค์ด์ค ์ ์์ด. ๋ณด๊ณ ์ ์์ฝ, ๊ณ ๊ฐ ํผ๋๋ฐฑ ๋ถ์, ๋ด์ค ํ๋ ์ด์
๋ฑ ๋ค์ํ ๋ถ์ผ์ ์ ์ฉ ๊ฐ๋ฅํ์ง.
2. AI ์ฑ๋ด ๊ฐ๋ฐ ๐ฌ
ํ์ฌ ๋ด๋ถ ๋ฌธ์๋ฅผ ํ์ตํ AI ์ฑ๋ด์ ๋ง๋ค์ด์ ์ง์๋ค์ ์ง๋ฌธ์ ์๋์ผ๋ก ๋ต๋ณํ๊ฒ ํ ์ ์์ด. ๊ณ ๊ฐ ์ง์ ์ฑ๋ด๋ ๊ฐ๋ฅํ๊ณ !
3. ์ปจ์คํ
๋ฐ ๊ต์ก ๐
LangChain์ ๋์
ํ๋ ค๋ ๊ธฐ์
์ ์ปจ์คํ
์ ์ ๊ณตํ๊ฑฐ๋, ๊ฐ๋ฐ์๋ค์๊ฒ ๊ต์ก์ ์งํํ ์๋ ์์ด.
4. ๋ง์ถคํ ์๋ฃจ์
๊ฐ๋ฐ ๐จ
๊ฐ ๊ธฐ์
์ ํน์ํ ์๊ตฌ์ฌํญ์ ๋ง๋ ์ปค์คํ
์๋ฃจ์
์ ๊ฐ๋ฐํด์ค ์ ์์ง.
์ฑ๊ณต์ ์ธ ํ๋ก์ ํธ๋ฅผ ์ํ ํ
๐ ํ๋ก์ ํธ ์งํ ์ฒดํฌ๋ฆฌ์คํธ
1. ์๊ตฌ์ฌํญ ๋ช
ํํ ํ๊ธฐ
- ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๊ฒ์ธ๊ฐ?
- ์ผ๋ง๋ ์์ฃผ ์คํ๋์ด์ผ ํ๋๊ฐ?
- ๊ฒฐ๊ณผ๋ฌผ์ ์ด๋ค ํ์์ด์ด์ผ ํ๋๊ฐ?
- ์์ฐ๊ณผ ์ผ์ ์ ์ด๋ป๊ฒ ๋๋๊ฐ?
2. ํ๋กํ ํ์
๋จผ์ ๋ง๋ค๊ธฐ
์์ ๊ท๋ชจ๋ก ๋จผ์ ํ
์คํธํด๋ณด๊ณ , ๊ณ ๊ฐ์ ํผ๋๋ฐฑ์ ๋ฐ์์ ๊ฐ์ ํด๋๊ฐ๋ ๊ฒ ์ค์ํด.
3. ๋ฌธ์ํ ์ฒ ์ ํ ํ๊ธฐ
์ฝ๋ ์ฃผ์, ์ฌ์ฉ ์ค๋ช
์, ์ ์ง๋ณด์ ๊ฐ์ด๋๋ฅผ ๊ผผ๊ผผํ๊ฒ ์์ฑํด์ผ ํด.
4. ๋ชจ๋ํฐ๋ง ์์คํ
๊ตฌ์ถ
์์คํ
์ด ์ ๋์๊ฐ๋์ง ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์์ด์ผ ํด.
๊ฐ๊ฒฉ ์ฑ ์ ๊ฐ์ด๋
์ฌ๋ฅ๋ท์์ ์๋น์ค๋ฅผ ์ ๊ณตํ ๋ ๊ฐ๊ฒฉ์ ์ด๋ ๊ฒ ์ฑ ์ ํ ์ ์์ด:
| ์๋น์ค ์ ํ | ์์ ๊ฐ๊ฒฉ๋ | ํฌํจ ๋ด์ฉ |
|---|---|---|
| ๊ธฐ๋ณธ ์๋ํ ์คํฌ๋ฆฝํธ | 30๋ง์ ~ 50๋ง์ | ๋จ์ ์์ฝ/๋ถ๋ฅ ๊ธฐ๋ฅ |
| ์ค๊ธ ์๋ํ ์์คํ | 100๋ง์ ~ 300๋ง์ | ์ค์ผ์ค๋ง, ์ด๋ฉ์ผ ๋ฐ์ก, ๋์๋ณด๋ |
| ๊ณ ๊ธ AI ์๋ฃจ์ | 500๋ง์ ~ 2,000๋ง์ | ์์ด์ ํธ, ๋ฉํฐ๋ชจ๋ฌ, ์ปค์คํ ๊ธฐ๋ฅ |
| ์ปจ์คํ (์๊ฐ๋น) | 10๋ง์ ~ 30๋ง์ | ๊ธฐ์ ์๋ฌธ, ์ํคํ ์ฒ ์ค๊ณ |
๋ฌผ๋ก ํ๋ก์ ํธ ๊ท๋ชจ์ ๋ณต์ก๋์ ๋ฐ๋ผ ์กฐ์ ์ด ํ์ํด! ๐ฐ
๐ฎ ๋ฏธ๋ ์ ๋ง๊ณผ ํธ๋ ๋
LangChain๊ณผ AI ์๋ํ์ ๋ฏธ๋๋ ์ด๋ป๊ฒ ๋ ๊น? ๐
์ฃผ๋ชฉํด์ผ ํ ํธ๋ ๋
1. ๋ฉํฐ๋ชจ๋ฌ AI์ ํ์ฐ
ํ
์คํธ๋ฟ๋ง ์๋๋ผ ์ด๋ฏธ์ง, ์์ฑ, ๋น๋์ค๋ฅผ ๋ชจ๋ ์ฒ๋ฆฌํ ์ ์๋ ์์คํ
์ด ํ์ค์ด ๋ ๊ฑฐ์ผ. GPT-4V, Gemini ๊ฐ์ ๋ชจ๋ธ๋ค์ด ์ด๋ฏธ ์ด ๋ฐฉํฅ์ผ๋ก ๊ฐ๊ณ ์์ง.
2. ๋ก์ปฌ LLM์ ๋ถ์
Llama 2, Mistral ๊ฐ์ ์คํ์์ค ๋ชจ๋ธ๋ค์ด ์ ์ ์ข์์ง๊ณ ์์ด. ๋ฐ์ดํฐ ๋ณด์์ด ์ค์ํ ๊ธฐ์
๋ค์ ์์ฒด ์๋ฒ์์ LLM์ ๋๋ฆฌ๋ ๋ฐฉํฅ์ผ๋ก ๊ฐ ๊ฑฐ์ผ.
3. ์์ด์ ํธ ๊ธฐ๋ฐ ์๋ํ
๋จ์ํ ์ ํด์ง ์์
์ ์ํํ๋ ๊ฒ ์๋๋ผ, AI๊ฐ ์ค์ค๋ก ํ๋จํ๊ณ ๊ณํ์ ์ธ์์ ๋ณต์กํ ์์
์ ์ํํ๋ ์์ด์ ํธ ์์คํ
์ด ์ฃผ๋ฅ๊ฐ ๋ ๊ฑฐ์ผ.
4. RAG(Retrieval-Augmented Generation)์ ์งํ
๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ LLM์ ๊ฒฐํฉํ RAG ๊ธฐ์ ์ด ๋์ฑ ์ ๊ตํด์ง ๊ฑฐ์ผ. ๋ ์ ํํ๊ณ ์ ๋ขฐํ ์ ์๋ ๋ต๋ณ์ ์ ๊ณตํ ์ ์๊ฒ ๋์ง.
5. ๋
ธ์ฝ๋/๋ก์ฐ์ฝ๋ ๋๊ตฌ
ํ๋ก๊ทธ๋๋ฐ ์์ด๋ LangChain ๊ธฐ๋ฐ ์๋ํ๋ฅผ ๊ตฌ์ถํ ์ ์๋ ๋๊ตฌ๋ค์ด ๋์ฌ ๊ฑฐ์ผ. ํ์ง๋ง ๋ณต์กํ ์ปค์คํฐ๋ง์ด์ง์ ์ฌ์ ํ ๊ฐ๋ฐ์์ ์์ญ์ด๊ฒ ์ง!
์ง๊ธ ์ค๋นํด์ผ ํ ๊ฒ๋ค
์ด ๋ถ์ผ์์ ์ ๋ฌธ๊ฐ๊ฐ ๋๋ ค๋ฉด:
๐ ๊ณ์ ํ์ตํ๊ธฐ
AI ๊ธฐ์ ์ ์ ๋ง ๋น ๋ฅด๊ฒ ๋ฐ์ ํด. ๋งค์ฃผ ์๋ก์ด ๋ชจ๋ธ๊ณผ ๊ธฐ๋ฒ์ด ๋์ค๋๊น ๊พธ์คํ ๊ณต๋ถํด์ผ ํด.
๐ ๏ธ ์ค์ ํ๋ก์ ํธ ๊ฒฝํ
์ด๋ก ๋ง ์๋ ๊ฒ๋ณด๋ค ์ค์ ๋ก ์์คํ
์ ๋ง๋ค์ด๋ณธ ๊ฒฝํ์ด ํจ์ฌ ์ค์ํด. ์์ ํ๋ก์ ํธ๋ผ๋ ์ง์ ํด๋ณด์!
๐ค ์ปค๋ฎค๋ํฐ ์ฐธ์ฌ
GitHub, Discord, Reddit ๋ฑ์์ ํ๋ฐํ๊ฒ ํ๋ํ๋ฉด์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ๊ต๋ฅํ์.
๐ผ ํฌํธํด๋ฆฌ์ค ๊ตฌ์ถ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ค๋ฉด ์ค๋ ฅ์ ์ฆ๋ช
ํ ์ ์๋ ํฌํธํด๋ฆฌ์ค๊ฐ ํ์ํด.
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์์ด! ๐ LangChain์ ํ์ฉํ ๋ฐ์ดํฐ ์ ๋ฆฌ์ ์์ฝ ์๋ํ์ ๋ํด ์ ๋ง ๋ง์ ๊ฑธ ๋ค๋ค์ง?
ํต์ฌ์ ๋ค์ ํ ๋ฒ ์ ๋ฆฌํ๋ฉด:
โจ ํต์ฌ ์์ฝ
1. LangChain์ ๊ฐ๋ ฅํ ๋๊ตฌ
LLM์ ์ค๋ฌด์ ์ ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ ์์ํฌ๋ก, ์ฒด์ธ, ์์ด์ ํธ, ๋ฉ๋ชจ๋ฆฌ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํด.
2. ์๋ํ๋ ์๊ฐ๊ณผ ๋น์ฉ์ ์ ์ฝ
๋ฐ๋ณต์ ์ธ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์
์ ์๋ํํ๋ฉด ์
๋ฌด ํจ์จ์ด ๊ทน์ ์ผ๋ก ํฅ์๋ผ.
3. ์ค์ ์ ์ฉ์ด ํต์ฌ
๊ณ ๊ฐ ํผ๋๋ฐฑ ๋ถ์, ๋ด์ค ํ๋ ์ด์
, ํ์๋ก ์์ฑ ๋ฑ ๋ค์ํ ๋ถ์ผ์ ์ ์ฉ ๊ฐ๋ฅํด.
4. ๋ณด์๊ณผ ์์ ์ฑ ์ค์
์๋ฌ ํธ๋ค๋ง, ๋ชจ๋ํฐ๋ง, ๋ฐ์ดํฐ ๋ณด์์ ํญ์ ๊ณ ๋ คํด์ผ ํด.
5. ๊ณ์ ๋ฐ์ ํ๋ ๋ถ์ผ
AI ๊ธฐ์ ์ ๋น ๋ฅด๊ฒ ์งํํ๋ ๊พธ์คํ ํ์ต์ด ํ์์ผ.
์ด์ ๋น์ ๋ LangChain ์ ๋ฌธ๊ฐ๊ฐ ๋ ์ค๋น๊ฐ ๋์์ด! ๐ ์ฌ๋ฅ๋ท์์ ์ด ์ง์์ ํ์ฉํด์ ๋ฉ์ง ์๋น์ค๋ฅผ ์ ๊ณตํ ์๋ ์๊ณ , ์์ ์ ์ ๋ฌด๋ฅผ ์๋ํํด์ ์์ฐ์ฑ์ ๋์ผ ์๋ ์์ง.
๊ธฐ์ตํด, AI๋ ๋๊ตฌ์ผ ๋ฟ์ด์ผ. ์ค์ํ ๊ฑด ์ด๋ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๊ฒ์ธ๊ฐ๋ฅผ ์ ์ํ๋ ๊ฑฐ์ผ. ๊ธฐ์ ์ ๊ทธ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์๋จ์ผ ๋ฟ์ด์ง. ๐ฏ
์, ์ด์ ๋น์ ์ ์ฐจ๋ก์ผ! ์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ฒซ ๋ฒ์งธ ์๋ํ ํ๋ก์ ํธ๋ฅผ ์์ํด๋ณด๋ ๊ฑด ์ด๋? ์์ ๊ฒ๋ถํฐ ์์ํด์ ์ ์ ํ์ฅํด๋๊ฐ๋ฉด ๋ผ. ๐ช
ํ์ด์ ๋น์ด! ๐
๊ถ๊ธํ ์ ์ด ์์ผ๋ฉด ์ธ์ ๋ LangChain ์ปค๋ฎค๋ํฐ๋ ์ฌ๋ฅ๋ท์์ ๋ฌผ์ด๋ด!
์ฐ๋ฆฌ ๋ชจ๋ ํจ๊ป ์ฑ์ฅํ๋ ๊ฑฐ์ผ. ๐
์ด ๊ธ์ด ๋์์ด ๋์๋ค๋ฉด, ๋น์ ์ ์๋ํ ํ๋ก์ ํธ ์ฑ๊ณต ์คํ ๋ฆฌ๋ฅผ ๊ณต์ ํด์ค!
๋ค์์ ๋ ๋ง๋์! ๐
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.