Kentlo's picture
Sync from GitHub 37f011465a0aa2abdfdd94d8bd5f31cfe932c669
28677d8 verified
Raw
History Blame Contribute Delete
53.3 kB
[
{
"id": "docs-ollama-models",
"certification": "Docs Study",
"title": "Ollama ๋ชจ๋ธ ์‹คํ–‰ ํ๋ฆ„",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-1", "docs-lab-2"],
"summary": "Ollama๋Š” ๋กœ์ปฌ์—์„œ LLM ๋ชจ๋ธ์„ ๋‚ด๋ ค๋ฐ›๊ณ  ์‹คํ–‰ํ•ด CLI ๋˜๋Š” REST API๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒฝ๋Ÿ‰ ๋ชจ๋ธ ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. `ollama run`์œผ๋กœ ๋Œ€ํ™”ํ•˜๊ณ , `ollama serve`๋กœ API ์„œ๋ฒ„๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.",
"example": "# ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ ๋ฐ ์‹คํ–‰\nollama pull qwen2.5:14b\nollama run qwen2.5:14b\n# ์„ค์น˜๋œ ๋ชจ๋ธ ๋ชฉ๋ก\nollama list",
"common_mistake": "๋ชจ๋ธ ์ด๋ฆ„์— ํƒœ๊ทธ(:14b, :latest ๋“ฑ)๋ฅผ ์ƒ๋žตํ•˜๋ฉด `:latest`๋กœ ์ž๋™ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ์ด ๋กœ์ปฌ์— ์—†์„ ๋•Œ `ollama run`์ด ์ž๋™์œผ๋กœ pullํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["ollama", "pull", "run", "serve", "model tag", "GGUF", "local LLM"],
"source_id": "official-docs-ollama",
"details": [
"**`ollama run`**: ๋ชจ๋ธ์ด ์—†์œผ๋ฉด ์ž๋™ ๋‹ค์šด๋กœ๋“œ ํ›„ ๋Œ€ํ™”ํ˜• ์„ธ์…˜ ์‹œ์ž‘. `--nowordwrap` ์˜ต์…˜์œผ๋กœ ๊ธด ์ถœ๋ ฅ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ.",
"**`ollama serve`**: 11434 ํฌํŠธ์—์„œ OpenAI ํ˜ธํ™˜ REST API ์ œ๊ณต. `OLLAMA_HOST=0.0.0.0`์œผ๋กœ ์™ธ๋ถ€ ์ ‘๊ทผ ํ—ˆ์šฉ ๊ฐ€๋Šฅ.",
"๋ชจ๋ธ ํŒŒ์ผ์€ `~/.ollama/models`์— GGUF ํ˜•์‹์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. `Modelfile`๋กœ ์ปค์Šคํ…€ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
]
},
{
"id": "docs-ollama-api",
"certification": "Docs Study",
"title": "Ollama REST API ํ˜ธ์ถœ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-3"],
"summary": "Ollama๋Š” `localhost:11434`์—์„œ REST API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. `/api/generate`(์™„์„ฑํ˜•), `/api/chat`(๋Œ€ํ™”ํ˜•), `/api/embeddings`(์ž„๋ฒ ๋”ฉ) ์—”๋“œํฌ์ธํŠธ๋ฅผ OpenAI ํ˜ธํ™˜ ํ˜•์‹์œผ๋กœ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
"example": "# chat ์—”๋“œํฌ์ธํŠธ\ncurl http://localhost:11434/api/chat -d '{\n \"model\": \"qwen2.5:7b\",\n \"messages\": [{\"role\": \"user\", \"content\": \"์•ˆ๋…•\"}],\n \"stream\": false\n}'",
"common_mistake": "Ollama ์„œ๋ฒ„(`ollama serve`)๊ฐ€ ์‹คํ–‰ ์ค‘์ด์ง€ ์•Š๊ฑฐ๋‚˜ ๋ชจ๋ธ์ด ๋กœ์ปฌ์— ์—†์œผ๋ฉด API ํ˜ธ์ถœ์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € `ollama list`๋กœ ๋ชจ๋ธ ์กด์žฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.",
"keywords": ["ollama", "REST API", "generate", "chat", "embeddings", "localhost:11434", "stream"],
"source_id": "official-docs-ollama",
"details": [
"**OpenAI ํ˜ธํ™˜ API**: `http://localhost:11434/v1/chat/completions`๋กœ OpenAI Python SDK๋‚˜ LangChain์˜ `ChatOpenAI(base_url=...)` ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",
"**์ŠคํŠธ๋ฆฌ๋ฐ**: `\"stream\": true` ์„ค์ • ์‹œ ์‘๋‹ต์„ JSON ์ค„ ๋‹จ์œ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ. `\"done\": true` ์ค„์ด ์™„๋ฃŒ ์‹ ํ˜ธ์ž…๋‹ˆ๋‹ค.",
"**์ž„๋ฒ ๋”ฉ**: `POST /api/embeddings`์— `{\"model\":\"nomic-embed-text\", \"prompt\":\"ํ…์ŠคํŠธ\"}`๋ฅผ ๋ณด๋‚ด๋ฉด ๋ฒกํ„ฐ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-dotenv",
"certification": "Docs Study",
"title": "python-dotenv โ€” ํ™˜๊ฒฝ๋ณ€์ˆ˜ & .env ๊ด€๋ฆฌ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-4"],
"summary": "python-dotenv๋Š” `.env` ํŒŒ์ผ์˜ ํ‚ค-๊ฐ’ ์Œ์„ `os.environ`์— ์ž๋™์œผ๋กœ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. API ํ‚ค ๊ฐ™์€ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ์ฝ”๋“œ์— ์ง์ ‘ ์“ฐ์ง€ ์•Š๊ณ  ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•ด ๊ด€๋ฆฌํ•˜๋Š” ํ‘œ์ค€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.",
"example": "# .env ํŒŒ์ผ\nOPENAI_API_KEY=sk-...\nOLLAMA_BASE_URL=http://localhost:11434\n\n# Python ์ฝ”๋“œ\nfrom dotenv import load_dotenv\nimport os\n\nload_dotenv() # .env ํŒŒ์ผ ๋กœ๋“œ\napi_key = os.getenv('OPENAI_API_KEY')",
"common_mistake": "`load_dotenv()`๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— `os.getenv()`๋ฅผ ์“ฐ๋ฉด ํ•ญ์ƒ `None`์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ํŒŒ์ผ ์ƒ๋‹จ์—์„œ ๊ฐ€์žฅ ๋จผ์ € `load_dotenv()`๋ฅผ ํ˜ธ์ถœํ•˜์„ธ์š”.",
"keywords": ["dotenv", "load_dotenv", ".env", "os.getenv", "ํ™˜๊ฒฝ๋ณ€์ˆ˜", "API ํ‚ค"],
"source_id": "official-docs-python",
"details": [
"**์šฐ์„ ์ˆœ์œ„**: `load_dotenv(override=False)`(๊ธฐ๋ณธ๊ฐ’)๋Š” ์ด๋ฏธ ์„ค์ •๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. CI/CD ํ™˜๊ฒฝ์—์„œ ์‹œ์Šคํ…œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ .env๋ณด๋‹ค ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.",
"**.gitignore**: `.env`๋Š” ๋ฐ˜๋“œ์‹œ `.gitignore`์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. `.env.example`์€ ํ‚ค ์ด๋ฆ„๋งŒ ์ ์–ด ํŒ€์›์—๊ฒŒ ์–ด๋–ค ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•œ์ง€ ์•Œ๋ ค์ฃผ๋Š” ์šฉ๋„๋กœ ์ปค๋ฐ‹ํ•ฉ๋‹ˆ๋‹ค.",
"**Streamlit ์—ฐ๋™**: ๋กœ์ปฌ ๊ฐœ๋ฐœ ์‹œ `load_dotenv()`๋กœ ํ‚ค๋ฅผ ๋กœ๋“œํ•˜๊ณ , ๋ฐฐํฌ ์‹œ์—๋Š” `st.secrets`๋กœ ์ ‘๊ทผํ•˜๋„๋ก ๋ถ„๊ธฐํ•˜๊ฑฐ๋‚˜ ์ฒ˜์Œ๋ถ€ํ„ฐ `st.secrets`๋งŒ ์“ฐ๋Š” ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-prompt-template",
"certification": "Docs Study",
"title": "ChatPromptTemplate โ€” ํ”„๋กฌํ”„ํŠธ ๊ตฌ์กฐํ™”",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-5", "docs-lab-6"],
"summary": "ChatPromptTemplate์€ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ, ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€, ๋ณ€์ˆ˜ ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋ฅผ ๊ตฌ์กฐํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœ f-string ๋Œ€์‹  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ  LangChain ํŒŒ์ดํ”„๋ผ์ธ์— ์—ฐ๊ฒฐ๋˜๋Š” ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์”๋‹ˆ๋‹ค.",
"example": "from langchain_core.prompts import ChatPromptTemplate\n\nprompt = ChatPromptTemplate.from_messages([\n (\"system\", \"๋‹น์‹ ์€ ๋„์›€์ด ๋˜๋Š” AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ: {context}\"),\n (\"human\", \"{question}\")\n])\n\n# ์ฒด์ธ์— ์—ฐ๊ฒฐ\nchain = prompt | llm | StrOutputParser()\nchain.invoke({\"context\": \"...\", \"question\": \"์š”์•ฝํ•ด์ค˜\"})",
"common_mistake": "`PromptTemplate`์€ ๋‹จ์ˆœ ๋ฌธ์ž์—ด ํฌ๋งท์šฉ(๋ณ€์ˆ˜ ํ•˜๋‚˜), `ChatPromptTemplate`์€ ์—ญํ• ์ด ๊ตฌ๋ถ„๋œ ๋ฉ”์‹œ์ง€ ๋ฐฐ์—ด์šฉ์ž…๋‹ˆ๋‹ค. RAG์—์„œ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์™€ ์งˆ๋ฌธ์„ ๊ตฌ๋ถ„ํ•˜๋ ค๋ฉด ํ•ญ์ƒ ChatPromptTemplate์„ ์”๋‹ˆ๋‹ค.",
"keywords": ["ChatPromptTemplate", "from_messages", "system", "human", "placeholder", "PromptTemplate"],
"source_id": "official-docs-langchain",
"details": [
"**MessagesPlaceholder**: ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ํ†ต์งธ๋กœ ์ฃผ์ž…ํ•  ๋•Œ ์”๋‹ˆ๋‹ค. `(\"placeholder\", \"{chat_history}\")`๋กœ ์„ ์–ธํ•˜๋ฉด ๋ฉ”์‹œ์ง€ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๊ทธ ์ž๋ฆฌ์— ํŽผ์ณ์ง‘๋‹ˆ๋‹ค.",
"**partial**: `prompt.partial(context=\"๊ณ ์ •๊ฐ’\")`์œผ๋กœ ํŠน์ • ๋ณ€์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ์ฑ„์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. context๋Š” ๊ณ ์ •ํ•˜๊ณ  question๋งŒ ๋Ÿฐํƒ€์ž„์— ๋ฐ›์„ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
"**ํƒ€์ž… ์•ˆ์ „์„ฑ**: from_messages์— `(\"system\", \"...\")` ํŠœํ”Œ ๋Œ€์‹  `SystemMessage(content=\"...\")`๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ํŠœํ”Œ ๋ฐฉ์‹์ด ๋” ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-document-loaders",
"certification": "Docs Study",
"title": "LangChain Document Loaders โ€” ๋ฌธ์„œ ์ผ๊ด„ ๋กœ๋“œ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-7"],
"summary": "Document Loader๋Š” PDF, ๋งˆํฌ๋‹ค์šด, ์›น ํŽ˜์ด์ง€, ๋””๋ ‰ํ† ๋ฆฌ ๋“ฑ ๋‹ค์–‘ํ•œ ์†Œ์Šค์—์„œ ๋ฌธ์„œ๋ฅผ LangChain `Document` ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. RAG ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ฒซ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.",
"example": "from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader\n\n# ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด ๋ชจ๋“  .md ํŒŒ์ผ ๋กœ๋“œ\nloader = DirectoryLoader('./notes', glob='**/*.md')\ndocs = loader.load()\n\n# ๊ฐ ๋ฌธ์„œ์˜ ์ถœ์ฒ˜ ํ™•์ธ\nfor doc in docs:\n print(doc.metadata['source'], len(doc.page_content))",
"common_mistake": "`loader.load()`๋Š” ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ๋Š” `loader.lazy_load()`๋กœ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์“ฐ๊ฑฐ๋‚˜ ๋ฐฐ์น˜๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["DirectoryLoader", "PyPDFLoader", "WebBaseLoader", "glob", "metadata", "source", "lazy_load"],
"source_id": "official-docs-langchain",
"details": [
"**metadata['source']**: ๋กœ๋“œ๋œ ๋ฌธ์„œ๋Š” `metadata` ๋”•์…”๋„ˆ๋ฆฌ์— ํŒŒ์ผ ๊ฒฝ๋กœ(`source`)๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. RAG ๋‹ต๋ณ€์— ์ถœ์ฒ˜๋ฅผ ํ‘œ์‹œํ•  ๋•Œ ์ด ๊ฐ’์„ ์”๋‹ˆ๋‹ค.",
"**glob ํŒจํ„ด**: `glob='**/*.md'`๋Š” ํ•˜์œ„ ํด๋”๊นŒ์ง€ ์žฌ๊ท€ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. `glob='*.pdf'`๋Š” ํ˜„์žฌ ํด๋”๋งŒ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.",
"**WebBaseLoader**: URL ๋ฆฌ์ŠคํŠธ๋ฅผ ๋„˜๊ธฐ๋ฉด ์›น ํŽ˜์ด์ง€๋ฅผ ํฌ๋กค๋งํ•ด Document๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. BeautifulSoup ๊ธฐ๋ฐ˜์ด๋ฉฐ `bs_kwargs`๋กœ ํŒŒ์‹ฑ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
]
},
{
"id": "docs-langchain-rag",
"certification": "Docs Study",
"title": "LangChain RAG ๊ธฐ๋ณธ ํ๋ฆ„",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-8", "docs-lab-9"],
"summary": "LangChain์€ ๋ฌธ์„œ ๋กœ๋“œ โ†’ ๋ถ„ํ•  โ†’ ์ž„๋ฒ ๋”ฉ โ†’ ๊ฒ€์ƒ‰ โ†’ ์ƒ์„ฑ์˜ RAG ํŒŒ์ดํ”„๋ผ์ธ์„ ์ปดํฌ๋„ŒํŠธ ์กฐํ•ฉ์œผ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. Retriever๊ฐ€ ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ฐพ์•„ LLM ์ปจํ…์ŠคํŠธ๋กœ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_community.vectorstores import Chroma\nretriever = Chroma.from_documents(docs, embeddings).as_retriever(k=3)\nchain = (\n {\"context\": retriever, \"question\": RunnablePassthrough()}\n | prompt | llm | StrOutputParser()\n)",
"common_mistake": "Retriever๊ฐ€ ์ฐพ์€ ๋ฌธ์„œ๋ฅผ ๊ทธ๋Œ€๋กœ ์ •๋‹ต์œผ๋กœ ๋ฏฟ์ง€ ๋งˆ์„ธ์š”. ๊ฒ€์ƒ‰๋œ ์ฒญํฌ๊ฐ€ ์งˆ๋ฌธ๊ณผ ๋‹ค๋ฅธ ๋งฅ๋ฝ์ผ ์ˆ˜ ์žˆ์–ด ํ”„๋กฌํ”„ํŠธ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ '์•„๋ž˜ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด'๋ผ๊ณ  ์ง€์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["langchain", "RAG", "retriever", "vectorstore", "document loader", "text splitter"],
"source_id": "official-docs-langchain",
"details": [
"**ํŒŒ์ดํ”„๋ผ์ธ ๋‹จ๊ณ„**: DocumentLoader โ†’ TextSplitter(์ฒญํฌ ๋ถ„ํ• ) โ†’ Embeddings โ†’ VectorStore โ†’ Retriever โ†’ LLM Chain.",
"**TextSplitter**: `RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)`์ด ์ผ๋ฐ˜์  ์‹œ์ž‘์ . overlap์ด ์žˆ์–ด์•ผ ์ฒญํฌ ๊ฒฝ๊ณ„์—์„œ ๋ฌธ๋งฅ ์†์‹ค์„ ์ค„์ž…๋‹ˆ๋‹ค.",
"`MultiQueryRetriever`๋‚˜ `ContextualCompressionRetriever`๋กœ ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœ ์œ ์‚ฌ๋„ ๊ฒ€์ƒ‰๋ณด๋‹ค ๊ด€๋ จ ์ฒญํฌ ์„ ๋ณ„ ์ •ํ™•๋„๊ฐ€ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค."
]
},
{
"id": "docs-lcel",
"certification": "Docs Study",
"title": "LangChain LCEL โ€” ์ฒด์ธ ๊ตฌ์„ฑ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-10", "docs-lab-11"],
"summary": "LCEL(LangChain Expression Language)์€ `|` ํŒŒ์ดํ”„ ์—ฐ์‚ฐ์ž๋กœ Runnable ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง๋ ฌยท๋ณ‘๋ ฌ ์—ฐ๊ฒฐํ•ด ์ฒด์ธ์„ ์„ ์–ธ์ ์œผ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_core.runnables import RunnablePassthrough\nchain = (\n RunnablePassthrough.assign(context=retriever)\n | prompt\n | llm\n | StrOutputParser()\n)\nresult = chain.invoke({\"question\": \"What is RAG?\"})",
"common_mistake": "์ฒด์ธ ๊ฐ ๋‹จ๊ณ„์˜ ์ž…์ถœ๋ ฅ ํƒ€์ž…์ด ๋งž์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. LLM์€ `str` ๋˜๋Š” `BaseMessage`๋ฅผ ๋ฐ›๊ณ  ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, ์•ž ๋‹จ๊ณ„๊ฐ€ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ์ด ์ค‘๊ฐ„์—์„œ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["LCEL", "Runnable", "pipe", "chain", "invoke", "stream", "batch"],
"source_id": "official-docs-langchain",
"details": [
"**3๊ฐ€์ง€ ์‹คํ–‰ ๋ฐฉ์‹**: `invoke`(๋‹จ์ผ ๋™๊ธฐ), `stream`(์ŠคํŠธ๋ฆฌ๋ฐ yield), `batch`(๋ฆฌ์ŠคํŠธ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ). ์ŠคํŠธ๋ฆฌ๋ฐ UI์—๋Š” `stream`์„ ์”๋‹ˆ๋‹ค.",
"**RunnablePassthrough**: ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ „๋‹ฌ. `.assign(key=fn)`์œผ๋กœ ์ถ”๊ฐ€ ํ‚ค๋ฅผ ๋ถ™์—ฌ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.",
"**RunnableParallel**: `{\"a\": fn1, \"b\": fn2}` ํ˜•ํƒœ๋กœ ์—ฌ๋Ÿฌ ์ฒด์ธ์„ ๋™์‹œ์— ์‹คํ–‰ํ•ด ๊ฒฐ๊ณผ๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ํ•ฉ์นฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-vectorstore",
"certification": "Docs Study",
"title": "VectorStore์™€ Retriever โ€” ๋ฒกํ„ฐ ๊ฒ€์ƒ‰",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-11", "docs-lab-12"],
"summary": "๋ฌธ์„œ๋ฅผ ์ž„๋ฒ ๋”ฉ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•ด ์ €์žฅํ•˜๊ณ , ์ฟผ๋ฆฌ์™€ ์ฝ”์‚ฌ์ธ ์œ ์‚ฌ๋„๋กœ ๊ด€๋ จ ์ฒญํฌ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. Chroma, FAISS, pgvector ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐฑ์—”๋“œ๋ฅผ LangChain์ด ์ถ”์ƒํ™”ํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_community.vectorstores import Chroma\nfrom langchain_ollama import OllamaEmbeddings\n\nembeddings = OllamaEmbeddings(model='nomic-embed-text')\ndb = Chroma.from_documents(docs, embeddings, persist_directory='./db')\nretriever = db.as_retriever(search_type='mmr', search_kwargs={'k': 4})",
"common_mistake": "์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฒกํ„ฐ ์ฐจ์›์ด ๋‹ฌ๋ผ์ ธ ๊ธฐ์กด ์ปฌ๋ ‰์…˜๊ณผ ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ์„ ๋ฐ”๊ฟ€ ๋•Œ๋Š” ์ปฌ๋ ‰์…˜์„ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["vectorstore", "Chroma", "FAISS", "embedding", "similarity", "MMR", "retriever"],
"source_id": "official-docs-langchain",
"details": [
"**๊ฒ€์ƒ‰ ์œ ํ˜•**: `similarity`(์ˆœ์ˆ˜ ์œ ์‚ฌ๋„ ์ƒ์œ„ k๊ฐœ), `mmr`(MMR: ๋‹ค์–‘์„ฑ ๊ณ ๋ ค), `similarity_score_threshold`(์ž„๊ณ„๊ฐ’ ์ด์ƒ๋งŒ).",
"**Chroma**: ๋กœ์ปฌ ํŒŒ์ผ๋กœ ์ €์žฅ ๊ฐ€๋Šฅ(`persist_directory`). ํ”„๋กœํ† ํƒ€์ž…์— ์ ํ•ฉ. ํ”„๋กœ๋•์…˜์€ pgvector(PostgreSQL), Qdrant, Pinecone ๊ถŒ์žฅ.",
"์ž„๋ฒ ๋”ฉ์€ ํ…์ŠคํŠธ์˜ ์˜๋ฏธ์  ์œ ์‚ฌ๋„๋ฅผ ์žก์ง€๋งŒ, ์ •ํ™•ํ•œ ํ‚ค์›Œ๋“œ ๋งค์นญ์ด ํ•„์š”ํ•˜๋ฉด BM25 ๋“ฑ ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰๊ณผ `EnsembleRetriever`๋กœ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ตฌ์„ฑ์„ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-streamlit-state",
"certification": "Docs Study",
"title": "Streamlit session_state โ€” ์ƒํƒœ ๊ด€๋ฆฌ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-16", "docs-lab-17"],
"summary": "Streamlit์€ ์ƒํ˜ธ์ž‘์šฉ๋งˆ๋‹ค ์Šคํฌ๋ฆฝํŠธ ์ „์ฒด๋ฅผ rerunํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„ํŠผ ํด๋ฆญยท์ž…๋ ฅ ๊ฒฐ๊ณผ๋ฅผ rerun ์ดํ›„์—๋„ ์œ ์ง€ํ•˜๋ ค๋ฉด `st.session_state`์— ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"example": "if 'count' not in st.session_state:\n st.session_state.count = 0\nif st.button('์ฆ๊ฐ€'):\n st.session_state.count += 1\nst.write(f'ํด๋ฆญ ํšŸ์ˆ˜: {st.session_state.count}')",
"common_mistake": "์ผ๋ฐ˜ ์ „์—ญ ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜ ๋‚ด ์ง€์—ญ ๋ณ€์ˆ˜๋Š” rerun๋งˆ๋‹ค ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ์ƒํƒœ ๋ณด์กด์ด ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฐ’์€ ๋ฐ˜๋“œ์‹œ `st.session_state`๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.",
"keywords": ["session_state", "rerun", "widget", "state management", "callback"],
"source_id": "official-docs-streamlit",
"details": [
"**์ดˆ๊ธฐํ™” ํŒจํ„ด**: `st.session_state.setdefault('key', default_value)` ๋˜๋Š” `if 'key' not in st.session_state:` ๋ธ”๋ก์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.",
"**Widget key**: `st.text_input('์ด๋ฆ„', key='username')`์œผ๋กœ ์œ„์ ฏ ๊ฐ’์ด ์ž๋™์œผ๋กœ `st.session_state.username`์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.",
"**callback**: `on_change=my_func`์œผ๋กœ ์œ„์ ฏ ๋ณ€๊ฒฝ ์‹œ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. callback ์•ˆ์—์„œ session_state๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด rerun ์ „์— ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-streamlit-cache",
"certification": "Docs Study",
"title": "Streamlit Cache โ€” ์บ์‹œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-18"],
"summary": "rerun๋งˆ๋‹ค ๋ฐ˜๋ณต ์‹คํ–‰๋˜๋Š” ๋น„์šฉ ๋†’์€ ๊ณ„์‚ฐยท๋ฆฌ์†Œ์Šค ๋กœ๋”ฉ์„ ์บ์‹œํ•ด ์„ฑ๋Šฅ์„ ๋†’์ž…๋‹ˆ๋‹ค. `@st.cache_data`๋Š” ์ง๋ ฌํ™” ๊ฐ€๋Šฅ ๋ฐ์ดํ„ฐ, `@st.cache_resource`๋Š” DBยท๋ชจ๋ธ ๊ฐ™์€ ๊ณต์œ  ๊ฐ์ฒด์— ์”๋‹ˆ๋‹ค.",
"example": "@st.cache_resource\ndef load_model():\n return SentenceTransformer('all-MiniLM-L6-v2')\n\n@st.cache_data(ttl=300)\ndef fetch_data(query: str):\n return db.execute(query).fetchall()",
"common_mistake": "`cache_data`์™€ `cache_resource`๋ฅผ ํ˜ผ๋™ํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค. LLM ๋ชจ๋ธ์ด๋‚˜ DB ์—ฐ๊ฒฐ์ฒ˜๋Ÿผ ๊ณต์œ ํ•ด์•ผ ํ•˜๋Š” ๊ฐ์ฒด๋Š” ๋ฐ˜๋“œ์‹œ `cache_resource`๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["cache_data", "cache_resource", "TTL", "rerun", "performance"],
"source_id": "official-docs-streamlit",
"details": [
"**`@st.cache_data`**: ํ•จ์ˆ˜ ์ธ์ˆ˜๋ฅผ ํ‚ค๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ณต์‚ฌํ•ด ์บ์‹œ. DataFrame, ๋ฆฌ์ŠคํŠธ ๋“ฑ ์ง๋ ฌํ™” ๊ฐ€๋Šฅ ๋ฐ˜ํ™˜๊ฐ’์— ์ ํ•ฉ. `ttl` ์ธ์ˆ˜๋กœ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์„ค์ • ๊ฐ€๋Šฅ.",
"**`@st.cache_resource`**: ์‹ฑ๊ธ€ํ†ค์ฒ˜๋Ÿผ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑํ•ด ๋ชจ๋“  ์„ธ์…˜์ด ๊ณต์œ . ๋ชจ๋ธ ๋กœ๋”ฉ, DB ์—ฐ๊ฒฐ, ๋ฒกํ„ฐ ์Šคํ† ์–ด ์ดˆ๊ธฐํ™”์— ์ ํ•ฉ.",
"์บ์‹œ ๋ฌดํšจํ™”: `st.cache_data.clear()` / `st.cache_resource.clear()`๋กœ ์ˆ˜๋™ ์ดˆ๊ธฐํ™”. ํ•จ์ˆ˜ ๋ณธ๋ฌธ์ด ๋ฐ”๋€Œ๋ฉด ์ž๋™์œผ๋กœ ์บ์‹œ๊ฐ€ ๋ฌดํšจํ™”๋ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-streamlit-secrets",
"certification": "Docs Study",
"title": "Streamlit Secrets โ€” ๋น„๋ฐ€ ๊ด€๋ฆฌ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-4"],
"summary": "API ํ‚ค, DB ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ์ฝ”๋“œ์— ๋„ฃ์ง€ ์•Š๊ณ  `.streamlit/secrets.toml` ํŒŒ์ผ ๋˜๋Š” Streamlit Cloud์˜ Secrets UI์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.",
"example": "# .streamlit/secrets.toml\n[database]\nhost = 'localhost'\npassword = 'secret'\n\n# ์ฝ”๋“œ์—์„œ ์ ‘๊ทผ\nimport streamlit as st\ndb_pass = st.secrets['database']['password']",
"common_mistake": "`.streamlit/secrets.toml`์„ `.gitignore`์— ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ Git์— ์ปค๋ฐ‹๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ์ œ์™ธํ•˜์„ธ์š”.",
"keywords": ["secrets", "secrets.toml", "API key", ".gitignore", "Streamlit Cloud"],
"source_id": "official-docs-streamlit",
"details": [
"๋กœ์ปฌ ๊ฐœ๋ฐœ: `.streamlit/secrets.toml`์— TOML ํ˜•์‹์œผ๋กœ ์ €์žฅ. ์•ฑ ์ฝ”๋“œ์—์„œ `st.secrets`๋กœ ๋”•์…”๋„ˆ๋ฆฌ์ฒ˜๋Ÿผ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.",
"Streamlit Cloud: ์•ฑ Settings โ†’ Secrets์— ๊ฐ™์€ TOML ๋‚ด์šฉ์„ ๋ถ™์—ฌ๋„ฃ์œผ๋ฉด ๋ฐฐํฌ ์‹œ ์ž๋™ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค.",
"`st.secrets`๋Š” `os.environ`๋ณด๋‹ค ๊ตฌ์กฐํ™”๋œ ์ ‘๊ทผ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. `st.secrets['key']`์™€ `st.secrets.key` ๋‘ ๋ฐฉ์‹ ๋ชจ๋‘ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-hf-spaces",
"certification": "Docs Study",
"title": "Hugging Face Spaces โ€” ์•ฑ ๋ฐฐํฌ",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-19"],
"summary": "Hugging Face Spaces๋Š” Streamlit, Gradio, Docker ์•ฑ์„ ๋ฌด๋ฃŒ๋กœ ํ˜ธ์ŠคํŒ…ํ•˜๋Š” ํ™˜๊ฒฝ์ž…๋‹ˆ๋‹ค. README.md ์ƒ๋‹จ์˜ YAML ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ SDK์™€ ํŒŒ์ผ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.",
"example": "# README.md ์ƒ๋‹จ (YAML frontmatter)\n---\ntitle: My App\nsdk: streamlit\napp_file: app.py\npython_version: '3.11'\n---",
"common_mistake": "API ํ‚ค ๋“ฑ ๋ฏผ๊ฐ ์ •๋ณด๋Š” ์ฝ”๋“œ์— ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ๋งˆ์„ธ์š”. Space Settings โ†’ Secrets์— ๋“ฑ๋กํ•˜๊ณ  ์ฝ”๋“œ์—์„œ๋Š” `os.environ['MY_KEY']`๋กœ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["huggingface", "spaces", "SDK", "secrets", "app_file", "README metadata"],
"source_id": "official-docs-huggingface",
"details": [
"**๋ฌด๋ฃŒ ํ‹ฐ์–ด**: CPU ๊ธฐ๋ณธ ์ œ๊ณต, ๊ณต๊ฐœ Space๋Š” ๋ฌด๋ฃŒ. GPU๋‚˜ ๋น„๊ณต๊ฐœ Space๋Š” ์œ ๋ฃŒ ํ”Œ๋žœ ๋˜๋Š” ZeroGPU(๋ฌด๋ฃŒ ๊ณต์œ  GPU) ํ™œ์šฉ.",
"**Secrets**: `Settings โ†’ Repository secrets`์— ํ‚ค-๊ฐ’์œผ๋กœ ๋“ฑ๋ก. ๋นŒ๋“œ ๋ฐ ๋Ÿฐํƒ€์ž„์— ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค.",
"Space๋ฅผ Duplicate(๋ณต์ œ)ํ•˜๋ฉด fork์ฒ˜๋Ÿผ ๋…๋ฆฝ๋œ Space๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ํƒ€์ธ์˜ ๋ชจ๋ธยท์•ฑ์„ ๋‚ด Secrets์™€ ํ•จ๊ป˜ ์ปค์Šคํ…€ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์ž…๋‹ˆ๋‹ค."
]
},
{
"id": "docs-hf-docker-space",
"certification": "Docs Study",
"title": "Hugging Face Docker Space",
"level": "์ž…๋ฌธ",
"related_practices": ["docs-lab-20"],
"summary": "Dockerfile๋กœ ์•ฑ ํ™˜๊ฒฝ์„ ์ง์ ‘ ์ •์˜ํ•˜๋Š” Space ๋ฐฐํฌ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. Streamlit/Gradio SDK ์ œ์•ฝ์„ ๋„˜์–ด FastAPI, ์ปค์Šคํ…€ ์„œ๋ฒ„ ๋“ฑ ์–ด๋–ค ์•ฑ๋„ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"example": "# README.md\n---\ntitle: My API\nsdk: docker\napp_port: 7860\n---\n\n# Dockerfile\nFROM python:3.11-slim\nCOPY . .\nRUN pip install -r requirements.txt\nEXPOSE 7860\nCMD [\"uvicorn\", \"main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"7860\"]",
"common_mistake": "Docker Space๋Š” ํฌํŠธ 7860์„ ๊ธฐ๋ณธ์œผ๋กœ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค(`app_port: 7860`). ์•ฑ์ด ๋‹ค๋ฅธ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด README ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ `app_port`์™€ ์ผ์น˜์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["docker", "Dockerfile", "Spaces", "app_port", "FastAPI", "custom server"],
"source_id": "official-docs-huggingface",
"details": [
"**๋นŒ๋“œ ์บ์‹œ**: Space๋Š” ์ด์ „ ๋นŒ๋“œ ๋ ˆ์ด์–ด๋ฅผ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. `COPY requirements.txt .` ํ›„ `RUN pip install`์„ ๋ฐฐ์น˜ํ•ด ์˜์กด์„ฑ ๋ ˆ์ด์–ด๋ฅผ ์•ฑ ์ฝ”๋“œ๋ณด๋‹ค ์•ž์— ๋‘๋ฉด ๋นŒ๋“œ๊ฐ€ ๋น ๋ฆ…๋‹ˆ๋‹ค.",
"**Secrets**: `docker build --build-arg`๊ฐ€ ์•„๋‹Œ Runtime Secrets๋กœ ์ฃผ์ž…ํ•ด์•ผ ์ด๋ฏธ์ง€์— ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. `os.environ`์œผ๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.",
"**๋ชจ๋ธ ํŒŒ์ผ**: ๋Œ€ํ˜• ๋ชจ๋ธ์€ Space ์ €์žฅ์†Œ์— ์ง์ ‘ ๋„ฃ์ง€ ๋ง๊ณ  `huggingface_hub.snapshot_download()`๋กœ ๋Ÿฐํƒ€์ž„์— ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ Dataset ์ €์žฅ์†Œ๋ฅผ ๋งˆ์šดํŠธํ•˜๋Š” ๋ฐฉ์‹์„ ์”๋‹ˆ๋‹ค."
]
},
{
"id": "docs-fastapi",
"certification": "Docs Study",
"title": "FastAPI โ€” LLM ์„œ๋น„์Šค API ์„œ๋ฒ„",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-lab-21"],
"summary": "FastAPI๋Š” Python ํƒ€์ž… ํžŒํŠธ ๊ธฐ๋ฐ˜์˜ ๊ณ ์„ฑ๋Šฅ ๋น„๋™๊ธฐ API ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. RAG ์ฒด์ธ์ด๋‚˜ LLM ๊ธฐ๋Šฅ์„ REST API ์—”๋“œํฌ์ธํŠธ๋กœ ๋…ธ์ถœํ•  ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ž…๋‹ˆ๋‹ค.",
"example": "from fastapi import FastAPI\nfrom pydantic import BaseModel\n\napp = FastAPI()\n\nclass Query(BaseModel):\n question: str\n\n@app.post('/ask')\nasync def ask(q: Query):\n answer = chain.invoke(q.question)\n return {\"answer\": answer}\n\n# ์‹คํ–‰\n# uvicorn main:app --reload",
"common_mistake": "LangChain ์ฒด์ธ ๊ฐ™์€ ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด๋ฅผ ์š”์ฒญ๋งˆ๋‹ค ์ƒ์„ฑํ•˜์ง€ ๋งˆ์„ธ์š”. ๋ชจ๋“ˆ ๋ ˆ๋ฒจ์—์„œ ํ•œ ๋ฒˆ ์ดˆ๊ธฐํ™”ํ•˜๊ฑฐ๋‚˜ `lifespan` ์ด๋ฒคํŠธ๋กœ ์•ฑ ์‹œ์ž‘ ์‹œ ๋กœ๋“œํ•˜์„ธ์š”.",
"keywords": ["fastapi", "uvicorn", "pydantic", "async", "REST API", "endpoint", "lifespan"],
"source_id": "official-docs-fastapi",
"details": [
"**Pydantic**: ์š”์ฒญ/์‘๋‹ต ์Šคํ‚ค๋งˆ๋ฅผ `BaseModel`๋กœ ์ •์˜ํ•˜๋ฉด ์ž๋™ ๊ฒ€์ฆ, OpenAPI ๋ฌธ์„œ(`/docs`), JSON ์ง๋ ฌํ™”๊ฐ€ ๋ฌด๋ฃŒ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.",
"**์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต**: `StreamingResponse`์™€ async generator๋กœ LLM ์ถœ๋ ฅ์„ Server-Sent Events(SSE)๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**lifespan**: `@asynccontextmanager`๋กœ ์•ฑ ์‹œ์ž‘ ์‹œ ๋ชจ๋ธยทDB ์—ฐ๊ฒฐ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์ข…๋ฃŒ ์‹œ ์ •๋ฆฌํ•˜๋Š” ํŒจํ„ด. FastAPI 0.95+ ๊ถŒ์žฅ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค."
]
},
{
"id": "docs-langsmith",
"certification": "Docs Study",
"title": "LangSmith โ€” LLM ์ถ”์  & ํ‰๊ฐ€",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-lab-13"],
"summary": "LangSmith๋Š” LangChain ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋ชจ๋“  LLM ํ˜ธ์ถœ, ํ”„๋กฌํ”„ํŠธ, ์‘๋‹ต, ์ง€์—ฐ์‹œ๊ฐ„์„ ์ž๋™ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„๊ทธ ๋””๋ฒ„๊น…, ํ”„๋กฌํ”„ํŠธ ๋น„๊ต, ํ‰๊ฐ€ ๋ฐ์ดํ„ฐ์…‹ ๊ด€๋ฆฌ๋ฅผ ํ•œ ๊ณณ์—์„œ ํ•ฉ๋‹ˆ๋‹ค.",
"example": "import os\nos.environ['LANGCHAIN_TRACING_V2'] = 'true'\nos.environ['LANGCHAIN_API_KEY'] = 'ls__...'\nos.environ['LANGCHAIN_PROJECT'] = 'my-rag-app'\n\n# ์ดํ›„ LangChain ์ฝ”๋“œ๋Š” ์ž๋™์œผ๋กœ ์ถ”์ ๋จ\nresult = chain.invoke({'question': '์งˆ๋ฌธ'})",
"common_mistake": "์ถ”์  ํ™œ์„ฑํ™”๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜ 3๊ฐœ(`TRACING_V2=true`, `API_KEY`, `PROJECT`)๊ฐ€ ๋ชจ๋‘ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ์กฐ์šฉํžˆ ์ถ”์ ์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.",
"keywords": ["langsmith", "tracing", "LangChain", "debugging", "evaluation", "dataset", "prompt"],
"source_id": "official-docs-langchain",
"details": [
"**Run ํŠธ๋ ˆ์ด์Šค**: ์ฒด์ธ ์‹คํ–‰๋งˆ๋‹ค tree ํ˜•ํƒœ๋กœ ๊ฐ ๋‹จ๊ณ„์˜ ์ž…์ถœ๋ ฅยทํ† ํฐ ์ˆ˜ยท์ง€์—ฐ์‹œ๊ฐ„์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์–ด๋А ๋‹จ๊ณ„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ๋Š”์ง€ ์ฆ‰์‹œ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**๋ฐ์ดํ„ฐ์…‹ & ํ‰๊ฐ€**: ํ”„๋กœ๋•์…˜ ์˜ค๋ฅ˜ ์ผ€์ด์Šค๋ฅผ Dataset์œผ๋กœ ์ €์žฅ โ†’ Evaluator(LLM-as-judge ๋“ฑ)๋กœ ์ž๋™ ํ‰๊ฐ€ โ†’ CI์— ํ†ตํ•ฉํ•˜๋Š” ํ๋ฆ„์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",
"**Playground**: LangSmith UI์—์„œ ํŠน์ • Run์˜ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ฐ”๋กœ ์ˆ˜์ •ํ•ด ์žฌ์‹คํ–‰ ๊ฐ€๋Šฅ. ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง ๋ฐ˜๋ณต์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-guardrails",
"certification": "Docs Study",
"title": "Guardrails AI โ€” LLM ์ถœ๋ ฅ ๊ฒ€์ฆ",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-lab-14"],
"summary": "Guardrails AI๋Š” LLM ์ž…๋ ฅ/์ถœ๋ ฅ์— ๊ฒ€์ฆ ๊ทœ์น™(Guard)์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์œ ํ•ด ์ฝ˜ํ…์ธ  ์ฐจ๋‹จ, JSON ์Šคํ‚ค๋งˆ ๊ฐ•์ œ, PII ๋งˆ์Šคํ‚น ๋“ฑ ํ”„๋กœ๋•์…˜ LLM ์•ฑ์˜ ์•ˆ์ „์žฅ์น˜ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.",
"example": "from guardrails import Guard\nfrom guardrails.hub import ToxicLanguage\n\nguard = Guard().use(ToxicLanguage(threshold=0.5, on_fail='exception'))\n\nresult = guard(\n llm_api=openai.chat.completions.create,\n prompt='์‚ฌ์šฉ์ž ์ž…๋ ฅ: {{user_input}}',\n user_input=user_message\n)",
"common_mistake": "Guard๋Š” LLM ํ˜ธ์ถœ์„ ๋ž˜ํ•‘ํ•˜๋ฏ€๋กœ ์‘๋‹ต ์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์š”์ฒญ์— heavy Guard๋ฅผ ๊ฑธ๋ฉด ์ง€์—ฐ์ด ์ปค์ง€๋‹ˆ, ์ž…๋ ฅ ๊ฒ€์ฆ๊ณผ ์ถœ๋ ฅ ๊ฒ€์ฆ์„ ๋ถ„๋ฆฌํ•ด ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์ ์šฉํ•˜์„ธ์š”.",
"keywords": ["guardrails", "guard", "validation", "toxic", "PII", "JSON schema", "on_fail", "safety"],
"source_id": "official-docs-guardrails",
"details": [
"**Guard ํ—ˆ๋ธŒ**: `guardrails hub install hub://...`์œผ๋กœ ์ปค๋ฎค๋‹ˆํ‹ฐ Guard๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. ToxicLanguage, ValidJson, DetectPII, RegexMatch ๋“ฑ ์ˆ˜์‹ญ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**on_fail ๋™์ž‘**: `'exception'`(์˜ˆ์™ธ ๋ฐœ์ƒ), `'reask'`(LLM์— ์žฌ์š”์ฒญ), `'fix'`(์ž๋™ ์ˆ˜์ •), `'noop'`(ํ†ต๊ณผ) ์ค‘ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.",
"**๊ตฌ์กฐํ™” ์ถœ๋ ฅ**: `Guard.from_pydantic(OutputModel)`์œผ๋กœ LLM์ด ํ•ญ์ƒ ์ง€์ •ํ•œ ์Šคํ‚ค๋งˆ์˜ JSON์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
]
},
{
"id": "docs-gradio",
"certification": "Docs Study",
"title": "Gradio โ€” ML ๋ฐ๋ชจ UI",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-lab-22"],
"summary": "Gradio๋Š” ML ๋ชจ๋ธ ๋ฐ๋ชจ๋ฅผ ๋ช‡ ์ค„๋กœ ๋งŒ๋“œ๋Š” UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. `gr.Interface`๋กœ ํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ธ๋ฉด ์ฆ‰์‹œ ์›น UI๊ฐ€ ์ƒ๊ธฐ๊ณ , HuggingFace Spaces์™€ ๋„ค์ดํ‹ฐ๋ธŒ ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค.",
"example": "import gradio as gr\n\ndef chat(message, history):\n response = chain.invoke(message)\n return response\n\ndemo = gr.ChatInterface(\n fn=chat,\n title='RAG ์ฑ—๋ด‡',\n examples=['๋ฌธ์„œ ์š”์•ฝํ•ด์ค˜', '์ฃผ์š” ํ‚ค์›Œ๋“œ๋Š”?']\n)\ndemo.launch()",
"common_mistake": "Streamlit๊ณผ ๋‹ฌ๋ฆฌ Gradio๋Š” ํ•จ์ˆ˜ ๋‹จ์œ„๋กœ UI๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋Š” `gr.State()`๋กœ, ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉํ•  ๋•Œ๋Š” `gr.Blocks()` ์ปจํ…์ŠคํŠธ๋ฅผ ์”๋‹ˆ๋‹ค.",
"keywords": ["gradio", "Interface", "Blocks", "ChatInterface", "State", "HuggingFace", "demo"],
"source_id": "official-docs-gradio",
"details": [
"**Interface vs Blocks**: `gr.Interface`๋Š” ์ž…๋ ฅโ†’ํ•จ์ˆ˜โ†’์ถœ๋ ฅ ๋‹จ์ˆœ ๊ตฌ์กฐ, `gr.Blocks`๋Š” ๋ ˆ์ด์•„์›ƒ๊ณผ ์ด๋ฒคํŠธ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑ. LLM ์ฑ—๋ด‡์—๋Š” `gr.ChatInterface`๊ฐ€ ๊ฐ€์žฅ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.",
"**์ŠคํŠธ๋ฆฌ๋ฐ**: `gr.ChatInterface(fn=chat)`์—์„œ `chat`์„ generator ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๊ณ  `yield`๋กœ ํ† ํฐ์„ ๋‚ด๋ณด๋‚ด๋ฉด ์ž๋™์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ UI๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.",
"**HF Spaces ํ†ตํ•ฉ**: `demo.launch()`๋งŒ ํ•˜๋ฉด HF Spaces Gradio SDK๋กœ ์ฆ‰์‹œ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ๋ณ„๋„ ์„œ๋ฒ„ ์„ค์ • ์—†์ด `sdk: gradio`๋งŒ README์— ์„ ์–ธํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-chroma",
"certification": "Docs Study",
"title": "Chroma โ€” ์˜์† ๋ฒกํ„ฐ DB",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-chroma-1", "docs-chroma-2"],
"summary": "Chroma๋Š” ์žฌ์‹œ์ž‘ํ•ด๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋˜๋Š” ์˜คํ”ˆ์†Œ์Šค ๋ฒกํ„ฐ DB์ž…๋‹ˆ๋‹ค. FAISS๋Š” `save_local()`์„ ์ง์ ‘ ํ˜ธ์ถœํ•ด์•ผ ํ•˜์ง€๋งŒ, Chroma๋Š” `persist_directory`๋งŒ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋กœ์ปฌ DB์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_chroma import Chroma\n\n# ์ƒ์„ฑ & ์ž๋™ ์ €์žฅ\ndb = Chroma.from_documents(\n chunks,\n embeddings,\n persist_directory='./chroma_db'\n)\n\n# ๋‹ค์Œ ์‹คํ–‰ ์‹œ โ€” ์ž„๋ฒ ๋”ฉ ์žฌ๊ณ„์‚ฐ ์—†์ด ๋กœ๋“œ\ndb = Chroma(\n persist_directory='./chroma_db',\n embedding_function=embeddings\n)",
"common_mistake": "FAISS์ฒ˜๋Ÿผ `save_local()`์„ ๋ณ„๋„๋กœ ํ˜ธ์ถœํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. `persist_directory`๋ฅผ ์ง€์ •ํ•˜๋ฉด ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๋‹จ, `persist_directory`๋ฅผ ์ƒ๋žตํ•˜๋ฉด ์ธ๋ฉ”๋ชจ๋ฆฌ๋กœ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["Chroma", "persist_directory", "from_documents", "collection", "chromadb", "์˜์†"],
"source_id": "official-docs-chromadb",
"details": [
"**FAISS vs Chroma**: FAISS๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜์œผ๋กœ `save_local()`๋กœ ์ˆ˜๋™ ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Chroma๋Š” `persist_directory`๋งŒ ์ง€์ •ํ•˜๋ฉด SQLite ๊ธฐ๋ฐ˜ ๋กœ์ปฌ DB์— ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.",
"**์ปฌ๋ ‰์…˜**: Chroma๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ปฌ๋ ‰์…˜ ๋‹จ์œ„๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. `collection_name` ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์—ฌ๋Ÿฌ ๋ฌธ์„œ ์„ธํŠธ๋ฅผ ๋ถ„๋ฆฌํ•ด ์ €์žฅํ•˜๊ณ  ๊ฐœ๋ณ„๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง**: `db.similarity_search(query, filter={\"source\": \"file.pdf\"})`์ฒ˜๋Ÿผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์กฐ๊ฑด์œผ๋กœ ๊ฒ€์ƒ‰ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. FAISS๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค."
]
},
{
"id": "docs-sentence-transformers",
"certification": "Docs Study",
"title": "sentence-transformers โ€” ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-embed-1"],
"summary": "sentence-transformers๋Š” HuggingFace ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. OpenAI API ์—†์ด ์™„์ „ ๋กœ์ปฌ RAG๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ํ•œ๊ตญ์–ด ํŠนํ™” ๋ชจ๋ธ๋„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"example": "from langchain_huggingface import HuggingFaceEmbeddings\n\nembeddings = HuggingFaceEmbeddings(\n model_name='sentence-transformers/all-MiniLM-L6-v2'\n)\n\n# ์ž„๋ฒ ๋”ฉ ๋ฒกํ„ฐ ์ƒ์„ฑ\nvector = embeddings.embed_query('ํŒŒ์ด์ฌ์ด ๋ญ์•ผ?')\nprint(len(vector)) # 384์ฐจ์›\n\n# Chroma์™€ ํ•จ๊ป˜\ndb = Chroma.from_documents(chunks, embeddings, persist_directory='./db')",
"common_mistake": "์ฒซ ์‹คํ–‰ ์‹œ ๋ชจ๋ธ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•˜๋ฏ€๋กœ ๋А๋ฆฝ๋‹ˆ๋‹ค. Streamlit ์•ฑ์—์„œ๋Š” `@st.cache_resource`๋กœ ๊ฐ์‹ธ ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œํ•˜์„ธ์š”. ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ์ค‘๊ฐ„์— ๋ฐ”๊พธ๋ฉด ๊ธฐ์กด VectorStore์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š์•„ ์ „์ฒด ์žฌ์ธ๋ฑ์‹ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["HuggingFaceEmbeddings", "sentence-transformers", "all-MiniLM-L6-v2", "embed_query", "๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ", "384์ฐจ์›"],
"source_id": "official-docs-huggingface",
"details": [
"**๋ชจ๋ธ ์„ ํƒ**: `all-MiniLM-L6-v2`๋Š” ๋น ๋ฅด๊ณ  ๊ฒฝ๋Ÿ‰(384์ฐจ์›), `all-mpnet-base-v2`๋Š” ํ’ˆ์งˆ์ด ๋” ๋†’์ง€๋งŒ ๋А๋ฆฝ๋‹ˆ๋‹ค. ํ•œ๊ตญ์–ด๊ฐ€ ๋งŽ๋‹ค๋ฉด `jhgan/ko-sroberta-multitask` ๊ฐ™์€ ํ•œ๊ตญ์–ด ํŠนํ™” ๋ชจ๋ธ์„ ์”๋‹ˆ๋‹ค.",
"**OpenAI vs ๋กœ์ปฌ**: OpenAI `text-embedding-3-small`์€ ํ’ˆ์งˆ์ด ๋†’์ง€๋งŒ API ๋น„์šฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ์ž„๋ฒ ๋”ฉ์€ ๋ฌด๋ฃŒ์ด๋‚˜ GPU ์—†์ด๋Š” ๋Œ€์šฉ๋Ÿ‰์—์„œ ๋А๋ฆฝ๋‹ˆ๋‹ค.",
"**์ฐจ์› ๊ณ ์ • ์ฃผ์˜**: ํ•œ ๋ฒˆ ์ž„๋ฒ ๋”ฉํ•œ ๋ชจ๋ธ์„ ๋ฐ”๊พธ๋ฉด ๊ธฐ์กด ์ธ๋ฑ์Šค์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒˆ ๋ชจ๋ธ๋กœ ์ „์ฒด ์ธ๋ฑ์Šค๋ฅผ ์žฌ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-ragas",
"certification": "Docs Study",
"title": "Ragas โ€” RAG ํ’ˆ์งˆ ์ž๋™ ํ‰๊ฐ€",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-ragas-1", "docs-ragas-2"],
"summary": "Ragas๋Š” RAG ์‹œ์Šคํ…œ์˜ ํ’ˆ์งˆ์„ ์ž๋™์œผ๋กœ ์ˆ˜์น˜ํ™”ํ•ฉ๋‹ˆ๋‹ค. Faithfulness(ํ™˜๊ฐ ์—†๋Š”๊ฐ€), Answer Relevancy(์งˆ๋ฌธ๊ณผ ๋งž๋Š” ๋‹ต์ธ๊ฐ€), Context Precision(๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ž˜ ์ฐพ์•˜๋Š”๊ฐ€)์„ LLM-as-judge๋กœ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.",
"example": "from ragas import evaluate\nfrom ragas.metrics import faithfulness, answer_relevancy, context_precision\nfrom datasets import Dataset\n\ndata = {\n 'question': ['LangChain์ด ๋ญ์•ผ?'],\n 'answer': ['LangChain์€ LLM ์•ฑ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.'],\n 'contexts': [['LangChain์€ LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์„ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.']],\n 'ground_truth': ['LangChain์€ LLM ์•ฑ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.']\n}\n\ndataset = Dataset.from_dict(data)\nresult = evaluate(dataset, metrics=[faithfulness, answer_relevancy, context_precision])\nprint(result)",
"common_mistake": "Ragas๋Š” ํ‰๊ฐ€์— LLM์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์œผ๋กœ OpenAI GPT๋ฅผ ์”๋‹ˆ๋‹ค. `evaluate(..., llm=your_llm, embeddings=your_embeddings)`๋กœ ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"keywords": ["ragas", "evaluate", "faithfulness", "answer_relevancy", "context_precision", "LLM-as-judge", "Dataset"],
"source_id": "official-docs-ragas",
"details": [
"**3๊ฐ€์ง€ ํ•ต์‹ฌ ๋ฉ”ํŠธ๋ฆญ**: Faithfulness(๋‹ต๋ณ€์ด ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ์— ๊ทผ๊ฑฐํ•˜๋Š”๊ฐ€ โ€” ํ™˜๊ฐ ํƒ์ง€), Answer Relevancy(๋‹ต๋ณ€์ด ์งˆ๋ฌธ๊ณผ ๊ด€๋ จ ์žˆ๋Š”๊ฐ€), Context Precision(๊ด€๋ จ ๋ฌธ์„œ๊ฐ€ ์ƒ์œ„ k๊ฐœ ์•ˆ์— ์žˆ๋Š”๊ฐ€).",
"**MLflow ์—ฐ๋™**: `result.to_pandas()`๋กœ DataFrame ๋ณ€ํ™˜ ํ›„ `mlflow.log_metrics()`๋กœ ๊ธฐ๋กํ•˜๋ฉด chunk_size, k ํŒŒ๋ผ๋ฏธํ„ฐ๋ณ„ ํ’ˆ์งˆ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**ground_truth ์—†์ด๋„ ๊ฐ€๋Šฅ**: `ground_truth`๊ฐ€ ์—†์œผ๋ฉด faithfulness, answer_relevancy๋งŒ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. Answer Correctness ๋ฉ”ํŠธ๋ฆญ์€ ground_truth๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-summarization",
"certification": "Docs Study",
"title": "LangChain ์š”์•ฝ ์ฒด์ธ โ€” map_reduce & stuff",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-news-1", "docs-news-2"],
"summary": "LangChain์˜ `load_summarize_chain`์€ ๊ธด ๋ฌธ์„œ๋ฅผ LLM ์ปจํ…์ŠคํŠธ ํ•œ๊ณ„ ๋‚ด์—์„œ ์š”์•ฝํ•˜๋Š” ์ฒด์ธ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฌธ์„œ๊ฐ€ ์งง์œผ๋ฉด `stuff`, ๊ธธ๋ฉด `map_reduce` ๋ฐฉ์‹์„ ์”๋‹ˆ๋‹ค.",
"example": "from langchain.chains.summarize import load_summarize_chain\nfrom langchain_core.documents import Document\n\n# map_reduce: ๊ฐ ์ฒญํฌ ์š”์•ฝ โ†’ ์ตœ์ข… ์š”์•ฝ\nchain = load_summarize_chain(llm, chain_type='map_reduce')\n\ndocs = [Document(page_content=article) for article in articles]\nresult = chain.invoke(docs)\nprint(result['output_text'])",
"common_mistake": "`stuff` ๋ฐฉ์‹์€ ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ํ•˜๋‚˜์˜ ํ”„๋กฌํ”„ํŠธ์— ๋„ฃ์Šต๋‹ˆ๋‹ค. ๋ฌธ์„œ๊ฐ€ LLM ์ปจํ…์ŠคํŠธ ์ฐฝ๋ณด๋‹ค ํฌ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ฌธ์„œ๋‚˜ ๊ธด ๋ฌธ์„œ๋Š” ํ•ญ์ƒ `map_reduce`๋ฅผ ๋จผ์ € ๊ณ ๋ คํ•˜์„ธ์š”.",
"keywords": ["load_summarize_chain", "map_reduce", "stuff", "refine", "output_text", "์š”์•ฝ"],
"source_id": "official-docs-langchain",
"details": [
"**chain_type ๋น„๊ต**: `stuff`(์ „์ฒด ํ•œ ๋ฒˆ), `map_reduce`(์ฒญํฌ๋ณ„ ์š”์•ฝ ํ›„ ํ•ฉ์นจ), `refine`(์ด์ „ ์š”์•ฝ์„ ๋‹ค์Œ ์ฒญํฌ์— ๋ฐ˜์˜). ํ’ˆ์งˆ์€ refine > map_reduce > stuff์ด์ง€๋งŒ ์†๋„๋Š” ๋ฐ˜๋Œ€์ž…๋‹ˆ๋‹ค.",
"**map_prompt / combine_prompt**: `load_summarize_chain`์— `map_prompt`์™€ `combine_prompt`๋ฅผ ์ง€์ •ํ•ด ์š”์•ฝ ์ง€์‹œ๋ฌธ์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**๋น„๋™๊ธฐ**: `chain.ainvoke(docs)`๋กœ ๋น„๋™๊ธฐ ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋‰ด์Šค ๊ธฐ์‚ฌ๋ฅผ ๋ณ‘๋ ฌ ์š”์•ฝํ•  ๋•Œ `asyncio.gather`์™€ ํ•จ๊ป˜ ์“ฐ๋ฉด ์†๋„๊ฐ€ ํฌ๊ฒŒ ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-sql-chain",
"certification": "Docs Study",
"title": "LangChain Text-to-SQL โ€” ์ž์—ฐ์–ด๋กœ DB ์กฐํšŒ",
"level": "์ค‘๊ธ‰",
"related_practices": ["docs-sql-1", "docs-sql-2"],
"summary": "LangChain์˜ `create_sql_query_chain`์€ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ SQL ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. `SQLDatabase`๋กœ DB ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™์œผ๋กœ ์ฝ์–ด LLM์—๊ฒŒ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_community.utilities import SQLDatabase\nfrom langchain.chains import create_sql_query_chain\n\ndb = SQLDatabase.from_uri('sqlite:///./mydb.db')\nchain = create_sql_query_chain(llm, db)\n\nsql = chain.invoke({'question': '๊ฐ€์žฅ ๋งŽ์ด ํŒ”๋ฆฐ ์ƒํ’ˆ 5๊ฐœ๋Š”?'})\nprint(sql) # SELECT product, SUM(quantity) ...\n\nresult = db.run(sql)\nprint(result)",
"common_mistake": "LLM์ด ์ƒ์„ฑํ•œ SQL์„ ๋ฐ”๋กœ `db.run()`์— ๋„˜๊ธฐ๋ฉด ์ž˜๋ชป๋œ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. Guardrails์˜ `ValidSQL`์ด๋‚˜ ๋ณ„๋„ ํŒŒ์‹ฑ์œผ๋กœ ๊ฒ€์ฆํ•œ ๋’ค ์‹คํ–‰ํ•˜์„ธ์š”.",
"keywords": ["SQLDatabase", "create_sql_query_chain", "from_uri", "db.run", "Text-to-SQL", "sqlite"],
"source_id": "official-docs-langchain",
"details": [
"**์Šคํ‚ค๋งˆ ์ž๋™ ์ฃผ์ž…**: `SQLDatabase`๋Š” ์—ฐ๊ฒฐ๋œ DB์˜ ํ…Œ์ด๋ธ”๋ช…๊ณผ ์ปฌ๋Ÿผ ์ •๋ณด๋ฅผ ์ž๋™์œผ๋กœ ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. LLM์ด ์กด์žฌํ•˜๋Š” ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ๋งŒ ์ฐธ์กฐํ•˜๋„๋ก ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.",
"**QuerySQLDatabaseTool**: SQL ์ƒ์„ฑ๊ณผ ์‹คํ–‰์„ ํ•˜๋‚˜์˜ ํˆด๋กœ ๋ฌถ์€ ๊ณ ์ˆ˜์ค€ ์ธํ„ฐํŽ˜์ด์Šค. Agent์— ๋“ฑ๋กํ•˜๋ฉด LLM์ด ํ•„์š”ํ•  ๋•Œ DB๋ฅผ ์ง์ ‘ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**๋ณด์•ˆ ์ฃผ์˜**: `db.run()`์€ ์‹ค์ œ DB๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ๊ธฐ ์ „์šฉ ์‚ฌ์šฉ์ž ๊ณ„์ •์œผ๋กœ ์—ฐ๊ฒฐํ•˜๊ฑฐ๋‚˜ SELECT๋งŒ ํ—ˆ์šฉํ•˜๋Š” wrapper๋ฅผ ๋‘๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-langgraph",
"certification": "Docs Study",
"title": "LangGraph โ€” ์ƒํƒœ ๊ธฐ๋ฐ˜ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-graph-1", "docs-graph-2"],
"summary": "LangGraph๋Š” LLM ์—์ด์ „ํŠธ๋ฅผ ๊ทธ๋ž˜ํ”„(๋…ธ๋“œ + ์—ฃ์ง€)๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœ ์ฒด์ธ์˜ Aโ†’Bโ†’C์™€ ๋‹ฌ๋ฆฌ ์กฐ๊ฑด ๋ถ„๊ธฐ, ๋ฃจํ”„, ๋ณ‘๋ ฌ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ด ์‹ค์ œ ์—์ด์ „ํŠธ ํ–‰๋™์„ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langgraph.graph import StateGraph, END\nfrom typing import TypedDict\n\nclass State(TypedDict):\n question: str\n answer: str\n\ndef retrieve(state: State) -> State:\n return {\"answer\": str(retriever.invoke(state[\"question\"]))}\n\ndef generate(state: State) -> State:\n return {\"answer\": chain.invoke(state)}\n\ngraph = StateGraph(State)\ngraph.add_node(\"retrieve\", retrieve)\ngraph.add_node(\"generate\", generate)\ngraph.set_entry_point(\"retrieve\")\ngraph.add_edge(\"retrieve\", \"generate\")\ngraph.add_edge(\"generate\", END)\n\napp = graph.compile()\nresult = app.invoke({\"question\": \"LangChain์ด ๋ญ์•ผ?\"})",
"common_mistake": "LangGraph๋Š” LangChain ์ฒด์ธ์ด ์•„๋‹™๋‹ˆ๋‹ค. `graph.compile()` ํ›„ `app.invoke()`๋ฅผ ์”๋‹ˆ๋‹ค. State TypedDict๋ฅผ ์ •ํ™•ํžˆ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด ๋…ธ๋“œ์—์„œ ํ‚ค ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["LangGraph", "StateGraph", "State", "node", "edge", "compile", "conditional_edges", "END"],
"source_id": "official-docs-langgraph",
"details": [
"**๋…ธ๋“œ vs ์ฒด์ธ**: ์ฒด์ธ์€ Aโ†’Bโ†’C ์„ ํ˜•. ๊ทธ๋ž˜ํ”„๋Š” A์—์„œ ์กฐ๊ฑด์— ๋”ฐ๋ผ B ๋˜๋Š” C๋กœ ๋ถ„๊ธฐ, D์—์„œ A๋กœ ๋ฃจํ”„ ๋“ฑ ๋ณต์žกํ•œ ํ๋ฆ„์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**conditional_edges**: `graph.add_conditional_edges('judge', router_fn, {'retry': 'retrieve', 'done': END})`์ฒ˜๋Ÿผ ๋…ธ๋“œ ๋ฐ˜ํ™˜๊ฐ’์— ๋”ฐ๋ผ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ReAct ์—์ด์ „ํŠธ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.",
"**checkpointer**: `MemorySaver`๋ฅผ `graph.compile(checkpointer=...)`์— ๋„˜๊ธฐ๋ฉด ์‹คํ–‰ ์ƒํƒœ๋ฅผ ์ €์žฅํ•ด ์ค‘๋‹จ ํ›„ ์žฌ๊ฐœ, ๋ฉ€ํ‹ฐํ„ด ๋Œ€ํ™” ์œ ์ง€๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-airflow",
"certification": "Docs Study",
"title": "Apache Airflow โ€” ์›Œํฌํ”Œ๋กœ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-lab-23"],
"summary": "Airflow๋Š” DAG(Directed Acyclic Graph)๋กœ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ •์˜ํ•˜๊ณ  ์Šค์ผ€์ค„๋งยท๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. ML ํŒŒ์ดํ”„๋ผ์ธ(๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ โ†’ ์ „์ฒ˜๋ฆฌ โ†’ ํ•™์Šต โ†’ ๋ฐฐํฌ)์ด๋‚˜ ๋ฌธ์„œ ์žฌ์ธ๋ฑ์‹ฑ ์ž๋™ํ™”์— ์”๋‹ˆ๋‹ค.",
"example": "from airflow import DAG\nfrom airflow.operators.python import PythonOperator\nfrom datetime import datetime\n\ndef reindex():\n # ๋ฌธ์„œ ๋กœ๋“œ โ†’ ์ž„๋ฒ ๋”ฉ โ†’ VectorStore ์—…๋ฐ์ดํŠธ\n pass\n\nwith DAG('daily_reindex', schedule='@daily', start_date=datetime(2024,1,1)) as dag:\n task = PythonOperator(task_id='reindex', python_callable=reindex)",
"common_mistake": "DAG ํŒŒ์ผ์€ Airflow ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ ํŒŒ์‹ฑํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์ตœ์ƒ์œ„์— ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ(DB ์—ฐ๊ฒฐ, ๋ชจ๋ธ ๋กœ๋“œ)์„ ๋‘๋ฉด ํŒŒ์‹ฑ๋งˆ๋‹ค ์‹คํ–‰๋ผ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋А๋ ค์ง‘๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ task ํ•จ์ˆ˜ ์•ˆ์— ๋„ฃ์œผ์„ธ์š”.",
"keywords": ["airflow", "DAG", "task", "operator", "schedule", "XCom", "pipeline", "orchestration"],
"source_id": "official-docs-airflow",
"details": [
"**Operator ์ข…๋ฅ˜**: `PythonOperator`(ํŒŒ์ด์ฌ ํ•จ์ˆ˜), `BashOperator`(์…ธ ๋ช…๋ น), `DockerOperator`(์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰) ๋“ฑ. ML ํŒŒ์ดํ”„๋ผ์ธ์—” ์ฃผ๋กœ PythonOperator๋ฅผ ์”๋‹ˆ๋‹ค.",
"**XCom**: task ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜. `ti.xcom_push(key, value)` / `ti.xcom_pull(task_ids, key)`๋กœ ์†Œ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰์€ S3/GCS ๊ฒฝ๋กœ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์„ ์”๋‹ˆ๋‹ค.",
"**์˜์กด์„ฑ**: `task_a >> task_b >> task_c` ๋˜๋Š” `task_a.set_downstream(task_b)` ๋กœ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณ‘๋ ฌ ์‹คํ–‰์€ `[task_b, task_c] >> task_d` ํ˜•ํƒœ๋กœ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-mlflow",
"certification": "Docs Study",
"title": "MLflow โ€” ์‹คํ—˜ ์ถ”์  & ๋ชจ๋ธ ๊ด€๋ฆฌ",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-lab-15"],
"summary": "MLflow๋Š” ML ์‹คํ—˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ, ๋ฉ”ํŠธ๋ฆญ, ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์ถ”์ ํ•˜๊ณ  ๋ชจ๋ธ์„ ๋ฒ„์ „ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. RAG์—์„œ chunk_size, k, ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋“ฑ ํŒŒ๋ผ๋ฏธํ„ฐ ์‹คํ—˜ ๊ฒฐ๊ณผ๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๋น„๊ตํ•  ๋•Œ ์”๋‹ˆ๋‹ค.",
"example": "import mlflow\n\nwith mlflow.start_run(run_name='rag-exp-1'):\n mlflow.log_param('chunk_size', 500)\n mlflow.log_param('k', 4)\n mlflow.log_metric('answer_relevance', 0.87)\n mlflow.log_metric('faithfulness', 0.92)\n mlflow.log_artifact('eval_results.json')",
"common_mistake": "run์„ `with mlflow.start_run()` ๋ธ”๋ก์œผ๋กœ ๊ฐ์‹ธ์ง€ ์•Š์œผ๋ฉด ์‹ค์ˆ˜๋กœ ์ด์ „ run์— ๋กœ๊น…๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ context manager๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.",
"keywords": ["mlflow", "experiment", "run", "log_param", "log_metric", "artifact", "model registry"],
"source_id": "official-docs-mlflow",
"details": [
"**UI**: `mlflow ui` ๋ช…๋ น์œผ๋กœ localhost:5000์—์„œ ์‹คํ—˜ ๋น„๊ต ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๋ณ„ ๋ฉ”ํŠธ๋ฆญ ์ถ”์ด๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"**Model Registry**: `mlflow.log_model()`๋กœ ๋ชจ๋ธ์„ ์ €์žฅํ•˜๊ณ  `Staging`โ†’`Production` ๋‹จ๊ณ„๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐํฌ ์‹œ `mlflow.pyfunc.load_model('models:/name/Production')`์œผ๋กœ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.",
"**LangChain ํ†ตํ•ฉ**: `mlflow.langchain.autolog()`๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ์ฒด์ธ ์‹คํ–‰๋งˆ๋‹ค ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋ฉ”ํŠธ๋ฆญ์ด ์ž๋™ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-mcp",
"certification": "Docs Study",
"title": "MCP โ€” Model Context Protocol",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-lab-24"],
"summary": "MCP(Model Context Protocol)๋Š” LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์™ธ๋ถ€ ๋„๊ตฌยท๋ฐ์ดํ„ฐ์†Œ์Šค์™€ ํ‘œ์ค€ํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” Anthropic ์˜คํ”ˆ ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. Claude Desktop์ด๋‚˜ Claude Code์— ๋กœ์ปฌ MCP ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด ํŒŒ์ผ ์‹œ์Šคํ…œ, DB, ์™ธ๋ถ€ API๋ฅผ LLM์ด ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"example": "# mcp_server.py (FastMCP ์‚ฌ์šฉ)\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP('my-server')\n\n@mcp.tool()\ndef search_docs(query: str) -> str:\n '''๋ฒกํ„ฐ DB์—์„œ ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.'''\n results = retriever.invoke(query)\n return '\\n'.join(r.page_content for r in results)\n\nmcp.run(transport='stdio')",
"common_mistake": "MCP ์„œ๋ฒ„๋Š” stdio ๋˜๋Š” SSE transport ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. Claude Desktop์€ stdio(๋กœ์ปฌ ํ”„๋กœ์„ธ์Šค), Claude Code๋Š” SSE(HTTP ์„œ๋ฒ„) ๋ฐฉ์‹์„ ์ฃผ๋กœ ์”๋‹ˆ๋‹ค. transport ๋ถˆ์ผ์น˜ ์‹œ ์—ฐ๊ฒฐ์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.",
"keywords": ["MCP", "Model Context Protocol", "tool", "resource", "FastMCP", "stdio", "Claude", "agent"],
"source_id": "official-docs-mcp",
"details": [
"**3๊ฐ€์ง€ ๊ธฐ๋ณธ ์š”์†Œ**: `tool`(LLM์ด ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜), `resource`(LLM์ด ์ฝ๋Š” ๋ฐ์ดํ„ฐ), `prompt`(์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ). ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋ฒ„๋Š” `tool`๋งŒ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.",
"**Claude Desktop ์—ฐ๊ฒฐ**: `~/Library/Application Support/Claude/claude_desktop_config.json`์— ์„œ๋ฒ„ ๊ฒฝ๋กœ์™€ ๋ช…๋ น์–ด๋ฅผ ๋“ฑ๋กํ•˜๋ฉด ๋Œ€ํ™” ์ค‘ ์ž๋™์œผ๋กœ ๋„๊ตฌ๊ฐ€ ๋ณด์ž…๋‹ˆ๋‹ค.",
"**FastMCP**: Anthropic ๊ณต์‹ Python SDK์˜ ๊ณ ์ˆ˜์ค€ ๋ž˜ํผ. `@mcp.tool()` ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ํ•จ์ˆ˜๋ฅผ ๋„๊ตฌ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์Šคํ‚ค๋งˆ ์ƒ์„ฑ, ์ž…์ถœ๋ ฅ ๊ฒ€์ฆ์ด ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-litellm",
"certification": "Docs Study",
"title": "LiteLLM โ€” ์—ฌ๋Ÿฌ LLM์„ ํ•˜๋‚˜์˜ ์ธํ„ฐํŽ˜์ด์Šค๋กœ",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-litellm-1"],
"summary": "LiteLLM์€ OpenAI, Anthropic, Ollama, Gemini ๋“ฑ 100๊ฐœ ์ด์ƒ์˜ LLM ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๋™์ผํ•œ API ํ˜•์‹์œผ๋กœ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ์„ ๋ฐ”๊ฟ€ ๋•Œ ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ๋ชจ๋ธ๋ช…๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.",
"example": "from litellm import completion\n\n# Ollama ๋กœ์ปฌ\nresponse = completion(\n model='ollama/llama3.2',\n messages=[{'role': 'user', 'content': '์•ˆ๋…•'}]\n)\n\n# OpenAI โ€” ์ฝ”๋“œ ๋™์ผ, ๋ชจ๋ธ๋ช…๋งŒ ๋ณ€๊ฒฝ\nresponse = completion(\n model='gpt-4o-mini',\n messages=[{'role': 'user', 'content': '์•ˆ๋…•'}]\n)\n\nprint(response.choices[0].message.content)",
"common_mistake": "๋ชจ๋ธ๋ช… ํ˜•์‹์€ `'ํ”„๋กœ๋ฐ”์ด๋”/๋ชจ๋ธ๋ช…'`์ž…๋‹ˆ๋‹ค. Ollama๋Š” `'ollama/llama3.2'`, Anthropic์€ `'anthropic/claude-3-5-sonnet-20241022'`. OpenAI๋งŒ ์˜ˆ์™ธ๋กœ ์ ‘๋‘์‚ฌ ์—†์ด `'gpt-4o-mini'`์ฒ˜๋Ÿผ ์”๋‹ˆ๋‹ค.",
"keywords": ["LiteLLM", "completion", "model", "provider", "๋ฒค๋” ๋…๋ฆฝ", "OpenAI ํ˜ธํ™˜", "ChatLiteLLM"],
"source_id": "official-docs-litellm",
"details": [
"**๋น„์šฉ ์ถ”์ **: `litellm.success_callback`์œผ๋กœ ๊ฐ ํ˜ธ์ถœ์˜ ํ† ํฐ ์ˆ˜์™€ ์˜ˆ์ƒ ๋น„์šฉ์„ ์ž๋™ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ชจ๋ธ์„ ์‹คํ—˜ํ•  ๋•Œ ๋น„์šฉ ๋น„๊ต์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
"**LangChain ํ†ตํ•ฉ**: `from langchain_community.chat_models import ChatLiteLLM`์œผ๋กœ LangChain ์ฒด์ธ์—์„œ LiteLLM์„ ์”๋‹ˆ๋‹ค. LCEL ํŒŒ์ดํ”„๋ผ์ธ์— ๋ฐ”๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.",
"**Fallback**: `completion(..., fallbacks=['gpt-4o-mini', 'ollama/llama3.2'])`์ฒ˜๋Ÿผ ์ฃผ ๋ชจ๋ธ ์‹คํŒจ ์‹œ ์ž๋™์œผ๋กœ ๋ฐฑ์—… ๋ชจ๋ธ๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-chainlit",
"certification": "Docs Study",
"title": "Chainlit โ€” ์ฑ—๋ด‡ UI ํŠนํ™” ํ”„๋ ˆ์ž„์›Œํฌ",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-chainlit-1"],
"summary": "Chainlit์€ LLM ์ฑ—๋ด‡ UI์— ํŠนํ™”๋œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. Streamlit๊ณผ ๋‹ฌ๋ฆฌ ์›น์†Œ์ผ“ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•ด ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต, ํŒŒ์ผ ์—…๋กœ๋“œ, ๋ฉ€ํ‹ฐ์Šคํ… ์ง„ํ–‰ ํ‘œ์‹œ๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.",
"example": "import chainlit as cl\n\n@cl.on_message\nasync def main(message: cl.Message):\n # ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต\n msg = cl.Message(content='')\n async for chunk in chain.astream({'question': message.content}):\n await msg.stream_token(chunk)\n await msg.send()\n\n# ์‹คํ–‰: chainlit run app.py",
"common_mistake": "Chainlit์€ `async` ๊ธฐ๋ฐ˜์ž…๋‹ˆ๋‹ค. LangChain์˜ `chain.invoke()`(๋™๊ธฐ)๋ฅผ ์“ฐ๋ฉด ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ `chain.ainvoke()` ๋˜๋Š” `chain.astream()`์„ ์‚ฌ์šฉํ•˜์„ธ์š”.",
"keywords": ["chainlit", "on_message", "Message", "stream_token", "async", "astream", "on_chat_start"],
"source_id": "official-docs-chainlit",
"details": [
"**๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋ฐ˜**: `@cl.on_chat_start`(์„ธ์…˜ ์‹œ์ž‘ ์‹œ ์ฒด์ธ ์ดˆ๊ธฐํ™”), `@cl.on_message`(๋ฉ”์‹œ์ง€ ์ˆ˜์‹ ), `@cl.on_stop`(์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘๋‹จ)์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
"**Step UI**: `async with cl.Step(name='๊ฒ€์ƒ‰ ์ค‘...'):` ๋ธ”๋ก์œผ๋กœ ์—์ด์ „ํŠธ์˜ ๊ฐ ๋‹จ๊ณ„(๊ฒ€์ƒ‰, ์ถ”๋ก  ๋“ฑ)๋ฅผ UI์— ์‹ค์‹œ๊ฐ„์œผ๋กœ ํŽผ์ณ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. LangGraph ์—์ด์ „ํŠธ์™€ ํ•จ๊ป˜ ์“ฐ๋ฉด ๊ฐ•๋ ฅํ•ฉ๋‹ˆ๋‹ค.",
"**ํŒŒ์ผ ์—…๋กœ๋“œ**: `@cl.on_message`์—์„œ `message.elements`๋กœ ์ฒจ๋ถ€ ํŒŒ์ผ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. PDF ์—…๋กœ๋“œ โ†’ ์ฆ‰์‹œ RAG ์ธ๋ฑ์‹ฑ ํ๋ฆ„์„ ๋ช‡ ์ค„๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค."
]
},
{
"id": "docs-redis",
"certification": "Docs Study",
"title": "Redis โ€” ๋Œ€ํ™” ๋ฉ”๋ชจ๋ฆฌ ์˜์†ํ™” & LLM ์บ์‹ฑ",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-redis-1"],
"summary": "Redis๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋กœ LLM ์•ฑ์—์„œ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ์˜์†ํ™”์™€ LLM ์‘๋‹ต ์บ์‹ฑ์— ์”๋‹ˆ๋‹ค. ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•ด๋„ ๋Œ€ํ™”๊ฐ€ ์œ ์ง€๋˜๊ณ , ๋™์ผ ์งˆ๋ฌธ์— ๋Œ€ํ•œ LLM ํ˜ธ์ถœ ๋น„์šฉ์„ ์ ˆ๊ฐํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_community.chat_message_histories import RedisChatMessageHistory\nfrom langchain_core.runnables.history import RunnableWithMessageHistory\n\ndef get_session_history(session_id: str):\n return RedisChatMessageHistory(\n session_id=session_id,\n url='redis://localhost:6379'\n )\n\nchain_with_history = RunnableWithMessageHistory(\n chain,\n get_session_history,\n input_messages_key='question',\n history_messages_key='chat_history'\n)",
"common_mistake": "Redis ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ์ค‘์ด์ง€ ์•Š์œผ๋ฉด ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ๊ฐœ๋ฐœ ์‹œ `docker run -d -p 6379:6379 redis`๋กœ ๋จผ์ € Redis๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”.",
"keywords": ["Redis", "RedisChatMessageHistory", "session_id", "url", "์˜์†ํ™”", "์บ์‹ฑ", "TTL"],
"source_id": "official-docs-redis",
"details": [
"**์ธ๋ฉ”๋ชจ๋ฆฌ ๊ต์ฒด**: ๊ธฐ์กด `ChatMessageHistory`(์žฌ์‹œ์ž‘ ์‹œ ์†Œ๋ฉธ)๋ฅผ `RedisChatMessageHistory`๋กœ ๊ต์ฒดํ•˜๋ฉด `get_session_history` ํ•จ์ˆ˜ ํ•œ ์ค„๋งŒ ๋ฐ”๊ฟ”๋„ ์˜์†์„ฑ์„ ์–ป์Šต๋‹ˆ๋‹ค.",
"**TTL**: `RedisChatMessageHistory(session_id, url, ttl=3600)`์œผ๋กœ 1์‹œ๊ฐ„ ํ›„ ์ž๋™ ๋งŒ๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋ฌดํ•œํžˆ ์Œ“์ด๋Š” ํžˆ์Šคํ† ๋ฆฌ์™€ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.",
"**LLM ์‘๋‹ต ์บ์‹ฑ**: `from langchain.cache import RedisCache; langchain.llm_cache = RedisCache(redis_client)`๋กœ ๋™์ผ ์งˆ๋ฌธ์— ๋Œ€ํ•œ LLM ์‘๋‹ต์„ ์บ์‹œํ•ด ๋น„์šฉ๊ณผ ์‘๋‹ต ์‹œ๊ฐ„์„ ์ค„์ž…๋‹ˆ๋‹ค."
]
},
{
"id": "docs-unstructured",
"certification": "Docs Study",
"title": "Unstructured โ€” PDFยทWordยท์ด๋ฏธ์ง€ ํŒŒ์‹ฑ",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-unstructured-1"],
"summary": "Unstructured๋Š” PDF, Word, Excel, PowerPoint, ์ด๋ฏธ์ง€ ๋“ฑ ๋น„์ •ํ˜• ๋ฌธ์„œ๋ฅผ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. LangChain์˜ `UnstructuredFileLoader`์™€ ํ†ตํ•ฉ๋˜์–ด ํ™•์žฅ์ž์— ์ƒ๊ด€์—†์ด ๋™์ผ ์ฝ”๋“œ๋กœ RAG ํŒŒ์ดํ”„๋ผ์ธ์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_community.document_loaders import UnstructuredFileLoader\n\n# ๋‹จ์ผ ํŒŒ์ผ โ€” ํ™•์žฅ์ž ์ž๋™ ๊ฐ์ง€\nloader = UnstructuredFileLoader('./report.pdf')\ndocs = loader.load()\n\n# ํ˜ผํ•ฉ ํ˜•์‹ ๋””๋ ‰ํ† ๋ฆฌ\nfrom langchain_community.document_loaders import DirectoryLoader\nloader = DirectoryLoader(\n './docs',\n glob='**/*',\n loader_cls=UnstructuredFileLoader\n)\ndocs = loader.load()",
"common_mistake": "`pip install unstructured`๋Š” ๊ธฐ๋ณธ ์„ค์น˜์ž…๋‹ˆ๋‹ค. PDF์—๋Š” `pip install unstructured[pdf]`, ์ด๋ฏธ์ง€ OCR์—๋Š” `pip install unstructured[local-inference]`์ฒ˜๋Ÿผ ๋ณ„๋„ extra๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["Unstructured", "UnstructuredFileLoader", "PDF", "Word", "OCR", "๋น„์ •ํ˜• ๋ฌธ์„œ", "elements"],
"source_id": "official-docs-unstructured",
"details": [
"**์ž๋™ ํŒŒ์ผ ๊ฐ์ง€**: ํ™•์žฅ์ž์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ์ ์ ˆํ•œ ํŒŒ์„œ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. `.pdf`, `.docx`, `.pptx`, `.jpg`, `.eml` ๋“ฑ์„ ๋™์ผ ์ฝ”๋“œ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.",
"**์š”์†Œ ๋ถ„๋ฅ˜**: `mode='elements'`๋กœ ๋กœ๋“œํ•˜๋ฉด Title, NarrativeText, Table, Image ๋“ฑ ์š”์†Œ ํƒ€์ž…์ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํ—ค๋”๋ฅผ ์ฒญํฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜ ํ…Œ์ด๋ธ”๋งŒ ๋”ฐ๋กœ ์ถ”์ถœํ•  ๋•Œ ์”๋‹ˆ๋‹ค.",
"**API vs ๋กœ์ปฌ**: Unstructured Cloud API๋Š” ๊ณ ํ’ˆ์งˆ OCR๊ณผ ๋ ˆ์ด์•„์›ƒ ๋ถ„์„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ์€ ๋ฌด๋ฃŒ์ด๋‚˜ ์ด๋ฏธ์ง€๋‚˜ ๋ณต์žกํ•œ ๋ ˆ์ด์•„์›ƒ์˜ PDF ํ’ˆ์งˆ์ด ๋‚ฎ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
]
},
{
"id": "docs-conversation-memory",
"certification": "Docs Study",
"title": "LangChain ๋Œ€ํ™” ๋ฉ”๋ชจ๋ฆฌ โ€” ๋ฉ€ํ‹ฐํ„ด ์ฑ—๋ด‡",
"level": "๊ณ ๊ธ‰",
"related_practices": ["docs-lab-6", "docs-brain-1"],
"summary": "RunnableWithMessageHistory๋Š” LangChain ์ฒด์ธ์— ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. ๋งค ์š”์ฒญ๋งˆ๋‹ค ์ด์ „ ๋Œ€ํ™”๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ session_id๋งŒ ๋„˜๊ธฐ๋ฉด ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.",
"example": "from langchain_community.chat_message_histories import ChatMessageHistory\nfrom langchain_core.runnables.history import RunnableWithMessageHistory\n\nstore = {} # session_id โ†’ ChatMessageHistory\n\ndef get_session_history(session_id: str):\n if session_id not in store:\n store[session_id] = ChatMessageHistory()\n return store[session_id]\n\nchain_with_history = RunnableWithMessageHistory(\n chain,\n get_session_history,\n input_messages_key=\"question\",\n history_messages_key=\"chat_history\"\n)\n\nchain_with_history.invoke(\n {\"question\": \"์•ˆ๋…•\"},\n config={\"configurable\": {\"session_id\": \"user-1\"}}\n)",
"common_mistake": "`session_id`๋ฅผ ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ๋™์ผํ•˜๊ฒŒ ๋„˜๊ธฐ๋ฉด ์ „์ฒด ์‚ฌ์šฉ์ž๊ฐ€ ํ•˜๋‚˜์˜ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋ณ„๋กœ ๊ณ ์œ ํ•œ session_id๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"keywords": ["RunnableWithMessageHistory", "ChatMessageHistory", "session_id", "chat_history", "๋ฉ€ํ‹ฐํ„ด"],
"source_id": "official-docs-langchain",
"details": [
"**ํžˆ์Šคํ† ๋ฆฌ ์ €์žฅ์†Œ**: ์ธ๋ฉ”๋ชจ๋ฆฌ(`ChatMessageHistory`)๋Š” ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ์‹œ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์—์„œ๋Š” `RedisChatMessageHistory`, `SQLChatMessageHistory` ๋“ฑ ์˜์† ์ €์žฅ์†Œ๋ฅผ ์”๋‹ˆ๋‹ค.",
"**ํžˆ์Šคํ† ๋ฆฌ ๊ธธ์ด ์ œํ•œ**: ๋Œ€ํ™”๊ฐ€ ๊ธธ์–ด์ง€๋ฉด ํ† ํฐ์ด ์ดˆ๊ณผ๋ฉ๋‹ˆ๋‹ค. `trim_messages()` ๋˜๋Š” ์ตœ๊ทผ N๊ฐœ ๋ฉ”์‹œ์ง€๋งŒ ์œ ์ง€ํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
"**ํ”„๋กฌํ”„ํŠธ ์—ฐ๋™**: `ChatPromptTemplate`์— `MessagesPlaceholder(variable_name=\"chat_history\")`๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ํ”„๋กฌํ”„ํŠธ์— ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค. history_messages_key์™€ placeholder ์ด๋ฆ„์ด ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."
]
}
]