track
Dacă îi ceri unui singur model să cerceteze un subiect, să scrie un articol, să îl revizuiască și să lustruiască versiunea finală dintr-o singură trecere, de obicei vei primi ceva mediocru la toate cele patru. Modelul nu are un rol clar în niciun moment, așa că ține în aer toate sarcinile simultan și rareori le face bine. Împărțirea muncii între agenți specializați duce, de regulă, la un rezultat mai bun, pentru că fiecare agent se poate concentra pe partea lui și o poate face corect.
În acest ghid, vom construi un roi de agenți care face exact asta. Vom folosi CrewAI pentru a coordona agenții și Gemini 3.5 Flash pentru raționamentul și redactarea lor. Roiul are patru membri: un Cercetător care adună informații actuale, un Scriitor care produce primul draft, un Reviewer care îmbunătățește claritatea și acuratețea, și un Manager care coordonează fluxul de lucru.
Îi vom oferi și Cercetătorului acces live la web prin Olostep, ca să poată aduce surse proaspete, nu doar să se bazeze pe cunoștințele interne ale modelului. La final, vei avea un roi ierarhic funcțional care primește un subiect și returnează un articol gata de publicare — și o înțelegere clară a momentelor când această abordare merită costul și când nu.
Ce este un Agent Swarm?
Un agent swarm este un grup de agenți AI care lucrează împreună pentru a finaliza o sarcină. În loc să ceri unui singur model să cerceteze, să scrie, să revizuiască și să finalizeze totul de unul singur, un roi de agenți împarte munca între mai mulți agenți specializați. Fiecare agent are un rol clar, iar sistemul îi coordonează pentru a produce un rezultat final mai solid.
Avantajul este specializarea. Un agent axat pe cercetare se poate concentra pe găsirea și verificarea surselor, un agent axat pe scriere se poate concentra pe structură și claritate, iar un reviewer poate verifica draftul față de cercetarea originală fără a fi distras de textul abia scris.
Un agent coordonator leagă pașii între ei și decide ce urmează. În roiul pe care îl construim, aceste roluri corespund direct cu Cercetătorul, Scriitorul, Reviewerul și Managerul.

Totuși, asta nu e gratis. Mai mulți agenți înseamnă mai multe apeluri către model, mai mult timp și mai multe locuri unde fluxul de lucru se poate împotmoli; un compromis la care ne vom întoarce la final, după ce vezi cât costă efectiv rularea.
Ce este CrewAI?
CrewAI este un framework open-source pentru construirea de sisteme multi-agent. Îți oferă o modalitate clară de a defini agenți, de a le atribui sarcini și de a alege cum se coordonează, astfel încât să te poți concentra pe roluri și pe fluxul de lucru, nu pe „instalația” care îi conectează.
Trei concepte fac cea mai mare parte a trebei.
- Agenții sunt muncitorii indivizi, fiecare definit de un rol, un obiectiv și un „background” care îi modelează comportamentul.
- Sarcinile descriu munca de făcut și rezultatul pe care ar trebui să-l producă fiecare.
- O echipă (crew) reunește agenți și sarcini și le rulează sub un proces ales
Acest proces poate fi secvențial, cu sarcini într-o ordine fixă, sau ierarhic, unde un agent manager deleagă munca altora și le validează rezultatul. Aici vom folosi procesul ierarhic, cu Managerul care se ocupă de delegare.
CrewAI gestionează și accesul la modele prin LiteLLM, o bibliotecă open-source care oferă o interfață unificată pentru mulți furnizori. Asta ne permite să definim un singur model Gemini 3.5 Flash și să-l refolosim pentru toți cei patru agenți, lucru pe care îl vom configura în pașii de mai jos.
Construirea unui Agent Swarm cu CrewAI și Gemini 3.5 Flash
Hai să începem cu setarea.
1. Cerințe preliminare
Înainte să începi, asigură-te că ai:
- Python 3.11 sau mai nou instalat
- Jupyter Notebook sau JupyterLab instalat
- Înțelegere de bază a Python și a notebook-urilor
- Un cont Google AI Studio
- Facturarea activată sau credite adăugate în contul tău Google pentru utilizarea Gemini
- Un cont gratuit Olostep
2. Instalează dependențele
Pornește Jupyter Notebook sau JupyterLab și deschide un notebook cu cel mai nou kernel Python disponibil în mediul tău. Apoi rulează celula de mai jos pentru a instala pachetele necesare.
%pip install --quiet -U \
crewai \
crewai-tools \
google-genai \
litellm \
olostep \
nest-asyncio
După ce instalarea s-a încheiat, rulează următoarea celulă pentru a verifica versiunile pachetelor instalate. Asta te ajută să confirmi că totul a fost instalat corect.
import importlib.metadata as md
for pkg in [
"crewai",
"crewai-tools",
"google-genai",
"litellm",
"olostep",
"nest-asyncio",
]:
print(f"{pkg:>16} {md.version(pkg)}")
Ar trebui să vezi un output similar cu acesta:
crewai 1.14.6
crewai-tools 1.14.6
google-genai 2.8.0
litellm 1.87.1
olostep 1.1.0
nest-asyncio 1.6.0
3. Setează variabilele de mediu
Ai nevoie de două chei API pentru acest proiect.
Mai întâi, creează o cheie API pentru Gemini din Google AI Studio. Deschide Google AI Studio, mergi la secțiunea API key și creează o cheie nouă pentru proiectul tău. Gemini oferă un nivel gratuit, dar cu utilizare și rate limitate. Pentru a testa și rula Gemini mai fiabil, conectează contul tău de facturare Google sau adaugă credite în contul tău Google, astfel încât utilizarea să poată fi facturată când depășești limitele nivelului gratuit.
Apoi, creează o cheie API Olostep. Înscrie-te pe Olostep, deschide dashboard-ul Olostep și generează o cheie API din pagina API keys. Vom folosi această cheie pentru a oferi agentului Cercetător acces live la web pentru căutare și scraping de pagini.
După ce ambele chei sunt gata, salvează-le ca variabile de mediu. Gemini va alimenta agenții CrewAI, iar Olostep va permite agentului Cercetător să colecteze informații actuale de pe web.
După crearea ambelor chei, salvează-le ca variabile de mediu înainte de a rula notebook-ul.
PowerShell:
$env:GEMINI_API_KEY="your-gemini-key"
$env:OLOSTEP_API_KEY="your-olostep-key"
macOS/Linux:
export GEMINI_API_KEY="your-gemini-key"
export OLOSTEP_API_KEY="your-olostep-key"
Acum rulează celula de mai jos pentru a verifica dacă cheia API Gemini a fost încărcată corect.
import os
api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
if not api_key:
raise RuntimeError(
"Set GEMINI_API_KEY or GOOGLE_API_KEY in your environment, "
"then restart the notebook kernel."
)
print(f"Gemini API key loaded")
Ar trebui să vezi:
Gemini API key loaded
4. Configurează Gemini 3.5 Flash prin LiteLLM
În acest proiect vom folosi Gemini 3.5 Flash ca LLM partajat pentru toți agenții CrewAI. Gemini 3.5 Flash este conceput pentru sarcini rapide, agentice și orientate pe cod. Este util aici pentru că agenții noștri trebuie să raționeze pe mai mulți pași, să urmeze instrucțiuni, să lucreze cu unelte și să producă ieșiri structurate.
Gemini 3.5 Flash acceptă și o fereastră mare de context, ieșiri lungi, reasoning și utilizare de tool-uri, ceea ce îl face o alegere solidă pentru fluxuri cu agenți. Deoarece un roi de agenți poate apela modelul de mai multe ori prin Cercetător, Scriitor, Reviewer și Manager, folosirea unui model Flash ajută la păstrarea unui răspuns rapid, oferind totodată calitate bună la raționament și scriere.
Dacă vrei să compari modelul cu alte LLM-uri de ultimă generație, îți recomand ghidurile noastre despre Gemini 3.5 Flash vs GPT-5.5 și Gemini 3.5 Flash vs Claude Opus 4.8.
Vom accesa Gemini prin LiteLLM. LiteLLM este o bibliotecă open-source care oferă o interfață unificată pentru apelarea multor furnizori de LLM, inclusiv Gemini, OpenAI, Anthropic, Bedrock, Vertex AI și alții. În această configurație, CrewAI folosește LiteLLM în fundal, astfel încât să putem pasa numele modelului Gemini într-un format simplu provider/model.
Acum vom crea un LLM CrewAI partajat și îl vom reutiliza pentru toți agenții. Asta păstrează setarea simplă, pentru că Cercetătorul, Scriitorul, Reviewerul și Managerul vor folosi același model Gemini.
from crewai import LLM
GEMINI_MODEL = "gemini/gemini-3.5-flash"
gemini_llm = LLM(
model=GEMINI_MODEL,
api_key=os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY"),
temperature=1.0,
)
print(f"LLM ready: {gemini_llm.model}")
print("Provider API: Google Gemini (via LiteLLM)")
Ar trebui să vezi un output similar cu acesta:
LLM ready: gemini-3.5-flash
Provider API: Google Gemini (via LiteLLM)
Asta confirmă că CrewAI poate acum accesa Gemini prin LiteLLM. În pașii următori, vom pasa acest obiect partajat gemini_llm fiecărui agent.
5. Adaugă acces web live
Agentul Cercetător are nevoie de acces la informații actuale. Pentru asta, vom crea un tool personalizat de căutare Olostep și îl vom atribui mai târziu agentului Cercetător.
Acest tool poate funcționa în două moduri. Poate returna doar rezumate ale căutării, ceea ce e mai rapid, sau poate face scraping paginilor din rezultate și returna conținutul în markdown. Adăugăm și un mic buget de căutare, astfel încât agentul să nu apeleze excesiv tool-ul web.
from pydantic import Field, PrivateAttr
from crewai.tools import BaseTool
try:
from olostep import Olostep, Olostep_BaseError
except ImportError:
raise ImportError(
"The 'olostep' package is not installed. Run %pip install -U olostep."
)
Mai întâi, încarcă cheia API Olostep din mediu.
olostep_api_key = os.getenv("OLOSTEP_API_KEY")
if not olostep_api_key or olostep_api_key == "your-olostep-api-key-here":
print(
"The Olostep tool will fail at call time unless OLOSTEP_API_KEY "
"is set in the environment."
)
else:
print("Olostep API key loaded.")
_olostep_client = Olostep(api_key=olostep_api_key) if olostep_api_key else None
Acum creează tool-ul personalizat CrewAI.
class OlostepSearchTool(BaseTool):
"""Search the web with a strict research-call budget."""
name: str = "olostep_web_search"
description: str = (
"Search the live web. Set scrape=false for fast discovery and "
"scrape=true for full-page evidence. You have at most 3 discovery "
"searches without scraping and 6 external search calls total. After "
"3 discovery searches, use scrape=true on the strongest query or "
"domain. Each call returns at most 3 results. Inputs: query, scrape, "
"limit, and optional include_domains."
)
default_limit: int = Field(default=3, ge=1, le=3)
max_chars_per_page: int = Field(default=4000, ge=500, le=12000)
_total_calls: int = PrivateAttr(default=0)
_discovery_calls: int = PrivateAttr(default=0)
_call_history: list[dict] = PrivateAttr(default_factory=list)
def _run(
self,
query: str,
scrape: bool = False,
limit: int | None = None,
include_domains: list[str] | None = None,
) -> str:
if _olostep_client is None:
return (
"OLOSTEP_API_KEY is not set. Set it in the environment "
"and restart the kernel."
)
if self._total_calls >= 6:
return (
"Research search budget exhausted: 6 external calls have "
"already been used. Stop searching and complete the task "
"with the collected sources."
)
if not scrape and self._discovery_calls >= 3:
return (
"Discovery-search limit reached. Do not run another "
"scrape=False search. Use scrape=True on the strongest query "
"or selected domains, or finish with the sources already found."
)
clean_query = query.strip()
if not clean_query:
return "Search query cannot be empty."
result_limit = max(1, min(limit or self.default_limit, 3))
kwargs = {"query": clean_query, "limit": result_limit}
if scrape:
kwargs["scrape_options"] = {
"formats": ["markdown"],
"timeout": 25,
}
if include_domains:
kwargs["include_domains"] = include_domains
self._total_calls += 1
if not scrape:
self._discovery_calls += 1
call_record = {
"number": self._total_calls,
"query": clean_query,
"scrape": scrape,
"limit": result_limit,
"domains": include_domains or [],
"status": "started",
"results": 0,
}
self._call_history.append(call_record)
try:
search = _olostep_client.searches.create(**kwargs)
except Olostep_BaseError as error:
call_record["status"] = "error"
call_record["error"] = f"{type(error).__name__}: {error}"
return (
f"Olostep search error: {type(error).__name__}: {error}\n"
f"Budget used: {self._total_calls}/6 total calls; "
f"{self._discovery_calls}/3 discovery calls."
)
if not search.links:
call_record["status"] = "no_results"
return (
f"No results found for query: {clean_query!r}\n"
f"Budget used: {self._total_calls}/6 total calls; "
f"{self._discovery_calls}/3 discovery calls."
)
call_record["status"] = "success"
call_record["results"] = len(search.links)
chunks = [
f"## Web results for: {clean_query!r}",
f"Page scraping: {'enabled' if scrape else 'disabled'}",
(
f"Budget used: {self._total_calls}/6 total calls; "
f"{self._discovery_calls}/3 discovery calls."
),
]
for index, link in enumerate(search.links, 1):
url = link.get("url", "")
title = link.get("title") or url or "Untitled result"
description = link.get("description") or "No description provided."
chunks.append(
f"### {index}. {title}\n"
f"URL: {url}\n"
f"Summary: {description}"
)
markdown = link.get("markdown_content")
if scrape and markdown:
body = markdown.strip()
if len(body) > self.max_chars_per_page:
body = body[: self.max_chars_per_page] + "\n...[truncated]"
chunks.append(f"Page content:\n{body}")
return "\n\n".join(chunks)
La final, creează o instanță a tool-ului.
olostep_search_tool = OlostepSearchTool()
print(f"Tool ready: {olostep_search_tool.name}")
Ar trebui să vezi un output similar cu acesta:
Olostep API key loaded.
Tool ready: olostep_web_search
Asta confirmă că tool-ul de căutare web Olostep este gata. Mai târziu, îl vom atașa agentului Cercetător pentru a putea colecta informații actuale de pe web.
6. Creează agenții
Acum vom crea agenții pentru roiul nostru. Vom defini trei agenți „worker” și un agent manager. Agenții worker fac sarcinile principale, în timp ce managerul coordonează fluxul de lucru.
Agentul Cercetător va folosi tool-ul Olostep pentru a găsi informații actuale. Agentul Scriitor va transforma cercetarea într-un draft. Agentul Reviewer va îmbunătăți draftul. Agentul Manager va decide cum să delege munca în echipa ierarhică.
Creează agenții worker
Să începem cu cei trei agenți worker.
from crewai import Agent
researcher = Agent(
role="Senior Research Analyst",
goal=(
"Find accurate, current, and well-sourced information about {topic}. "
"Produce concrete, verifiable points for the writer."
),
backstory=(
"You are a meticulous research analyst. You decide whether each web "
"search needs page scraping. Use scrape=False when titles, URLs, and "
"summaries are enough. Use scrape=True when you need full-page evidence "
"to verify a claim or understand a source in detail. Cite source URLs."
),
llm=gemini_llm,
tools=[olostep_search_tool],
verbose=True,
allow_delegation=False,
)
Apoi, creează agentul Scriitor.
writer = Agent(
role="Blog Post Writer",
goal=(
"Turn the research into a complete, publication-ready educational "
"article on {topic}. Include a strong title, an unlabeled italic "
"summary, TL;DR, detailed body sections, conclusion, and FAQs. Add a "
"table only when it makes a comparison or decision easier to understand."
),
backstory=(
"You are a seasoned technology educator and writer. You create "
"practical, approachable articles similar in structure to high-quality "
"DataCamp content, without copying its wording or branding. You use "
"clear headings, short paragraphs, concrete examples, and sourced facts."
),
llm=gemini_llm,
verbose=True,
allow_delegation=False,
)
Acum creează agentul Reviewer.
reviewer = Agent(
role="Senior Editor",
goal=(
"Review the draft for factual accuracy, clarity, structure, and "
"completeness. Ensure it follows the required article order and return "
"a polished, publication-ready version."
),
backstory=(
"You are a detail-oriented senior editor for an educational technology "
"publication. You verify claims, improve flow, remove repetition, and "
"ensure the title is followed by an unlabeled italic summary and TL;DR. "
"You keep a table only when it adds real value, remove any Key Takeaways "
"section, and ensure Conclusion appears immediately before FAQs."
),
llm=gemini_llm,
verbose=True,
allow_delegation=False,
)
Creează agentul manager
La final, creează agentul Manager.
manager = Agent(
role="Content Coordinator",
goal=(
"Coordinate research, writing, and review for {topic}. Delegate each "
"task to the correct specialist and ensure the reviewed article is the "
"final result."
),
backstory=(
"You are an experienced editorial lead. You keep the workflow simple: "
"research first, writing second, and editorial review last."
),
llm=gemini_llm,
verbose=True,
allow_delegation=True,
)
Managerul este configurat separat când construim echipa ierarhică. De aceea nu a fost adăugat încă în lista agenților worker.
7. Definește sarcinile
Acum vom defini sarcinile pentru roiul de agenți. Aceste sarcini nu sunt atribuite direct agenților worker. În schimb, agentul Manager va decide ce worker ar trebui să preia fiecare sarcină.
În acest flux de lucru, prima sarcină este cercetarea, a doua este scrierea, iar a treia este revizuirea. Fiecare sarcină include și un rezultat așteptat, astfel încât agenții să știe exact ce trebuie să producă.
from crewai import Task
research_task = Task(
description=(
"Delegate to the Senior Research Analyst. Research '{topic}' using at "
"most 6 web-tool calls. Use no more than 3 calls with scrape=False, "
"then use scrape=True only when detailed evidence is needed. Collect "
"5-7 useful findings, source URLs, and common reader questions."
),
expected_output=(
"Clear markdown research notes with 5-7 sourced findings and 3-5 "
"potential FAQ questions."
),
)
Apoi, creează sarcina de scriere. Această sarcină folosește ca context sarcina de cercetare, astfel încât Scriitorul să poată construi articolul din cercetarea colectată.
write_task = Task(
description=(
"Delegate to the Blog Post Writer. Write one complete educational "
"markdown article on '{topic}', approximately 1,000-1,500 words. Start "
"with one H1 title and an unlabeled italic summary. Then include TL;DR, "
"an introduction, useful body sections, an optional table only when it "
"improves understanding, a Conclusion, and FAQs. Use source links."
),
expected_output=(
"One continuous publication-ready markdown article with title, italic "
"summary, TL;DR, body, Conclusion, and 3-5 FAQs."
),
context=[research_task],
)
Acum, creează sarcina de revizuire. Această sarcină folosește ca context atât cercetarea, cât și scrierea, astfel încât Reviewerul să poată verifica articolul față de cercetarea inițială.
review_task = Task(
description=(
"Delegate to the Senior Editor. Review the article for accuracy, "
"clarity, structure, and source support. Return only the corrected full "
"article. Keep the same continuous article format and ensure FAQs are "
"the final section."
),
expected_output=(
"Only the final reviewed markdown article, beginning with its H1 title "
"and ending with the final FAQ answer."
),
context=[research_task, write_task],
)
La final, adaugă toate cele trei sarcini într-o listă.
tasks = [research_task, write_task, review_task]
print(f"Defined {len(tasks)} manager-delegated tasks.")
Ar trebui să vezi:
Defined 3 manager-delegated tasks.
8. Construiește fluxul de lucru
Acum vom conecta agenții și sarcinile într-un singur flux CrewAI. Vom folosi un proces ierarhic în care agentul Manager coordonează agenții worker.
În această configurație, Cercetătorul, Scriitorul și Reviewerul sunt adăugați ca agenți worker. Managerul este transmis separat prin manager_agent, deoarece el este responsabil de delegare și coordonare.
from crewai import Crew, Process
crew = Crew(
agents=[researcher, writer, reviewer],
tasks=tasks,
manager_agent=manager,
process=Process.hierarchical,
verbose=True,
memory=False,
)
Acum printează detaliile echipei pentru a confirma că fluxul a fost asamblat corect.
print("Hierarchical crew assembled.")
print(f" Workers : {[a.role for a in crew.agents]}")
print(f" Manager : {crew.manager_agent.role}")
print(f" Tasks : {len(crew.tasks)}")
print(f" Process : {crew.process}")
Ar trebui să vezi un output similar cu acesta:
Hierarchical crew assembled.
Workers : ['Senior Research Analyst', 'Blog Post Writer', 'Senior Editor']
Manager : Content Coordinator
Tasks : 3
Process : Process.hierarchical
Asta confirmă că roiul ierarhic de agenți este gata. În pasul următor, vom rula echipa pe un subiect și vom genera răspunsul final.
9. Rulează roiul de agenți
Acum putem rula roiul de agenți. Vom furniza un subiect și vom porni fluxul cu kickoff_async().
Jupyter rulează deja o buclă de evenimente în fundal. Din acest motiv, aplicăm mai întâi nest_asyncio, ca fluxul asincron CrewAI să poată rula în interiorul notebook-ului.
try:
import nest_asyncio
nest_asyncio.apply()
print("nest_asyncio applied: CrewAI can now run inside this kernel.")
except ModuleNotFoundError:
print("nest_asyncio not installed. Run: pip install nest-asyncio")
Ar trebui să vezi:
nest_asyncio applied: CrewAI can now run inside this kernel.
Apoi, definește un subiect și creează o funcție helper pentru a rula echipa.
from datetime import datetime
topic = "Learning Adaptive Thresholds for LLM Tool Use: When to Rely on Memory and When to Call a Tool"
async def run_crew(topic: str):
"""Run the CrewAI crew asynchronously."""
print(f"Starting crew at {datetime.now().isoformat(timespec='seconds')}")
print(f"Topic: {topic!r}\n")
result = await crew.kickoff_async(inputs={"topic": topic})
print(f"\nFinished at {datetime.now().isoformat(timespec='seconds')}")
print(f"Result type: {type(result).__name__}")
return result
result = await run_crew(topic)
Când fluxul începe, CrewAI dă mai întâi sarcina agentului Manager. Managerul deleagă apoi munca de cercetare Analistului Senior de Cercetare. Cercetătorul folosește tool-ul olostep_web_search pentru a căuta pe web informații despre praguri adaptive, utilizarea uneltelor de către LLM, memorie și decizii de apelare a uneltelor.

În jurnal, Cercetătorul folosește atât apeluri doar de căutare, cât și apeluri cu scraping. Bugetul de căutare este folosit integral, ajungând la 6 din 6 apeluri web totale și 3 din 3 apeluri de descoperire. O cerere suplimentară doar de căutare este blocată pentru că limita de căutare de descoperire a fost deja atinsă. Asta arată că regulile de buget din tool-ul nostru personalizat Olostep funcționează corect.

După faza de cercetare, Managerul continuă fluxul delegând pașii de scriere și revizuire. Agenții produc notele de cercetare, redactează articolul și apoi lustruiesc răspunsul final. Rularea se încheie cu succes și returnează un obiect CrewOutput.
Ar trebui să vezi un output similar cu acesta:
Starting crew at 2026-06-05T13:54:07
Topic: 'Learning Adaptive Thresholds for LLM Tool Use: When to Rely on Memory and When to Call a Tool'
Crew Execution Started
Task Started
Agent: Content Coordinator
Agent: Senior Research Analyst
Tool: olostep_web_search
Budget used: 6/6 total calls; 3/3 discovery calls
Finished at 2026-06-05T13:59:29
Result type: CrewOutput
Asta înseamnă că întregul roi a rulat cu succes. Managerul a coordonat fluxul de lucru, Cercetătorul a colectat informații actuale, Scriitorul a creat draftul, iar Reviewerul a îmbunătățit rezultatul final.
10. Vezi rezultatul
După ce roiul își termină rularea, putem afișa articolul final generat de echipă. Valoarea result.raw conține răspunsul final din fluxul de lucru.
from IPython.display import Markdown, display
raw = result.raw
if "Blog Post" in raw:
raw = raw.split("Blog Post", 1)[1].strip()
display(Markdown(raw))
Acest lucru va afișa rezultatul final ca markdown formatat în interiorul notebook-ului.
În cele mai multe cazuri, blogul generat va fi deja bine structurat, cu mai multe subtitluri, secțiuni clare, diagrame sau explicații vizuale acolo unde este potrivit și o concluzie puternică.
Combinația de cercetare, scriere și revizuire editorială ajută la obținerea unui conținut gata de publicare. După ce am revizuit ieșirea, personal i-aș da undă verde pentru publicare.

În continuare, putem tipări o trasabilitate a execuției. Asta ne ajută să înțelegem cum a lucrat roiul, ce agenți au participat, câte apeluri web au fost folosite și cum a fost procesată fiecare sarcină.
from collections import Counter
def role_name(value):
"""Return a readable role/name for CrewAI agent values."""
if value is None:
return "Not reported"
if isinstance(value, str):
return value
return getattr(value, "role", None) or getattr(value, "name", None) or str(value)
print("=" * 70)
print("SWARM EXECUTION TRACE")
print("=" * 70)
# Hierarchical crew structure
print("\nCOORDINATION")
print(f"Process : {crew.process}")
print(f"Manager : {role_name(crew.manager_agent)}")
print(f"Workers : {', '.join(role_name(agent) for agent in crew.agents)}")
print(
"Flow : Manager receives each task, delegates specialist work, "
"validates the response, and passes approved context to the next task."
)
Now print the web-tool usage recorded by the custom Olostep tool.
history = list(getattr(olostep_search_tool, "_call_history", []))
discovery_calls = sum(not item["scrape"] for item in history)
scrape_calls = sum(item["scrape"] for item in history)
successful_calls = sum(item["status"] == "success" for item in history)
print("\nWEB TOOL USAGE")
print(f"Total external calls : {len(history)}/6")
print(f"Without scraping : {discovery_calls}/3")
print(f"With scraping : {scrape_calls}")
print(f"Successful calls : {successful_calls}")
if history:
for item in history:
mode = "scrape" if item["scrape"] else "search only"
domains = ", ".join(item["domains"]) if item["domains"] else "all domains"
print(
f" {item['number']}. {mode}; status={item['status']}; "
f"results={item['results']}; scope={domains}"
)
print(f" Query: {item['query']}")
else:
print(" No Olostep calls were recorded.")
Finally, print the task-by-task trace.
print("\nTASK-BY-TASK TRACE")
agent_activity = Counter()
for index, task_out in enumerate(result.tasks_output, start=1):
task = crew.tasks[index - 1] if index <= len(crew.tasks) else None
reported_agent = role_name(getattr(task_out, "agent", None))
processed_by = sorted(getattr(task, "processed_by_agents", set()) or [])
participants = processed_by or [reported_agent]
for participant in participants:
agent_activity[role_name(participant)] += 1
delegations = getattr(task, "delegations", 0) if task else 0
used_tools = getattr(task, "used_tools", 0) if task else 0
tool_errors = getattr(task, "tools_errors", 0) if task else 0
start_time = getattr(task, "start_time", None) if task else None
end_time = getattr(task, "end_time", None) if task else None
duration = end_time - start_time if start_time and end_time else None
print(f"\n--- Task {index} ---")
print(f"Reported owner : {reported_agent}")
print(f"Processed by : {', '.join(map(role_name, participants))}")
print(f"Delegations : {delegations}")
print(f"Tool uses : {used_tools}")
print(f"Tool errors : {tool_errors}")
if duration is not None:
print(f"Duration : {duration}")
preview = getattr(task_out, "raw", "").strip().replace("\n", " ")
print(f"Output preview : {preview[:300]}{'...' if len(preview) > 300 else ''}")
print("\nAGENT ACTIVITY")
for agent, count in agent_activity.most_common():
print(f"{agent}: participated in {count} task(s)")
if not agent_activity:
print("CrewAI did not expose per-agent processing metadata for this run.")
Ar trebui să vezi un output similar cu acesta:

