Een meerstaps onderzoeksloop ontwerpen
Plannen → zoeken → lezen → extraheren → synthetiseren → herhalen tot er voldoende diepgang is.
Een meerstaps onderzoeksloop ontwerpen is een gratis AI-agenten-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject AI-agenten. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus AI-agenten bevat in totaal 4 lessen.
Wat is een onderzoeksloop?
Een onderzoeksloop is een patroon voor agents waarin een LLM plant, zoekt, leest, extraheert, hiaten vaststelt en herhaalt totdat het onderzoeksdoel is bereikt.
In tegenstelling tot een eenmalige zoekopdracht past de loop zich aan op basis van wat hij vindt — hij volgt onverwachte aanknopingspunten en laat doodlopende wegen los.
Fase 1: Vraag opsplitsen
De eerste stap is de onderzoeksvraag opsplitsen in deelvragen. Zo ontstaat een gericht zoekplan en voorkom je dat de agent doelloos rondbladert.
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: Op het web zoeken
Voer voor elke deelvraag een zoekopdracht op het web uit. Gebruik een zoek-API (Serper, Brave, Bing) om een lijst met URL's en fragmenten op te halen. Lees niet alle pagina's — geef bronnen van hoge kwaliteit voorrang.
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: Feiten lezen en extraheren
Haal elke URL op en extraheer de belangrijkste feiten die relevant zijn voor de deelvraag. Gebruik een LLM om het artikel te lezen en een lijst met feiten en de bron-URL te maken.
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: Kennishiaten vaststellen
Vraag de LLM na het lezen om de verzamelde feiten te beoordelen en vast te stellen wat nog onbekend is. Deze hiaten worden de zoekopdrachten voor de volgende ronde.
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', [])Stopvoorwaarden
De loop heeft een duidelijke stopvoorwaarde nodig om te voorkomen dat hij eindeloos blijft draaien. Stop wanneer:
- er geen nieuwe hiaten worden vastgesteld
- er in de laatste iteratie geen nieuwe feiten zijn toegevoegd
- het maximumaantal iteraties is bereikt (veiligheidslimiet)
- het totale aantal feiten een drempel overschrijdt (voldoende diepgang)
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))
Feiten ontdubbelen
Meerdere bronnen melden vaak hetzelfde feit. Ontdubbel feiten door de LLM te vragen semantisch gelijkwaardige feiten samen te voegen en de best onderbouwde versie te behouden.
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)]De volledige onderzoeksloop
Combineer alle fasen in één functie research() die de loop aanstuurt totdat deze stopt.
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}Herkomst bijhouden
Elk feit moet de bron-URL bevatten, zodat het eindrapport bronverwijzingen kan opnemen. Verwijder tijdens de verwerking nooit bronmetagegevens — de laag voor bronverwijzingen heeft deze nodig.
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']})")
Parallel zoeken voor snelheid
Opeenvolgende zoekopdrachten zijn traag — 5 zoekopdrachten × 3 URL's × 1 keer ophalen = 15 opeenvolgende HTTP-aanroepen. Gebruik concurrent.futures om het ophalen binnen elke iteratie te paralleliseren.
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 collectedVoortgang van de loop bewaken
Registreer elke iteratie, zodat je kunt nagaan waarom de loop is gestopt of langer heeft gedraaid dan verwacht. Vermeld het aantal zoekopdrachten, de toegevoegde feiten en de vastgestelde hiaten.
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'])
Wat beëindigt de onderzoeksloop wanneer er geen nieuwe informatie wordt gevonden?
Als je de stopvoorwaarden begrijpt, voorkom je oneindige loops en zorg je ervoor dat de agent binnen een redelijke tijd resultaten oplevert.
Samenvatting van het ontwerp van de onderzoeksloop
De meerstaps onderzoeksloop volgt: opsplitsen → zoeken → lezen → extraheren → hiaten vaststellen → herhalen. Stop wanneer er geen hiaten meer zijn, er geen nieuwe feiten worden gevonden of een harde iteratielimiet is bereikt.
Houd altijd de bron-URL bij elk feit bij, paralleliseer zoekopdrachten voor snelheid en ontdubbel feiten voordat je ze samenvoegt.
Leer AI-agenten met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 60
- Lessen
- 239
Veelgestelde vragen
Is de les “Een meerstaps onderzoeksloop ontwerpen” gratis?
Ja — de volledige tekst van “Een meerstaps onderzoeksloop ontwerpen” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus AI-agenten wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus AI-agenten bevat in totaal 4 lessen.
Wat leer ik in “Een meerstaps onderzoeksloop ontwerpen”?
Plannen → zoeken → lezen → extraheren → synthetiseren → herhalen tot er voldoende diepgang is. Je oefent met AI-agenten door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met AI-agenten te beginnen?
Ervaring vooraf is niet nodig. AI-agenten op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.
Hoe lang duurt de les “Een meerstaps onderzoeksloop ontwerpen”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over AI-agenten?
Ja. Elke les over AI-agenten bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Een meerstaps onderzoeksloop ontwerpen
- Bronverificatie en bronvermelding
- Gestructureerde rapporten genereren
- Feiten controleren en hallucinaties voorkomen