์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿค– LangChain์œผ๋กœ ๋ฐ์ดํ„ฐ ์ •๋ฆฌยท์š”์•ฝ ์ž๋™ํ™” ์™„๋ฒฝ ๊ฐ€์ด๋“œ: AI๊ฐ€ ๋‹น์‹ ์˜ ์—…๋ฌด๋ฅผ ํ˜์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•

๐Ÿค– LangChain์œผ๋กœ ๋ฐ์ดํ„ฐ ์ •๋ฆฌยท์š”์•ฝ ์ž๋™ํ™” ์™„๋ฒฝ ๊ฐ€์ด๋“œ: AI๊ฐ€ ๋‹น์‹ ์˜ ์—…๋ฌด๋ฅผ ํ˜์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•

๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ, ์ด์ œ AI์—๊ฒŒ ๋งก๊ธฐ๊ณ  ์ปคํ”ผ ํ•œ ์ž”์˜ ์—ฌ์œ ๋ฅผ ์ฆ๊ฒจ๋ณด์„ธ์š” โ˜•

์•ˆ๋…•! ์˜ค๋Š˜์€ ์ •๋ง ํฅ๋ฏธ๋กœ์šด ์ฃผ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์™”์–ด. ๋ฐ”๋กœ LangChain์„ ํ™œ์šฉํ•œ ๋ฐ์ดํ„ฐ ์ •๋ฆฌ์™€ ์š”์•ฝ ์ž๋™ํ™”์ธ๋ฐ, ์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๊ณ ? ๐Ÿค”

์ƒ์ƒํ•ด๋ด. ๋งค์ผ ์•„์นจ ์ถœ๊ทผํ•˜๋ฉด ์ˆ˜๋ฐฑ ํŽ˜์ด์ง€์˜ ๋ณด๊ณ ์„œ, ๋์—†๋Š” ์ด๋ฉ”์ผ, ๊ณ ๊ฐ ํ”ผ๋“œ๋ฐฑ, ์‹œ์žฅ ์กฐ์‚ฌ ์ž๋ฃŒ๋“ค์ด ์Œ“์—ฌ์žˆ์–ด. ์ด๊ฑธ ์ผ์ผ์ด ์ฝ๊ณ  ์ •๋ฆฌํ•˜๊ณ  ์š”์•ฝํ•˜๋ ค๋ฉด... ์•„๋งˆ ํ‡ด๊ทผ ์‹œ๊ฐ„์ด ์ž์ •์„ ๋„˜๊ธธ ๊ฑฐ์•ผ. ๐Ÿ˜ฑ

ํ•˜์ง€๋งŒ LangChain์„ ํ™œ์šฉํ•˜๋ฉด? ์ด ๋ชจ๋“  ์ž‘์—…์„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์–ด! AI๊ฐ€ ๋‹น์‹ ์˜ ๋˜‘๋˜‘ํ•œ ๋น„์„œ๊ฐ€ ๋˜์–ด ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ฆฌํ•˜๊ณ , ํ•ต์‹ฌ๋งŒ ์™์™ ๋ฝ‘์•„๋‚ด๊ณ , ์‹ฌ์ง€์–ด ์ธ์‚ฌ์ดํŠธ๊นŒ์ง€ ์ œ๊ณตํ•ด์ค˜. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ตœ๊ณ ์˜ ๋ฐ์ดํ„ฐ ๋ถ„์„ ์ „๋ฌธ๊ฐ€๋ฅผ ๊ณ ์šฉํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์•ผ! ๐Ÿš€

๋ฐ์ดํ„ฐ ์ž๋™ํ™” ํ”„๋กœ์„ธ์Šค ์›๋ณธ ๋ฐ์ดํ„ฐ ๐Ÿ“š ๋ณด๊ณ ์„œ, ๋ฌธ์„œ LangChain ๐Ÿค– AI ์ฒ˜๋ฆฌ ์ •๋ฆฌ๋œ ๊ฒฐ๊ณผ โœจ ์š”์•ฝ, ์ธ์‚ฌ์ดํŠธ ์ž๋™ ๋ฐฐํฌ ๐Ÿ“ง ๋ฆฌํฌํŠธ ์ „์†ก ์‹œ๊ฐ„ ์ ˆ์•ฝ โฐ ์ •ํ™•๋„ ํ–ฅ์ƒ ๐ŸŽฏ ๋น„์šฉ ์ ˆ๊ฐ ๐Ÿ’ฐ ํ™•์žฅ์„ฑ ๐Ÿ“ˆ ํ•˜๋ฃจ 8์‹œ๊ฐ„ ์ž‘์—…์„ 30๋ถ„์œผ๋กœ ๋‹จ์ถ•! LangChain ์ž๋™ํ™”๋กœ ์—…๋ฌด ํšจ์œจ 95% ํ–ฅ์ƒ

๐ŸŽฏ 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