Skip to content

Chatbot #157

@vinkkees

Description

@vinkkees

simple_chatbot.py

Een compacte 'AI' chatbot: TF-IDF retrieval + eenvoudige leerfunctie.

Scherp, voorspellend en makkelijk uit te breiden.

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import readline # handig voor commandline historie op unix-achtige systemen

class SimpleChatBot:
def init(self, pairs=None):
"""
pairs: lijst van (user_tekst, bot_antwoord) tuples
De bot zoekt in de user_tekst voorbeelden naar de meest vergelijkbare zin
en geeft het corresponderende bot_antwoord terug.
"""
self.pairs = pairs or []
self._build_vectorizer()

def _build_vectorizer(self):
    # Corpus = alle user-zinnen in de dataset
    self.user_texts = [p[0] for p in self.pairs]
    if self.user_texts:
        self.vectorizer = TfidfVectorizer(ngram_range=(1,2), min_df=1)
        self.user_tfidf = self.vectorizer.fit_transform(self.user_texts)
    else:
        # lege vectorizer (voorkomt errors voor nog geen data)
        self.vectorizer = TfidfVectorizer(ngram_range=(1,2), min_df=1)
        self.user_tfidf = None

def respond(self, text, top_k=1):
    """
    Geef een antwoord op 'text'. Als er geen dataset is, antwoord simpele fallback.
    top_k bepaalt hoeveel beste matches worden overwogen voor randomisatie (optioneel).
    """
    text = text.strip()
    if not text:
        return "Zeg iets, of zwijg plechtig. (Maar zeg iets.)"

    # Als we nog geen voorbeelden hebben:
    if not self.user_texts:
        return "Ik ben nog leerling. Leer me iets door 'learn: jouw zin => mijn antwoord' te typen."

    # Transformeer en vergelijk
    x = self.vectorizer.transform([text])
    sims = cosine_similarity(x, self.user_tfidf).flatten()
    best_idx = np.argmax(sims)
    best_score = sims[best_idx]

    # Drempel: als geen goede match, geef fallback
    if best_score < 0.25:
        return ("Dat is intrigerend maar ik heb daar geen goede zet voor. "
                "Vertel me wat het betekent of leer me met: learn: vraag => antwoord")

    # soms wil je variatie: kies uit de top_k best overeenkomende antwoorden
    if top_k > 1:
        top_indices = np.argsort(sims)[-top_k:]
        chosen = np.random.choice(top_indices)
        return self.pairs[chosen][1]
    else:
        return self.pairs[best_idx][1]

def learn_from_pair(self, user_text, bot_answer):
    """Voeg een nieuw voorbeeld toe en rebuild de vectorizer."""
    self.pairs.append((user_text.strip(), bot_answer.strip()))
    self._build_vectorizer()

def save_to_file(self, path):
    """Sla de dataset op (eenvoudig formaat)."""
    with open(path, "w", encoding="utf-8") as f:
        for u, b in self.pairs:
            # tab-gescheiden, simpele escaping
            u_escaped = u.replace("\t", "    ").replace("\n", " ")
            b_escaped = b.replace("\t", "    ").replace("\n", " ")
            f.write(u_escaped + "\t" + b_escaped + "\n")

def load_from_file(self, path):
    """Laad dataset van disk."""
    new_pairs = []
    try:
        with open(path, "r", encoding="utf-8") as f:
            for line in f:
                if not line.strip(): continue
                parts = line.rstrip("\n").split("\t")
                if len(parts) >= 2:
                    new_pairs.append((parts[0], "\t".join(parts[1:])))
        self.pairs = new_pairs
        self._build_vectorizer()
    except FileNotFoundError:
        print(f"Geen dataset gevonden op {path}; start met lege dataset.")

def demo():
# Voorbeeld dataset: voeg hier je eigen moeilijke, schaakachtige zinnen toe.
initial_pairs = [
("hallo", "Salve. Wat wens je te weten?"),
("hoe gaat het", "Adequaat. En bij jou?"),
("wat is schaak", "Schaak is een strategisch bordspel waarin anticipatie essentieel is."),
("leer me schaken", "Begin met de basisprincipes: ontwikkel stukken, controleer centrum, en vermijd vroegtijdige koningszwakte."),
("vertel een mop", "Twee torens lopen naar het bord... de rest is vertelling."),
("bedankt", "Graag gedaan. Mijn circuits glimmen van voldoening.")
]

bot = SimpleChatBot(initial_pairs)
bot.load_from_file("my_bot_memory.txt")  # optioneel: laad eerdere leerdata

print("Slimme ChatBot (TF-IDF retrieval). Type 'quit' om te stoppen.")
print("Leer de bot nieuw gedrag met: learn: jouw vraag => mijn antwoord")
print("Sla geheugen op met: save")
print("Laad geheugen met: load")
print("-" * 60)

while True:
    try:
        user = input("Jij: ").strip()
    except (KeyboardInterrupt, EOFError):
        print("\nEinde. Memory wordt automatisch opgeslagen.")
        bot.save_to_file("my_bot_memory.txt")
        break

    if not user:
        continue

    if user.lower() in ("quit", "exit", "stop"):
        print("Bot: Tot ziens. Je zet was goed, je eindezelfde mind ook.")
        bot.save_to_file("my_bot_memory.txt")
        break

    # leer commando: learn: vraag => antwoord
    if user.lower().startswith("learn:"):
        payload = user[6:].strip()
        if "=>" in payload:
            q, a = payload.split("=>", 1)
            bot.learn_from_pair(q.strip(), a.strip())
            print("Bot: Acknowledged. Ik heb die zet opgeslagen.")
        else:
            print("Bot: Foutief leerformaat. Gebruik: learn: vraag => antwoord")
        continue

    if user.lower() == "save":
        bot.save_to_file("my_bot_memory.txt")
        print("Bot: geheugen opgeslagen naar my_bot_memory.txt")
        continue

    if user.lower() == "load":
        bot.load_from_file("my_bot_memory.txt")
        print("Bot: geheugen geladen (my_bot_memory.txt)")
        continue

    # normaal gesprek
    reply = bot.respond(user, top_k=3)
    print("Bot:", reply)

if name == "main":
demo()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions