Patroon voor de deep-researchlus
Query → lezen → extraheren → synthetiseren → opnieuw zoeken: onderzoek over meerdere hops.
Patroon voor de deep-researchlus is een gratis AI-agenten-les op CoddyKit. Dit is les 3 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 de lus voor diepgaand onderzoek?
Een lus voor diepgaand onderzoek is een agentpatroon waarbij de agent herhaaldelijk zoekt, leest, kennishiaten vaststelt en opnieuw zoekt totdat er voldoende informatie is verzameld om een uitgebreid antwoord te schrijven.
In tegenstelling tot één zoekopdracht bootst dit patroon de werkwijze van een menselijke onderzoeker na: verkennen → leren → onbekende zaken ontdekken → dieper graven.
Overzicht van de lusarchitectuur
De lus voor diepgaand onderzoek bestaat uit vijf fasen die zich herhalen:
- Eerste zoekopdracht — zoek met de oorspronkelijke vraag van de gebruiker
- Beste resultaten lezen — haal de belangrijkste feiten uit de beste drie bronnen
- Hiaten vaststellen — welke belangrijke vragen zijn nog niet beantwoord?
- Vervolgzoekopdrachten — genereer gerichte zoekopdrachten voor de hiaten
- Synthetiseren — combineer alle verzamelde feiten tot een uiteindelijke antwoord
def deep_research(question, max_iterations=3):
all_facts = []
queries_used = [question]
for iteration in range(max_iterations):
results = search_and_rank(queries_used[-1])
facts = extract_facts(results, question)
all_facts.extend(facts)
gaps = identify_knowledge_gaps(question, all_facts)
if not gaps:
print(f'Research complete after {iteration + 1} iterations')
break
follow_up = generate_follow_up_query(gaps)
queries_used.append(follow_up)
print(f'Iteration {iteration + 1}: {follow_up}')
return synthesize_answer(question, all_facts)Fase 1: eerste zoekopdracht
Bij de eerste zoekopdracht wordt de oorspronkelijke vraag van de gebruiker gebruikt. Haal drie tot vijf resultaten op en haal de onbewerkte tekstinhoud eruit. In deze fase gaat het om breedte: een overzicht krijgen van het onderwerp en de verschillende invalshoeken.
from tavily import TavilyClient
import os
client = TavilyClient(api_key=os.getenv('TAVILY_API_KEY'))
def initial_search(question, n_results=5):
results = client.search(
query=question,
max_results=n_results,
search_depth='advanced' # deeper extraction for research tasks
)
return results.get('results', [])
# Also capture the direct answer if available
def get_direct_answer(question):
results = client.search(query=question, max_results=3)
return results.get('answer', '') # Tavily's synthesized answerFase 2: belangrijkste feiten eruit halen
Geef de onbewerkte zoekresultaten door aan de LLM en vraag deze om specifieke, verifieerbare feiten te halen die relevant zijn voor de onderzoeksvraag. Feiten moeten atomair zijn: één bewering per item.
EXTRACT_FACTS_PROMPT = '''You are a research assistant.
From the search results below, extract all factual claims relevant to the question.
Question: {question}
Search results:
{results_text}
Return a JSON list of facts:
["GPT-4 was released in March 2023",
"GPT-4 supports 128k context window",
...]
Only include verifiable facts. No opinions or summaries. JSON:'''
def extract_facts(results, question):
import json
results_text = '\n\n'.join(
f"[{i+1}] {r['title']}\n{r['content'][:600]}"
for i, r in enumerate(results)
)
response = llm_call(EXTRACT_FACTS_PROMPT.format(
question=question, results_text=results_text
))
return json.loads(response)Fase 3: kennishiaten vaststellen
Vraag de LLM na elke zoekiteratie: welke belangrijke aspecten van de oorspronkelijke vraag zijn, uitgaande van wat we tot nu toe weten, nog niet behandeld?
Deze hiaten worden de zoekopdrachten voor de volgende ronde.
GAPS_PROMPT = '''You are a research analyst.
Original research question: {question}
Facts gathered so far:
{facts}
What important aspects of the question are still NOT covered by these facts?
List 2-3 specific knowledge gaps as a JSON array of strings.
If the question is fully answered, return an empty array [].
JSON:'''
def identify_knowledge_gaps(question, facts):
import json
facts_text = '\n'.join(f'- {f}' for f in facts)
response = llm_call(GAPS_PROMPT.format(
question=question, facts=facts_text
))
return json.loads(response)Fase 4: vervolgzoekopdrachten genereren
Zet kennishiaten om in effectieve zoekopdrachten. Een hiaat als "We kennen het prijsmodel niet" wordt een gerichte zoekopdracht als "GPT-4 API-prijs per token 2024".
QUERY_GEN_PROMPT = '''Convert these knowledge gaps into specific web search queries.
Original topic: {topic}
Knowledge gaps:
{gaps}
Return a JSON array of search query strings.
Make each query specific and searchable.
JSON:'''
def generate_follow_up_queries(topic, gaps):
import json
gaps_text = '\n'.join(f'- {g}' for g in gaps)
response = llm_call(QUERY_GEN_PROMPT.format(
topic=topic, gaps=gaps_text
))
queries = json.loads(response)
return queries[:3] # max 3 follow-up queries per iterationBeëindigingscriteria
Zonder duidelijke beëindigingscriteria blijft de lus oneindig draaien. Stop wanneer: (1) er geen hiaten zijn vastgesteld, (2) het maximumaantal iteraties is bereikt, (3) het tokenbudget is opgebruikt of (4) nieuwe feiten te veel overeenkomen met bestaande feiten (afnemende meeropbrengst).
MAX_ITERATIONS = 4
MAX_FACTS = 50
MIN_NEW_FACTS_TO_CONTINUE = 3
def should_continue(gaps, all_facts, new_facts, iteration):
if iteration >= MAX_ITERATIONS:
print(f'Stopping: max iterations ({MAX_ITERATIONS}) reached')
return False
if not gaps:
print('Stopping: no knowledge gaps found')
return False
if len(all_facts) >= MAX_FACTS:
print(f'Stopping: fact budget ({MAX_FACTS}) reached')
return False
if len(new_facts) < MIN_NEW_FACTS_TO_CONTINUE:
print(f'Stopping: diminishing returns ({len(new_facts)} new facts)')
return False
return True
if __name__ == '__main__':
print('Continue?', should_continue(gaps=['gap1'], all_facts=['f'] * 10, new_facts=['f1', 'f2', 'f3'], iteration=1))
print('Continue?', should_continue(gaps=[], all_facts=['f'] * 10, new_facts=['f1'], iteration=1))
Fase 5: het uiteindelijke antwoord samenstellen
Geef nadat de lus is beëindigd alle verzamelde feiten door aan de LLM om ze samen te voegen. Het is de taak van de LLM om de feiten te ordenen tot een samenhangend, goed gestructureerd antwoord met bronvermelding.
SYNTHESIS_PROMPT = '''You are a research writer.
Original question: {question}
Researched facts (gathered from {n_iterations} search iterations):
{facts}
Write a comprehensive, well-structured answer based on these facts.
Format with clear sections. Be specific and cite facts where relevant.
If any aspect of the question could not be fully answered, say so.
Answer:'''
def synthesize_answer(question, all_facts, n_iterations):
facts_text = '\n'.join(f'- {f}' for f in all_facts)
return llm_call(SYNTHESIS_PROMPT.format(
question=question,
facts=facts_text,
n_iterations=n_iterations
))Bronnen bijhouden tijdens iteraties
In een lus met meerdere iteraties verzamel je feiten uit veel verschillende bronnen. Houd bij uit welke bron elk feit afkomstig is, zodat het uiteindelijke antwoord er correct naar kan verwijzen.
def extract_facts_with_sources(results, question):
import json
PROMPT = '''Extract facts from these search results. For each fact include the source URL.
Question: {question}
Results: {results_text}
Return JSON list:
[{{"fact": "GPT-4 supports 128k context", "source": "https://openai.com/..."}}, ...]
JSON:'''
results_text = '\n\n'.join(
f"[URL: {r['url']}]\n{r['content'][:500]}"
for r in results
)
response = llm_call(PROMPT.format(
question=question, results_text=results_text
))
return json.loads(response)Zoekopdrachten parallel uitvoeren
Wanneer je meerdere vervolgzoekopdrachten hebt, voer je die parallel uit met asyncio om de totale onderzoekstijd te verkorten. Zo kan een lus met drie iteraties van 30 seconden naar 15 seconden worden teruggebracht.
import asyncio
async def async_search(client, query):
# Tavily has async support
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(
None,
lambda: client.search(query=query, max_results=3)
)
return result.get('results', [])
async def parallel_search(queries):
tasks = [async_search(client, q) for q in queries]
results_list = await asyncio.gather(*tasks)
# Flatten
return [r for results in results_list for r in results]
# Run in event loop
all_results = asyncio.run(parallel_search(follow_up_queries))Feiten ontdubbelen
Verschillende bronnen beschrijven vaak hetzelfde feit met andere woorden. Ontdubbel de feiten vóór het samenstellen van het antwoord met behulp van embedding-similariteit, zodat dezelfde informatie niet meerdere keren in het uiteindelijke antwoord wordt herhaald.
def deduplicate_facts(facts, similarity_threshold=0.92):
if not facts:
return facts
# Get embeddings for all facts
embeddings = [embed(f) for f in facts]
unique_indices = [0] # always keep first
for i in range(1, len(facts)):
is_duplicate = False
for j in unique_indices:
sim = cosine_similarity(embeddings[i], embeddings[j])
if sim > similarity_threshold:
is_duplicate = True
break
if not is_duplicate:
unique_indices.append(i)
unique_facts = [facts[i] for i in unique_indices]
print(f'Deduplicated: {len(facts)} -> {len(unique_facts)} facts')
return unique_factsKennistoets
Wat is de juiste beëindigingsvoorwaarde voor een lus voor diepgaand onderzoek wanneer er geen kennishiaten zijn gevonden?
Samenvatting: patroon van de lus voor diepgaand onderzoek
Het patroon van de lus voor diepgaand onderzoek: zoeken → feiten eruit halen → hiaten vinden → vervolgzoekopdrachten genereren → herhalen → samenvoegen. Dit weerspiegelt de werkwijze van een menselijke onderzoeker.
Belangrijke ontwerpkeuzes zijn beëindigingscriteria (maximumaantal iteraties, geen hiaten, afnemende meeropbrengst), bronnen bijhouden voor bronvermelding, parallel zoeken voor snelheid en feiten ontdubbelen vóór het samenvoegen. Beperk de lus tot drie à vier iteraties om diepgang en kosten en vertraging in balans te houden.
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 “Patroon voor de deep-researchlus” gratis?
Ja — de volledige tekst van “Patroon voor de deep-researchlus” 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 “Patroon voor de deep-researchlus”?
Query → lezen → extraheren → synthetiseren → opnieuw zoeken: onderzoek over meerdere hops. 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 3 van 4.
Hoe lang duurt de les “Patroon voor de deep-researchlus”?
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
- Tavily en SerpAPI voor zoeken door agents
- Zoekresultaten rangschikken en filteren
- Patroon voor de deep-researchlus
- Webzoekopdrachten combineren met RAG