์ฐ๋ฆฌ ์คํฐ๋์์๋ LangChain ๊ธฐ๋ฐ์ ์ธํ๋ฃจ์ธ์ ๋งค์นญ ์์ด์ ํธ ํ๋ซํผ์ ๋ง๋ค๊ณ ์๋ค.
๊ด๊ณ ์ฃผ๊ฐ ๊ด๊ณ ์ปจ์
, ๊ด๊ณ ์์ดํ
, ๊ด๊ณ ์ฃผ ๋ธ๋๋๋ช
์ธ ๊ฐ์ง๋ฅผ ์
๋ ฅํ๋ฉด, ์์ด์ ํธ๊ฐ ์ต์ ์ ์ธํ๋ฃจ์ธ์ ํ๋ณด๋ฅผ ์ถ์ฒํด์ฃผ๋ ์์คํ
์ด๋ค.
์์ด์ ํธ๋ ๋ค์ 5๊ฐ์ ๋๊ตฌ๋ฅผ ์์จ์ ์ผ๋ก ์ ํํ๋ฉด์ ๋์ํ๋ค.
| ๋๊ตฌ | ์ญํ |
|---|---|
search_influencers_by_category | ์นดํ ๊ณ ๋ฆฌ๋ก DB ๊ฒ์ |
search_influencers_by_platform | ํ๋ซํผ์ผ๋ก DB ๊ฒ์ |
search_influencers_by_followers | ํ๋ก์ ๋ฒ์๋ก DB ๊ฒ์ |
get_influencer_details | ํน์ ์ธํ๋ฃจ์ธ์ ์์ธ ์ ๋ณด ์กฐํ |
calculate_match_score | 4์ฐจ์ ๋งค์นญ ์ ์ ๊ณ์ฐ |
๊ตฌํ ์์ฒด๋ ์ด๋ ต์ง ์์์ง๋ง, ์ด ์์ด์ ํธ๊ฐ ์ ์๋ํ๊ณ ์๋์ง ์ด๋ป๊ฒ ํ๊ฐํ ์ง๊ฐ ๊ด๊ฑด์ด์๋ค.
์ผ๋ฐ์ ์ธ ์ํํธ์จ์ด๋ผ๋ฉด ๋จ์ ํ
์คํธ๋ก "์
๋ ฅ A โ ์ถ๋ ฅ B"๋ฅผ ๊ฒ์ฆํ๋ฉด ๊ทธ๋ง์ด๋ค.
๊ทธ๋ฐ๋ฐ LLM ์์ด์ ํธ๋ ๋ค๋ฅด๋ค. ๊ฐ์ ์
๋ ฅ์ ์ค๋ ๋งค๋ฒ ์กฐ๊ธ์ฉ ๋ค๋ฅธ ๊ฒฝ๋ก๋ก ๋๊ตฌ๋ฅผ ํธ์ถํ๊ณ , ๋ค๋ฅธ ๋ฌธ์ฅ์ผ๋ก ์๋ตํ๋ค. ์ ํด์ง ์ ๋ต์ด ์๋ค.
๊ทธ๋ ๋ค๊ณ ๋งค๋ฒ ๋์ผ๋ก ์ง์ ์๋ต์ ์ฝ์ด๋ณด๋ ๋ฐฉ์์ ์ง์ ๊ฐ๋ฅํ์ง ์๋ค. ํ ์คํธ ์ผ์ด์ค๋ฅผ 10๊ฐ๋ง ๋ง๋ค์ด๋ ์ฝ๋๋ฅผ ์กฐ๊ธ ๋ฐ๊ฟ ๋๋ง๋ค 10๋ฒ์ ์ฝ์ด์ผ ํ๋ค.
์ด ๋ฌธ์ ๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ํํํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
๊ฐ ์ง๋ฌธ์ "๋ง๋ค/ํ๋ฆฌ๋ค"๋ก ํ๋จํ ์ ์๋ค. ์ฆ, ์๋ํํ ์ ์๋ค. ์ฐ๋ฆฌ๊ฐ ํ์ํ ๊ฑด ์ด ์ฒดํฌ๋ฆฌ์คํธ๋ฅผ ์ฝ๋๋ก ํํํ๋ ๊ฒ์ด์๋ค.
ํ์์๋ ์์ฐ์ค๋ฝ๊ฒ ๋ ๊ฐ์ง ๋ฐฉํฅ์ด ๋ ์ฌ๋๋ค.
์ฒซ ๋ฒ์งธ๋ ๋จ๊ณ๋ณ ํ์ดํ๋ผ์ธ ๋ฐฉ์์ด๋ค.
์์ด์ ํธ๋ฅผ ๊ทธ๋๋ก ์คํํ๊ณ , ์๋ต์ ํ์ฑํ๊ณ , DB์ ๋์กฐํ๊ณ , ๋ฏธ๋ฆฌ ์ ์ํ ์ฒดํฌ๋ฆฌ์คํธ๋ก ์ ์๋ฅผ ๋ด๋ ๊ฒ์ด๋ค. ์ง๊ด์ ์ด๊ณ ๊ตฌํ๋ ์ด๋ ต์ง ์๋ค.
๋ ๋ฒ์งธ๋ ์คํฐ๋์์ ์ง๊ณ ๋์ด๊ฐ ์ ์๋ DSPy๋ฅผ ํ์ฉํ๋ ๋ฐฉ์์ด๋ค.
DSPy๋ ๋จ์ํ ํ๊ฐ๋ฅผ ์๋ํํ๋ ๋ฐ ๊ทธ์น์ง ์๊ณ , ๊ทธ ํ๊ฐ ๊ฒฐ๊ณผ๋ฅผ ํผ๋๋ฐฑ ์ผ์ ํ๋กฌํํธ ์์ฒด๋ฅผ ์๋์ผ๋ก ๊ฐ์ ํด์ฃผ๋ ํ๋ ์์ํฌ์ด๋ค.
DSPy์ MIPROv2 optimizer๋ ํ๊ฐ ์งํ๋ฅผ ํ ๋๋ก ํ๋กฌํํธ๋ฅผ ์ต์ ํํ๋ ์ญํ ์ ํ๋ค. ์๋์ผ๋ก ํ๋กฌํํธ๋ฅผ ์ด๋ฆฌ์ ๋ฆฌ ์์ ํ๋ ๋์ , ํ๊ฐ ๊ธฐ์ค์ ์ฝ๋๋ก ์ ์ํด ๋๋ฉด optimizer๊ฐ ์ค์ค๋ก ๋ ์ข์ ํ๋กฌํํธ๋ฅผ ์ฐพ์์ค๋ค๋ ์์ด๋์ด์ด๋ค.
์ด ๊ธ์ ์ฐ๋ฆฌ ํ์ด DSPy๋ฅผ ์ด๋ป๊ฒ ์ดํดํ๊ณ , ์ค์ ๋ก ํ๋ก์ ํธ์ ์ด๋ป๊ฒ ์ ์ฉํด๋ดค์ผ๋ฉฐ, ๊ทธ ๊ณผ์ ์์ ๋ฌด์์ ๋ฐฐ์ ๋์ง์ ๋ํ ๊ธฐ๋ก์ด๋ค.
DSPy(Declarative Self-improving Python)๋ ์คํ ํผ๋ NLP ์ฐ๊ตฌํ์ด ๋ง๋ ํ๋ ์์ํฌ๋ก, ํ๋กฌํํธ๋ฅผ ์ง์ ์์ฑํ๋ ๋์ LLM ํ๋ก๊ทธ๋จ์ ๊ตฌ์กฐ๋ฅผ ์ ์ธํ๊ณ ์ต์ ํ๋ฅผ ์๋ํํ๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ค.
์ด๊ฒ ์ด๋ค ์๋ฏธ์ธ์ง๋ ๊ธฐ์กด ๋ฐฉ์์ ๋ฌธ์ ๋ฅผ ๋จผ์ ๋ณด๋ฉด ๋ ์ ์๋ฟ๋๋ค.
LLM ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ํ ๋ฒ์ฏค ์ด๋ฐ ๊ฒฝํ์ ํ๋ค.
์์ด์ ํธ๊ฐ ์๋ฑํ ๋ต์ ๋ด๋๋๋ค. โ ํ๋กฌํํธ๋ฅผ ์์ ํ๋ค. โ ์กฐ๊ธ ๋์์ง๋ค. โ ๊ทธ๋ฐ๋ฐ ๋ค๋ฅธ ์ผ์ด์ค์์ ๋ ํ๋ฆฐ๋ค. โ ๋ค์ ์์ ํ๋ค. โ ๊ทธ๋ฌ๋ฉด ์ฒ์์ ์ ๋๋ ์ผ์ด์ค๊ฐ ๋ง๊ฐ์ง๋ค. โ ...
์ด ๊ณผ์ ์ ์ํํธ์จ์ด ๊ฐ๋ฐ์ด๋ผ๊ธฐ๋ณด๋ค ๊ฐ์ ์์กดํ ์กฐ์จ์ ๊ฐ๊น๋ค. ํ๋กฌํํธ๊ฐ ์ ์ ๋๋์ง, ์ ์ ๋๋์ง ๋ช ํํ ์ค๋ช ํ๊ธฐ ์ด๋ ต๋ค. ์กฐ๊ธ๋ง ๋ค๋ฅธ ๋ชจ๋ธ๋ก ๋ฐ๊พธ๊ฑฐ๋, ๋ค๋ฅธ ํ์คํฌ์ ์ ์ฉํ๋ฉด ์ฒ์๋ถํฐ ๋ค์ ํด์ผ ํ๋ค.
DSPy๋ ์ด ๋ฌธ์ ์ ์ ๋ฉด์ผ๋ก ๋์ ํ๋ค.
"ํ๋กฌํํธ ๋ฌธ์์ด์ ์ง์ ๋ง์ง๋ ๋์ , LLM์๊ฒ ๋ฌด์์ ์ํ๋์ง ์ ์ธํ๊ณ ์ต์ ํ๋ ํ๋ ์์ํฌ์ ๋งก๊ฒจ๋ผ."
๋ ์ ๊ทผ์ ์ฐจ์ด๋ฅผ ์ฝ๋๋ก ์ง์ ๋น๊ตํด๋ณด๋ฉด ๋ ๋ช ํํ๋ค.
prompt = """
๋น์ ์ ์ธํ๋ฃจ์ธ์ ๋งค์นญ ์ ๋ฌธ๊ฐ์
๋๋ค.
๋ค์ ๊ด๊ณ ์ ๋ณด๋ฅผ ๋ถ์ํ์ฌ ์ ํฉํ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ถ๋ก ํ์ธ์.
๊ด๊ณ ์ปจ์
: {ad_concept}
๊ด๊ณ ์์ดํ
: {ad_item}
๊ด๊ณ ์ฃผ: {advertiser}
๋ฐ๋์ ๋ค์ ์ค์์๋ง ์ ํํ์ธ์:
beauty, fashion, tech, food, travel, fitness, gaming, music, education, lifestyle
์นดํ
๊ณ ๋ฆฌ:
"""
response = llm.invoke(prompt.format(...))์ด ๋ฐฉ์์ ๋ฌธ์ ๋ ํ๋กฌํํธ ๋ฌธ์์ด ์์ฒด๊ฐ ๋ก์ง์ด ๋๋ค๋ ์ ์ด๋ค. ๋ชจ๋ธ์ ๋ฐ๊พธ๋ฉด "๋ฐ๋์"๋ผ๋ ๊ฐ์กฐ์ด์ ํจ๊ณผ๊ฐ ๋ฌ๋ผ์ง๋ค. ์ถ๋ ฅ ํ์์ด ์กฐ๊ธ๋ง ๋ฌ๋ผ์ ธ๋ ํ์ฑ์ด ๊นจ์ง๋ค. ์ฑ๋ฅ์ ๋์ด๋ ค๋ฉด ์ฌ๋์ด ์ง์ ๋ฌธ์ฅ์ ๊ณ ์ณ์ผ ํ๋ค.DSPy ๋ฐฉ์ (์ ์ธํ ๋ช ์ธ)
class InferCategories(dspy.Signature):
"""๊ด๊ณ ์ ๋ณด๋ฅผ ๋ถ์ํ์ฌ DB ๊ฒ์์ ์ฌ์ฉํ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ถ๋ก ํฉ๋๋ค."""
ad_concept: str = dspy.InputField(desc="๊ด๊ณ ์ปจ์
์ค๋ช
")
ad_item: str = dspy.InputField(desc="๊ด๊ณ ์ํ/์๋น์ค๋ช
")
advertiser: str = dspy.InputField(desc="๊ด๊ณ ์ฃผ ๋ธ๋๋๋ช
")
categories: str = dspy.OutputField(
desc="์ผํ๋ก ๊ตฌ๋ถ๋ ์นดํ
๊ณ ๋ฆฌ. ๋ฐ๋์ ๋ค์ ์ค์์๋ง ์ ํ: beauty, fashion, ..."
)
predictor = dspy.Predict(InferCategories)
result = predictor(ad_concept="...", ad_item="...", advertiser="...")
ํ๋กฌํํธ ๋ฌธ์์ด์ด ์ฌ๋ผ์ก๋ค. ๋์ "์ด๋ค ์ ๋ ฅ์ ๋ฐ์์ ์ด๋ค ์ถ๋ ฅ์ ๋ด๋๋๊ฐ"๋ง ์ ์ธํ๋ค. ์ค์ ํ๋กฌํํธ๋ DSPy๊ฐ ์๋์ผ๋ก ๊ตฌ์ฑํ๋ค.
๊ธฐ์กด ๋ฐฉ์์์ ํ๋กฌํํธ๋ ๋ชจ๋ธ๊ณผ ๊ฐํ๊ฒ ๊ฒฐํฉ๋์ด ์๋ค. GPT-4์ ๋ง๊ฒ ํ๋ํ ํ๋กฌํํธ๋ฅผ Claude์ ๊ทธ๋๋ก ์ฐ๋ฉด ์ฑ๋ฅ์ด ๋ฌ๋ผ์ง๋ค. Ollama ๋ก์ปฌ ๋ชจ๋ธ์ ์ฐ๋ฉด ๋ ๋ฌ๋ผ์ง๋ค.
DSPy๋ ์ด ๊ฒฐํฉ์ ๋๋๋ค. ๊ฐ๋ฐ์๋ "๋ฌด์์ ์ํ๋๊ฐ(What)"๋ง ์ ์ธํ๊ณ , "์ด๋ป๊ฒ ํํํ ๊ฒ์ธ๊ฐ(How)"๋ DSPy์ Optimizer๊ฐ ๋ด๋นํ๋ค.
๋ชจ๋ธ์ด ๋ฐ๋๋ฉด Optimizer๋ฅผ ๋ค์ ๋๋ ค์ ๊ทธ ๋ชจ๋ธ์ ๋ง๋ ์ต์ ํํ์ ์๋์ผ๋ก ์ฐพ์๋ธ๋ค.
์ด ๊ด์ ์์ DSPy๋ฅผ ํ ๋ฌธ์ฅ์ผ๋ก ์ ์ํ๋ฉด ์ด๋ ๋ค.
LLM ํธ์ถ์ ์ ์ธํ์ผ๋ก ๊ตฌ์กฐํํ๊ณ , ํ๊ฐ ๊ธฐ์ค(metric)์ ์ ์ํ๋ฉด ํ๋กฌํํธ์ few-shot ์์๋ฅผ ์๋์ผ๋ก ์ต์ ํํด์ฃผ๋ ํ๋ ์์ํฌ
DSPy์ ์ค๊ณ๋ ๋จธ์ ๋ฌ๋์ ํ์ต ๋ฃจํ์์ ์ง์ ์๊ฐ์ ๋ฐ์๋ค.
์ ํต์ ์ธ ML์์๋ ๋ชจ๋ธ ๊ฐ์ค์น๋ฅผ ์ง์ ์์ ํ๋ ๋์ , ์์ค ํจ์(loss function)๋ฅผ ์ ์ํ๊ณ ์ญ์ ํ๋ก ์ต์ ํํ๋ค. ๊ฐ๋ฐ์๋ "๋ฌด์์ด ์ข์ ์ถ๋ ฅ์ธ์ง"๋ง ์ ์ํ๋ฉด ๋๋ค.
DSPy๋ ์ด ํจ๋ฌ๋ค์์ ํ๋กฌํํธ์ ์ ์ฉํ๋ค.
| ML | DSPy |
|---|---|
| ๋ชจ๋ธ ๊ฐ์ค์น | ํ๋กฌํํธ & few-shot ์์ |
| ์์ค ํจ์ | metric ํจ์ (0.0 ~ 1.0) |
| ์ญ์ ํ + Optimizer | MIPROv2 ๋ฑ Teleprompter |
| ํ์ต ๋ฐ์ดํฐ | trainset (dspy.Example ๋ชฉ๋ก) |
"๊ฐ์ค์น๋ฅผ ์ง์ ์์ ํ์ง ์๊ณ ์์ค ํจ์๋ก ์ต์ ํํ๋ค"๋ ๊ฒ ๋ฅ๋ฌ๋์ ํต์ฌ์ด๋ฏ, "ํ๋กฌํํธ๋ฅผ ์ง์ ์์ ํ์ง ์๊ณ metric์ผ๋ก ์ต์ ํํ๋ค"๋ ๊ฒ DSPy์ ํต์ฌ์ด๋ค.
LangChain์ LLM ํธ์ถ, ์ฒด์ธ ๊ตฌ์ฑ, ๋๊ตฌ ์ฐ๊ฒฐ ๋ฑ LLM ์ ํ๋ฆฌ์ผ์ด์ ๋น๋ฉ ๋ธ๋ก์ ์ ๊ณตํ๋ ํ๋ ์์ํฌ์ด๋ค. ์ด๋ค ํ๋กฌํํธ๋ฅผ ์ธ์ง๋ ๊ฐ๋ฐ์๊ฐ ๊ฒฐ์ ํ๋ค.
DSPy๋ ๊ทธ ์์์ ์๋ํ๋ ์ต์ ํ ๋ ์ด์ด์ด๋ค. LLM ์ฐ๊ฒฐ์ LangChain์ฒ๋ผ ํ๊ณ , ํ๋กฌํํธ ์์ฒด๋ฅผ ์ด๋ป๊ฒ ๊ตฌ์ฑํ ์ง๋ DSPy๊ฐ ์ฐพ์์ค๋ค.
๋์ ๋์ฒด ๊ด๊ณ๊ฐ ์๋๋ผ ์ญํ ์ด ๋ค๋ฅด๋ค. ์ค์ ๋ก DSPy๋ ๋ด๋ถ์ ์ผ๋ก LiteLLM์ ํตํด OpenAI, Anthropic, Ollama ๋ฑ ๋ค์ํ ๋ชจ๋ธ์ ์ฐ๊ฒฐ๋๋ค. ์ฐ๋ฆฌ ํ๋ก์ ํธ์์๋ DSPy๊ฐ Ollama ์์์ ์๋ํ๋๋ก ๊ตฌ์ฑํ๋ค.
DSPy๋ฅผ ์ด๋ฃจ๋ ํต์ฌ ๊ฐ๋ ์ Signature, Module, Optimizer ์ธ ๊ฐ์ง์ด๋ค. ๊ฐ๊ฐ์ด ๋ฌด์์ธ์ง, ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ ํ๋ก์ ํธ์์ ์ด๋ป๊ฒ ํํ๋๋์ง๋ฅผ ํจ๊ป ์ดํด๋ณด๊ฒ ๋ค.
Signature๋ LLM ํธ์ถ ํ๋์ ์ ์ถ๋ ฅ ๋ช ์ธ์ด๋ค. "์ด๋ค ์ ๋ณด๋ฅผ ์ฃผ๋ฉด ์ด๋ค ์ ๋ณด๋ฅผ ๋๋ ค๋ฌ๋ผ"๋ฅผ ์ฝ๋๋ก ์ ์ธํ๋ ๊ฒ์ด๋ค.
์ผ๋ฐ์ ์ธ ํํ๋ ์ด๋ ๋ค.
class ์๊ทธ๋์ฒ_์ด๋ฆ(dspy.Signature):
"""์ด LLM ํธ์ถ์ ๋ชฉ์ ์ ์ค๋ช
ํ๋ docstring (์๋์ผ๋ก ํ๋กฌํํธ ์ง์์ฌํญ์ด ๋๋ค)"""
์
๋ ฅํ๋: str = dspy.InputField(desc="ํ๋ ์ค๋ช
")
์ถ๋ ฅํ๋: str = dspy.OutputField(desc="ํ๋ ์ค๋ช
")
docstring๊ณผ ๊ฐ ํ๋์ desc๊ฐ ๊ทธ๋๋ก ํ๋กฌํํธ์ ์ง์์ฌํญ์ผ๋ก ๋ณํ๋๋ค. ๊ฐ๋ฐ์๊ฐ ํ๋กฌํํธ ๋ฌธ์์ด์ ์ง์ ์์ฑํ์ง ์์๋ ๋๋ค.
์ฐ๋ฆฌ ํ๋ก์ ํธ์์๋ Signature๋ฅผ ๋ ๊ฐ ์ค๊ณํ๋ค.
class InferCategories(dspy.Signature):
"""๊ด๊ณ ์ ๋ณด๋ฅผ ๋ถ์ํ์ฌ DB ๊ฒ์์ ์ฌ์ฉํ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ถ๋ก ํฉ๋๋ค.
๋ฐ๋์ ํ๊ตญ์ด๋ก ๋ต๋ณํ์ธ์."""
ad_concept: str = dspy.InputField(desc="๊ด๊ณ ์ปจ์
์ค๋ช
")
ad_item: str = dspy.InputField(desc="๊ด๊ณ ์ํ/์๋น์ค๋ช
")
advertiser: str = dspy.InputField(desc="๊ด๊ณ ์ฃผ ๋ธ๋๋๋ช
")
categoreis: str = dspy.OutputField(
desc=(
"์ผํ๋ก ๊ตฌ๋ถ๋ ์นดํ
๊ณ ๋ฆฌ. ๋ฐ๋์ ๋ค์ ์ค์์๋ง ์ ํ: "
"beauty, fashion, tech, food, travel, fitness, gaming, music, education, lifestyle"
)
)โก๏ธ "์ด๋์คํ๋ฆฌ ์ ๊ธฐ๋ ์คํจ์ผ์ด" ๊ด๊ณ ๊ฐ ๋ค์ด์ค๋ฉด beauty๋ฅผ ์ถ๋ ฅํ๋ LLM ํธ์ถ ํ๋๋ฅผ ์ ์ธํ ๊ฒ์ด๋ค.class RankAndRecommend(dspy.Signature):
"""๋งค์นญ ์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ถ์ํ์ฌ TOP3๋ฅผ ์ ์ ํ๊ณ ํ๊ตญ์ด ์ถ์ฒ ๊ฒฐ๊ณผ๋ฅผ ์์ฑํฉ๋๋ค.
๋ฐ๋์ ํ๊ตญ์ด๋ก๋ง ๋ต๋ณํ์ธ์. ์ค๊ตญ์ด ์ฌ์ฉ ์ ๋ ๊ธ์ง."""
ad_concept: str = dspy.InputField(desc="๊ด๊ณ ์ปจ์
")
ad_item: str = dspy.InputField(desc="๊ด๊ณ ์์ดํ
")
scores_summary: str = dspy.InputField(
desc="์ธํ๋ฃจ์ธ์๋ณ overall_grade(S>A>B>C>D)์ ์ฐจ์ ํ๊ฐ(์/์ค/ํ) JSON"
)
top3_names: list[str] = dspy.OutputField(
desc="overall_grade ๊ธฐ์ค ์์ 3๋ช
์ ์ธํ๋ฃจ์ธ์ ์ด๋ฆ ๋ฆฌ์คํธ (์ ํํ 3๊ฐ)"
)
recommendation: str = dspy.OutputField(
desc="ํ๊ตญ์ด ์ถ์ฒ ๊ฒฐ๊ณผ๋ฌธ. ๊ฐ ์ธํ๋ฃจ์ธ์๋ณ ์ด๋ฆ, ์ข
ํฉ ๋ฑ๊ธ, ์ถ์ฒ ์ด์ ํฌํจ"
)โก๏ธ ์ฌ๊ธฐ์ ์ฃผ๋ชฉํ ๊ฒ์ด ์๋ค. ์ถ๋ ฅ ํ๋๋ก top3_names: list[str]์ฒ๋ผ ํ์
์ ์ ์ธํ๋ฉด DSPy๊ฐ LLM ์ถ๋ ฅ์ ํด๋น ํ์
์ผ๋ก ํ์ฑํด์ค๋ค. ๊ธฐ์กด ๋ฐฉ์์์ ์ ๊ท์์ผ๋ก ์ด๋ฆ์ ์ผ์ผ์ด ์ถ์ถํ๋ ์ฝ๋๊ฐ ํ์ ์์ด์ง๋ค.Signature๊ฐ LLM ํธ์ถ ํ๋๋ฅผ ์ ์ํ๋ค๋ฉด, Module์ ์ฌ๋ฌ Signature์ ๊ฒฐ์ ๋ก ์ Python ์ฝ๋๋ฅผ ์กฐํฉํด ์์ ํ ํ๋ก๊ทธ๋จ ํ๋๋ฅผ ๋ง๋๋ ๋จ์์ด๋ค. PyTorch์ nn.Module์์ ์ง์ ์๊ฐ์ ๋ฐ์ ๊ตฌ์กฐ๋ค.
Module์ ๋ง๋ค ๋๋ ๋ ๊ฐ์ง๋ฅผ ์ ์ํ๋ค.
__init__: ์ด๋ค Predictor(LLM ํธ์ถ)๋ฅผ ์ฌ์ฉํ ์ง ์ ์ธforward(): ์ค์ ์คํ ํ๋ฆ (LLM ํธ์ถ + Python ๋ก์ง)์ฐ๋ฆฌ ํ๋ก์ ํธ์ InfluencerMatchingModule์ด ์ด ๊ตฌ์กฐ๋ฅผ ์ ๋ณด์ฌ์ค๋ค.
class InfluencerMatchingModule(dspy.Module):
def __init__(self, repo):
super().__init__()
self.repo = repo
self.infer_categories = dspy.Predict(InferCategories) # LLM ํธ์ถ 1
self.rank = dspy.ChainOfThought(RankAndRecommend) # LLM ํธ์ถ 2
def forward(self, ad_concept: str, ad_item: str, advertiser: str):
# -- Step 1: LLM โ ์นดํ
๊ณ ๋ฆฌ ์ถ๋ก ----------------------
cats_pred = self.infer_categories(
ad_concept=ad_concept, ad_item=ad_item, advertiser=advertiser
)
categories = [c.strip() for c in cats_pred.categories.split(",")]
# -- Step 2: Python โ DB ๊ฒ์ (๊ฒฐ์ ๋ก ์ ) --------------
influencers = self.repo.find_by_categories(categories)
# -- Step 3: Python โ ๋งค์นญ ์ ์ ๊ณ์ฐ (๊ฒฐ์ ๋ก ์ ) --------
scores = []
for inf in influencers:
result = calculate_match_score_raw(inf, categories, [])
scores.append({ "name": inf.name, "overall_grade": result["overall_grade"], ... })
scores.sort(key=lambda x: grade_order[x["overall_grade"]], reverse=True)
# -- Step 4: LLM โ TOP3 ์ ์ + ์ถ์ฒ๋ฌธ ์์ฑ -------------
rank_pred = self.rank(
ad_concept=ad_concept,
ad_item=ad_item,
scores_summary=json.dumps(scores[:10], ensure_ascii=False),
)
return rank_pred
forward()์ ํ๋ฆ์ ๋ณด๋ฉด LLM์ด ๋ด๋นํ๋ ๋ถ๋ถ๊ณผ Python์ด ๋ด๋นํ๋ ๋ถ๋ถ์ด ๋ช
ํํ๊ฒ ๋๋๋ค.
| ๋จ๊ณ | ๋ด๋น | ์ด์ |
|---|---|---|
| ์นดํ ๊ณ ๋ฆฌ ์ถ๋ก | LLM | ์์ฐ์ด ์ดํด๊ฐ ํ์ |
| DB ๊ฒ์ | Python | ์ ํํ ์ฟผ๋ฆฌ๊ฐ ํ์, LLM์ด ๊ฐ์ ํ๋ฉด ์ค๋ฅ ๊ฐ๋ฅ์ฑ |
| ์ ์ ๊ณ์ฐ | Python | ์์น ๊ณ์ฐ์ ๊ฒฐ์ ๋ก ์ ์ด์ด์ผ ํจ |
| TOP3 ์ ์ + ์ถ์ฒ๋ฌธ | LLM | ์์ฐ์ด ์์ฑ์ด ํ์ |
์ด๊ฒ์ด Module์ด ์ฃผ๋ ํต์ฌ ๊ฐ์น์ด๋ค. LLM์ด ํด์ผ ํ ์ผ๊ณผ Python์ด ํด์ผ ํ ์ผ์ ์ฝ๋ ๊ตฌ์กฐ๋ก ๋ถ๋ฆฌํ๋ค. ๊ธฐ์กด ReAct ์์ด์ ํธ์ฒ๋ผ LLM์ด ํ๋ฆ ์ ์ฒด๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ด ์๋๋ผ, ํ๋ฆ์ forward() ์ฝ๋๊ฐ ๊ณ ์ ํ๊ณ LLM์ ํ์ํ ์ง์ ์์๋ง ํธ์ถ๋๋ค.
dspy.Predict์ dspy.ChainOfThought์ ์ฐจ์ด๋ ์ง๊ณ ๊ฐ์.
dspy.Predict: Signature๋ฅผ ๊ทธ๋๋ก LLM์ ์ ๋ฌํด ์ถ๋ ฅ์ ๋ฐ๋๋ค. ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ LLM ํธ์ถdspy.ChainOfThought: LLM์๊ฒ ์ต์ข
๋ต ์ ์ ์ถ๋ก ๊ณผ์ ์ ๋จผ์ ์ฐ๋๋ก ์ ๋ํ๋ค. RankAndRecommend์ฒ๋ผ ์ฌ๋ฌ ํ๋ณด ์ค ์์๋ฅผ ๋งค๊ธฐ๋ ์์
์ ์ ํฉํ๋ค.Optimizer๊ฐ DSPy์ ํต์ฌ์ด๋ค. ์์ ๋ง๋ Module์ ๋ ์ ์๋ํ๋๋ก ํ๋กฌํํธ์ few-shot ์์๋ฅผ ์๋์ผ๋ก ์ต์ ํํด์ฃผ๋ ์ญํ ์ ํ๋ค.
์๋ ์๋ฆฌ๋ฅผ ์ดํดํ๋ ค๋ฉด ๋จผ์ ๋ ๊ฐ์ง๊ฐ ํ์ํ๋ค.
trainset: ์ต์ ํ์ ๊ธฐ์ค์ด ๋๋ ์์ ๋ฐ์ดํฐ
def _make_example(tc: TestCase) -> dspy.Example:
return dspy.Example(
ad_concept=tc.ad_concept,
ad_item=tc.ad_item,
advertiser=tc.advertiser,
expected_categories=tc.expected_categories,
competitor_brands=tc.competitor_brands,
).with_inputs("ad_concept", "ad_item", "advertiser")
trainset = [_make_example(tc) for tc in TEST_CASES] # 10๊ฐ
์ฐ๋ฆฌ ํ๋ก์ ํธ์์๋ ์ด๋์คํ๋ฆฌ, bhc, ์ผ์ฑ์ ์ ๋ฑ ์ค์ ํ๊ตญ ๋ธ๋๋ 10๊ฐ๋ฅผ trainset์ผ๋ก ๊ตฌ์ฑํ๋ค.
def matching_metric(example, prediction, trace=None) -> float:
recommended_names = list(prediction.top3_names or [])
# evidence_pack ๊ตฌ์ฑ ํ 4๊ฐ ๋ฉํธ๋ฆญ ๊ณ์ฐ
results = [
calculate_evidence_coverage(evidence_pack), # ๋ฐ์ดํฐ ์์ ์ฑ
calculate_semantic_relevance(evidence_pack), # ๊ด๊ณ ์ ํฉ๋
calculate_groundedness(evidence_pack), # ํ ๋ฃจ์๋ค์ด์
์ฌ๋ถ
calculate_competitor_check(evidence_pack), # ๊ฒฝ์์ฌ ์ด๋ ฅ ์ฌ๋ถ
]
passed = sum(r["passed"] for r in results)
return passed / len(results) # 0.0 ~ 1.04๊ฐ ๋ฉํธ๋ฆญ ์ค ๋ช ๊ฐ๋ฅผ ํต๊ณผํ๋์ง ๋น์จ๋ก ๋ฐํํ๋ค. 4๊ฐ ๋ชจ๋ ํต๊ณผํ๋ฉด 1.0, ํ๋๋ ๋ชป ํต๊ณผํ๋ฉด 0.0์ด๋ค.์ด ๋ ๊ฐ์ง๊ฐ ์ค๋น๋๋ฉด Optimizer๋ฅผ ์คํํ๋ค.
from dspy.teleprompt import MIPROv2
optimizer = MIPROv2(
metric=metric,
auto="light", # ํ์ ์์ฐ: light / medium / heavy
num_threads=1,
)
optimized_module = optimizer.compile(
module,
trainset=trainset,
)
optimized_module.save("outputs/dspy_optimized.json")
MIPROv2๊ฐ ํ๋ ์ผ์ ๋จ๊ณ๋ณ๋ก ํ๋ฉด ์ด๋ ๋ค.
์ด ๊ณผ์ ์์ ๊ฐ๋ฐ์๊ฐ ํ๋ ์ผ์ ์๋ค. metric์ ์ ์ํ๊ณ compile()์ ํธ์ถํ๋ฉด ๋๋จธ์ง๋ ์๋์ด๋ค.
์ต์ ํ ๊ฒฐ๊ณผ๋ JSON ํ์ผ๋ก ์ ์ฅ๋๊ณ , ์ดํ์๋ Module์ ๋ก๋ํด์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ค.
optimized_module = InfluencerMatchingModule(repo)
optimized_module.load("outputs/dspy_optimized.json")
result = optimized_module(
ad_concept="๊ฑด๊ฐํ๊ณ ์์ฐ์ค๋ฌ์ด ์ด๋ฏธ์ง์ ๋ทฐํฐ ์ ํ",
ad_item="์ ๊ธฐ๋ ์คํจ์ผ์ด ๋ผ์ธ",
advertiser="์ด๋์คํ๋ฆฌ",
)
print(result.top3_names) # ['ํ์์ฐ', '๊น๋ฏผ์ค', '์ด์์ง']
print(result.recommendation) # ํ๊ตญ์ด ์ถ์ฒ ๊ฒฐ๊ณผ๋ฌธ
Signature๋ ๋ถํ, Module์ ์์ฑํ, Optimizer๋ ์์ฑํ์ ๋ ์ ๋ง๋ค์ด์ฃผ๋ ๊ณต์ฅ์ด๋ค.
์ฐ๋ฆฌ ํ๋ก์ ํธ์ ์์ด์ ํธ๋ ReAct(Reasoning + Acting) ํจํด ๊ธฐ๋ฐ์ผ๋ก, LangChain์ผ๋ก ๊ตฌํ๋์ด ์๋ค. ์์ด์ ํธ๋ 5๊ฐ์ ๋๊ตฌ๋ฅผ ์์ ๋กญ๊ฒ ์ ํํ๋ฉด์ ๋์ํ๋ค.
์ด ๊ตฌ์กฐ์ ํน์ง์ LLM์ด ํ๋ฆ์ ์ง์ ๊ฒฐ์ ํ๋ค๋ ๊ฒ์ด๋ค. ์ด๋ค ๋๊ตฌ๋ฅผ ๋ช ๋ฒ ํธ์ถํ ์ง, ์ด๋ค ์์๋ก ์งํํ ์ง๋ฅผ ๋ชจ๋ LLM์ด ํ๋จํ๋ค.
๋๋ถ์ ์ ์ฐํ์ง๋ง, ๊ทธ ์ ์ฐํจ์ด ํ๊ฐ๋ฅผ ์ด๋ ต๊ฒ ๋ง๋ ๋ค. ์คํํ ๋๋ง๋ค ๊ฒฝ๋ก๊ฐ ๋ฌ๋ผ์ง๊ณ , ์ต์ข
์๋ต์ ํ์๋ ์ผ์ ํ์ง ์๋ค.
์ด ์์ด์ ํธ๋ฅผ DSPy๋ก ์ฌ์ค๊ณํ ๋, ๊ธฐ์กด ์ฝ๋์ ๊ฐ ์์๊ฐ DSPy์ ์ด๋ค ๊ฐ๋ ์ ๋์๋๋์ง๋ฅผ ๋จผ์ ์ ๋ฆฌํ๋ค.
| ๊ธฐ์กด ์ฝ๋ | DSPy ๊ฐ๋ | ์ญํ |
|---|---|---|
AGENT_SYSTEM_PROMPT (๋ฌธ์์ด) | Signature | LLM ํธ์ถ์ ์ ์ถ๋ ฅ ๋ช ์ธ |
AgentService (LangChain) | InfluencerMatchingModule | ์ ์ฒด ์คํ ํ๋ฆ |
TEST_CASES ๋ฆฌ์คํธ | trainset | ์ต์ ํ ๊ธฐ์ค ๋ฐ์ดํฐ |
metrics.py 4๊ฐ ํจ์ | matching_metric | ์์ธก ํ์ง ์ธก์ ํจ์ |
| ์๋ ํ๋กฌํํธ ์์ | MIPROv2 | ์๋ ์ต์ ํ |
ํต์ฌ์ ๋ง์ง๋ง ํ์ด๋ค. ๊ธฐ์กด์๋ ์์ด์ ํธ๊ฐ ์๋ชป๋ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋์ผ๋ฉด AGENT_SYSTEM_PROMPT๋ฅผ ์ด์ด์ ์ง์ ๋ฌธ์ฅ์ ์์ ํ๋ค. DSPy์์๋ ๊ทธ ์์
์ MIPROv2๊ฐ ๋์ ํ๋ค.
๋งคํ ํ๋ณด๋ค ์ค์ํ ๊ฒ์, DSPy ๋ฐฉ์์ด ๊ธฐ์กด ์์ด์ ํธ์ ๊ตฌ์กฐ์ ์ผ๋ก ์ด๋ป๊ฒ ๋ค๋ฅธ๊ฐ์ด๋ค.
๊ธฐ์กด LangChain ReAct ์์ด์ ํธ: LLM์ด ํ๋ฆ์ ๊ฒฐ์ ํ๋ค.

DSPy InfluencerMatchingModule: ์ฝ๋๊ฐ ํ๋ฆ์ ๊ฒฐ์ ํ๊ณ , LLM์ ์ง์ ๋ ์ง์ ์์๋ง ํธ์ถ๋๋ค.

ReAct ์์ด์ ํธ๋ DB ๊ฒ์๊ณผ ์ ์ ๊ณ์ฐ์ Python์ผ๋ก ์ฒ๋ฆฌํ๋ค. search_influencers_by_category๋ calculate_match_score๋ ๋ชจ๋ Python ํจ์๋ค. ์ฐจ์ด๋ ๊ทธ Python ํจ์๊ฐ ์ด๋ป๊ฒ ํธ์ถ๋๋๋์ ์๋ค.
ReAct์์๋ LLM์ด "์ง๊ธ ์ด ๋๊ตฌ๋ฅผ ์จ์ผ๊ฒ ๋ค"๊ณ ํ๋จํด์ผ Python ํจ์๊ฐ ์คํ๋๋ค. LLM์ด ํ๋ฆ์ ์ฃผ๋๊ถ์ ๊ฐ๊ณ ์์ด์, ์ด๋ค ๋๊ตฌ๋ฅผ ๋ช ๋ฒ ํธ์ถํ ์ง, ์ด๋ค ์์๋ก ์งํํ ์ง๊ฐ ์คํํ ๋๋ง๋ค ๋ฌ๋ผ์ง ์ ์๋ค.
DSPy Module์์๋ forward() ์ฝ๋๊ฐ ํ๋ฆ์ ์ฒ์๋ถํฐ ๊ณ ์ ํ๋ค. DB ๊ฒ์๊ณผ ์ ์ ๊ณ์ฐ์ LLM์ ํ๋จ์ ๊ฑฐ์น์ง ์๊ณ ์ฝ๋๊ฐ ์ง์ ํธ์ถํ๋ค. LLM์ "์์ฐ์ด๋ก ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ถ๋ก ํ๋ ๊ฒ"๊ณผ "์์ฐ์ด๋ก ์ถ์ฒ๋ฌธ์ ์์ฑํ๋ ๊ฒ", ๋ฑ ๋ ์ง์ ์์๋ง ๊ฐ์
ํ๋ค.
๊ฒฐ๊ตญ ๋ ๋ฐฉ์ ๋ชจ๋ Python์ด ๊ณ์ฐํ๋ ๊ฑด ๊ฐ์ง๋ง, ๋ฌ๋ผ์ง๋ ๊ฒ์ ๊ทธ ๊ณ์ฐ์ LLM์ ๊ฒฐ์ ์ ์ํด ํธ๋ฆฌ๊ฑฐํ๋๋, ์ฝ๋๊ฐ ์ง์ ํธ๋ฆฌ๊ฑฐํ๋๋๋ค.
DSPy Optimizer๊ฐ ์๋ํ๋ ค๋ฉด "์ข์ ์ถ์ฒ์ด๋ ๋ฌด์์ธ๊ฐ"๋ฅผ ์ฝ๋๋ก ์ ์ํด์ผ ํ๋ค. ์ฐ๋ฆฌ๋ ์ด๋ฅผ 4๊ฐ์ ๋ฉํธ๋ฆญ์ผ๋ก ํํํ๋ค.
has_followers: ํ๋ก์ ์ > 0has_engagement: ์ฐธ์ฌ์จ ๋ฐ์ดํฐ ์กด์ฌhas_platform: ํ๋ซํผ ์ ๋ณด ์กด์ฌhas_description: ์๊ฐ ํ ์คํธ ์กด์ฌcategory_fit: ๊ธฐ๋ ์นดํ ๊ณ ๋ฆฌ์ ์ผ์น
similarity= |๊ด๊ณ ํค์๋ โฉ ์ธํ๋ฃจ์ธ์ ํค์๋| / |๊ด๊ณ ํค์๋|
name_in_db: ์ถ์ฒ ์ด๋ฆ์ด DB์ ์ค์ ๋ก ์กด์ฌcategory_mentioned: ์๋ต์ ํด๋น ์นดํ ๊ณ ๋ฆฌ๊ฐ ์ธ๊ธ๋จfollower_data_exists: ํ๋ก์ ์ ๋ฐ์ดํฐ๊ฐ ์ค์ ๋ก ์์
LOW: ์ด๋ ฅ ์์MEDIUM: ๋์ผ ์นดํ ๊ณ ๋ฆฌ ๊ด๊ณ ์ด๋ ฅ ์์HIGH: ๊ฒฝ์์ฌ ์ง์ ๊ด๊ณ ์ด๋ ฅ ์์ โ FAIL
์ด 4๊ฐ ์ค ๋ช ๊ฐ๋ฅผ ํต๊ณผํ๋์ง ๋น์จ์ด MIPROv2์ ์ ๋ฌ๋๋ ์ต์ข ์ ์์ด๋ค. Optimizer๋ ์ด ์ ์๋ฅผ ๋์ด๋ ๋ฐฉํฅ์ผ๋ก ํ๋กฌํํธ๋ฅผ ์๋์ผ๋ก ์กฐ์ ํ๋ค.
ML์์ ํ์ต ๋ฐ์ดํฐ์ ๊ฒ์ฆ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฆฌํ๋ฏ, DSPy์์๋ ๊ฐ์ ์์น์ ์ ์ฉํ๋ค.
# TEST_CASES (10๊ฐ) - MIPROv2 ์ต์ ํ์ ์ฌ์ฉํ๋ trainset
# ์ด๋์คํ๋ฆฌ, bhc, ์ผ์ฑ์ ์, ๋์ดํค, ์์ด๋น์ค๋น ๋ฑ
# TEST_SET (3๊ฐ) - ์ต์ ํ ์๋ฃ ํ ์ต์ข
์ฑ๋ฅ ๊ฒ์ฆ์๋ง ์ฌ์ฉํ๋ held-out testset
# ๋ค์ด์จ, ์ ์ฃผํญ๊ณต, GS25
held-out testset์ ์ญํ ์ด ์ค์ํ๋ค. Optimizer๊ฐ trainset 10๊ฐ์ ์ ์๋ฅผ ๋์ด๋ ๊ณผ์ ์์ ๊ทธ ๋ฐ์ดํฐ์ ๊ณผ์ ํฉ๋ ์ ์๋ค. ์ต์ ํ์ ํ ๋ฒ๋ ๋ ธ์ถ๋์ง ์์ 3๊ฐ ์ผ์ด์ค๋ก ์ต์ข ์ฑ๋ฅ์ ๊ฒ์ฆํด์ผ ์ค์ ์ผ๋ฐํ ์ฑ๋ฅ์ ์ ์ ์๋ค.
์ค๊ณ๋ฅผ ๋ง๋ฌด๋ฆฌํ๊ณ ์ฝ๋๋ฅผ ๋๋ ค๋ณด๋ฉด์ ํฅ๋ฏธ๋ก์ด ๋ฌธ์ ํ๋๊ฐ ๋๋ฌ๋ฌ๋ค. RankAndRecommend์์ LLM์๊ฒ ์์ 10๋ช
์ ์ ์ ์์ฝ JSON์ ๋๊ธฐ๊ณ TOP3 ์ด๋ฆ์ ๋ฝ์๋ฌ๋ผ๊ณ ํ๋๋ฐ, LLM์ด ๊ฐ๋ JSON์ ์๋ ์ด๋ฆ์ ๋ง๋ค์ด๋๋ค.
# LLM์ด ์ ์ ๊ธฐ์ค TOP3 ์์ ์๋ ์ด๋ฆ์ ํฌํจํ๋ฉด DB ๊ธฐ์ค์ผ๋ก ๋์ฒด
valid_names = {s["name"] for s in scores}
final_top3 = [n for n in llm_top3 if n in valid_names][:3]
if len(final_top3) < 3:
for name in db_top3:
if name not in final_top3:
final_top3.append(name)
if len(final_top3) == 3:
break
์ด ๋ฐฉ์ด ์ฝ๋๋ DSPy๋ฅผ ๋์ ํ๋ฉด์ ์๊ธด ๊ฒ์ด๋ค. ๊ธฐ์กด ReAct ์์ด์ ํธ๋ ๋๊ตฌ๋ฅผ ํตํด DB ๊ฒฐ๊ณผ๋ฅผ ์ง์ ๋ฐ์์ค๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์ข ๋ฅ์ ํ ๋ฃจ์๋ค์ด์ ์ด ๊ตฌ์กฐ์ ์ผ๋ก ์ฐจ๋จ๋๋ค. ๋ฐ๋ฉด DSPy ๋ฐฉ์์์๋ LLM์๊ฒ "์ ์ ์์ฝ ํ ์คํธ"๋ฅผ ๊ฑด๋ค๋ฉด์ ์๋ก์ด ํ ๋ฃจ์๋ค์ด์ ๊ฒฝ๋ก๋ฅผ ๋ง๋ค์ด๋ฒ๋ ธ๋ค.
๊ฐ์ฅ ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ ์๋ค.
์ฒ์ ์๋๋ "๊ธฐ์กด LangChain ์์ด์ ํธ๋ฅผ DSPy๋ก ํ๊ฐํ๊ณ , MIPROv2๋ก ํ๋กฌํํธ๋กค ์ต์ ํํด์ ์์ด์ ํธ ์ฑ๋ฅ์ ๋์ด์"๋ ๊ฒ์ด์๋ค. ๊ทธ๋ฐ๋ฐ ์ค์ ๋ก ๋ง๋ค์ด์ง ๊ฒ์ ๋ค์ ๋ค์ฌ๋ค๋ณด๋ ๊ทธ๊ฒ ์๋์๋ค.
InfluencerMatchingModule์ ๊ธฐ์กด AgentService๋ฅผ ๊ฐ์ผ ๋ํผ๊ฐ ์๋์๋ค. ์์ ํ ๋ค๋ฅธ ์ํคํ
์ฒ์๋ค.
MIPROv2๊ฐ ์ต์ ํํ๋ ๋์์ InferCategories์ RankAndRecommend, ๋ Signature์ ํ๋กฌํํธ์ด๋ค. ๊ทธ๋ฐ๋ฐ ์ด ํ๋กฌํํธ๋ ์ค์ ๋ก ๋ฐฐํฌ๋ ์์ด์ ํธ์ AGENT_SYSTEM_PROMPT์ ์๋ฌด ๊ด๋ จ์ด ์๋ค. DSPy Module ์์์๋ง ์กด์ฌํ๋ ๋ณ๋์ ํ๋กฌํํธ์ด๋ค.
์ฆ, MIPROv2 ์ต์ ํ๋ฅผ ์๋ฌด๋ฆฌ ๋๋ ค๋ ์ค์ ์๋น์ค ์ค์ธ LangChain ์์ด์ ํธ๋ ํ ๊ธ์๋ ๊ฐ์ ๋์ง ์๋๋ค. ์ฐ๋ฆฌ๋ ์์ด์ ํธ๋ฅผ ๋ ์ข๊ฒ ๋ง๋ค๋ ค๊ณ ํ๋๋ฐ, ๊ธฐ์กด ์์ด์ ํธ๋ฅผ ์์ ์ ์ฐ๊ณ ์์ด์ ํธ๋ฅผ ๋ค๋ฅธ ๊ฒ์ผ๋ก ๊ฐ์์น์ด ๋ค์ ๊ทธ๊ฒ์ ์ต์ ํํ๊ณ ์์๋ค.
๊ธฐ์กด ReAct ์์ด์ ํธ์์๋ ์ด๋ฐ ์ข
๋ฅ์ ํ ๋ฃจ์๋ค์ด์
์ด ๊ตฌ์กฐ์ ์ผ๋ก ๋งํ์๋ค. get_influencer_details ๋๊ตฌ๊ฐ DB์์ ์ง์ ์ธํ๋ฃจ์ธ์ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์, ์์ด์ ํธ๊ฐ ์ต์ข
์๋ต์ ์ฐ๋ ์ด๋ฆ์ ๋๊ตฌ ํธ์ถ ๊ฒฐ๊ณผ์์ ์จ ๊ฒ์ด๋ค. LLM์ด ์ด๋ฆ์ ์ง์ด๋ผ ์ฌ์ง๊ฐ ์๋ค.
๋ฐ๋ฉด DSPy Module์ RankAndRecommend๋ LLM์๊ฒ ์ ์ ์์ฝ JSON ํ
์คํธ๋ฅผ ๋๊ธฐ๊ณ , ๊ฑฐ๊ธฐ์ TOP3 ์ด๋ฆ์ ๋ฝ์๋ฌ๋ผ๊ณ ์์ฒญํ๋ค.
scores_summary = json.dumps(scores[:10], ensure_ascii=False, indent=2)
rank_pred = self.rank(
ad_concept=ad_concept,
ad_item=ad_item,
scores_summary=scores_summary, # ํ
์คํธ๋ก ์ ๋ฌ
)
# LLM์ด ๋ฐํํ ์ด๋ฆ: rank_pred.top3_names
๋ฌธ์ ๋ LLM์ด ์ด JSON์ ์ฝ๊ณ TOP3์ ๊ณ ๋ฅด๋ ๊ณผ์ ์์ ๊ฐ๋ JSON์ ์๋ ์ด๋ฆ์ ๋ง๋ค์ด๋๋ค๋ ๊ฒ์ด๋ค. "ํ์์ฐ"์ด ์๋๋ฐ "ํ์์ฐ"์ด top3_names์ ํฌํจ๋๋ ์์ด๋ค.
๊ฒฐ๊ตญ ์ด๋ฅผ ๋ง๊ธฐ ์ํ ๋ฐฉ์ด ์ฝ๋๊ฐ ์๊ฒผ๋ค.
valid_names = {s["name"] for s in scores}
final_top3 = [n for n in llm_top3 if n in valid_names][:3]
# LLM์ด ๋ฝ์ ์ด๋ฆ์ด 3๊ฐ๊ฐ ์ ๋๋ฉด ์ ์ ๊ธฐ์ค ์์ ์ด๋ฆ์ผ๋ก ์ฑ์
if len(final_top3) < 3:
for name in db_top3:
if name not in final_top3:
final_top3.append(name)
if len(final_top3) == 3:
break
์ด ์ฝ๋์ ์กด์ฌ ์์ฒด๊ฐ ๋ฌธ์ ๋ฅผ ๋๋ฌ๋ธ๋ค. DSPy๋ฅผ ๋์ ํ๋ฉด์ "LLM์ด ์์ฐ์ด ํ ์คํธ๋ฅผ ๋ณด๊ณ ์ด๋ฆ์ ์ ํํ๋ค"๋ ์๋ก์ด ๋จ๊ณ๋ฅผ ๋ง๋ค์๊ณ , ๊ทธ ๋จ๊ณ๊ฐ ์๋ก์ด ํ ๋ฃจ์๋ค์ด์ ๊ฒฝ๋ก๊ฐ ๋๋ค. ํด๊ฒฐํ๋ ค๋ ๋ฌธ์ ๋ฅผ ๋ ๋ณต์กํ๊ฒ ๋ง๋ ์ ์ด๋ค.
DSPy์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ MIPROv2์ ์ต์ ํ์ธ๋ฐ, ์ด๊ฒ ๋ก์ปฌ ํ๊ฒฝ์์๋ ์ฐ๊ธฐ ์ด๋ ค์ ๋ค.
MIPROv2๊ฐ ํ๋ ์ผ์ ๋จ์ํํ๋ฉด ์ด๋ ๋ค. ํ๋กฌํํธ ํ๋ณด๋ฅผ ์ฌ๋ฌ ๊ฐ ๋ง๋ค๊ณ , ๊ฐ ํ๋ณด๋ฅผ trainset ์ ์ฒด์ ๋๋ ค๋ณด๊ณ , ์ ์๋ฅผ ๋น๊ตํด์ ๊ฐ์ฅ ์ข์ ๊ฒ์ ์ ํํ๋ค. ์ด ๊ณผ์ ์ ์ฌ๋ฌ ๋ผ์ด๋ ๋ฐ๋ณตํ๋ค.
๊ฐ์ฅ ๊ฐ๋ฒผ์ด ์ค์ ์ธ auto="light"๋ก ํด๋ LLM ํธ์ถ์ด ์์ญ ๋ฒ ๋ฐ์ํ๋ค. ์ฌ๊ธฐ์ trainset 10๊ฐ๋ฅผ ๊ณฑํ๋ฉด ์ด ํธ์ถ ์๊ฐ ์๋ฐฑ ๋ฒ์ ์ด๋ฅธ๋ค.
optimizer = MIPROv2(
metric=metric,
auto="light",
num_threads=1, # ๋ก์ปฌ Ollama: ๋ณ๋ ฌ 1๊ฐ (API ์ฌ์ฉ ์ ๋๋ ค๋ ๋จ)
)
num_threads=1์ด๋ผ๋ ์ฃผ์์ด ์ํฉ์ ์ ๋ณด์ฌ์ค๋ค. OpenAI๋ Anthropic API๋ฅผ ์ฐ๋ฉด ์ค๋ ๋๋ฅผ ๋๋ ค์ ๋ณ๋ ฌ๋ก ๋๋ฆด ์ ์๋๋ฐ, ๋ก์ปฌ Ollama๋ ๊ทธ๊ฒ ์ ๋๋ค. ์์ฒญ์ ํ๋์ฉ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํ๊ณ , ๊ฐ ํธ์ถ์ ๋ช ์ด์ฉ ๊ฑธ๋ฆฌ๋ฉด ์ ์ฒด ์ต์ ํ๊ฐ ๋ช ์๊ฐ์ด ๋ ์ ์๋ค.
์คํฐ๋ ํน์ฑ์ ์คํ์ ๋น ๋ฅด๊ฒ ๋๋ ค๋ณด๊ณ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ ๊ฒ ์ค์ํ๋ฐ, ํ ๋ฒ ์ต์ ํ ์คํ์ ๋ช ์๊ฐ์ด ๊ฑธ๋ฆฌ๋ ๋ฐฉ์์ ์ง์ํ๊ธฐ ์ด๋ ค์ ๋ค.
์ธ ๋ฌธ์ ๋ ๊ฐ๊ฐ ๋ ๋ฆฝ์ ์ธ ๊ฒ์ฒ๋ผ ๋ณด์ด์ง๋ง ํ๋์ ๊ณตํต๋ ์์ธ์์ ์ถ๋ฐํ๋ค.
DSPy๊ฐ ์ง์ง ํ์ ๋ฐํํ๋ ์ํฉ์ ์ด๋ ๋ค. LLM ํธ์ถ์ด 2~3๊ฐ๋ก ๊ตฌ์ฑ๋ ๋ช ํํ ํ์ดํ๋ผ์ธ์ด ์๊ณ , ๊ฐ ํธ์ถ์ ์ ์ถ๋ ฅ์ด ์ ์ ์๋์ด ์์ผ๋ฉฐ, ์ถฉ๋ถํ trainset๊ณผ ๋น ๋ฅธ ์ถ๋ก ํ๊ฒฝ์ด ๊ฐ์ถฐ์ ธ ์์ ๋๋ค.
์ฐ๋ฆฌ ํ๋ก์ ํธ์ ์ํฉ์ ๋ฌ๋๋ค. ํ๊ฐํ๋ ค๋ ๋์์ ReAct ์์ด์ ํธ, ์ฆ LLM์ด ํ๋ฆ์ ์์จ์ ์ผ๋ก ๊ฒฐ์ ํ๋ ๋ฐฉ์์ด์๋ค. ์ด ๊ตฌ์กฐ๋ forward()๋ก ๊ณ ์ ๋ ํ๋ฆ์ ์ ์ ํ๋ DSPy Module๊ณผ ๋ง์ง ์์๋ค. ๋ง์ถ๋ ค๋ค ๋ณด๋ ์์ด์ ํธ๋ฅผ ๊ต์ฒดํ๋ ๊ฒฐ๊ณผ๊ฐ ๋๊ณ , ๊ต์ฒด ๊ณผ์ ์์ ์คํ๋ ค ์๋ก์ด ๋ฌธ์ ๊ฐ ์๊ฒผ์ผ๋ฉฐ, ์ต์ ํ๋ฅผ ๋๋ฆฌ๊ธฐ์๋ ํ๊ฒฝ์ด ๋๋ฌด ๋๋ ธ๋ค.
๋จ๊ณ๋ณ ํ์ดํ๋ผ์ธ์ ๊ตฌ์กฐ๊ฐ ๋จ์ํ๋ค. ๊ธฐ์กด LangChain ์์ด์ ํธ๋ฅผ ๊ทธ๋๋ก ์คํํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ธก์ ํ๋ค.
AgentService.run_matching() ํธ์ถ (์ค์ ์์ด์ ํธ)outputs/step1_results.json์ ์ ์ฅoutputs/step2_evidence.json์ ์ ์ฅoutputs/step3_metrics.json์ ์ ์ฅevaluation/reports/ ํด๋์ ์ ์ฅDSPy Module์ด ์์ด์ ํธ๋ฅผ ๊ต์ฒดํ๋ ๊ฒ๊ณผ ๋ฌ๋ฆฌ, ์ฌ๊ธฐ์๋ ์์ด์ ํธ๊ฐ ๋ฐ๋์ง ์๋๋ค. Step 1์์ ์ค์ ๋ก AgentService๊ฐ ์คํ๋๊ณ , Step 2~4๋ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ถ์ํ ๋ฟ์ด๋ค. ํ๊ฐ ํ์ดํ๋ผ์ธ์ด ์์ด์ ํธ์ ๋ด๋ถ ๊ตฌ์กฐ์ ์ ํ ๊ฐ์
ํ์ง ์๋๋ค.
๊ฐ ๋จ๊ณ๊ฐ JSON ํ์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๊ณ ๋ค์ ๋จ๊ณ๊ฐ ๊ทธ๊ฒ์ ์ฝ๋ ๊ตฌ์กฐ๊ฐ ์ฒ์์๋ ๋จ์ํ ํธ์ ์ค๊ณ์ฒ๋ผ ๋ณด์ผ ์ ์๋ค. ๊ทธ๋ฐ๋ฐ ์ค์ ๋ก ์จ๋ณด๋ฉด ์ด๊ฒ ๊ฝค ์ค์ํ๋ค.
LLM ํธ์ถ์ด ํฌํจ๋ Step 1์ ๋๋ฆฌ๊ณ ๋น์ฉ์ด ๋ ๋ค. 10๊ฐ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์ ๋ถ ๋๋ฆฌ๋ฉด ๋ช ๋ถ์ด ๊ฑธ๋ฆฐ๋ค. ๋ฐ๋ฉด Step 2~4๋ ์ ์ฅ๋ JSON์ ์ฝ์ด์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๊ฑฐ์ ์ฆ์ ๋๋๋ค.
๋ฉํธ๋ฆญ ๊ณ์ฐ ๋ก์ง์ ์กฐ๊ธ ๋ฐ๊พธ๊ณ ์ถ์ ๋ Step 1์ ๋ค์ ๋๋ฆด ํ์ ์์ด, Step 3๋ง ๋ค์ ์คํํ๋ฉด ๋๋ค. ๋ฆฌํฌํธ ํ์์ ๋ฐ๊พธ๊ณ ์ถ์ผ๋ฉด Step 4๋ง ๋ค์ ์คํํ๋ฉด ๋๋ค. MIPROv2๊ฐ ๋ชจ๋ ๊ณผ์ ์ ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ฉด์ ์ค๊ฐ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๊ธฐ ์ด๋ ค์ ๋ ๊ฒ๊ณผ ๋์กฐ์ ์ด๋ค.
DSPy ๋ฐฉ์๊ณผ ๋จ๊ณ๋ณ ํ์ดํ๋ผ์ธ ๋ฐฉ์์ ์ ์ด์ ํด๊ฒฐํ๋ ค๋ ๋ฌธ์ ์ ์ฑ๊ฒฉ์ด ๋ฌ๋๋ค.
DSPy์ MIPROv2๊ฐ ์ง์ง ํ์ ๋ฐํํ๋ ์ํฉ์ ๋ช ํํ๊ฒ ์ ์๋ LLM ํ์ดํ๋ผ์ธ์ด ์๊ณ , ์ง๊ธ ๊ทธ ํ์ดํ๋ผ์ธ์ ํ๋กฌํํธ๋ฅผ ๋ ์ข๊ฒ ๋ง๋ค๊ณ ์ถ์ ๋๋ค. ์๋ฅผ ๋ค์ด "๋ถ๋ฅ ์ ํ๋๋ฅผ ๋์ด๊ณ ์ถ๋ค", "์์ฝ ํ์ง์ ๋์ด๊ณ ์ถ๋ค"์ฒ๋ผ LLM ํธ์ถ ์์ฒด๋ฅผ ์ต์ ํํ๋ ๊ฒ ๋ชฉํ์ผ ๋ ๊ฐ๋ ฅํ๋ค.
์ฐ๋ฆฌ๊ฐ ์ค์ ๋ก ํ์ํ๋ ๊ฒ์ ๋ฌ๋๋ค. ์์ด์ ํธ๊ฐ ์ง๊ธ ์ ์๋ํ๊ณ ์๋์ง ์ธก์ ํ๋ ๊ฒ, ์ฆ ํ๊ฐ๊ฐ ๋จผ์ ์๋ค. ๋ฌด์์ด ๋ฌธ์ ์ธ์ง๋ ๋ชจ๋ฅด๋ ์ํ์์ ์ต์ ํ๋ถํฐ ํ๋ ค ํ๋ ๊ฒ์ด๋ค.
| DSPy + MIPROv2 | ๋จ๊ณ๋ณ ํ์ดํ๋ผ์ธ | |
|---|---|---|
| ๋ชฉ์ | LLM ํ์ดํ๋ผ์ธ ํ๋กฌํํธ ์๋ ์ต์ ํ | ๊ธฐ์กด ์์ด์ ํธ ํ์ง ์ธก์ |
| ๋์ | DSPy๋ก ์๋ก ์ค๊ณํ ๋ณ๋ ์์คํ | ์ค์ ๋ฐฐํฌ ์ค์ธ ์์ด์ ํธ |
| ์คํ ํ๊ฒฝ | ๋ก์ปฌ Ollama์์ ์ต์ ํ ์คํ์ด ๋นํ์ค์ | ๋ก์ปฌ Ollama์์ ์ถฉ๋ถํ ๋์ |
| ๊ฒฐ๊ณผ ํด์ | ์ต์ ํ ์๋ฃ๊น์ง ๊ฒฐ๊ณผ ํ์ธ ์ด๋ ค์ | ๋จ๊ณ๋ณ JSON์ผ๋ก ์ค๊ฐ ํ์ธ ๊ฐ๋ฅ |
| ์์ด์ ํธ ์ฝ๋ ๋ณ๊ฒฝ | ์์ด์ ํธ ์์ฒด๊ฐ ๊ต์ฒด๋จ | ์์ |
DSPy๋ก๋ ๊ธฐ์กด LangChain ์์ด์ ํธ๋ฅผ ์ต์ ํํ ๋ฐฉ๋ฒ์ด ์๋ค.
MIPROv2๊ฐ ํ๋กฌํํธ๋ฅผ ์ต์ ํํ๋ ค๋ฉด "์ด๋ค ํ๋กฌํํธ๋ฅผ ๋ฐ๊ฟ์ผ ํ๋์ง" ์์์ผ ํ๋ค. ๊ทธ๋ฌ๋ฌ๋ฉด ์์คํ
์์ DSPy์ Signature๊ณผ Predictor๊ฐ ์์ด์ผ ํ๋ค. MIPROv2๋ ๊ทธ๊ฒ๋ค์ ์ฐพ์์ ํ๋ํ๋ค.
LangChain ReAct ์์ด์ ํธ๋ DSPy์ ๋ฌด๊ดํ๊ฒ ๋ง๋ค์ด์ง ๋ธ๋๋ฐ์ค์ด๋ค. MIPROv2 ์ ์ฅ์์๋ ๋ด๋ถ๋ฅผ ๋ค์ฌ๋ค๋ณผ ์๊ฐ ์๋ค.
์ฆ, DSPy + MIPROv2๋ ์ฒ์๋ถํฐ DSPy๋ก ์ค๊ณ๋ ์์คํ
์ ์ต์ ํํ๋ ๋๊ตฌ์ด๋ค. ๊ธฐ์กด์ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋ง๋ค์ด์ง ์์ด์ ํธ๋ฅผ ํ๊ฐํ๊ฑฐ๋ ๊ฐ์ ํ๋ ๋๊ตฌ๊ฐ ์๋๋ค.
์ฐ๋ฆฌ ํ๋ก์ ํธ์ฒ๋ผ ์ด๋ฏธ LangChain์ผ๋ก ๋ง๋ค์ด์ง ์์ด์ ํธ๊ฐ ์๋ ์ํฉ์์๋ ์ ์ด์ DSPy๊ฐ ๋ง๋ ๋๊ตฌ๊ฐ ์๋์๋ ๊ฑฐ๊ณ , ๊ทธ ๋ถ์ผ์น๋ฅผ ํด๊ฒฐํ๋ ค๋ค ์์ด์ ํธ๋ฅผ ๊ต์ฒดํ๋ ๊ฒฐ๊ณผ๋ก ์ด์ด์ง ๊ฒ์ด๋ค.
forward()๋ ์คํ ํ๋ฆ์ ์ฝ๋๋ก ๊ณ ์ ํ๋ค. ์ด ๊ตฌ์กฐ๋ "ํญ์ ๊ฐ์ ๋จ๊ณ๋ฅผ ๊ฐ์ ์์๋ก ์คํํ๋" ํ์ดํ๋ผ์ธ์ ์ ํฉํ๋ค. ์๋ฅผ ๋ค์ด, "๋ฌธ์ ์์ฝ โ ํต์ฌ ํค์๋ ์ถ์ถ โ ์นดํ
๊ณ ๋ฆฌ ๋ถ๋ฅ"์ฒ๋ผ ์์๊ฐ ๋ช
ํํ๊ฒ ์ ํด์ง ํ์ดํ๋ผ์ธ์ด ์ข์ ์๋ค.forward()๋ก ํํํ๊ธฐ ์ด๋ ต๊ณ , ์ต์ง๋ก ๋ง์ถ๋ฉด ์์ด์ ํธ์ ์ ์ฐํจ์ ์๋๋ค.| ์ ํฉ | ๋ถ์ ํฉ | |
|---|---|---|
| ์์คํ ์ค๊ณ ์์ | ์ฒ์๋ถํฐ DSPy๋ก ์ค๊ณ | ๊ธฐ์กด ์์คํ ์ ๋์ค์ DSPy๋ก ์ ํ |
| ์์ด์ ํธ ์ ํ | ํ๋ฆ์ด ๊ณ ์ ๋ ํ์ดํ๋ผ์ธ | ๋์ ์ผ๋ก ๊ฒฝ๋ก๊ฐ ๋ฐ๋๋ ReAct ์์ด์ ํธ |
| ํ์ง ๊ธฐ์ค | metric์ ์ฝ๋๋ก ์ ์ ๊ฐ๋ฅ | ์ฃผ๊ด์ ํ๋จ์ ์์กด |
| ์ถ๋ก ํ๊ฒฝ | API ๊ธฐ๋ฐ (๋ณ๋ ฌ ํธ์ถ ๊ฐ๋ฅ) | ๋ก์ปฌ ๋ชจ๋ธ (์์ฐจ ์ฒ๋ฆฌ๋ง ๊ฐ๋ฅ) |
| ํ์ฌ ์ํ | ํ๊ฐ๋ ๋ง์ณค๊ณ ํ๋กฌํํธ๋ฅผ ์๋์ผ๋ก ์ต์ ํํ๊ณ ์ถ์ ๋ | ํ๊ฐ ์์ฒด๊ฐ ์์ง ์ ๋์ ๋ |