Această trasabilitate arată că roiul ierarhic a funcționat conform așteptărilor. Managerul a coordonat toate cele trei sarcini, Cercetătorul a gestionat pasul de cercetare, Scriitorul a creat draftul articolului, iar Reviewerul a îmbunătățit răspunsul final.
Totuși, asta evidențiază și o limitare importantă: fluxurile multi-agent pot deveni costisitoare.
În această rulare, fluxul a folosit 6 apeluri web externe și multiple apeluri LLM la nivelul Managerului, Cercetătorului, Scriitorului și Reviewerului. O singură rulare poate costa până la aproximativ 0,28 USD, iar crearea acestui ghid a costat aproximativ 2,78 USD în timpul testării. E mult pentru un proiect de tutorial simplu.

Gânduri finale
Roiurile de agenți și fluxurile multi-agent arată grozav pe hârtie. Ideea de a avea un Cercetător, un Scriitor, un Reviewer și un Manager care lucrează împreună pare puternică, iar în acest ghid, rezultatul final a fost de fapt suficient de bun pentru a fi publicat cu retușuri minore.
Dar, în practică, această configurație poate costa mai mult, dura mai mult și crea mai multe puncte de eșec. În cazul meu, o rulare costă în jur de 0,28 USD, iar crearea acestui ghid a costat aproximativ 2,78 USD. E mult pentru un proiect de tutorial simplu.
Pentru aplicații reale, nu aș folosi mereu un roi multi-agent complet. Aș prefera o configurare mai programatică, unde sarcinile simple sunt gestionate de cod sau reguli, iar doar sarcinile complexe sunt predate agenților. Putem reduce costurile limitând apelurile către unelte, folosind mai puțini agenți, scurtând prompturile și folosind un flux liniar în loc de unul ierarhic.
Desigur, odată ce optimizăm prea mult, s-ar putea să nu mai fie un adevărat roi de agenți. Devine mai mult un flux AI bazat pe reguli. De aceea, obiectivul este echilibrul: folosește roiuri de agenți când aduc valoare reală, dar păstrează lucrurile simple când nu.