Design av forskningsloopar i flera steg
Planera → sök → läs → extrahera → sammanfoga → upprepa tills djupet är tillräckligt.
Design av forskningsloopar i flera steg är en gratis lektion i AI-agenter på CoddyKit. Detta är lektion 1 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för AI-agenter, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i AI-agenter innehåller totalt 4 lektioner.
Vad är en research-loop?
En research-loop är ett agentiskt mönster där en LLM planerar, söker, läser, extraherar, identifierar luckor och upprepar processen tills forskningsmålet är uppnått.
Till skillnad från en sökning i ett enda steg anpassar sig loopen efter vad den hittar — den följer oväntade ledtrådar och överger återvändsgränder.
Fas 1: Dela upp frågan
Det första steget är att bryta ned forskningsfrågan i delfrågor. Det skapar en strukturerad sökplan och hindrar agenten från att planlöst bläddra.
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)Fas 2: Webbsökning
Gör en webbsökning för varje delfråga. Använd ett sök-API (Serper, Brave, Bing) för att hämta en lista med URL:er och utdrag. Läs inte alla sidor — prioritera källor av hög kvalitet först.
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
]Fas 3: Läsa och extrahera fakta
Hämta varje URL och extrahera viktiga fakta som är relevanta för delfrågan. Använd en LLM för att läsa artikeln och skapa en lista med fakta tillsammans med källans URL.
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]Fas 4: Identifiera kunskapsluckor
Be LLM att granska de insamlade fakta efter läsningen och identifiera vad som fortfarande är okänt. Dessa luckor blir nästa omgång sökfrågor.
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', [])Avslutningsvillkor
Loopen behöver ett tydligt stoppvillkor för att inte köras för evigt. Stoppa när:
- Inga nya luckor identifieras
- Inga nya fakta har lagts till i den senaste iterationen
- Det maximala antalet iterationer har nåtts (säkerhetsgräns)
- Det totala antalet fakta överskrider ett tröskelvärde (tillräckligt djup)
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))
Deduplikera fakta
Flera källor rapporterar ofta samma fakta. Deduplikera dem genom att be LLM slå samman semantiskt likvärdiga fakta och behålla den version som har bäst källstöd.
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)]Den fullständiga research-loopen
Kombinera alla faser i en enda funktion, research(), som driver loopen tills den avslutas.
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}Spåra proveniens
Varje faktum måste innehålla sin käll-URL så att slutrapporten kan innehålla källhänvisningar. Ta aldrig bort källmetadata under bearbetningen — den behövs för citeringslagret.
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']})")
Parallell sökning för högre hastighet
Sekventiella sökningar är långsamma — 5 frågor × 3 URL:er × 1 hämtning vardera = 15 sekventiella HTTP-anrop. Använd concurrent.futures för att parallellisera hämtningar inom varje iteration.
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 collectedÖvervaka loopens förlopp
Logga varje iteration så att ni kan felsöka varför loopen stoppades eller kördes längre än förväntat. Inkludera antalet frågor, antalet tillagda fakta och de identifierade luckorna.
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'])
Vad avslutar research-loopen när ingen ny information hittas?
Genom att förstå avslutningsvillkoren undviker ni oändliga loopar och säkerställer att agenten levererar resultat inom en rimlig tidsram.
Sammanfattning av research-loopens design
Research-loopen i flera steg följer: bryt ned → sök → läs → extrahera → identifiera luckor → upprepa. Stoppa när luckorna är tomma, när inga nya fakta hittas eller när en fast iterationsgräns nås.
Spåra alltid käll-URL:er tillsammans med varje faktum, parallellisera sökningar för högre hastighet och deduplicera fakta före syntesen.
Lär dig AI-agenter med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 60
- Lektioner
- 239
Vanliga frågor
Är lektionen ”Design av forskningsloopar i flera steg” gratis?
Ja – hela texten till ”Design av forskningsloopar i flera steg” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i AI-agenter, kan Ni uppgradera till CoddyKit PRO. Kursen i AI-agenter innehåller totalt 4 lektioner.
Vad lär jag mig i ”Design av forskningsloopar i flera steg”?
Planera → sök → läs → extrahera → sammanfoga → upprepa tills djupet är tillräckligt. Ni övar på AI-agenter med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig AI-agenter?
Du behöver inga förkunskaper. Utbildningen i AI-agenter på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.
Hur lång tid tar lektionen ”Design av forskningsloopar i flera steg”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här AI-agenter-lektionen?
Ja. Varje AI-agenter-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Design av forskningsloopar i flera steg
- Källverifiering och källhänvisningar
- Generering av strukturerade rapporter
- Faktagranskning och förebyggande av hallucinationer