Projeto de ciclos de pesquisa em várias etapas
Planejar → pesquisar → ler → extrair → sintetizar → repetir até alcançar profundidade suficiente.
Projeto de ciclos de pesquisa em várias etapas é uma aula grátis de AI Agents no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Agents, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Agents inclui 4 aulas no total.
O que é um ciclo de pesquisa?
Um ciclo de pesquisa é um padrão baseado em agente no qual um LLM planeja, pesquisa, lê, extrai, identifica lacunas e repete até que o objetivo da pesquisa seja atingido.
Ao contrário de uma pesquisa única, o ciclo se adapta com base no que encontra — seguindo pistas inesperadas e descartando caminhos sem saída.
Fase 1: Decomposição da pergunta
O primeiro passo é dividir a pergunta de pesquisa em subperguntas. Isso cria um plano de pesquisa orientado e impede que o agente navegue sem objetivo.
import openai, json
client = openai.OpenAI(api_key='YOUR_OPENAI_KEY')
def decompose_question(question: str) -> list[str]:
prompt = (
f'Break this research question into 3-5 focused sub-questions.\n'
f'Each sub-question should be independently searchable.\n'
f'Question: "{question}"\n'
f'Return JSON: {{"sub_questions": ["..."]}}'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
return json.loads(resp.choices[0].message.content)['sub_questions']
questions = decompose_question('What are the main causes of inflation in 2024?')
print(questions)Fase 2: Pesquisa na web
Faça uma pesquisa na web para cada subpergunta. Use uma API de pesquisa (Serper, Brave, Bing) para obter uma lista de URLs e trechos. Não leia todas as páginas — priorize primeiro as fontes de alta qualidade.
import requests
SERPER_KEY = 'YOUR_SERPER_API_KEY'
def search_web(query: str, num_results: int = 5) -> list[dict]:
resp = requests.post(
'https://google.serper.dev/search',
headers={'X-API-KEY': SERPER_KEY, 'Content-Type': 'application/json'},
json={'q': query, 'num': num_results}
)
resp.raise_for_status()
results = resp.json().get('organic', [])
return [
{'title': r['title'], 'url': r['link'], 'snippet': r.get('snippet', '')}
for r in results
]Fase 3: Leitura e extração de fatos
Acesse cada URL e extraia os principais fatos relevantes para a subpergunta. Use um LLM para ler o artigo e produzir uma lista de fatos com a URL da fonte.
import httpx
from bs4 import BeautifulSoup
def fetch_text(url: str, max_chars: int = 4000) -> str:
try:
resp = httpx.get(url, timeout=10, follow_redirects=True,
headers={'User-Agent': 'ResearchAgent/1.0'})
soup = BeautifulSoup(resp.text, 'html.parser')
for tag in soup(['script', 'style', 'nav', 'footer']):
tag.decompose()
return soup.get_text(separator=' ', strip=True)[:max_chars]
except Exception:
return ''
def extract_facts(text: str, question: str, url: str) -> list[dict]:
prompt = (
f'Extract key facts from the text that answer: "{question}"\n'
f'Return JSON: {{"facts": ["fact1", ...]}}\n\n'
f'TEXT:\n{text[:3000]}'
)
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
facts = json.loads(resp.choices[0].message.content).get('facts', [])
return [{'fact': f, 'source': url} for f in facts]Fase 4: Identificação de lacunas de conhecimento
Depois da leitura, peça ao LLM que analise os fatos acumulados e identifique o que ainda é desconhecido. Essas lacunas se tornam as consultas de pesquisa da próxima rodada.
def identify_gaps(original_question: str, facts: list[dict]) -> list[str]:
fact_text = '\n'.join(f'- {f["fact"]}' for f in facts[:20])
prompt = (
f'Original question: "{original_question}"\n'
f'Facts gathered so far:\n{fact_text}\n\n'
f'What key aspects are still unanswered? '
f'Return 0-3 follow-up search queries (0 if research is complete).\n'
f'JSON: {{"gaps": ["search query 1", ...]}}'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
return json.loads(resp.choices[0].message.content).get('gaps', [])Condições de encerramento
O ciclo precisa de uma condição clara de parada para evitar ser executado indefinidamente. Pare quando:
- Nenhuma lacuna nova for identificada
- Nenhum fato novo tiver sido adicionado na última iteração
- O número máximo de iterações for atingido (limite de segurança)
- O total de fatos exceder um limite (profundidade suficiente)
def should_continue(gaps: list[str], new_facts_this_round: int,
iteration: int, total_facts: int) -> bool:
if iteration >= 5:
return False # Hard cap: 5 iterations
if new_facts_this_round == 0:
return False # No new information found
if not gaps:
return False # LLM says research is complete
if total_facts >= 50:
return False # Sufficient depth reached
return True
if __name__ == '__main__':
print('Continue (has gaps, new facts)?', should_continue(['gap1'], 4, iteration=1, total_facts=10))
print('Continue (no new facts)?', should_continue(['gap1'], 0, iteration=1, total_facts=10))
Eliminando fatos duplicados
Várias fontes frequentemente relatam o mesmo fato. Elimine duplicidades pedindo ao LLM que mescle fatos semanticamente equivalentes e mantenha a versão mais bem fundamentada.
def deduplicate_facts(facts: list[dict]) -> list[dict]:
if len(facts) <= 3:
return facts
fact_text = '\n'.join(
f'{i}: {f["fact"]} (source: {f["source"]})' for i, f in enumerate(facts)
)
prompt = (
f'Remove duplicate or near-duplicate facts. Keep the most informative version.\n'
f'Return JSON: {{"keep_indices": [0, 1, ...]}}\n\n'
f'FACTS:\n{fact_text}'
)
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
indices = json.loads(resp.choices[0].message.content).get('keep_indices', [])
return [facts[i] for i in indices if i < len(facts)]O ciclo completo de pesquisa
Combine todas as fases em uma única função research() que conduza o ciclo até o encerramento.
def research(question: str) -> dict:
all_facts = []
iteration = 0
# Phase 1: Decompose into sub-questions
queries = decompose_question(question)
while True:
new_facts_this_round = 0
for query in queries:
results = search_web(query, num_results=3)
for result in results:
text = fetch_text(result['url'])
if not text:
continue
facts = extract_facts(text, query, result['url'])
all_facts.extend(facts)
new_facts_this_round += len(facts)
all_facts = deduplicate_facts(all_facts)
gaps = identify_gaps(question, all_facts)
iteration += 1
if not should_continue(gaps, new_facts_this_round, iteration, len(all_facts)):
break
queries = gaps # next iteration searches for the gaps
return {'facts': all_facts, 'iterations': iteration}Rastreando a origem
Cada fato deve conter sua URL de origem para que o relatório final possa incluir citações. Nunca remova os metadados da fonte durante o processamento — eles são necessários para a camada de citações.
def add_fact(fact_list: list, fact_text: str, source_url: str, iteration: int):
fact_list.append({
'fact': fact_text,
'source': source_url,
'iteration': iteration,
'verified': False # set to True after cross-referencing
})
# Example:
facts_store = []
add_fact(facts_store, 'Global inflation peaked at 9.1% in June 2022',
'https://bls.gov/news.release/cpi.htm', iteration=1)
print(f'Logged {len(facts_store)} fact(s):')
for f in facts_store:
print(f" - {f['fact']} (source: {f['source']})")
Pesquisa paralela para ganhar velocidade
Pesquisas sequenciais são lentas — 5 consultas × 3 URLs × 1 acesso cada = 15 chamadas HTTP sequenciais. Use concurrent.futures para paralelizar os acessos dentro de cada iteração.
from concurrent.futures import ThreadPoolExecutor, as_completed
def parallel_research_round(queries: list[str]) -> list[dict]:
collected = []
def process_query(query):
results = search_web(query, num_results=3)
facts = []
for r in results:
text = fetch_text(r['url'])
if text:
facts.extend(extract_facts(text, query, r['url']))
return facts
with ThreadPoolExecutor(max_workers=4) as ex:
futures = {ex.submit(process_query, q): q for q in queries}
for future in as_completed(futures):
collected.extend(future.result())
return collectedMonitorando o progresso do ciclo
Registre cada iteração para poder fazer debug do motivo pelo qual o ciclo parou ou durou mais do que o esperado. Inclua a contagem de consultas, os fatos adicionados e as lacunas identificadas.
import logging
log = logging.getLogger('research_loop')
import sys
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
def log_iteration(iteration: int, queries: list[str],
new_facts: int, total_facts: int, gaps: list[str]):
log.info(
'Iteration %d | Queries: %d | New facts: %d | Total: %d | Gaps: %d',
iteration, len(queries), new_facts, total_facts, len(gaps)
)
if gaps:
for g in gaps:
log.debug(' Gap query: %s', g)
if __name__ == '__main__':
log_iteration(iteration=2, queries=['who are our top competitors?'], new_facts=4, total_facts=12, gaps=['pricing data'])
O que encerra o ciclo de pesquisa quando nenhuma informação nova é encontrada?
Entender as condições de encerramento evita ciclos infinitos e garante que o agente entregue resultados dentro de um intervalo de tempo razoável.
Recapitulação do projeto do ciclo de pesquisa
O ciclo de pesquisa em várias etapas segue: decompose → pesquisar → ler → extrair → identificar lacunas → repetir. Pare quando não houver lacunas, nenhum fato novo for encontrado ou um limite rígido de iterações for atingido.
Sempre acompanhe as URLs de origem de cada fato, paralelize as pesquisas para ganhar velocidade e elimine fatos duplicados antes da síntese.
Perguntas Frequentes
A aula “Projeto de ciclos de pesquisa em várias etapas” é grátis?
Sim — o texto completo de “Projeto de ciclos de pesquisa em várias etapas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Agents, atualize para CoddyKit PRO. O curso de AI Agents inclui 4 aulas no total.
O que vou aprender em “Projeto de ciclos de pesquisa em várias etapas”?
Planejar → pesquisar → ler → extrair → sintetizar → repetir até alcançar profundidade suficiente. Você pratica AI Agents com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Agents?
Nenhuma experiência prévia é necessária. AI Agents no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.
Quanto tempo leva a aula “Projeto de ciclos de pesquisa em várias etapas”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Agents?
Sim. Cada aula de AI Agents inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Projeto de ciclos de pesquisa em várias etapas
- Verificação de fontes e citações
- Geração de relatórios estruturados
- Verificação de fatos e prevenção de alucinações