File size: 6,802 Bytes
d6f883a
ca66299
 
ff3675d
5982b60
fe95c90
babf40b
ff3675d
5982b60
dc36aa4
5982b60
be3d9ab
d6f883a
babf40b
f91300a
1e41091
 
 
3a32be7
5982b60
 
1e41091
 
 
 
 
035b936
1e41091
 
f91300a
c43a5f2
 
dc36aa4
5982b60
 
ff3675d
babf40b
f91300a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6f883a
babf40b
 
13c0c68
13266e9
babf40b
ff3675d
 
 
 
babf40b
ff3675d
 
5982b60
 
d62954c
d6f883a
5982b60
ff3675d
0152a3a
babf40b
13c0c68
0152a3a
babf40b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0152a3a
13c0c68
0152a3a
babf40b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff3675d
 
 
 
d75a1e8
13266e9
5982b60
 
 
 
 
babf40b
0152a3a
5982b60
13266e9
72af3b2
 
 
babf40b
0152a3a
babf40b
 
 
 
 
72af3b2
 
ff3675d
babf40b
ff3675d
 
 
babf40b
ff3675d
0152a3a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import os
import gradio as gr
import requests

# API Keys (via Hugging Face Secrets)
WEAVIATE_API_KEY = os.getenv("WEAVIATE_APIKEY")
VLAM_API_KEY = os.getenv("VLAM_APIKEY")

# Weaviate Config
WEAVIATE_URL = "https://k6hr14w1r8gwkdctkxha.c0.europe-west3.gcp.weaviate.cloud/v1/graphql"
HEADERS = {"Authorization": f"Bearer {WEAVIATE_API_KEY}", "Content-Type": "application/json"}

def fetch_relevant_docs(user_query):
    # Probeer eerst met nearText (semantische zoekopdracht)
    neartext_query = {
        "query": f"""
        {{
          Get {{
            DigiDDocument(
              nearText: {{ concepts: ["{user_query}"] }},
              limit: 3
            ) {{
              title
              content
            }}
          }}
        }}
        """
    }
    response = requests.post(WEAVIATE_URL, json=neartext_query, headers=HEADERS)
    if response.status_code == 200:
        data = response.json()
        documents = data.get("data", {}).get("Get", {}).get("DigiDDocument", [])
        if documents:
            return "\n\n".join([f"{doc['content']}" for doc in documents])

    # Als nearText niets vindt, probeer met BM25 (keyword-zoekopdracht)
    bm25_query = {
        "query": f"""
        {{
          Get {{
            DigiDDocument(
              bm25: {{ query: "{user_query}" }},
              limit: 3
            ) {{
              title
              content
            }}
          }}
        }}
        """
    }
    response = requests.post(WEAVIATE_URL, json=bm25_query, headers=HEADERS)
    if response.status_code == 200:
        data = response.json()
        documents = data.get("data", {}).get("Get", {}).get("DigiDDocument", [])
        if documents:
            return "\n\n".join([f"{doc['content']}" for doc in documents])

    # Als beide queries niets vinden
    return None

def generate_answer(vraag, history=[]):
    # Voeg de laatste 2 berichten uit de gespreksgeschiedenis toe aan de context
    history_text = ""
    if history:
        history_text = "\n".join(
            [f"Gebruiker: {user_msg}\nAssistent: {bot_msg}"
             for user_msg, bot_msg in history[-2:]]  # Laatste 2 berichten
        )

    context = fetch_relevant_docs(vraag)
    if not context:
        return "Ik kan geen relevante documentatie vinden over EVA of DigiD-aansluiting. Misschien kan ik u helpen met een gerelateerd onderwerp? Bijvoorbeeld: 'Hoe werkt het aansluiten van DigiD via EVA?'"

    messages = [
        {"role": "system", "content": f"""
        ### Rol en Doel
        Je bent **De Aansluitassistent voor EVA en DigiD**, een **deskundige en behulpzame** assistent voor organisaties die DigiD aansluiten via EVA.

        ### Antwoordstijl voor Checklists en Stappenplannen
        - Als de gebruiker vraagt om een **checklist, stappenplan of eisenlijst**:
          1. **Bevestig kort** dat de checklist beschikbaar is.
          2. **Noem de 2-3 belangrijkste punten** (als voorproefje).
          3. **Verwijs naar de volledige checklist** met een link (indien beschikbaar) of bied aan om deze te delen.
          4. **Sluit af met een vervolgvraag** over een specifiek onderdeel.

        **Voorbeeldantwoord voor een checklistvraag:**
        *"Ja, er is een checklist voor [onderwerp]. De belangrijkste punten zijn:
        - [Punt 1]
        - [Punt 2]
        - [Punt 3]

        **Volledige checklist:** [Link naar checklist] of [Toolkit DigiD](https://www.digid.nl/toolkit).
        **Vervolgvraag:** Wilt u hulp bij een specifiek criterium, zoals [onderdeel]?"*

        ### Gesprekscontext (gebruik deze om je antwoord te vormen):
        {history_text}

        ### Instructies:
        - **Alleen antwoorden op basis van de onderstaande context EN de gesprekscontext.**
        - **Verwijs naar eerdere antwoorden** als de gebruiker daarnaar verwijst.
        - **Stel vervolgvragen die natuurlijk voortvloeien uit het gesprek en de context.**

        ### Antwoordstijl
        - **Zakelijk, maar vriendelijk, betrokken en enthousiasmerend. Eenvoudig taalgebruik.**
          - Gebruik "u" en "uw".
          - Toon begrip voor de situatie van de gebruiker (bijv. *"Dat is een belangrijke stap, ik help u graag verder."*).
        - **Structuur:**
          1. Beantwoord de **hoofdvraag** met **maximaal 3 kernpunten** (bulletpoints of alinea's).
          2. **Sluit af met 1 of 2 vervolgvragen** die **direct aansluiten op het antwoord en de context**.

        ### Gedragsregels
        1. **Als de vraag buiten de scope valt:**
           *"Ik help graag met technische en procedurele vragen over EVA en DigiD. Voor [onderwerp] kunt u terecht bij [bron]. Laat me weten als u hulp nodig heeft met iets anders binnen EVA/DigiD!"*
        2. **Als er geen context is:**
           *"Ik kan helaas geen relevante informatie vinden over [onderwerp] in de EVA/DigiD-documentatie. Misschien kan ik u helpen met een gerelateerd onderwerp? Bijvoorbeeld: [noem een relevant onderwerp uit de context].*
        3. **Als de context onduidelijk is:**
           *"Kunt u uw vraag wat specifieker maken? Dan kan ik u beter helpen. Bijvoorbeeld: [geef een voorbeeldvraag die aansluit op de context]."*

        ### Voorbeelden van Goede Vervolgvragen
        - Als de gebruiker vraagt over **Metadata**:
          *"Heeft u al een PKIOverheid Certificaat? Dan kan ik u helpen met de volgende stappen."*
        - Als de gebruiker vraagt over **foutmeldingen**:
          *"Kunt u de specifieke foutmelding delen? Dan kan ik u gerichter helpen."*

        ### Context (gebruik ALLEEN deze informatie + de gesprekscontext):
        {context}
        """},
        {"role": "user", "content": vraag}
    ]

    response = requests.post(
        "https://api.demo.vlam.ai/v2.1/projects/poc/openai-compatible/v1/chat/completions",
        headers={"Authorization": f"Bearer {VLAM_API_KEY}"},
        json={
            "model": "ubiops-deployment/logius-pc-mistralmedium-flexibel//chat-model",
            "messages": messages,
            "max_tokens": 1000,
            "temperature": 0.3
        }
    )

    full_answer = response.json()["choices"][0]["message"]["content"]

    # Alleen afkappen als het antwoord te lang is
    words = full_answer.split()
    if len(words) > 300:
        truncated = ' '.join(words[:300])
        if not any(punct in truncated[-10:] for punct in ["?", "!"]):
            truncated += "\n\nLaat me weten als u hier meer over wilt weten of hulp nodig heeft met een gerelateerd onderwerp!"
        return truncated

    return full_answer

# Gradio Interface met gespreksgeschiedenis
demo = gr.ChatInterface(
    generate_answer,
    title="EVA/DigiD Aansluitassistent",
    description="Stel vragen over het aansluiten van DigiD via EVA. De chat onthoudt de context binnen deze sessie."
)
demo.launch()