AI ยท LLM ยท

[AI] ๐Ÿค CrewAI: ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ AI ํ˜‘์—… ํ”„๋ ˆ์ž„์›Œํฌ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

CrewAI๋Š” ์—ฌ๋Ÿฌ AI Agent๊ฐ€ ์ธ๊ฐ„ ํŒ€์ฒ˜๋Ÿผ ์—ญํ• ์„ ๋‚˜๋ˆ„๊ณ  ํ˜‘๋ ฅํ•˜์—ฌ ๋ณต์žกํ•œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ๋‹จ์ผ LLM ํ˜ธ์ถœ๋กœ๋Š” ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์šด ๋ฆฌ์„œ์น˜ยท์ž‘์„ฑยท๋ถ„์„ยท์ฝ”๋”ฉ ๋“ฑ ๋‹ค๋‹จ๊ณ„ ์ž‘์—…์„ Agent ํŒ€์ด ๋ถ„์—…ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” ํ•ต์‹ฌ ๊ตฌ์„ฑ์š”์†Œ, ํ”„๋กœ์„ธ์Šค ์œ ํ˜•, ๋ฉ”๋ชจ๋ฆฌ ์‹œ์Šคํ…œ, Python ์ฝ”๋“œ ์˜ˆ์ œ๋ฅผ ์‹ค๋ฌด ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


๐Ÿค CrewAI๋ž€? #

CrewAI๋Š” ์—ญํ•  ๊ธฐ๋ฐ˜ AI Agent ํ˜‘์—… ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. ๊ฐ Agent๋Š” ํŠน์ • ์ „๋ฌธ ์—ญํ• (๋ฆฌ์„œ์ฒ˜, ์ž‘๊ฐ€, ๋ถ„์„๊ฐ€ ๋“ฑ)์„ ๋ถ€์—ฌ๋ฐ›๊ณ , ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ Agent์—๊ฒŒ ์œ„์ž„ํ•˜๋ฉด์„œ ํŒ€ ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

1์‚ฌ์šฉ์ž ์ž…๋ ฅ (๋ชฉํ‘œ)
2        โ†“
3    Crew ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ
4   โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”
5Agent A    Agent B    Agent C
6(๋ฆฌ์„œ์ฒ˜)   (์ž‘๊ฐ€)     (๊ฒ€์ˆ˜์ž)
7   โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜
8    ์ตœ์ข… ๊ฒฐ๊ณผ๋ฌผ
ํ•ญ๋ชฉ๋‹จ์ผ LLMCrewAI ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ
์ž‘์—… ๋ณต์žก๋„๋‹จ์ˆœ ๋‹จ๋ฐœ์„ฑ๋‹ค๋‹จ๊ณ„ยท๋ณตํ•ฉ ์ž‘์—…
์ „๋ฌธํ™”๋ฒ”์šฉ์—ญํ• ๋ณ„ ์ „๋ฌธํ™”
์ปจํ…์ŠคํŠธ๋‹จ์ผ ๋Œ€ํ™”์—์ด์ „ํŠธ ๊ฐ„ ๊ณต์œ ยท๋ˆ„์ 
ํ™•์žฅ์„ฑ์ œํ•œ์ Agent ์ถ”๊ฐ€๋กœ ์ˆ˜ํ‰ ํ™•์žฅ

๐Ÿงฑ ํ•ต์‹ฌ ๊ตฌ์„ฑ์š”์†Œ #

Agent โ€” ์ž์œจ ์ž‘์—… ๋‹จ์œ„ #

Agent๋Š” ํŠน์ • ์—ญํ• ๊ณผ ๋ชฉํ‘œ๋ฅผ ๊ฐ€์ง„ ์ž์œจ์  AI ์†Œํ”„ํŠธ์›จ์–ด์ž…๋‹ˆ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ์„ค๋ช…
roleAgent์˜ ๊ธฐ๋Šฅ/์ง์ฑ… ์ •์˜
goal๊ฐœ๋ณ„ ๋ชฉํ‘œ๋กœ ์˜์‚ฌ๊ฒฐ์ • ๋ฐฉํ–ฅ ์ œ์‹œ
backstory๋ฐฐ๊ฒฝ ์Šคํ† ๋ฆฌ๋กœ ์—ญํ•  ๋งฅ๋ฝ ๊ฐ•ํ™”
tools์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„๊ตฌ ๋ชฉ๋ก
llm์‚ฌ์šฉํ•  LLM (๊ธฐ๋ณธ๊ฐ’: OpenAI GPT-4)
verboseTrue ์„ค์ • ์‹œ ์‹คํ–‰ ๋กœ๊ทธ ์ถœ๋ ฅ
allow_delegationTrue๋ฉด ๋‹ค๋ฅธ Agent์—๊ฒŒ ์ž‘์—… ์œ„์ž„ ๊ฐ€๋Šฅ
memoryTrue๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์‹œ์Šคํ…œ ํ™œ์„ฑํ™”
max_iter์ตœ๋Œ€ ๋ฐ˜๋ณต ํšŸ์ˆ˜ (๊ธฐ๋ณธ๊ฐ’: 15)

Task โ€” ๊ตฌ์ฒด์  ์ž‘์—… ๋‹จ์œ„ #

Agent๊ฐ€ ์ˆ˜ํ–‰ํ•  ๋ช…ํ™•ํ•œ ๊ณผ์ œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ์„ค๋ช…
description์ž‘์—… ๋‚ด์šฉ๊ณผ ์ˆ˜ํ–‰ ๋ฐฉ๋ฒ•
agent๋‹ด๋‹น Agent ์ง€์ •
expected_output๊ธฐ๋Œ€ ๊ฒฐ๊ณผ๋ฌผ ํ˜•์‹ ์„ค๋ช…
toolsํ•ด๋‹น Task์—๋งŒ ์‚ฌ์šฉ๋˜๋Š” ๋„๊ตฌ
context์ด์ „ Task ์ถœ๋ ฅ์„ ์ปจํ…์ŠคํŠธ๋กœ ์ „๋‹ฌ
output_file๊ฒฐ๊ณผ๋ฅผ ํŒŒ์ผ๋กœ ์ €์žฅ

Tool โ€” Agent์˜ ์™ธ๋ถ€ ๋Šฅ๋ ฅ #

Agent๊ฐ€ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ฑฐ๋‚˜ ์™ธ๋ถ€ ์„œ๋น„์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

1from crewai_tools import SerperDevTool, ScrapeWebsiteTool, WebsiteSearchTool
2
3search_tool = SerperDevTool()      # Google ๊ฒ€์ƒ‰
4scrape_tool = ScrapeWebsiteTool()  # ์›น ํŽ˜์ด์ง€ ์Šคํฌ๋ž˜ํ•‘
5rag_tool    = WebsiteSearchTool()  # ์›น ์ฝ˜ํ…์ธ  RAG ๊ฒ€์ƒ‰

Process โ€” ์ž‘์—… ํ๋ฆ„ ์กฐ์œจ ๋ฐฉ์‹ #

ํ”„๋กœ์„ธ์Šค์„ค๋ช…
Process.sequential์ž‘์—…์„ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰, ์•ž ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค์Œ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋จ
Process.hierarchical๋งค๋‹ˆ์ € Agent๊ฐ€ ์ž‘์—…์„ ํ•˜์œ„ Agent์—๊ฒŒ ์œ„์ž„ยท๊ฒ€์ˆ˜

Crew โ€” ์ตœ์ƒ์œ„ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ #

Agent์™€ Task๋ฅผ ๋ชจ์•„์„œ ์‹คํ–‰ ๊ณ„ํš์„ ๊ด€๋ฆฌํ•˜๋Š” ์ตœ์ƒ์œ„ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.


๐Ÿ“ฆ ์„ค์น˜ #

1pip install crewai
2pip install 'crewai[tools]'   # ๊ธฐ๋ณธ ๋„๊ตฌ ๋ชจ์Œ ํฌํ•จ

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ • (.env ํŒŒ์ผ):

1OPENAI_API_KEY=sk-...
2SERPER_API_KEY=...             # SerperDevTool ์‚ฌ์šฉ ์‹œ ํ•„์š”

๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘: ๋ธ”๋กœ๊ทธ ๊ธ€ ์ž๋™ ์ž‘์„ฑ ์—์ด์ „ํŠธ #

๋ฆฌ์„œ์ฒ˜ Agent๊ฐ€ ์ฃผ์ œ๋ฅผ ์กฐ์‚ฌํ•˜๊ณ , ์—๋””ํ„ฐ Agent๊ฐ€ ๋ธ”๋กœ๊ทธ ๊ธ€์„ ์ž‘์„ฑํ•˜๋Š” 2์ธ ํŒ€ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

 1import os
 2from dotenv import load_dotenv
 3from crewai import Agent, Task, Crew, Process
 4from crewai_tools import SerperDevTool, ScrapeWebsiteTool
 5
 6load_dotenv()
 7os.environ["OPENAI_MODEL_NAME"] = "gpt-4o"
 8
 9# โ”€โ”€ Tools โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
10search_tool = SerperDevTool()
11scrape_tool = ScrapeWebsiteTool()
12
13# โ”€โ”€ Agents โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
14researcher = Agent(
15    role="์‹œ๋‹ˆ์–ด ๋ฆฌ์„œ์ฒ˜",
16    goal="์ฃผ์–ด์ง„ ์ฃผ์ œ์— ๋Œ€ํ•ด ์›น์—์„œ ์ตœ์‹  ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ํ•ต์‹ฌ ์ธ์‚ฌ์ดํŠธ๋ฅผ ์ •๋ฆฌํ•œ๋‹ค",
17    backstory="๋‹ค์–‘ํ•œ ๊ธฐ์ˆ  ๋ฌธ์„œ์™€ ๋ธ”๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ตœ๊ณ ์˜ ๋ฆฌ์„œ์น˜ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š” ์ „๋ฌธ๊ฐ€",
18    tools=[search_tool, scrape_tool],
19    allow_delegation=False,
20    verbose=True,
21)
22
23editor = Agent(
24    role="IT ๋ธ”๋กœ๊ฑฐ / ์—๋””ํ„ฐ",
25    goal="๋ฆฌ์„œ์น˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ฝ๊ธฐ ์‰ฝ๊ณ  ์‹ค์šฉ์ ์ธ ๋ธ”๋กœ๊ทธ ๊ธ€์„ ์ž‘์„ฑํ•œ๋‹ค",
26    backstory="๊ฐœ๋ฐœ์ž ๋…์ž๋ฅผ ์œ„ํ•ด ๋ช…ํ™•ํ•˜๊ณ  ์œ ์ตํ•œ ๊ธฐ์ˆ  ์ฝ˜ํ…์ธ ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ „๋ฌธ ๋ธ”๋กœ๊ฑฐ",
27    verbose=True,
28)
29
30# โ”€โ”€ Tasks โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
31research_task = Task(
32    description="""'{topic}'์— ๊ด€ํ•œ ์ตœ์‹  ์ •๋ณด๋ฅผ ์›น์—์„œ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๋ถ„์„ํ•˜์„ธ์š”.
33    ํ•ต์‹ฌ ๊ฐœ๋…, ์ฃผ์š” ๊ธฐ๋Šฅ, ์‹ค์ œ ํ™œ์šฉ ์‚ฌ๋ก€๋ฅผ ์ •๋ฆฌํ•˜์„ธ์š”.""",
34    agent=researcher,
35    expected_output="์ฃผ์ œ์˜ ํ•ต์‹ฌ ๋‚ด์šฉ, ์ฃผ์š” ๊ธฐ๋Šฅ, ํ™œ์šฉ ์‚ฌ๋ก€๋ฅผ ํฌํ•จํ•œ ๋ฆฌ์„œ์น˜ ๋ณด๊ณ ์„œ",
36)
37
38writing_task = Task(
39    description="""๋ฆฌ์„œ์น˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ '{topic}'์— ๋Œ€ํ•œ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
40    ๋„์ž…๋ถ€, ์ฃผ์š” 3๊ฐœ ์„น์…˜, ์‹ค์šฉ์  ์˜ˆ์‹œ, ๊ฒฐ๋ก ์œผ๋กœ ๊ตฌ์„ฑํ•˜์„ธ์š”.""",
41    agent=editor,
42    expected_output="๋งˆํฌ๋‹ค์šด ํ˜•์‹์˜ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ (๋„์ž…๋ถ€, 3๊ฐœ ์„น์…˜, ๊ฒฐ๋ก  ํฌํ•จ)",
43    context=[research_task],       # ๋ฆฌ์„œ์น˜ ๊ฒฐ๊ณผ๋ฅผ ์ปจํ…์ŠคํŠธ๋กœ ์ „๋‹ฌ
44    output_file="output_blog.md",
45)
46
47# โ”€โ”€ Crew โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
48crew = Crew(
49    agents=[researcher, editor],
50    tasks=[research_task, writing_task],
51    process=Process.sequential,
52    verbose=True,
53)
54
55result = crew.kickoff(inputs={"topic": "CrewAI ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ํ”„๋ ˆ์ž„์›Œํฌ"})
56print(result.raw)

โš™๏ธ ํ”„๋กœ์„ธ์Šค ์œ ํ˜• #

Sequential (์ˆœ์ฐจ ์ฒ˜๋ฆฌ) #

์ž‘์—… ๋ชฉ๋ก ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ํ•˜๋ฉฐ, ์•ž Task์˜ ์ถœ๋ ฅ์ด ๋‹ค์Œ Task์˜ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ํ๋ฆ„์ž…๋‹ˆ๋‹ค.

1crew = Crew(
2    agents=[researcher, editor, reviewer],
3    tasks=[research_task, writing_task, review_task],
4    process=Process.sequential,
5)
1research_task โ†’ writing_task โ†’ review_task
2    (์ถœ๋ ฅ)  โ†’   (์ปจํ…์ŠคํŠธ)  โ†’   (์ปจํ…์ŠคํŠธ)

Hierarchical (๊ณ„์ธต ์ฒ˜๋ฆฌ) #

๋งค๋‹ˆ์ € Agent๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋˜์–ด ์ž‘์—…์„ ํ•˜์œ„ Agent์—๊ฒŒ ์œ„์ž„ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ˆ˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณต์žกํ•˜๊ณ  ๋™์ ์ธ ์›Œํฌํ”Œ๋กœ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

1from langchain_openai import ChatOpenAI
2
3crew = Crew(
4    agents=[researcher, editor, reviewer],
5    tasks=[complex_task],
6    process=Process.hierarchical,
7    manager_llm=ChatOpenAI(model="gpt-4o"),  # ๋งค๋‹ˆ์ € LLM ์ง€์ •
8)
1Manager Agent (์ž๋™ ์ƒ์„ฑ)
2  โ”œโ”€โ”€ researcher ์—๊ฒŒ ์กฐ์‚ฌ ์œ„์ž„
3  โ”œโ”€โ”€ editor ์—๊ฒŒ ์ž‘์„ฑ ์œ„์ž„
4  โ””โ”€โ”€ ๊ฒฐ๊ณผ ๊ฒ€์ˆ˜ ํ›„ ์ตœ์ข… ์Šน์ธ

๐Ÿง  ๋ฉ”๋ชจ๋ฆฌ ์‹œ์Šคํ…œ #

CrewAI v1.10+ ์—์„œ๋Š” ํ†ตํ•ฉ ๋ฉ”๋ชจ๋ฆฌ ์‹œ์Šคํ…œ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Agent๊ฐ€ ์ด์ „ ์‹คํ–‰ ๋งฅ๋ฝ์„ ๊ธฐ์–ตํ•˜๊ณ  ์ค‘๋ณต ์ž‘์—…์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ์œ ํ˜•๋ฒ”์œ„์„ค๋ช…
Short-termํ˜„์žฌ Crew ์‹คํ–‰ ๋‚ด๋Œ€ํ™” ์ค‘ ์ปจํ…์ŠคํŠธ ์œ ์ง€
Long-term์‹คํ–‰ ๊ฐ„ ์˜์†๋ฒกํ„ฐ ์Šคํ† ์–ด์— ์ €์žฅ, ์žฌ์‹คํ–‰ ์‹œ ํ™œ์šฉ
Entityํ˜„์žฌ ์‹คํ–‰ ๋‚ด์–ธ๊ธ‰๋œ ์ธ๋ฌผยท์กฐ์งยท๊ฐœ๋… ์ถ”์ 
 1researcher = Agent(
 2    role="๋ฆฌ์„œ์ฒ˜",
 3    goal="์ •๋ณด ์ˆ˜์ง‘",
 4    backstory="...",
 5    memory=True,           # ๋ฉ”๋ชจ๋ฆฌ ํ™œ์„ฑํ™”
 6)
 7
 8crew = Crew(
 9    agents=[researcher],
10    tasks=[task],
11    memory=True,           # Crew ๋ ˆ๋ฒจ ๋ฉ”๋ชจ๋ฆฌ ํ™œ์„ฑํ™”
12)

๐Ÿ”ง ์ปค์Šคํ…€ Tool ๊ตฌํ˜„ #

@tool ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ Python ํ•จ์ˆ˜๋ฅผ Agent๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๋กœ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

 1from crewai.tools import tool
 2
 3@tool('naver_news_search')
 4def search_naver_news(query: str) -> str:
 5    """๋„ค์ด๋ฒ„ ๋‰ด์Šค API์—์„œ ์ตœ์‹  ๊ธฐ์‚ฌ URL์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค."""
 6    import requests
 7    # ์‹ค์ œ API ํ˜ธ์ถœ ๊ตฌํ˜„
 8    response = requests.get(
 9        "https://openapi.naver.com/v1/search/news.json",
10        params={"query": query, "display": 5},
11        headers={
12            "X-Naver-Client-Id": os.getenv("NAVER_CLIENT_ID"),
13            "X-Naver-Client-Secret": os.getenv("NAVER_CLIENT_SECRET"),
14        }
15    )
16    items = response.json().get("items", [])
17    return "\n".join(item["link"] for item in items)

Tip: docstring์ด Tool์˜ ์„ค๋ช…์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. Agent๊ฐ€ ์–ธ์ œ ์ด Tool์„ ์จ์•ผ ํ•˜๋Š”์ง€ ๋ช…ํ™•ํžˆ ์ž‘์„ฑํ•˜๋ฉด ํ˜ธ์ถœ ์ •ํ™•๋„๊ฐ€ ๋†’์•„์ง‘๋‹ˆ๋‹ค.


๐Ÿ“‹ YAML ์„ค์ • ๋ฐฉ์‹ (๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ ๊ถŒ์žฅ) #

Agent์™€ Task ์ •์˜๋ฅผ YAML ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด ์ฝ”๋“œ์™€ ์„ค์ •์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

config/agents.yaml:

1researcher:
2  role: ์‹œ๋‹ˆ์–ด ๋ฆฌ์„œ์ฒ˜
3  goal: ์ฃผ์–ด์ง„ ์ฃผ์ œ์˜ ์ตœ์‹  ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ํ•ต์‹ฌ ์ธ์‚ฌ์ดํŠธ๋ฅผ ์ •๋ฆฌํ•œ๋‹ค
4  backstory: ๋‹ค์–‘ํ•œ ๊ธฐ์ˆ  ๋ฌธ์„œ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ตœ๊ณ ์˜ ๋ฆฌ์„œ์น˜ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š” ์ „๋ฌธ๊ฐ€
5
6editor:
7  role: IT ๋ธ”๋กœ๊ฑฐ ์—๋””ํ„ฐ
8  goal: ๋ฆฌ์„œ์น˜ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ธฐ ์‰ฌ์šด ๋ธ”๋กœ๊ทธ ๊ธ€๋กœ ์ž‘์„ฑํ•œ๋‹ค
9  backstory: ๊ฐœ๋ฐœ์ž ๋…์ž๋ฅผ ์œ„ํ•ด ๋ช…ํ™•ํ•œ ๊ธฐ์ˆ  ์ฝ˜ํ…์ธ ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ „๋ฌธ ๋ธ”๋กœ๊ฑฐ

config/tasks.yaml:

 1research_task:
 2  description: "{topic}"์— ๊ด€ํ•œ ์ตœ์‹  ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ํ•ต์‹ฌ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜์„ธ์š”.
 3  expected_output: ํ•ต์‹ฌ ๊ฐœ๋…, ์ฃผ์š” ๊ธฐ๋Šฅ, ํ™œ์šฉ ์‚ฌ๋ก€๋ฅผ ํฌํ•จํ•œ ๋ฆฌ์„œ์น˜ ๋ณด๊ณ ์„œ
 4  agent: researcher
 5
 6writing_task:
 7  description: ๋ฆฌ์„œ์น˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ "{topic}" ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
 8  expected_output: ๋งˆํฌ๋‹ค์šด ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ
 9  agent: editor
10  context:
11    - research_task

crew.py:

 1from crewai import Agent, Task, Crew, Process
 2from crewai.project import CrewBase, agent, task, crew
 3
 4@CrewBase
 5class BlogCrew:
 6    agents_config = 'config/agents.yaml'
 7    tasks_config  = 'config/tasks.yaml'
 8
 9    @agent
10    def researcher(self) -> Agent:
11        return Agent(config=self.agents_config['researcher'], tools=[search_tool])
12
13    @agent
14    def editor(self) -> Agent:
15        return Agent(config=self.agents_config['editor'])
16
17    @task
18    def research_task(self) -> Task:
19        return Task(config=self.tasks_config['research_task'])
20
21    @task
22    def writing_task(self) -> Task:
23        return Task(config=self.tasks_config['writing_task'])
24
25    @crew
26    def crew(self) -> Crew:
27        return Crew(
28            agents=self.agents,
29            tasks=self.tasks,
30            process=Process.sequential,
31        )

๐Ÿ—๏ธ ์‹ค์ „ ์˜ˆ์ œ: ๋‰ด์Šค ๋ฆฌ์„œ์น˜ ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ #

๊ฒ€์ƒ‰ โ†’ ๋‚ด์šฉ ์ถ”์ถœ โ†’ ์š”์•ฝ ์‘๋‹ต์„ 3๊ฐœ Agent๊ฐ€ ๋ถ„๋‹ดํ•˜๋Š” ํŒŒ์ดํ”„๋ผ์ธ์ž…๋‹ˆ๋‹ค.

 1from crewai import Agent, Task, Crew, Process
 2from crewai.tools import tool
 3
 4@tool('fetch_article')
 5def fetch_article(url: str) -> str:
 6    """์ฃผ์–ด์ง„ URL์˜ ๊ธฐ์‚ฌ ๋ณธ๋ฌธ์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค."""
 7    from newspaper import Article
 8    article = Article(url)
 9    article.download()
10    article.parse()
11    return article.text
12
13# Agents
14searcher = Agent(
15    role="๋‰ด์Šค ๊ฒ€์ƒ‰ ์ „๋ฌธ๊ฐ€",
16    goal="์‚ฌ์šฉ์ž ์งˆ๋ฌธ์— ๋งž๋Š” ์ตœ์‹  ๋‰ด์Šค URL์„ ์ˆ˜์ง‘ํ•œ๋‹ค",
17    backstory="๋‹ค์–‘ํ•œ ๋‰ด์Šค ์†Œ์Šค์—์„œ ์ •ํ™•ํ•œ ๊ธฐ์‚ฌ๋ฅผ ์ฐพ๋Š” ๋ฆฌ์„œ์น˜ ์ „๋ฌธ๊ฐ€",
18    tools=[search_tool],
19)
20
21analyzer = Agent(
22    role="์ฝ˜ํ…์ธ  ๋ถ„์„๊ฐ€",
23    goal="๊ธฐ์‚ฌ URL์—์„œ ๋ณธ๋ฌธ์„ ์ถ”์ถœํ•˜๊ณ  ํ•ต์‹ฌ ๋‚ด์šฉ์„ ๋ถ„์„ํ•œ๋‹ค",
24    backstory="๋ณต์žกํ•œ ๊ธฐ์‚ฌ๋ฅผ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ํ•ต์‹ฌ์„ ์ถ”์ถœํ•˜๋Š” ๋ถ„์„๊ฐ€",
25    tools=[fetch_article],
26)
27
28answerman = Agent(
29    role="์ตœ์ข… ์‘๋‹ต ์ž‘์„ฑ์ž",
30    goal="๋ถ„์„๋œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ช…ํ™•ํ•œ ๋‹ต๋ณ€์„ ์ œ๊ณตํ•œ๋‹ค",
31    backstory="๋ณต์žกํ•œ ์ •๋ณด๋ฅผ ์‰ฝ๊ณ  ์ •ํ™•ํ•˜๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ์ „๋ฌธ๊ฐ€",
32)
33
34# Tasks
35search_task = Task(
36    description="'{query}'์— ๊ด€ํ•œ ์ตœ์‹  ๋‰ด์Šค ๊ธฐ์‚ฌ URL 5๊ฐœ๋ฅผ ์ˆ˜์ง‘ํ•˜์„ธ์š”.",
37    agent=searcher,
38    expected_output="๊ด€๋ จ ๋‰ด์Šค ๊ธฐ์‚ฌ URL ๋ชฉ๋ก (์ตœ์†Œ 5๊ฐœ)",
39)
40
41analysis_task = Task(
42    description="์ˆ˜์ง‘๋œ URL์—์„œ ๊ธฐ์‚ฌ ๋ณธ๋ฌธ์„ ์ถ”์ถœํ•˜๊ณ  ํ•ต์‹ฌ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜์„ธ์š”.",
43    agent=analyzer,
44    expected_output="๊ฐ ๊ธฐ์‚ฌ์˜ ํ•ต์‹ฌ ๋‚ด์šฉ ์š”์•ฝ",
45    context=[search_task],
46)
47
48answer_task = Task(
49    description="๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ '{query}'์— ๋Œ€ํ•œ ์ข…ํ•ฉ ๋‹ต๋ณ€์„ ์ž‘์„ฑํ•˜์„ธ์š”.",
50    agent=answerman,
51    expected_output="์ถœ์ฒ˜์™€ ๊ทผ๊ฑฐ๋ฅผ ํฌํ•จํ•œ ๋ช…ํ™•ํ•œ ๋‹ต๋ณ€",
52    context=[analysis_task],
53)
54
55# Crew ์‹คํ–‰
56crew = Crew(
57    agents=[searcher, analyzer, answerman],
58    tasks=[search_task, analysis_task, answer_task],
59    process=Process.sequential,
60)
61
62result = crew.kickoff(inputs={"query": "2026๋…„ AI Agent ํŠธ๋ Œ๋“œ"})
63print(result.raw)

โ“ ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ #

Q. CrewAI์™€ LangChain์˜ ์ฐจ์ด๋Š”? #

LangChain์€ LLM ์ฒด์ด๋‹๊ณผ ๋„๊ตฌ ํ†ตํ•ฉ์„ ์œ„ํ•œ ๋ฒ”์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. CrewAI๋Š” ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ํ˜‘์—… ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์— ํŠนํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, LangChain ์œ„์—์„œ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Q. OpenAI ์™ธ์— ๋‹ค๋ฅธ LLM์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”? #

๋„ค, llm ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ Anthropic Claude, Google Gemini, Ollama(๋กœ์ปฌ LLM) ๋“ฑ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1from langchain_anthropic import ChatAnthropic
2
3agent = Agent(
4    role="๋ถ„์„๊ฐ€",
5    goal="...",
6    backstory="...",
7    llm=ChatAnthropic(model="claude-sonnet-4-6"),
8)

Q. Sequential๊ณผ Hierarchical ์ค‘ ์–ด๋–ค ๊ฑธ ์จ์•ผ ํ•˜๋‚˜์š”? #

๋‹จ๊ณ„๋ณ„ ํ๋ฆ„์ด ๋ช…ํ™•ํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๋ฉด Sequential, ์ž‘์—…์˜ ๋ณต์žก๋„๊ฐ€ ๋†’๊ณ  ๋™์  ์œ„์ž„์ด ํ•„์š”ํ•˜๋ฉด Hierarchical์„ ์„ ํƒํ•˜์„ธ์š”.


๐Ÿ“š ์ฐธ๊ณ  #

Advertisement