Automated commit
This commit is contained in:
parent
b272879187
commit
0f71428f04
154
app/agent.py
154
app/agent.py
|
|
@ -1,85 +1,113 @@
|
|||
"""Agent template — customize this for your domain.
|
||||
"""Vergunningzoeker agent — agentic search over permits.
|
||||
|
||||
This is where you define what the AI assistant can do. The default
|
||||
implementation provides a simple Q&A agent. Replace or extend the
|
||||
`run_agent` function with your own logic.
|
||||
|
||||
The agent receives:
|
||||
- question: the user's message
|
||||
- history: previous messages in the conversation
|
||||
- db: SQLAlchemy database session (for querying your app's data)
|
||||
|
||||
The agent returns:
|
||||
- answer: the text response to show the user
|
||||
- steps: list of transparency steps (shown in the UI)
|
||||
|
||||
Each step has:
|
||||
- type: "search", "query", "tool", "reasoning", etc.
|
||||
- label: human-readable description
|
||||
- detail: the raw data (search terms, query results, etc.)
|
||||
|
||||
Example customization for a permit search app:
|
||||
|
||||
def run_agent(question, history, db):
|
||||
steps = []
|
||||
|
||||
# Step 1: Generate search terms
|
||||
terms = ask_llm_for_search_terms(question)
|
||||
steps.append({"type": "search", "label": "Zoektermen", "detail": terms})
|
||||
|
||||
# Step 2: Query database
|
||||
results = search_permits(db, terms)
|
||||
steps.append({"type": "query", "label": f"{len(results)} resultaten", "detail": [...]})
|
||||
|
||||
# Step 3: Synthesize answer
|
||||
answer = ask_llm_to_answer(question, results)
|
||||
return {"answer": answer, "steps": steps}
|
||||
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:
|
||||
"""Process a user question and return an answer with transparency steps.
|
||||
|
||||
Override this function with your domain-specific agent logic.
|
||||
The default implementation is a simple LLM Q&A with conversation context.
|
||||
|
||||
Args:
|
||||
question: The user's current message.
|
||||
history: List of {"role": "user"|"assistant", "content": "..."} dicts.
|
||||
db: SQLAlchemy database session for querying app data.
|
||||
|
||||
Returns:
|
||||
{"answer": str, "steps": list[dict]}
|
||||
"""
|
||||
steps = []
|
||||
|
||||
# Build conversation context
|
||||
context_messages = ""
|
||||
context_str = ""
|
||||
if history:
|
||||
recent = history[-6:] # last 3 exchanges
|
||||
context_messages = "\n".join(
|
||||
f"{'Gebruiker' if m['role'] == 'user' else 'Assistent'}: {m['content']}"
|
||||
recent = history[-4:]
|
||||
context_str = "\n".join(
|
||||
f"{'Gebruiker' if m['role'] == 'user' else 'Assistent'}: {m['content'][:200]}"
|
||||
for m in recent
|
||||
)
|
||||
|
||||
# Call LLM
|
||||
prompt = question
|
||||
if context_messages:
|
||||
prompt = f"Eerdere berichten:\n{context_messages}\n\nNieuwe vraag: {question}"
|
||||
# Step 1: Generate search queries
|
||||
full_prompt = question
|
||||
if context_str:
|
||||
full_prompt = f"Gesprekscontext:\n{context_str}\n\nNieuwe vraag: {question}"
|
||||
|
||||
steps.append({"type": "reasoning", "label": "Vraag naar LLM gestuurd", "detail": None})
|
||||
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()]
|
||||
|
||||
result = druppie.call("llm", "chat", {
|
||||
"prompt": prompt,
|
||||
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 behulpzame assistent. Beantwoord vragen in het Nederlands. "
|
||||
"Wees beknopt maar volledig."
|
||||
"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."
|
||||
),
|
||||
})
|
||||
|
||||
answer = result.get("answer", "Geen antwoord ontvangen.")
|
||||
return {"answer": answer, "steps": steps}
|
||||
return {
|
||||
"answer": answer_result.get("answer", "Geen antwoord ontvangen."),
|
||||
"steps": steps,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue