CrewAI + Scrapeless: Execute uma Equipe de Coleta Multi-Agent com um LLM Local.
Advanced Bot Mitigation Engineer
TL;DR:
- Este é um verdadeiro fluxo de trabalho multi-agente. Três agentes CrewAI acessam uma página ao vivo, extraem registros estruturados e validam o resultado por meio de uma transferência de tarefas sequencial.
- Somente o coletor tem acesso à web. O Coletor de Página da Web recebe a ferramenta
scrape_markdowndo Scrapeless MCP; o extractor e o validador trabalham apenas com os resultados das tarefas anteriores. - O LLM roda localmente. Todos os três agentes usam
qwen2.5:0.5batravés do Ollama, de modo que o fluxo de trabalho não requer uma chave de API LLM em nuvem. - A execução bem-sucedida não prova a extração correta.
crew.kickoff()foi concluído, mas a saída da validação capturada se contradisse e teve que ser rejeitada. - A validação em produção deve ser determinística. Analise a saída do modelo em Python, valide os campos necessários e compare os valores extraídos com a resposta original do MCP.
- Gratuito para começar. Novas contas do Scrapeless incluem tempo de execução gratuito do Navegador de Scraping — inscreva-se em app.scrapeless.com.
Introdução: Uma Equipe Completa Não É Necessariamente um Pipeline Confiável
Um Agente CrewAI conectado a uma ferramenta Scrapeless MCP já pode acessar uma página ao vivo. Uma equipe CrewAI é uma reivindicação diferente: dois ou mais agentes com responsabilidades separadas devem passar o trabalho uns para os outros e terminar com crew.kickoff() retornando um resultado.
Este guia constrói esse segundo fluxo de trabalho.
Você criará uma equipe de três agentes que:
- Acessa uma página ao vivo através do Servidor MCP Scrapeless.
- Extrai registros de citação estruturados do Markdown retornado.
- Valida esses registros antes que eles deixem o fluxo de trabalho.
Todos os três agentes rodam em um modelo local do Ollama. Nenhuma chave de API da OpenAI, Anthropic ou de outro LLM em nuvem é necessária. A única credencial externa é a chave da API Scrapeless utilizada pela ferramenta MCP.
A integração é completada com sucesso. No entanto, a resposta final capturada é internamente inconsistente. Essa distinção é a lição mais importante deste tutorial: um fluxo de trabalho de agente concluído é evidência de execução, não evidência de que os dados resultantes são confiáveis.
O Que Você Pode Fazer com Esta Equipe
O fluxo de trabalho atribui uma responsabilidade a cada agente:
- Coletor de Página da Web: Chama a ferramenta
scrape_markdowndo Scrapeless MCP contra uma URL ao vivo e retorna o conteúdo da página. - Especialista em Extração de Dados: Lê o Markdown coletado e o converte em uma matriz JSON.
- Validador de QA: Verifica os registros extraídos em busca de campos ausentes e relata o resultado.
O CrewAI passa as saídas das tarefas por meio de um contexto de tarefa explícito. O extractor recebe a saída da tarefa de coleta, e o validador recebe a saída da tarefa de extração.
Nada precisa ser copiado manualmente entre os três agentes.
Isso difere de conectar um modelo local diretamente a uma função Python de coleta e extração. O guia de web scraping do Ollama cobre esse padrão mais simples.
Aqui, o CrewAI controla a orquestração:
- Três agentes
- Três prompts delimitados
- Três limites de token separados
- Uma transferência sequencial gerida pelo framework
Por Que Usar uma Equipe em vez de Um Único Agente?
Um único agente com uma ferramenta de scraping deve decidir o que coletar, como interpretar a página, como formatar o resultado e se esse resultado é aceitável.
Dividir essas responsabilidades em papéis separados oferece um controle mais claro sobre o fluxo de trabalho.
Cada agente recebe:
- Um objetivo específico
- Uma tarefa focada
- Seu próprio limite de saída
- Acesso apenas às ferramentas que precisa
Isso pode ser especialmente útil com modelos locais menores. Ao invés de pedir a um modelo para coletar, extrair, formatar e validar simultaneamente, cada etapa gerencia um trabalho mais limitado.
A separação também oferece pontos de inspeção mais claros. Em um pipeline de produção, você pode salvar ou validar a saída após cada tarefa e identificar se uma falha ocorreu durante a recuperação, extração ou validação.
Por Que o Servidor MCP Scrapeless?
A especificação do Protocolo de Contexto de Modelo define uma maneira padrão para que um cliente de IA descubra e invoque ferramentas expostas por um servidor.
O Servidor MCP Scrapeless expõe dados da web e capacidades de navegador através daquela interface de ferramenta. O CrewAI não precisa implementar renderização de páginas, roteamento de proxy ou infraestrutura de navegador diretamente. Ele apenas precisa se conectar ao servidor e anexar a ferramenta necessária a um agente.
A visão geral do Servidor MCP Scrapeless explica a família de ferramentas mais ampla, incluindo recuperação de páginas, busca e ferramentas de controle de navegador.
Para este fluxo de trabalho, a equipe precisa apenas de uma ferramenta:
text
scrape_markdown
Recupera a página alvo e retorna o conteúdo em um formato que o agente de extração a montante pode ler.
Você pode conferir a documentação do desenvolvedor do Scrapeless para os detalhes mais recentes de conexão do servidor e argumentos das ferramentas.
Pré-requisitos
Antes de executar a equipe, você precisa de:
- Python 3.10 ou posterior
- CrewAI e Ferramentas CrewAI
- Uma chave de API Scrapeless
- Ollama instalado e em execução localmente
- O modelo
qwen2.5:0.5bbaixado no Ollama
Você não precisa de OPENAI_API_KEY, ANTHROPIC_API_KEY ou outra credencial LLM hospedada. Cada objeto LLM do CrewAI neste exemplo aponta para o servidor local Ollama.
Passo 1 — Instalar o CrewAI e Suporte MCP
O exemplo verificado usa pacotes CrewAI fixos:
bash
pip install "crewai==1.15.4" "crewai-tools[mcp]==1.15.4"
O extra [mcp] instala as dependências do cliente MCP necessárias pelo MCPServerAdapter. O adaptador converte definições de ferramentas MCP em ferramentas que os agentes CrewAI podem chamar.
Passo 2 — Configurar o Modelo Local Ollama
Puxe o modelo usado neste exemplo:
bash
ollama pull qwen2.5:0.5b
Confirme que o Ollama pode vê-lo:
bash
ollama list
Normalmente, o Ollama expõe sua API local em:
text
http://localhost:11434
Se o Ollama estiver instalado, mas a equipe não conseguir se conectar, confirme que o serviço Ollama está em execução antes de iniciar o script Python.
Em seguida, configure a chave de API Scrapeless no seu shell:
bash
export SCRAPELESS_API_KEY="sua_chave_api_aqui"
Ler a chave de uma variável de ambiente mantém ela fora do arquivo fonte em Python.
Passo 3 — Apontar o CrewAI para o Ollama
Crie uma configuração LLM separada para cada agente:
python
from crewai import LLM
fetch_llm = LLM(
model="ollama/qwen2.5:0.5b",
base_url="http://localhost:11434",
max_tokens=180,
temperature=0,
)
extract_llm = LLM(
model="ollama/qwen2.5:0.5b",
base_url="http://localhost:11434",
max_tokens=100,
temperature=0,
)
validate_llm = LLM(
model="ollama/qwen2.5:0.5b",
base_url="http://localhost:11434",
max_tokens=70,
temperature=0,
)
Todas as três configurações usam o mesmo modelo, mas seus limites de saída refletem suas responsabilidades:
- O coletor tem mais espaço para retornar o conteúdo da página.
- O extrator precisa de tokens suficientes para um pequeno array JSON.
- O validador só precisa produzir um relatório compacto.
Definir temperature=0 reduz a variação na saída. Isso não garante resultados determinísticos ou corretos, especialmente com um modelo local pequeno.
Na inferência local apenas com CPU, max_tokens também é um controle importante em tempo de execução. Limites inferiores impedem que um agente gere respostas desnecessariamente longas, embora o tamanho do modelo, hardware, comprimento do contexto e número de interações do agente também afetem o tempo de execução.
Passo 4 — Conectar o CrewAI ao Servidor MCP Scrapeless
Importe MCPServerAdapter e defina a conexão remota MCP:
python
import os
from crewai_tools import MCPServerAdapter
server_params = {
"url": "https://api.scrapeless.com/mcp",
"transport": "streamable-http",
"headers": {
"x-api-token": os.environ["SCRAPELESS_API_KEY"]
},
}
A configuração contém três valores importantes:
urlaponta para o endpoint MCP Scrapeless.transportinforma ao cliente para usar HTTP Streamável.x-api-tokenautentica a solicitação com sua chave Scrapeless.
A chave é acessada com:
python
os.environ["SCRAPELESS_API_KEY"]
O Python irá interromper imediatamente se a variável de ambiente estiver ausente, o que é preferível a iniciar silenciosamente uma equipe sem credenciais de ferramenta válidas.
Passo 5 — Verificar a Ferramenta MCP Disponível
Antes de construir a equipe completa, verifique se o adaptador pode descobrir a ferramenta solicitada:
python
with MCPServerAdapter(server_params, "scrape_markdown") as tools:
print([tool.name for tool in tools])
A execução de verificação retornou:
text
['scrape_markdown']
Isso confirma que:
- O cliente MCP alcançou o servidor.
- O servidor aceitou o cabeçalho de autenticação.
- A ferramenta solicitada estava disponível para o CrewAI.
Isso não prova que cada solicitação futura de página conterá os dados esperados, ou que um modelo a montante interpretará a resposta corretamente.
A Superfície da Ferramenta MCP Usada Aqui
MCPServerAdapter pode expor ferramentas do servidor para agentes CrewAI. Passar "scrape_markdown" limita esse fluxo de trabalho à única ferramenta que ele precisa:
python
with MCPServerAdapter(server_params, "scrape_markdown") as tools:
...
Essa superfície de ferramenta mais reduzida é útil para a confiabilidade do agente. O coletor não precisa selecionar entre ferramentas de navegador ou pesquisa não relacionadas, e os agentes de extração e validação não recebem ferramentas.
A fronteira de permissão é direta:
| Agente | Acesso à ferramenta | Responsabilidade |
|---|---|---|
| Recuperador de Página Web | scrape_markdown |
Recuperar a página alvo |
| Especialista em Extração de Dados | Nenhum | Converter conteúdo buscado em JSON |
| Validador de QA | Nenhum | Verificar os registros extraídos |
Apenas o recuperador pode fazer uma requisição web ao vivo.
Como Usá-lo na Prática: Construir e Executar a Equipe
Defina os Três Papéis dos Agentes
Crie um Agente para cada etapa:
python
from crewai import Agent
recuperador = Agent(
role="Recuperador de Página Web",
goal=(
"Buscar a URL exata dada usando a ferramenta scrape_markdown "
"e devolver sua saída bruta."
),
backstory=(
"Recupera páginas web públicas para membros da equipe que não podem "
"navegar na web por conta própria."
),
tools=tools,
llm=fetch_llm,
max_iter=2,
)
extrator = Agent(
role="Especialista em Extração de Dados",
goal=(
"Transformar o markdown da página recuperada em um registro "
"estruturado limpo de cada citação e seu autor."
),
backstory=(
"Lê markdown bruto raspado e extrai exatamente os "
"campos que um banco de dados precisa."
),
llm=extract_llm,
max_iter=2,
)
validador = Agent(
role="Validador de QA",
goal="Verificar os registros de citações extraídas quanto à completude antes de serem enviados.",
backstory=(
"Rejeita registros incompletos ou malformados e relata "
"exatamente o que verificou."
),
llm=validate_llm,
max_iter=2,
)
Apenas recuperador recebe tools=tools.
O extrator e o validador devem atuar a partir do contexto da tarefa. Eles não podem navegar independentemente pela página alvo ou fazer outra requisição MCP.
Por que Definir max_iter=2?
max_iter limita o número de ciclos de raciocínio e ação que um agente pode realizar durante uma tarefa.
Um valor de 2 dá ao recuperador espaço suficiente para solicitar uma chamada de ferramenta e, em seguida, produzir uma resposta final. Também evita que um modelo pequeno continue através de um número excessivo de loops internos.
O limite é um mecanismo de controle, não uma garantia de correção. Se um agente produzir JSON malformado ou aceitar campos vazios, atingir o limite de iteração com sucesso não torna essa saída válida.
Anexe a Ferramenta MCP Apenas ao Recuperador
O papel do recuperador é definido de forma restrita:
- Receber a URL alvo.
- Chamar
scrape_markdown. - Retornar a saída da ferramenta sem comentários adicionais.
O extrator nunca recebe a URL ao vivo como uma instrução de navegação. Ele lê o conteúdo retornado pelo recuperador.
O validador nunca recebe a ferramenta MCP. Ele lê apenas a saída do extrator.
Essa separação facilita a compreensão e a auditoria do fluxo de dados.
Conecte o Contexto da Tarefa Sequencial
As tarefas do CrewAI podem referenciar tarefas anteriores através do argumento context:
python
extrair_tarefa = Task(
...,
context=[recuperar_tarefa],
)
validar_tarefa = Task(
...,
context=[extrair_tarefa],
)
A transferência é, portanto:
text
recuperar_tarefa → extrair_tarefa → validar_tarefa
O extrator recebe a resposta final da tarefa de busca. O validador recebe a resposta final da tarefa de extração.
Não é necessária nenhuma passagem manual de strings.
Execute a Equipe Com crew.kickoff()
O seguinte é o script completo:
python
import os
from crewai import Agent, Crew, LLM, Process, Task
from crewai_tools import MCPServerAdapter
URL_ALVO = "https://quotes.toscrape.com/tag/obvious/"
parametros_do_servidor = {
"url": "https://api.scrapeless.com/mcp",
"transport": "streamable-http",
"headers": {
"x-api-token": os.environ["SCRAPELESS_API_KEY"]
},
}
fetch_llm = LLM(
model="ollama/qwen2.5:0.5b",
base_url="http://localhost:11434",
max_tokens=180,
temperature=0,
)
extract_llm = LLM(
model="ollama/qwen2.5:0.5b",
base_url="http://localhost:11434",
max_tokens=100,
temperature=0,
)
validate_llm = LLM(
model="ollama/qwen2.5:0.5b",
base_url="http://localhost:11434",
max_tokens=70,
temperature=0,
)
with MCPServerAdapter(parametros_do_servidor, "scrape_markdown") as tools:
recuperador = Agent(
role="Recuperador de Página Web",
goal=(
f"Buscar a URL exata {URL_ALVO} usando a "
"ferramenta scrape_markdown e devolver sua saída bruta."
),
backstory=(
"Recupera páginas web públicas para membros da equipe que "
"não podem navegar na web por conta própria."
),
tools=tools,
llm=fetch_llm,
max_iter=2,
)
extrator = Agent(
role="Especialista em Extração de Dados",
goal=(
"Transformar o markdown da página recuperada em um registro "
"estruturado limpo de cada citação e seu autor."
),
backstory=(
"Lê markdown bruto raspado e extrai exatamente "
"os campos que um banco de dados precisa."
),
llm=extract_llm,
max_iter=2,
)
validador = Agent(
role="Validador de QA",
goal=(
"Verificar os registros de citações extraídas quanto à completude "
"antes de serem enviados."
),
backstory=(
"Rejeita registros incompletos ou malformados e "
"relata exatamente o que verificou."
O resultado da integração ainda é útil:
- CrewAI criou três agentes.
- O coletor recebeu uma ferramenta Scrapeless MCP ao vivo.
- O contexto da tarefa conectou os três estágios.
- `crew.kickoff()` foi concluído.
- Nenhuma chave de LLM na nuvem foi usada.
No entanto, a execução não suporta confiar nos campos resultantes. A saída final combinou uma contagem zero, valores vazios e uma declaração de sucesso.
Um modelo sub-1B pode ser útil para testar orquestração, mas essa execução mostra por que não deve ser tratado automaticamente como um extrator de dados estruturados confiável.
## Adicionar Validação Determinística Após a Equipe
A validação em linguagem natural ainda é saída do modelo. Um agente validador pode interpretar mal dados malformados, negligenciar valores vazios ou gerar uma conclusão que entra em conflito com seu próprio relatório.
Portanto, a validação em produção deve ocorrer em código comum após o fluxo de trabalho do modelo.
### Analisar a Saída do Extrator
Use `json.loads` para analisar a resposta do extrator.
Rejeite o resultado se:
- Não for JSON válido.
- O valor de nível superior não for um array.
- A resposta incluir prosa ao redor do JSON.
- O array estiver inesperadamente vazio.
### Validar os Campos Necessários
Para cada registro, verifique se:
- `text` existe.
- `author` existe.
- Ambos os valores são strings.
- Ambos os valores permanecem não vazios após a remoção de espaços em branco.
- Nenhum valor é um espaço reservado óbvio.
Não permita que uma declaração em linguagem natural como “todos os campos estão completos” sobreponha uma verificação programática falhada.
### Rejeitar Contagens Contraditórias
Se o validador relatar uma contagem, compare-a com o comprimento real do array JSON.
Um relatório que afirma uma entrada completa enquanto também relata uma contagem de zero deve falhar imediatamente.
### Comparar Valores Extraídos com a Fonte
Mantenha o Markdown original retornado por `scrape_markdown`.
Para campos copiados, como citações e nomes de autores, confirme que os valores extraídos aparecem na resposta da fonte. Isso ajuda a detectar conteúdo alucinado, truncado ou substituído.
### Preservar Saídas Intermediárias da Tarefa
O exemplo imprime apenas a saída final da equipe. Para depuração e monitoramento em produção, preserve a saída de cada tarefa.
Isso lhe dá três artefatos separados:
1. Markdown bruto coletado
2. JSON extraído
3. Relatório de validação
Com essas saídas disponíveis, você pode identificar exatamente em qual estágio os dados foram perdidos em vez de inferir a partir da resposta final.
### Aumentar a Capacidade do Modelo Quando Necessário
Uma validação mais robusta pode rejeitar resultados ruins, mas não pode restaurar o texto da fonte que um modelo não conseguiu preservar.
Se um modelo pequeno perder dados repetidamente durante a extração ou validação, use um modelo local maior ou um modelo hospedado mais capaz. Não enfraqueça as verificações apenas para fazer o pipeline parecer bem-sucedido.
## Conclusão
Uma equipe CrewAI operando em um modelo local Ollama pode se conectar ao Servidor Scrapeless MCP, chamar uma ferramenta de scraping ao vivo, passar dados por três agentes e completar `crew.kickoff()` sem uma chave de LLM na nuvem.
Esse é o resultado da integração.
A saída final capturada ainda estava errada o suficiente para ser rejeitada: ela relatou uma contagem zero e valores vazios enquanto simultaneamente afirmava que os dados estavam completos.
Trate a conclusão do fluxo de trabalho e a correção dos dados como condições separadas. Preserve as saídas intermediárias, valide registros estruturados em Python, compare valores extraídos com a resposta do MCP e falhe no pipeline sempre que essas verificações discordarem da conclusão do modelo.
## Pronto para Construir uma Equipe de Chamada de Ferramentas?
[Crie uma conta gratuita no Scrapeless](https://app.scrapeless.com/passport/login/?utm_source=website&utm_medium=blog&utm_campaign=mcpserver&utm_term=crewai-multi-agent-scraping-scrapeless) para obter sua chave de API e conectar o CrewAI a uma ferramenta de dados da web ao vivo.
Depois de entender o número de páginas e chamadas de agentes que seu fluxo de trabalho requer, revise os [planos de preços do Scrapeless](https://www.scrapeless.com/pt/pricing?utm_source=website&utm_medium=blog&utm_campaign=mcpserver&utm_term=crewai-multi-agent-scraping-scrapeless).
Para perguntas sobre implementação e suporte da comunidade:
- [Junte-se ao Discord do Scrapeless](https://discord.gg/VU2vtbq7Q2)
- [Junte-se à comunidade do Telegram do Scrapeless](https://t.me/scrapeless)
## FAQ
**P: Uma equipe CrewAI precisa de uma chave de LLM na nuvem para funcionar?**
Não. Configurar `model="ollama/qwen2.5:0.5b"` e `base_url="http://localhost:11434"` direciona cada agente para o servidor local Ollama. A equipe não precisa de uma chave de LLM hospedada da OpenAI, Anthropic ou outros.
Este fluxo de trabalho ainda precisa de uma chave de API do Scrapeless porque o coletor chama o Servidor Scrapeless MCP remoto.
**P: Como o CrewAI passa a saída de um agente para o próximo?**
Cada `Task` a jusante recebe uma lista `context` contendo uma tarefa anterior.
Por exemplo:
```python
extract_task = Task(
...,
context=[fetch_task],
)
O CrewAI inclui a resposta final da tarefa de coleta no contexto do extrator. O mesmo mecanismo passa o resultado da extração para o validador.
P: Por que apenas o coletor recebe a ferramenta MCP?
O fetcher é o único agente responsável por recuperar o conteúdo das páginas ao vivo. O extractor e o validator devem operar com base nas saídas de tarefas existentes.
Limitar o acesso às ferramentas reduz escolhas desnecessárias e torna o fluxo de trabalho mais fácil de auditar.
Q: O que controla max_iter=2?
max_iter limita quantos ciclos de raciocínio e ação um agente pode realizar durante uma tarefa.
Um valor de 2 dá ao fetcher a margem para uma chamada de ferramenta e uma resposta final, enquanto impede um loop sem fim. Ele limita a execução, mas não garante que a resposta final do agente seja correta.
Q: O modelo local pequeno extraiu a citação correta?
Não. A equipe foi concluída, mas a saída do validador final começou com:
text
0 ["", "", ""]
Então, afirmou que existia uma entrada completa e que todos os campos estavam não vazios. Como essas declarações se contradizem, o resultado deve ser rejeitado.
Q: Por que usar validação determinística se a equipe já tem um agente de QA?
Um agente de QA ainda é um LLM. Ele pode ignorar campos faltantes ou gerar uma conclusão que entre em conflito com os dados que lhe foram dados para inspecionar.
As verificações determinísticas em Python fornecem uma decisão independente de aprovação/reprovação com base na sintaxe JSON, comprimento do array, campos obrigatórios e correspondência de fonte.
Q: Quão lenta foi a execução local do Ollama?
A execução completa da verificação levou 542 segundos na máquina testada. Essa medida se aplica apenas ao hardware específico, modelo, página, prompts e configuração de agente usados no teste.
A inferência local da CPU pode levar minutos quando várias turnos de agentes estão envolvidos.
Q: O que deve ser registrado em uma equipe de produção?
Preservar pelo menos:
- A resposta da ferramenta MCP
- Cada saída de tarefa intermediária
- Os dados estruturados analisados
- Erros de validação determinística
- O resultado final da equipe
- O tempo de execução para cada etapa
Isso torna possível localizar a etapa que alterou ou descartou os dados de origem.
Q: O que devo verificar antes de raspar um site ao vivo?
Revise os termos do site e as diretivas de /robots.txt, que seguem o Protocolo de Exclusão de Robôs.
Mantenha a lista de alvos limitada, use páginas públicas e evite dar a um agente autônomo uma instrução de rastreamento em aberto.
Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.



