Open-Source AI Cookbook documentation
๐ค ๋ฒกํฐ ๊ฒ์ ์์ด์ ํธ: ํ๊น ํ์ด์ค ํ๋ธ๋ฅผ ๋ฐฑ์๋๋ก ํ๋ ์ง๋ฅํ ๊ฒ์ ์์ง
๐ค ๋ฒกํฐ ๊ฒ์ ์์ด์ ํธ: ํ๊น ํ์ด์ค ํ๋ธ๋ฅผ ๋ฐฑ์๋๋ก ํ๋ ์ง๋ฅํ ๊ฒ์ ์์ง
์ฐธ์กฐ: Martin Elstner ์์ฑ์: ์์
๊ฒ์ ์์ง์ ํฌ๊ฒ ํค์๋ ๊ฒ์๊ณผ ๋ฒกํฐ ๊ฒ์์ผ๋ก ๋๋ ์ ์์ต๋๋ค. ํค์๋ ๊ฒ์๊ณผ ๋ฌ๋ฆฌ, ๋ฒกํฐ ๊ฒ์์ ์ฌ์ฉํ ๋๋ ๋ ๊ฐ์ง๋ฅผ ๊ณ ๋ คํด์ผ ํฉ๋๋ค.
- ์ ํฉํ ์๋ฒ ๋ฉ ๋ชจ๋ธ๋ก ๋ฐ์ดํฐ์ ๊ณผ ์ฟผ๋ฆฌ๋ฅผ ์๋ฒ ๋ฉํ๋ ์์
- ์๋ฒ ๋ฉ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ DB
ํ์ง๋ง ์๋ฒ ๋ฉ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ฒกํฐ ๊ฒ์๋ง์ผ๋ก๋ โ์ฌ์ฉ์๊ฐ ์ํ๋ ๋ต๋ณโ์ ๋ณด์ฅํ๊ธฐ ์ด๋ ต์ต๋๋ค.
๊ทธ๋์ ๊ฒ์ ๊ณผ์ ์์ ์์ด์ ํธ๊ฐ ์์จ์ ์ผ๋ก ํ๋จํ๊ณ ์ต์ ํํ๋ค๋ฉด, ์ฌ์ฉ์ ์๋์ ๋ ๊ฐ๊น์ด ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
Agentic ์ ๊ทผ ๋ฐฉ์์ ์ฐจ๋ณ์
๊ธฐ์กด ๋ฒกํฐ ๊ฒ์ ์ํฌํ๋ก์ฐ
๋ฐ์ดํฐ โก ๋ฐ์ดํฐ ์๋ฒ ๋ฉ(๊ณ ์ ๋ชจ๋ธ) โก ์ธ๋ฑ์ค ์์ฑ โก ์ฌ์ฉ์ ์ง์ โก ์ ์ฌ๋ ๊ฒ์ โก ๋ต๋ณ๋๋ํ Agentic ๋ฐฉ์
์ฌ์ฉ์ ์ง์ ๋ถ์(๊ฒ์ ์ ๋ต ์ธ์) โก ์ต์ ์๋ฒ ๋ฉ ๋ชจ๋ธ ์ ํ โก ๋ฐ์ดํฐ ์๋ฒ ๋ฉ โก ์ธ๋ฑ์ค ์์ฑ โก ์ ์ฌ๋ ๊ฒ์ โก ๊ฒ์๊ฒฐ๊ณผ ๋ฐํ์ผ๋ก ๋ต๋ณ ์ ์ DuckDB?
ํ๊น ํ์ด์ค์ ๋ฐ์ดํฐ์ ์ ํ์ผ(parquet) ํ์ผ์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋๋ฐ, ์ด๋ ๋น ๋ฅธ ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์คํ ์ธ DuckDB๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ํ์ผ๋ค๊ณผ ์ํธ์์ฉํ ์ ์์ต๋๋ค. ๋ํ DuckDB์ ๊ธฐ๋ฅ ์ค ํ๋๋ ๋ฒกํฐ ์ ์ฌ๋ ๊ฒ์์ผ๋ก, ์ธ๋ฑ์ค ์ ๋ฌด์ ๊ด๊ณ์์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ด๋ฒ ๋ ธํธ๋ถ์์๋ ๋จ์ผ Agent์ ์ฌ๋ฌ๊ฐ์ง ๋๊ตฌ๋ฅผ ์ฃผ์ด ์ํํ๋ ๊ฐ๋จํ Agentic ๋ฒกํฐ ๊ฒ์ ์์ง์ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
ํ์ํ ์์กด์ฑ์ ์ค์นํฉ๋๋ค :
# ๋ณธ ์์ ํ์ผ์ Python 3.10 ์ด์ ๋ฒ์ ์์๋ง ์คํํ ์ ์์ต๋๋ค.
!pip install -U smolagents datasets sentence-transformers duckdb openaiHuggingFace์ ์ถ๋ก API๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ๋ก๊ทธ์ธํฉ๋๋ค :
from huggingface_hub import notebook_login
notebook_login()from smolagents import tool
from datasets import Dataset
import os
# ๋๊ตฌ ์ฌ์ฉ์ ์ํด OPENAI ํค๋ฅผ ๋ฐ๊ธ ๋ฐ์์ผํฉ๋๋ค.
os.environ["OPENAI_API_KEY"] = "YOUR KEY"๋๊ตฌ ์ ์
์ ์ํ ๋๊ตฌ๋ ์๋์ ๊ฐ์ต๋๋ค.
- ์๋ฒ ๋ฉ ์์ฑ ๋๊ตฌ
- ์ธ๋ฑ์ค ์์ฑ ๋๊ตฌ
- ์ ์ฌ๋ ๊ฒ์ ๋๊ตฌ
- ๋ต๋ณ ์์ฑ ๋๊ตฌ
๋๊ตฌ1 : ์๋ฒ ๋ฉ ์์ฑ
์ผ๋ฐ์ ์ผ๋ก, ์๋ฒ ๋ฉ ์์ ์์๋ ์์ ๋ฐฐ์น ์ฌ์ด์ฆ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒญํนํ์ง๋ง ์ฌ๊ธฐ์๋ ๋จ์ํ ๋ฐ์ดํฐ์ ์ ์๋ฒ ๋ฉ์ผ๋ก ๋ณํํ๋ ๊ณผ์ ๋ง ์ํํ๊ฒ ์ต๋๋ค.
@tool
def create_embeddings(
dataset: Dataset,
model_id: str,
column_name: str,
) -> Dataset:
"""
์ฃผ์ด์ง ๋ฐ์ดํฐ์
์ ๋ํด ์๋ฒ ๋ฉ์ ์์ฑํฉ๋๋ค.
Args:
dataset: ์๋ฒ ๋ฉ์ ์์ฑํ ๋์ ๋ฐ์ดํฐ์
model_id: ์๋ฒ ๋ฉ์ ์ฌ์ฉํ ๋ชจ๋ธ
column_name: ์๋ฒ ๋ฉํ ์ด ์ด๋ฆ
Returns:
์๋ฒ ๋ฉ์ด ์ถ๊ฐ๋ ๋ฐ์ดํฐ์
"""
from sentence_transformers import SentenceTransformer
model = SentenceTransformer(model_id)
def embed_batch(batch):
embeddings = model.encode(batch[column_name], convert_to_numpy=True)
batch["embeddings"] = embeddings.tolist()
return batch
dataset = dataset.map(embed_batch, batched=True)
return datasetDuckDB๋ก ๋ฒกํฐ ๊ฒ์ ์ํํ๊ธฐ
duckdb๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ์
์์ ๋ฒกํฐ ๊ฒ์์ ์ํํ ์ ์์ต๋๋ค.
์ธ๋ฑ์ค๋ ์ฌ์ฉํ๊ฑฐ๋ ์ฌ์ฉํ์ง ์์ ์ ์์ต๋๋ค. ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ง ์๊ณ ๊ฒ์ํ๋ ๊ฒ์ ๋ ๋๋ฆฌ์ง๋ง ๋ ์ ํํ๊ณ , ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ฌ ๊ฒ์ํ๋ ๊ฒ์ ๋ ๋น ๋ฅด์ง๋ง ๋ ์ ํํฉ๋๋ค.
์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ง ์๊ณ ๊ฒ์ํ๊ธฐ
์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ง ์๊ณ ๊ฒ์ํ๋ ๊ฒ์ ๋๋ฆฐ ์์ ์ง๋ง ์ผ๋ฐ์ ์ผ๋ก ์ฝ 10๋ง ํ๊น์ง์ ์์ ๋ฐ์ดํฐ์ ์์๋ ์ถฉ๋ถํ ๋น ๋ฅด๊ฒ ๋์ํฉ๋๋ค. ํ์ง๋ง ์ด๋ฒ ๋ ธํธ๋ถ์์๋ DuckDB์ ์ธ๋ฑ์ค๋ฅผ ํ์ฉํด์ ๊ฒ์ํด๋ณด๊ฒ ์ต๋๋ค.
์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ฌ ๊ฒ์ํ๊ธฐ
์ด ์ ๊ทผ๋ฒ์ ๋ฐ์ดํฐ์ ์ ๋ก์ปฌ ๋ณต์ฌ๋ณธ์ ์์ฑํ๊ณ ์ด๋ฅผ ์ฌ์ฉํ์ฌ ์ธ๋ฑ์ค๋ฅผ ์์ฑํฉ๋๋ค. ์ฝ๊ฐ์ ์ค๋ฒํค๋๊ฐ ์์ง๋ง ํ๋ฒ ์ธ๋ฑ์ค๋ฅผ ์์ฑํ ํ์๋ ๊ฒ์ ์๋๊ฐ ๊ฐ์ ๋ ๊ฒ์ ๋๋ค.
๋๊ตฌ2 : DuckDB ์ธ๋ฑ์ค ๋ง๋ค๊ธฐ
@tool
def create_db_index(
dataset_with_embeddings: Dataset, # ์๋ฒ ๋ฉ์ด ํฌํจ๋ ๋ฐ์ดํฐ์
table_name: str,
embedding_column: str = "embeddings"
) -> None:
"""
์๋ฒ ๋ฉ์ด ํฌํจ๋ ๋ฐ์ดํฐ์
์ ๋ํด DuckDB ์ธ๋ฑ์ค๋ฅผ ์์ฑํฉ๋๋ค.
Args:
dataset_with_embeddings: ์ด๋ฏธ ์๋ฒ ๋ฉ์ด ํฌํจ๋ ๋ฐ์ดํฐ์
table_name: ์์ฑํ ํ
์ด๋ธ ์ด๋ฆ
embedding_column: ์๋ฒ ๋ฉ ์ด ์ด๋ฆ
Returns:
None
"""
import duckdb
# VSS ํ์ฅ ์ค์น ๋ฐ ๋ก๋
duckdb.sql("INSTALL vss; LOAD vss;")
duckdb.sql(f"DROP TABLE IF EXISTS {table_name};")
# ๋ฐ์ดํฐ์
์ pandas DataFrame์ผ๋ก ๋ณํ
df = dataset_with_embeddings.to_pandas()
# DuckDB์ DataFrame ๋ฑ๋ก
duckdb.register(f"{table_name}_temp", df)
# ๋ชจ๋ธ์์ ์๋ฒ ๋ฉ ์ฐจ์ ๊ฐ์ ธ์ค๊ธฐ
embedding_dim = len(df[embedding_column].iloc[0])
# embedding_dim = model.get_sentence_embedding_dimension()
# ํ
์ด๋ธ ์์ฑ (์๋ฒ ๋ฉ์ FLOAT ๋ฐฐ์ด๋ก ๋ณํ)
duckdb.sql(f"""
CREATE TABLE {table_name} AS
SELECT *, {embedding_column}::FLOAT[{embedding_dim}] AS {embedding_column}_float
FROM {table_name}_temp;
""")
# HNSW ์ธ๋ฑ์ค ์์ฑ
duckdb.sql(f"""
CREATE INDEX idx_{embedding_column} ON {table_name}
USING HNSW ({embedding_column}_float) WITH (metric = 'cosine');
""")
# ์์ ํ
์ด๋ธ ์ ๋ฆฌ
duckdb.sql(f"DROP VIEW IF EXISTS {table_name}_temp;")์ด ๋๊ตฌ๋ฅผ ํตํด ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ฒกํฐ ๊ฒ์์ ์ํํ ์ ์์ผ๋ฉฐ, ๊ฒฐ๊ณผ๋ ์ฆ์ ๋ฐํ๋ฉ๋๋ค.
๋๊ตฌ3 : ๋ฒกํฐ ๊ฒ์ ์ํํ๊ธฐ
@tool
def similarity_search_with_duckdb_index(
query: str,
table_name: str,
model_id: str,
k: int = 5,
embedding_column: str = "embeddings"
)-> dict:
"""
DuckDB ์ธ๋ฑ์ค๋ฅผ ์ด์ฉํด ๋ฒกํฐ ๊ฒ์์ ์ํํฉ๋๋ค.
Args:
query: ๊ฒ์ํ ์ฟผ๋ฆฌ ๋ฌธ์์ด
model_id: ์๋ฒ ๋ฉ์ ์ฌ์ฉํ ๋ชจ๋ธ
k: ๋ฐํํ ๊ฒฐ๊ณผ ์
table_name: ๊ฒ์ํ ํ
์ด๋ธ ์ด๋ฆ
embedding_column: ์๋ฒ ๋ฉ ์ปฌ๋ผ ์ด๋ฆ
Returns:
dict: ๊ฒ์ ๊ฒฐ๊ณผ
"""
from sentence_transformers import SentenceTransformer
import duckdb
model = SentenceTransformer(model_id)
embedding = model.encode(query).tolist()
return duckdb.sql(
query=f"""
SELECT *, array_cosine_distance({embedding_column}_float, {embedding}::FLOAT[{model.get_sentence_embedding_dimension()}]) as distance
FROM {table_name}
ORDER BY distance
LIMIT {k};
"""
).to_df()๋ฌด๊ฑฐ์ด ๋ฒกํฐ ๊ฒ์ ์์ง์ ๋ฐ๋ก ๋ฐฐํฌํ ํ์ ์๊ณ , ์ ์ฅ์๋ ํ๋ธ์์ ์ฒ๋ฆฌ๋ฉ๋๋ค.
๋๊ตฌ4 : ๋ต๋ณ ์์ฑ ๋๊ตฌ
์ ์ฌ๋ ๊ฒ์ ๊ฒฐ๊ณผ ์ฒญํฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก, LLM์ด ์ฌ์ฉ์๊ฐ ์ํ ๋งํ ๋ต๋ณ์ ์์ฑํฉ๋๋ค.
@tool
def generate_answer(chunks: list, query: str) -> str:
"""
์ฟผ๋ฆฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฃผ์ด์ง ํ
์คํธ ์ฒญํฌ ๋ชฉ๋ก์์ ๋ต๋ณ์ ์์ฑํฉ๋๋ค.
Args:
chunks: ๋ต๋ณ ์์ฑ์ ์ฌ์ฉํ ํ
์คํธ ์ฒญํฌ ๋ชฉ๋ก
query: ๋ต๋ณํ ์ฟผ๋ฆฌ ๋ฌธ์์ด
Returns:
str: ์์ฑ๋ ๋ต๋ณ
"""
import openai # OPENAI ํค ๋ฐ๊ธ์ด ํ์ํฉ๋๋ค.
context = "\n\n".join(chunks)
prompt = f"Context:\n{context}\n\nQuestion: {query}\nAnswer:"
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
return response["choices"][0]["message"]["content"]๋๊ตฌ๋ฅผ ์ ์ํ์์ผ๋, ๋ฐ์ดํฐ์ ์ ๋ก๋ํ๊ณ ์์ด์ ํธ๋ฅผ ๋์ํด๋ณด๊ฒ ์ต๋๋ค.
์ฌ์ฉํ ๋ฐ์ดํฐ์ ์ huggingface-KREW/KoCultre-Descriptions์ผ๋ก, ํ๊ตญ์ด ๋ฐ(meme)์ ๋ํ ๋ฐ์ดํฐ์ ๋๋ค.
from datasets import load_dataset
dataset = load_dataset("huggingface-KREW/KoCultre-Descriptions")์์ด์ ํธ๋ฅผ ์ ์ํฉ๋๋ค. ์ฌ์ฉํ ๋ชจ๋ธ์ Qwen/Qwen2.5-Coder-32B-Instruct ์ ๋๋ค.
from smolagents import CodeAgent, InferenceClientModel
model = InferenceClientModel(
"Qwen/Qwen2.5-Coder-32B-Instruct",
provider="together",
max_tokens=2048
)
tools = [
create_embeddings, # ์๋ฒ ๋ฉ ์์ฑ ๋๊ตฌ
create_db_index, # ์ธ๋ฑ์ค ์์ฑ ๋๊ตฌ
similarity_search_with_duckdb_index, # ์ ์ฌ๋ ๊ฒ์ ๋๊ตฌ
generate_answer # ๋ต๋ณ ์์ฑ ๋๊ตฌ
]
agent = CodeAgent(
model=model,
tools=tools,
verbosity_level=2,
max_steps=15
)์์ด์ ํธ๊ฐ ์์จ์ ์ผ๋ก ์๋ฒ ๋ฉ ๋ชจ๋ธ์ ํํ ์ ์๋๋ก ํ๋กฌํํธ๋ฅผ ๊ตฌ์ฑํ๊ฒ ์ต๋๋ค.
def agentic_prompt(query: str):
return f"""
๋น์ ์ ์ง๋ฅํ ๊ฒ์ ์ ๋ฌธ๊ฐ์
๋๋ค. {query}:
๊ฒ์์ ์ํด ๋จผ์ ์ฟผ๋ฆฌ์ ๋ฐ์ดํฐ์
์ ๋ถ์ํ๊ณ , ๋ถ์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ๊ฐ์ฅ ์ ํฉํ ์๋ฒ ๋ฉ ๋ชจ๋ธ์ ์ ํํ์ธ์
- sentence-transformers/all-MiniLM-L6-v2 (fast, lightweight, good for simple queries)
- sentence-transformers/all-mpnet-base-v2 (balanced performance)
- intfloat/e5-large-v2 (high quality for complex tasks)
- minishlab/potion-base-8M (very efficient)
"""๋ฐ์ดํฐ์ ์ ๋ํ ์ค๋ช ๊ณผ, ๊ฒ์์ด๋ฅผ ์ ๋ ฅํ๊ณ ๊ฒ์์์ง ์์ด์ ํธ๋ฅผ ์คํํฉ๋๋ค.
result = agent.run(
agentic_prompt(query="์์ด๋ ๊ด๋ จ ๋ฐ ์๋ ค์ฃผ์ธ์"),
additional_args={"dataset": dataset,
"dataset_description": "ํ๊ตญ์ด ๋ฐ(meme)์ ๋ํ ๋ฐ์ดํฐ์
์ผ๋ก, meme๊ณผ(title), meme์ ๋ป(content)์ ์๋ ค์ค๋ค.",
"column_name": "content"}
)>>> print(f"Final result: {result}")Final result: ์๋ง๊ฐ๋๋ ๋ฐ์ ์ต๊ทผ SNS์ ์จ๋ผ์ธ ์ปค๋ฎค๋ํฐ์์ ์ ํํ๋ ํํ์ผ๋ก, ์ฃผ๋ก ๋๊ตฐ๊ฐ๊ฐ ๋งค์ฐ ๊ท์ฝ๊ฑฐ๋ ์ฌ๋์ค๋ฌ์์ ๋ชจ์ฑ์ ์ ๊ฐ์ ์ ๋๋ ๋ ์ฌ์ฉ๋ฉ๋๋ค. ์ด ํํ์ ์์ด๋ ํฌ๋ค ๋ฌธํ์์ ์์ฃผ ์ฌ์ฉ๋๋ฉฐ, '์๋ง๊ฐ ๋ผ'๊ฐ ์ฌ๋ฐ๋ฅธ ํ๊ธฐ์ด์ง๋ง ์๋์ ์ธ ๋ง์ถค๋ฒ ํ๊ดด๋ฅผ ํตํด ๋ฐ์ผ๋ก์์ ๋ ํนํ ์ฑ๊ฒฉ์ ๊ฐ๊ฒ ๋์์ต๋๋ค.
Final result: ์๋ง๊ฐ๋๋ ๋ฐ์ ์ต๊ทผ SNS์ ์จ๋ผ์ธ ์ปค๋ฎค๋ํฐ์์ ์ ํํ๋ ํํ์ผ๋ก, ์ฃผ๋ก ๋๊ตฐ๊ฐ๊ฐ ๋งค์ฐ ๊ท์ฝ๊ฑฐ๋ ์ฌ๋์ค๋ฌ์์ ๋ชจ์ฑ์ ์ ๊ฐ์ ์ ๋๋ ๋ ์ฌ์ฉ๋ฉ๋๋ค. ์ด ํํ์ ์์ด๋ ํฌ๋ค ๋ฌธํ์์ ์์ฃผ ์ฌ์ฉ๋๋ฉฐ, '์๋ง๊ฐ ๋ผ'๊ฐ ์ฌ๋ฐ๋ฅธ ํ๊ธฐ์ด์ง๋ง ์๋์ ์ธ ๋ง์ถค๋ฒ ํ๊ดด๋ฅผ ํตํด ๋ฐ์ผ๋ก์์ ๋
ํนํ ์ฑ๊ฒฉ์ ๊ฐ๊ฒ ๋์์ต๋๋ค.Conclusion
๋จ์ํ ๊ฒ์ ๊ฒฐ๊ณผ๋ง์ ๊ฐ์ ธ์ค๋ ๊ฒ์ด ์๋๋ผ, ๊ฒ์๊ฒฐ๊ณผ๋ฅผ ๋ฐํ์ผ๋ก ์ฟผ๋ฆฌ์ ๋ฐ๋ฅธ ๋ต๋ณ์ ์ ์ ํด์ ๋ณด์ฌ์ฃผ์์ต๋๋ค.โ๐ป
์ง๊ธ๊น์ง ๊ฐ๋จํ ์์ด์ ํธ ์์คํ ์ ๋ง๋ค์ด๋ณด์์ต๋๋ค. ์ฌ๊ธฐ์ ํ์ง ํ๊ฐ, ๋ถ์ ๋ฑ์ ์ถ๊ฐํ๋ค๋ฉด ์ง์ ํ Agentic ๊ฒ์ ์์ง์ ๊ตฌํํ ์ ์์ต๋๋ค.
Update on GitHub