Wzorzec pętli pogłębionego researchu
Zapytanie → odczyt → ekstrakcja → synteza → ponowne zapytanie: wieloetapowy research.
Wzorzec pętli pogłębionego researchu to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.
Czym jest pętla pogłębionego researchu
Pętla pogłębionego researchu to wzorzec agenta, w którym agent iteracyjnie wyszukuje informacje, czyta je, identyfikuje luki w wiedzy i ponownie wyszukuje dane, aż zgromadzi wystarczająco dużo informacji do napisania wyczerpującej odpowiedzi.
W przeciwieństwie do pojedynczego wyszukiwania ten wzorzec naśladuje sposób pracy ludzkiego badacza: eksploracja → nauka → odkrywanie niewiadomych → pogłębianie analizy.
Przegląd architektury pętli
Pętla pogłębionego researchu składa się z pięciu powtarzających się faz:
- Początkowe zapytanie — wyszukiwanie na podstawie oryginalnego pytania użytkownika
- Odczytanie najważniejszych wyników — wyodrębnienie kluczowych faktów z 3 najlepszych źródeł
- Identyfikacja luk — jakie ważne pytania pozostają bez odpowiedzi?
- Zapytania uzupełniające — generowanie ukierunkowanych wyszukiwań dotyczących luk
- Synteza — połączenie wszystkich zebranych faktów w końcową odpowiedź
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)Faza 1: Wyszukiwanie początkowe
Pierwsze wyszukiwanie wykorzystuje oryginalne pytanie użytkownika. Należy pobrać 3–5 wyników i wyodrębnić z nich surową treść tekstową. Ta faza koncentruje się na szerokim rozpoznaniu tematu — uzyskaniu ogólnego obrazu jego zakresu.
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 answerFaza 2: Wyodrębnianie najważniejszych faktów
Należy przekazać surowe wyniki wyszukiwania do LLM-a i poprosić go o wyodrębnienie konkretnych, weryfikowalnych faktów istotnych dla pytania badawczego. Fakty powinny być atomowe — jeden fakt na element.
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)Faza 3: Identyfikowanie luk w wiedzy
Po każdej iteracji wyszukiwania należy zapytać LLM-a: biorąc pod uwagę dotychczasową wiedzę, jakie ważne aspekty oryginalnego pytania wciąż nie zostały omówione?
Te luki stają się zapytaniami w kolejnej rundzie wyszukiwania.
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)Faza 4: Generowanie zapytań uzupełniających
Luki w wiedzy należy przekształcić w skuteczne zapytania wyszukiwania. Luka w rodzaju „Nie znamy modelu cenowego” staje się ukierunkowanym zapytaniem, takim jak „GPT-4 API pricing 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 iterationKryteria zakończenia
Bez jasno określonych kryteriów zakończenia pętla będzie działać bez końca. Należy ją zatrzymać, gdy: (1) nie zidentyfikowano żadnych luk, (2) osiągnięto maksymalną liczbę iteracji, (3) wyczerpano budżet tokenów lub (4) nowe fakty są zbyt podobne do już istniejących (malejące korzyści).
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))
Faza 5: Synteza końcowej odpowiedzi
Po zakończeniu pętli należy przekazać wszystkie zebrane fakty do LLM-a w celu ich syntezy. Zadaniem LLM-a jest uporządkowanie faktów w spójną, dobrze zorganizowaną odpowiedź z przypisaniem informacji do źródeł.
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
))Śledzenie źródeł w kolejnych iteracjach
W pętli obejmującej wiele iteracji fakty są zbierane z wielu różnych źródeł. Należy śledzić źródło każdego faktu, aby końcowa odpowiedź mogła dokładnie je cytować.
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)Równoległe wykonywanie wyszukiwań
W przypadku wielu zapytań uzupełniających należy wykonywać je równolegle za pomocą asyncio, aby skrócić łączny czas researchu. Może to skrócić pętlę obejmującą 3 iteracje z 30 do 15 sekund.
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))Deduplikacja faktów
Różne źródła często przedstawiają ten sam fakt innymi słowami. Przed syntezą należy usunąć duplikaty faktów za pomocą podobieństwa embeddingów, aby uniknąć wielokrotnego powtarzania tych samych informacji w końcowej odpowiedzi.
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_factsSprawdzenie wiedzy
Jaki jest prawidłowy warunek zakończenia pętli pogłębionego researchu, gdy nie znaleziono żadnych luk w wiedzy?
Podsumowanie: wzorzec pętli pogłębionego researchu
Wzorzec pętli pogłębionego researchu wygląda następująco: wyszukiwanie → wyodrębnianie faktów → znajdowanie luk → generowanie zapytań uzupełniających → powtarzanie → synteza. Odzwierciedla on sposób pracy ludzkiego badacza.
Najważniejsze decyzje projektowe to: kryteria zakończenia (maksymalna liczba iteracji, brak luk, malejące korzyści), śledzenie źródeł na potrzeby przypisywania informacji, równoległe wyszukiwanie w celu zwiększenia szybkości oraz deduplikacja faktów przed syntezą. Należy ograniczyć pętlę do 3–4 iteracji, aby zachować równowagę między głębią, kosztem i opóźnieniem.
Ucz się AI Agents dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 60
- Lekcje
- 239
Często zadawane pytania
Czy lekcja „Wzorzec pętli pogłębionego researchu” jest bezpłatna?
Tak — pełny tekst „Wzorzec pętli pogłębionego researchu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.
Co nauczysz się w „Wzorzec pętli pogłębionego researchu”?
Zapytanie → odczyt → ekstrakcja → synteza → ponowne zapytanie: wieloetapowy research. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Agents?
Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Wzorzec pętli pogłębionego researchu”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?
Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tavily i SerpAPI do wyszukiwania przez agenta
- Ranking i filtrowanie wyników wyszukiwania
- Wzorzec pętli pogłębionego researchu
- Łączenie wyszukiwania internetowego z RAG