Leer hoe je een Deep Research Assistant bouwt met de agentische redeneercapaciteiten van Jan-v1, inclusief lokale deployment, Streamlit-appontwikkeling en meer.
Het Jan-team heeft onlangs Jan-v1 uitgebracht, een geavanceerd agentisch redeneermodel dat verfijnde onderzoeksautomatisering introduceert via verbeterd gebruik van tools en meerstaps probleemoplossing.
In deze tutorial focus ik op de unieke agentische redeneercapaciteiten van Jan-v1 en laat ik zien hoe het fungeert als een complex onderzoeksworkflow via een lokaal gedeployde Streamlit-interface, aangestuurd door llama.cpp.
In deze tutorial leg ik uit hoe je:
Jan-v1 lokaal deployt met GGUF-kwantisatie voor optimale prestaties en privacy
Een Streamlit-app bouwt voor geautomatiseerd onderzoek met realtime voortgangsweergave
Intelligente webzoekintegratie implementeert met dynamische querygeneratie
Een professionele rapportsynthese maakt met formatspecifieke outputstructurering
Geavanceerde tekstverwerking toepast om schone, business-ready documentatie te garanderen
Aan het einde ziet je app er zo uit:
Wat is Jan-v1?
Jan-v1 is een model met 4B-parameters, gebouwd op de Lucy- en Qwen3-4B-thinking-basis, ontworpen voor agentische workflows zoals toolgebruik, meerstaps redeneren en geautomatiseerd onderzoek. In tegenstelling tot conventionele LLM’s blinkt Jan-v1 uit in het coördineren tussen tools, databronnen en redeneerstappen via gestructureerde scaffolding en op gedrag afgestemde beloningen.
Enkele kernfeatures van dit model:
Agent-ready architectuur: Dit model is geoptimaliseerd voor integratie in de praktijk en verwerkt webzoekopdrachten, synthese en gestructureerde rapportgeneratie met verbeterd redeneren en tool-API’s.
Efficiëntie: Ondanks het kleine formaat levert Jan-v1 prestaties die je normaal bij veel grotere modellen ziet, dankzij gerichte RL-training die focust op doelgerichte output.
Privacy-first deployment: Het compacte GGUF-formaat maakt snelle, lokale inferentie mogelijk, ideaal voor privé onderzoekstaken met lage latentie op laptops of CPU’s.
Op onderzoek afgestemde training: Jan-v1 gebruikt nieuwe beloningsfuncties die informativiteit en beknoptheid balanceren — cruciaal voor taken met hoge inzet zoals analyse en rapportage — waardoor hallucinaties en breedsprakigheid worden geminimaliseerd.
Gebouwd op Qwen3-4B-thinking: Dit model erft robuust chain-of-thought-redeneren en toolbediening van Qwen3’s thinking-modusfusie, wat het bijzonder effectief maakt voor geautomatiseerde onderzoekspijplijnen.
Jan-v1 lokaal instellen
Voor dit project deployen we Jan-v1 lokaal met Llama.cpp en de gekwantiseerde GGUF-versie voor optimale prestaties en middelengebruik.
Stap 1: Installeer de dependencies
Zorg eerst dat je de vereiste Python-packages hebt:
pip install llama-cpp-python==0.3.15
pip install streamlit aiohttp Mogelijk gemaakt door
Deze opdracht installeert alle kerndependencies die nodig zijn voor AI-modelintegratie, het bouwen van een webinterface en asynchrone webbewerkingen.
Stap 2: Download de Jan-v1 GGUF-versie
Maak voordat je het model downloadt een nieuwe map models/ met het volgende commando:
Download daarna het model met het volgende commando:
curl-L https://huggingface.co/janhq/Jan-v1-4B-GGUF/resolve/main/Jan-v1-4B-Q4_K_M.gguf -o Jan-v1-4B-Q4_K_M.ggufMogelijk gemaakt door
Na deze stap heb je het Jan-v1 4B-model in GGUF-formaat succesvol gedownload en in de map models/ geplaatst. Deze gekwantiseerde versie biedt een goede balans tussen prestaties en geheugenefficiëntie, ideaal voor lokale inferentie op CPU’s of Colab.
Stap 3: Stel de Serper API-sleutel in
Meld je aan of log in bij je Serper-account: https://serper.dev/api-keys en kies het tabblad API Keys, kopieer vervolgens de standaard API-sleutel voor deze demo. Laten we deze sleutel als omgevingsvariabele instellen als volgt:
Je bent nu klaar om het model te laden en ermee te werken in je research-assistantpipeline.
Demo: Deep Research Assistant met Jan-v1
Laten we een Streamlit-applicatie bouwen die de geavanceerde onderzoeksvaardigheden van Jan-v1 demonstreert, met intelligente querygeneratie, geautomatiseerd webzoeken (met Serper) en professionele rapportsynthese.
Stap 1: Importeer de libraries
We beginnen met het importeren van alle benodigde libraries voor onze research-assistant. Deze ondersteunen alles van het bouwen van de webinterface tot asynchroon webzoeken en lokale AI-modelintegratie.
import streamlit as st
import json
import time
import re
from datetime import datetime
from typing import List, Dict, Optional
import asyncio
import aiohttp
from llama_cpp import Llama
import os
from dataclasses import dataclassMogelijk gemaakt door
We importeren alle kernlibraries die in de hele app worden gebruikt. Denk aan UI-componenten (Streamlit), concurrency-tools (asyncio, aiohttp) en de LLM-backend (llama_cpp). We nemen ook utilities mee voor parsen, typen en datumafhandeling.
Stap 2: Helperfuncties
Nu implementeren we een paar helperfuncties die rapportopmaak en inhoudsextractie afhandelen. Deze functies zorgen ervoor dat onze gegenereerde rapporten professionele kwaliteit en een consistente structuur behouden.
defsections_for_format(fmt:str)->list[str]:
fmt =(fmt or"").strip().lower()if fmt =="executive":return["EXECUTIVE SUMMARY"]if fmt =="detailed":return["INTRODUCTION","DETAILED ANALYSIS","CURRENT TRENDS AND DEVELOPMENTS","IMPLICATIONS AND RECOMMENDATIONS","CONCLUSION",]if fmt =="academic":return["ABSTRACT","INTRODUCTION","METHODOLOGY","FINDINGS","DISCUSSION","CONCLUSION",]if fmt =="presentation":return["OVERVIEW","KEY INSIGHTS","RECOMMENDATIONS","NEXT STEPS","CONCLUSION",]return["INTRODUCTION","DETAILED ANALYSIS","CONCLUSION"]defextract_final_block(text:str)->str:
m = re.search(r"<final>([\s\S]*?)</final>", text, flags=re.IGNORECASE)if m:
cleaned_text = m.group(1).strip()else:
cleaned_text = text
preamble_patterns =[r"^(?:note:|okay,|hmm,|internal|let me|i (?:will|’ll)|as an ai|thinking|plan:|here is your report|the following is|i have prepared|i am presenting|based on the provided information|below is the report|i hope this meets your requirements|this report outlines|this is the final report).*?$",r"^(?:Here is the report|I have compiled the report|The report is provided below|This is the requested report).*?$",# More specific preamblesr"^(?:Please find the report below|Here's the report).*?$"]for pattern in preamble_patterns:
cleaned_text = re.sub(pattern,"", cleaned_text, flags=re.IGNORECASE | re.MULTILINE).strip()
cleaned_text = re.sub(r"(?m)^\s*[-*•]\s+","", cleaned_text)
cleaned_text = re.sub(r"[#`*_]{1,3}","", cleaned_text)
headers =["EXECUTIVE SUMMARY","INTRODUCTION","DETAILED ANALYSIS","CURRENT TRENDS AND DEVELOPMENTS","IMPLICATIONS AND RECOMMENDATIONS","CONCLUSION","ABSTRACT","METHODOLOGY","FINDINGS","DISCUSSION","OVERVIEW","KEY INSIGHTS","RECOMMENDATIONS","NEXT STEPS"]
sorted_headers =sorted(headers, key=len, reverse=True)
first_pos =-1for h in sorted_headers:match= re.search(r'\b'+ re.escape(h)+r'\b', cleaned_text, flags=re.IGNORECASE)ifmatch:if first_pos ==-1ormatch.start()< first_pos:
first_pos =match.start()if first_pos >=0:
cleaned_text = cleaned_text[first_pos:].strip()return cleaned_textMogelijk gemaakt door
Zo past elke functie in de pipeline:
sections_for_format()-functie: Deze functie genereert sectiekoppen die zijn toegesneden op het gekozen rapportformat (bijv. executive, detailed, academic). Zo voldoet elk rapport aan professionele standaarden en verwachtingen.
extract_final_block()-functie: Deze postprocessingstap isoleert de kern van het rapport. Hij extraheert tekst tussen <final>-tags en verwijdert inleidingen, stopzinnen en markdown-artefacten. Ook detecteert hij de vroegste standaardkop (bijv. "INTRODUCTION", "SUMMARY") om irrelevante inleidende tekst te schrappen, zodat de uiteindelijke output schoon, gestructureerd en klaar voor gebruik is.
Deze helperfuncties zijn cruciaal om consistente, hoogwaardige output te behouden over verschillende rapportformaten en onderzoeksscenario’s voor een denkend model als Jan-v1.
Stap 3: De configuraties instellen
Laten we het kernconfiguratiesysteem opzetten dat modelparameters, API-instellingen en onderzoekspreferenties beheert. Deze modulaire aanpak zorgt voor eenvoudige aanpasbaarheid en optimale prestaties.
Stap 3.1: Onderzoeksconfiguratie
Deze stap definieert een configuratiesysteem en modellader voor efficiënte lokale inferentie met het Jan-v1-model. Het systeem ondersteunt reproduceerbare onderzoekstaken op omgevingen met alleen CPU, zoals laptops of Colab Pro, met behoud van flexibiliteit en veiligheid.
@dataclassclassResearchConfig:
model_path:str="models/Jan-v1-4B-Q4_K_M.gguf"#change as per your location
max_tokens:int=2048
temperature:float=0.6
top_p:float=0.95
top_k:int=20
context_length:int=4096
search_api_key:str= os.getenv("SERPER_API_KEY","")
search_engine:str="serper"Mogelijk gemaakt door
Hier is een overzicht van de configuratie uit de bovenstaande code:
Gecentraliseerde parameters: ResearchConfig is een @dataclass die alle LLM- en zoekmachineconfiguraties centraliseert, waardoor je eenvoudig kunt tunen voor verschillende onderzoeksscenario’s.
Inferentiehyperparameters: Parameters zoals temperature, top_p en top_k zijn ingesteld om creativiteit te balanceren met feitelijke onderbouwing — belangrijk voor onderzoeksgerichte outputs.
Geheugen en context: De context_length is ingesteld op 4096 tokens, geoptimaliseerd voor de langvormige redeneercapaciteiten van Jan-v1.
API-integratie: De search_api_key maakt integratie met Serper mogelijk voor realtime webzoek-augmentatie.
Stap 3.2: Deep Research-assistent
In deze stap definiëren we een Deep Research-assistentklasse die fungeert als de kerncontroller voor alle onderzoekstaken. Zo worden low-level model- en confighandelingen geabstraheerd en worden high-level interfaces voor generatie, zoeken en synthese aangeboden.
classDeepResearchAssistant:def__init__(self, config: ResearchConfig):
self.config = config
self.llm =None
self.demo_mode =Falsedefload_model(self):try:ifnot os.path.exists(self.config.model_path):print(f"Model file not found: {self.config.model_path}")returnFalse
file_size_gb = os.path.getsize(self.config.model_path)/(1024**3)if file_size_gb <1.0:print(f"Model file too small ({file_size_gb:.1f} GB).")returnFalse
self.llm = Llama(
model_path=self.config.model_path,
n_ctx=self.config.context_length,
verbose=False,
n_threads=max(1,min(4, os.cpu_count()//2)),
n_gpu_layers=0,
use_mmap=True,
use_mlock=False,
n_batch=128,
f16_kv=True,)
test = self.llm("Hi", max_tokens=3, temperature=0.1, echo=False)
ok =bool(test and'choices'in test)print("Model loaded "if ok else"Model loaded but test generation failed ")return ok
except Exception as e:print(f"Model loading failed: {e}")returnFalseMogelijk gemaakt door
De klasse DeepResearchAssistant is de hoofdorkestrator die het laden van het model, configuratievalidatie en een nette interface voor onderzoekstaken beheert. Dit configuratiesysteem biedt foutafhandeling en validatie, zodat het model correct wordt geladen en klaar is voor onderzoek. Belangrijkste features van deze klasse:
Model initialiseren: De methode load_model() initialiseert veilig het Jan-v1 GGUF-model via llama-cpp-python, geoptimaliseerd voor lokale CPU-inferentie.
Validatiechecks: Eerst verifiëren we het modelpad en de minimale bestandsgrootte om ontbrekende of corrupte bestanden te detecteren.
Memory mapping: Vervolgens gebruiken we use_mmap=True voor efficiënte, schijf-ondersteunde loading zonder het volledige RAM te gebruiken.
Geoptimaliseerde inferentie: We configureren ook threads, batchgrootte en zetten f16_kv=True aan voor lager geheugengebruik. Met n_gpu_layers=0 draait alles op de CPU.
Sanity check: We voeren een sanity-test uit met een lichte prompt na het laden om te bevestigen dat het model correct reageert.
Deze setup zorgt voor snelle, lichte lokale inferentie, ideaal om Jan-v1 op laptops of Colab te draaien zonder GPU’s.
Stap 3.3: Een reactie genereren
Vervolgens implementeren we het kernsysteem voor reactiegeneratie, samen met webzoekfunctionaliteit die onze onderzoeksautomatisering aandrijft. Hiervoor gebruiken we Serper-API-services zoals hieronder:
defgenerate_response(self, prompt:str, max_tokens:int=None, extra_stops: Optional[List[str]]=None)->str:ifnot self.llm:return"Model not loaded."
stops =["</s>","<|im_end|>","<|endoftext|>"]if extra_stops:
stops.extend(extra_stops)
mt = max_tokens or self.config.max_tokens
try:
resp = self.llm(
prompt,
max_tokens=mt,
temperature=self.config.temperature,
top_p=self.config.top_p,
top_k=self.config.top_k,
stop=stops,
echo=False)return resp["choices"][0]["text"].strip()except Exception as e:returnf"Error generating response: {str(e)}"asyncdefsearch_web(self, query:str, num_results:int=10)-> List[Dict]:if self.config.search_api_key:returnawait self.search_serper(query, num_results)return[]asyncdefsearch_serper(self, query:str, num_results:int)-> List[Dict]:
url ="https://google.serper.dev/search"
payload ={"q": query,"num": num_results}
headers ={"X-API-KEY": self.config.search_api_key,"Content-Type":"application/json"}asyncwith aiohttp.ClientSession()as session:asyncwith session.post(url, json=payload, headers=headers)as response:
data =await response.json()
results =[]for item in data.get('organic',[]):
results.append({'title': item.get('title',''),'url': item.get('link',''),'snippet': item.get('snippet',''),'source':'web'})return results
defgenerate_search_queries(self, topic:str, focus_area:str, depth:str)-> List[str]:
counts ={'surface':5,'moderate':8,'deep':15,'comprehensive':25}
n = counts.get(depth,8)
base =[f"{topic} overview",f"{topic} recent developments",f"{topic} academic studies",f"{topic} case studies",f"{topic} policy and regulation",f"{topic} technical approaches",f"{topic} market analysis",f"{topic} statistics and data",]return base[:n]Mogelijk gemaakt door
De bovenstaande code valt uiteen in de volgende kernfuncties:
generate_response()-functie: Deze functie handelt alle interacties met het Jan-v1-model af met correcte foutafhandeling en configureerbare stopsequenties. Dit borgt consistente, hoogwaardige tekstgeneratie voor onderzoeksynthese.
search_web() en search_serper()-functies: Deze functies implementeren asynchroon webzoeken met de Serper-API, waardoor snelle, parallelle zoekopdrachten over meerdere queries mogelijk zijn. Het async-ontwerp voorkomt UI-blokkering tijdens onderzoeksoperaties.
generate_search_queries()-functie: Om diverse zoekvragen te maken op basis van onderzoeksdiepte en focustopic, implementeren we de functie generate_search_queries(). Deze zorgt voor een brede dekking van het onderzoeksonderwerp — van overzichten en technische benaderingen tot casestudy’s, regelgeving en marktanalyse. De gebruiker kan het aantal queries opschalen van 5 tot 25 afhankelijk van de gekozen diepte (surface, moderate, deep of comprehensive).
Dit reactiegeneratiesysteem vormt de kern van onze onderzoeksautomatisering en combineert de redeneercapaciteiten van Jan-v1 met realtime webzoeken om onderzoeksrapporten te creëren.
Stap 3.4: Onderzoek synthetiseren
Vervolgens implementeren we een synthese-engine die ruwe zoekresultaten omzet in verzorgde, professionele rapporten.
defsynthesize_research(self, topic:str, search_results: List[Dict], focus_area:str, report_format:str)->str:
context_lines =[]for i, result inenumerate(search_results[:20]):
title = result.get("title","")
snippet = result.get("snippet","")
context_lines.append(f"Source {i+1} Title: {title}\nSource {i+1} Summary: {snippet}")
context ="\n".join(context_lines)
sections = sections_for_format(report_format)
sections_text ="\n".join(sections)
synthesis_prompt =f"""
You are an expert research analyst. Write the final, polished report on: "{topic}" for a professional, real-world audience.
***CRITICAL INSTRUCTIONS:***
- Your entire response MUST be the final report, wrapped **EXACTLY** inside <final> and </final> tags.
- DO NOT output any text, thoughts, or commentary BEFORE the <final> tag or AFTER the </final> tag.
- DO NOT include any conversational filler, internal thoughts, or commentary about the generation process (e.g., "As an AI...", "I will now summarize...", "Here is your report:").
- DO NOT use markdown formatting (e.g., #, ##, *, -).
- DO NOT use bullet points or lists.
- Maintain a formal, academic/professional tone throughout.
- Ensure the report is complete and self-contained.
- Include the following section headers, in this order, and no others:
{sections_text}
Guidance:
- Base your writing strictly on the Research Notes provided below. If the notes lack specific data, write a careful, methodology-forward analysis without inventing facts or numbers.
Research Notes:
{context}
Now produce ONLY the final report:
<final>
...your report here...
</final>
"""
raw = self.generate_response(synthesis_prompt, max_tokens=1800, extra_stops=["</final>"])
final_report = extract_final_block(raw)
final_report = re.sub(r"(?m)^\s*[-*•]\s+","", final_report)
final_report = re.sub(r"[#`*_]{1,3}","", final_report)
first =next((h for h in sections if h in final_report),None)if first:
final_report = final_report[final_report.find(first):].strip()return final_reportMogelijk gemaakt door
De bovenstaande code benut de geavanceerde redeneercapaciteiten van Jan-v1 om ruwe webzoekresultaten om te zetten in professionele rapporten die normaal uren handmatig onderzoek en schrijven vergen. Laten we de belangrijkste mogelijkheden toelichten:
Contextuele inbedding: De code zet ongestructureerde zoekhits om in gestructureerde onderzoeksnotities door titels en samenvattingen in genummerde vorm te combineren. Dit zorgt voor hoge informatiedichtheid en minimaliseert contextfragmentatie wanneer het aan het model wordt doorgegeven.
Dynamische promptconstructie: Met de functie sections_for_format() genereren we sectiekoppen op maat van het gekozen outputformat, die met strikte generatieregels in de prompt worden geïnjecteerd om het modelgedrag te begrenzen.
Hallucinatiemitigatie: Door het gebruik van <final>...</final>-tags af te dwingen en conversatiescaffolding te verbieden, voorkomen we dat het model irrelevante tekst, speculatieve samenvattingen of overbodige kadering introduceert.
Postprocessing en normalisatie: Na decodering wordt het rapport opgeschoond met regex-filters om markdown-artefacten, lijstmarkeringen en losse opmaak te verwijderen. De functie zorgt er ook voor dat de output begint bij de eerste geldige sectiekop om de structurele consistentie te verbeteren.
Stap 4: Hoofdflow van de Streamlit-applicatie
Tot slot bouwen we de complete gebruikersinterface die al onze onderzoeksfunctionaliteit samenbrengt in een interactieve applicatie.
De bovenstaande code is opgedeeld in lagen, van het laden van het model en het verzamelen van gebruikersinput tot querygeneratie, webzoeken en uiteindelijke rapportsynthese. Laten we deze lagen bespreken:
UI-laag (Streamlit-frontend): De app heeft een header met gradientstijl en een responsieve lay-out voor een verzorgde gebruikerservaring. De interface blijft intuïtief terwijl je fijnmazige controle krijgt over onderzoeksparameters — ideaal voor zowel technische als niet-technische gebruikers.
Configureerbare pipeline: Gebruikers kunnen de workflow aanpassen met selectors voor diepte, focusgebied (general, academic, business), tijdsbestek en outputstijl (executive, academic, enz.).
Stateful agentbeheer: De DeepResearchAssistant wordt één keer per sessie geïnitialiseerd via st.session_state, zodat de modelstaat behouden blijft en herhaald laden wordt vermeden.
Realtime feedback en async-zoekopdrachten: De voortgang wordt gevolgd met dynamische Streamlit-voortgangsbalken en statusupdates. Webzoeken wordt asynchroon uitgevoerd via asyncio, waardoor parallelle queries mogelijk zijn zonder de UI te blokkeren.
Query- en synthese-loop: Zoekqueries worden automatisch gegenereerd op basis van topic en diepte via een prompt-engineeringstrategie. Opgehaalde resultaten worden geaggregeerd en doorgegeven aan het Jan-v1-model voor gestructureerde rapportgeneratie.
Outputvisualisatie en export: Het eindrapport wordt weergegeven in een Markdown-blok met uitklapbare citaties. Gebruikers kunnen het rapport exporteren in TXT- of JSON-formaat of de workflow met één klik opnieuw uitvoeren.
Om het zelf te proberen, sla de code op als app.pyen start:
Deze Deep Research Assistant laat de praktische kracht zien van de geavanceerde redeneercapaciteiten van Jan-v1 bij het automatiseren van complexe onderzoeksworkflows. Het systeem combineert succesvol:
Intelligente automatisering: Maakt end-to-end onderzoek mogelijk van querygeneratie tot professionele rapportsynthese, en bespaart zo uren handwerk.
Schaalbaar ontwerp: De configureerbare onderzoeksdiepte past zich aan van snelle overzichten tot uitgebreide analyses, afhankelijk van de behoefte.
Professionele kwaliteit: Helpt bij het genereren van rapporten die geschikt zijn voor executive-briefings, academisch onderzoek en technische documentatie.
Toegankelijk voor gebruikers: Deze interface maakt geavanceerde AI-onderzoeksvaardigheden beschikbaar voor gebruikers ongeacht hun technische achtergrond.
De applicatie laat zien hoe de 91,1% SimpleQA-nauwkeurigheid van Jan-v1 en de verbeterde redeneercapaciteiten kunnen worden ingezet om productieklare tools te bouwen die direct waarde leveren in workflows voor onderzoek, analyse en contentcreatie.
Ik ben een Google Developers Expert in ML (Gen AI), een Kaggle 3x Expert en een Women Techmakers Ambassador met meer dan 3 jaar ervaring in tech. In 2020 heb ik een healthtech-startup mee opgericht en ik volg een master computer science aan Georgia Tech, met als specialisatie machine learning.
Het Jan-team heeft onlangs Jan-v1 uitgebracht, een geavanceerd agentisch redeneermodel dat verfijnde onderzoeksautomatisering introduceert via verbeterd gebruik van tools en meerstaps probleemoplossing.
In deze tutorial focus ik op de unieke agentische redeneercapaciteiten van Jan-v1 en laat ik zien hoe het fungeert als een complex onderzoeksworkflow via een lokaal gedeployde Streamlit-interface, aangestuurd door llama.cpp.
In deze tutorial leg ik uit hoe je:
Aan het einde ziet je app er zo uit:
Wat is Jan-v1?
Jan-v1 is een model met 4B-parameters, gebouwd op de Lucy- en Qwen3-4B-thinking-basis, ontworpen voor agentische workflows zoals toolgebruik, meerstaps redeneren en geautomatiseerd onderzoek. In tegenstelling tot conventionele LLM’s blinkt Jan-v1 uit in het coördineren tussen tools, databronnen en redeneerstappen via gestructureerde scaffolding en op gedrag afgestemde beloningen.
Enkele kernfeatures van dit model:
Jan-v1 lokaal instellen
Voor dit project deployen we Jan-v1 lokaal met
Llama.cppen de gekwantiseerde GGUF-versie voor optimale prestaties en middelengebruik.Stap 1: Installeer de dependencies
Zorg eerst dat je de vereiste Python-packages hebt:
Deze opdracht installeert alle kerndependencies die nodig zijn voor AI-modelintegratie, het bouwen van een webinterface en asynchrone webbewerkingen.
Stap 2: Download de Jan-v1 GGUF-versie
Maak voordat je het model downloadt een nieuwe map
models/met het volgende commando:Download daarna het model met het volgende commando:
Na deze stap heb je het Jan-v1 4B-model in GGUF-formaat succesvol gedownload en in de map
models/geplaatst. Deze gekwantiseerde versie biedt een goede balans tussen prestaties en geheugenefficiëntie, ideaal voor lokale inferentie op CPU’s of Colab.Stap 3: Stel de Serper API-sleutel in
Meld je aan of log in bij je Serper-account: https://serper.dev/api-keys en kies het tabblad API Keys, kopieer vervolgens de standaard API-sleutel voor deze demo. Laten we deze sleutel als omgevingsvariabele instellen als volgt:
Je bent nu klaar om het model te laden en ermee te werken in je research-assistantpipeline.
Demo: Deep Research Assistant met Jan-v1
Laten we een Streamlit-applicatie bouwen die de geavanceerde onderzoeksvaardigheden van Jan-v1 demonstreert, met intelligente querygeneratie, geautomatiseerd webzoeken (met Serper) en professionele rapportsynthese.
Stap 1: Importeer de libraries
We beginnen met het importeren van alle benodigde libraries voor onze research-assistant. Deze ondersteunen alles van het bouwen van de webinterface tot asynchroon webzoeken en lokale AI-modelintegratie.
We importeren alle kernlibraries die in de hele app worden gebruikt. Denk aan UI-componenten (Streamlit), concurrency-tools (asyncio, aiohttp) en de LLM-backend (llama_cpp). We nemen ook utilities mee voor parsen, typen en datumafhandeling.
Stap 2: Helperfuncties
Nu implementeren we een paar helperfuncties die rapportopmaak en inhoudsextractie afhandelen. Deze functies zorgen ervoor dat onze gegenereerde rapporten professionele kwaliteit en een consistente structuur behouden.
Zo past elke functie in de pipeline:
sections_for_format()-functie: Deze functie genereert sectiekoppen die zijn toegesneden op het gekozen rapportformat (bijv. executive, detailed, academic). Zo voldoet elk rapport aan professionele standaarden en verwachtingen.extract_final_block()-functie: Deze postprocessingstap isoleert de kern van het rapport. Hij extraheert tekst tussen<final>-tags en verwijdert inleidingen, stopzinnen en markdown-artefacten. Ook detecteert hij de vroegste standaardkop (bijv. "INTRODUCTION", "SUMMARY") om irrelevante inleidende tekst te schrappen, zodat de uiteindelijke output schoon, gestructureerd en klaar voor gebruik is.Deze helperfuncties zijn cruciaal om consistente, hoogwaardige output te behouden over verschillende rapportformaten en onderzoeksscenario’s voor een denkend model als Jan-v1.
Stap 3: De configuraties instellen
Laten we het kernconfiguratiesysteem opzetten dat modelparameters, API-instellingen en onderzoekspreferenties beheert. Deze modulaire aanpak zorgt voor eenvoudige aanpasbaarheid en optimale prestaties.
Stap 3.1: Onderzoeksconfiguratie
Deze stap definieert een configuratiesysteem en modellader voor efficiënte lokale inferentie met het Jan-v1-model. Het systeem ondersteunt reproduceerbare onderzoekstaken op omgevingen met alleen CPU, zoals laptops of Colab Pro, met behoud van flexibiliteit en veiligheid.
Hier is een overzicht van de configuratie uit de bovenstaande code:
ResearchConfigis een@dataclassdie alle LLM- en zoekmachineconfiguraties centraliseert, waardoor je eenvoudig kunt tunen voor verschillende onderzoeksscenario’s.temperature,top_pentop_kzijn ingesteld om creativiteit te balanceren met feitelijke onderbouwing — belangrijk voor onderzoeksgerichte outputs.context_lengthis ingesteld op 4096 tokens, geoptimaliseerd voor de langvormige redeneercapaciteiten van Jan-v1.search_api_keymaakt integratie met Serper mogelijk voor realtime webzoek-augmentatie.Stap 3.2: Deep Research-assistent
In deze stap definiëren we een Deep Research-assistentklasse die fungeert als de kerncontroller voor alle onderzoekstaken. Zo worden low-level model- en confighandelingen geabstraheerd en worden high-level interfaces voor generatie, zoeken en synthese aangeboden.
De klasse
DeepResearchAssistantis de hoofdorkestrator die het laden van het model, configuratievalidatie en een nette interface voor onderzoekstaken beheert. Dit configuratiesysteem biedt foutafhandeling en validatie, zodat het model correct wordt geladen en klaar is voor onderzoek. Belangrijkste features van deze klasse:load_model()initialiseert veilig het Jan-v1 GGUF-model viallama-cpp-python, geoptimaliseerd voor lokale CPU-inferentie.use_mmap=Truevoor efficiënte, schijf-ondersteunde loading zonder het volledige RAM te gebruiken.f16_kv=Trueaan voor lager geheugengebruik. Metn_gpu_layers=0draait alles op de CPU.Deze setup zorgt voor snelle, lichte lokale inferentie, ideaal om Jan-v1 op laptops of Colab te draaien zonder GPU’s.
Stap 3.3: Een reactie genereren
Vervolgens implementeren we het kernsysteem voor reactiegeneratie, samen met webzoekfunctionaliteit die onze onderzoeksautomatisering aandrijft. Hiervoor gebruiken we Serper-API-services zoals hieronder:
De bovenstaande code valt uiteen in de volgende kernfuncties:
generate_response()-functie: Deze functie handelt alle interacties met het Jan-v1-model af met correcte foutafhandeling en configureerbare stopsequenties. Dit borgt consistente, hoogwaardige tekstgeneratie voor onderzoeksynthese.search_web()ensearch_serper()-functies: Deze functies implementeren asynchroon webzoeken met de Serper-API, waardoor snelle, parallelle zoekopdrachten over meerdere queries mogelijk zijn. Het async-ontwerp voorkomt UI-blokkering tijdens onderzoeksoperaties.generate_search_queries()-functie: Om diverse zoekvragen te maken op basis van onderzoeksdiepte en focustopic, implementeren we de functiegenerate_search_queries(). Deze zorgt voor een brede dekking van het onderzoeksonderwerp — van overzichten en technische benaderingen tot casestudy’s, regelgeving en marktanalyse. De gebruiker kan het aantal queries opschalen van 5 tot 25 afhankelijk van de gekozen diepte (surface, moderate, deep of comprehensive).Dit reactiegeneratiesysteem vormt de kern van onze onderzoeksautomatisering en combineert de redeneercapaciteiten van Jan-v1 met realtime webzoeken om onderzoeksrapporten te creëren.
Stap 3.4: Onderzoek synthetiseren
Vervolgens implementeren we een synthese-engine die ruwe zoekresultaten omzet in verzorgde, professionele rapporten.
De bovenstaande code benut de geavanceerde redeneercapaciteiten van Jan-v1 om ruwe webzoekresultaten om te zetten in professionele rapporten die normaal uren handmatig onderzoek en schrijven vergen. Laten we de belangrijkste mogelijkheden toelichten:
sections_for_format()genereren we sectiekoppen op maat van het gekozen outputformat, die met strikte generatieregels in de prompt worden geïnjecteerd om het modelgedrag te begrenzen.<final>...</final>-tags af te dwingen en conversatiescaffolding te verbieden, voorkomen we dat het model irrelevante tekst, speculatieve samenvattingen of overbodige kadering introduceert.regex-filters om markdown-artefacten, lijstmarkeringen en losse opmaak te verwijderen. De functie zorgt er ook voor dat de output begint bij de eerste geldige sectiekop om de structurele consistentie te verbeteren.Stap 4: Hoofdflow van de Streamlit-applicatie
Tot slot bouwen we de complete gebruikersinterface die al onze onderzoeksfunctionaliteit samenbrengt in een interactieve applicatie.
De bovenstaande code is opgedeeld in lagen, van het laden van het model en het verzamelen van gebruikersinput tot querygeneratie, webzoeken en uiteindelijke rapportsynthese. Laten we deze lagen bespreken:
DeepResearchAssistantwordt één keer per sessie geïnitialiseerd viast.session_state, zodat de modelstaat behouden blijft en herhaald laden wordt vermeden.asyncio, waardoor parallelle queries mogelijk zijn zonder de UI te blokkeren.TXT- ofJSON-formaat of de workflow met één klik opnieuw uitvoeren.Om het zelf te proberen, sla de code op als
app.pyen start: