"""Vergunningzoeker agent — agentic search over permits. Receives questions, generates search queries, searches the permit database, and synthesizes answers. Each step is logged for transparency. """ import json from druppie_sdk import DruppieClient from app.models import Permit druppie = DruppieClient() def run_agent(question: str, history: list[dict], db) -> dict: steps = [] # Build conversation context context_str = "" if history: recent = history[-4:] context_str = "\n".join( f"{'Gebruiker' if m['role'] == 'user' else 'Assistent'}: {m['content'][:200]}" for m in recent ) # Step 1: Generate search queries full_prompt = question if context_str: full_prompt = f"Gesprekscontext:\n{context_str}\n\nNieuwe vraag: {question}" query_result = druppie.call("llm", "chat", { "prompt": ( "De gebruiker stelt een vraag over vergunningen in een database. " "Genereer 1-3 zoektermen (komma-gescheiden) waarmee relevante " "vergunningen gevonden kunnen worden. Antwoord ALLEEN met de zoektermen.\n\n" f"Vraag: {full_prompt}" ), "system": "Je genereert zoektermen voor een vergunningendatabase. Antwoord alleen met komma-gescheiden zoektermen.", }) search_terms = [t.strip() for t in query_result.get("answer", question).split(",") if t.strip()] steps.append({ "type": "search", "label": f"Zoektermen: {', '.join(search_terms)}", "detail": search_terms, }) # Step 2: Search database with each term found = {} search_details = [] for term in search_terms[:3]: like = f"%{term}%" results = db.query(Permit).filter( Permit.permit_number.ilike(like) | Permit.applicant_name.ilike(like) | Permit.permit_holder_name.ilike(like) | Permit.location.ilike(like) | Permit.permit_type.ilike(like) | Permit.applicable_law.ilike(like) | Permit.work_type.ilike(like) | Permit.extracted_text.ilike(like) ).limit(10).all() new_ids = [p.id for p in results if p.id not in found] for p in results: found[p.id] = p search_details.append(f'"{term}" → {len(results)} resultaten ({len(new_ids)} nieuw)') steps.append({ "type": "query", "label": f"{len(found)} vergunningen gevonden", "detail": search_details, }) # Step 3: Build context from found permits if found: context_parts = [] for p in found.values(): parts = [f"Vergunning #{p.id}: {p.permit_number or 'geen nummer'}"] if p.applicant_name: parts.append(f" Aanvrager: {p.applicant_name}") if p.permit_holder_name: parts.append(f" Houder: {p.permit_holder_name}") if p.permit_type: parts.append(f" Type: {p.permit_type}") if p.location: parts.append(f" Locatie: {p.location}") if p.issue_date: parts.append(f" Datum: {p.issue_date}") if p.archive_status: parts.append(f" Archiefstatus: {p.archive_status}") if p.applicable_law: parts.append(f" Wet: {p.applicable_law}") if p.work_type: parts.append(f" Type werk: {p.work_type}") if p.extracted_text: parts.append(f" Tekst (fragment): {p.extracted_text[:400]}") context_parts.append("\n".join(parts)) context = "\n\n".join(context_parts) else: context = "Geen vergunningen gevonden in de database." # Step 4: LLM synthesizes answer steps.append({"type": "reasoning", "label": "Antwoord genereren", "detail": None}) answer_result = druppie.call("llm", "chat", { "prompt": ( f"Beantwoord de volgende vraag op basis van de vergunningendata.\n\n" f"Vraag: {full_prompt}\n\n" f"Gevonden vergunningen ({len(found)} resultaten):\n\n{context}" ), "system": ( "Je bent een assistent voor waterschap-medewerkers. Beantwoord vragen " "over vergunningen op basis van de aangeleverde data. Wees specifiek " "en verwijs naar vergunningnummers. Als de data onvoldoende is, zeg dat eerlijk." ), }) return { "answer": answer_result.get("answer", "Geen antwoord ontvangen."), "steps": steps, }