AI-agenten · Les

Patroon voor de deep-researchlus

Query → lezen → extraheren → synthetiseren → opnieuw zoeken: onderzoek over meerdere hops.

Les 3 van 413 stappen

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:

  1. Eerste zoekopdracht — zoek met de oorspronkelijke vraag van de gebruiker
  2. Beste resultaten lezen — haal de belangrijkste feiten uit de beste drie bronnen
  3. Hiaten vaststellen — welke belangrijke vragen zijn nog niet beantwoord?
  4. Vervolgzoekopdrachten — genereer gerichte zoekopdrachten voor de hiaten
  5. 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 answer

Fase 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 iteration

Beë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_facts

Kennistoets

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.

Gratis beginnen

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

  1. Tavily en SerpAPI voor zoeken door agents
  2. Zoekresultaten rangschikken en filteren
  3. Patroon voor de deep-researchlus
  4. Webzoekopdrachten combineren met RAG
← Terug naar AI-agenten