GitHub avatar

Fox's Blog

From ELIZA to LLMs: 60 Years of Conversational AI, Rebuilt in TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- five radically different architectures for the same problem, ported to TypeScript with their original data. From 1966 to modern LLMs, here's how conversational AI learned to talk, and what a chatbot repo teaches us about 60 years of research.

From ELIZA to LLMs: 60 Years of Conversational AI, Rebuilt in TypeScript

In 1966, Joseph Weizenbaum wrote 420 lines of MAD-SLIP on an IBM 7094 to create the first chatbot in history. The program was called ELIZA, and it simulated a Rogerian psychotherapist using basic patterns and sentence permutations. Six decades later, conversational AI has become mainstream -- ChatGPT, Claude, Gemini are in every conversation.

But between these two extremes, there was PARRY (the paranoid chatbot, 1972), ALICE (the AIML king with 99,000 categories, 1995), Jabberwacky (the first to learn without rules, 1997), and Cleverbot (its industrial successor, 2008). Five programs, five architectures, one problem: making a machine talk.

This repo contains these five bots, ported to TypeScript with their original data -- ELIZA scripts, PARRY dictionaries, ALICE AIML files. Each port is self-contained, ready to run, and documented in full detail. The goal isn't just to run them: it's to understand how they worked, why they made history, and what their respective architectures teach us about the AI of yesterday... and today.

bun run eliza    # Talk to ELIZA (1966)
bun run parry    # Talk to PARRY (1972)
bun run alice    # Talk to ALICE (1995)
bun run jabber   # Talk to Jabberwacky
bun run cleverbot # Talk to Cleverbot
bun run meeting  # ELIZA vs PARRY automatic

Let's dissect each bot, look at their code, then bridge to modern LLMs through the Luna Protocol articles.


ELIZA (1966): the art of pretending to understand

Let's start with the oldest, and probably the most impressive in its simplicity. ELIZA has no intelligence in the modern sense. No neural network, no statistics, no learning. Just text patterns and a bit of permutation.

The principle

The DOCTOR script (the psychotherapist version) works with a table of keywords, each associated with decomposition patterns and reassembly rules. Here's a typical rule:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO is the keyword. 0 is a decomposition pattern that says "capture everything that follows" (like a wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. is the reassembly rule. That's it.

When you say "Hello, I'm sad today", ELIZA:

  1. Uppercases the text: HELLO I'M SAD TODAY
  2. Scans each word against its keyword table
  3. Finds HELLO → pushes it on the keyword stack
  4. Takes the highest-priority keyword
  5. Tries each decomposition pattern in order
  6. If it matches, selects the next reassembly rule (round-robin)
  7. Replaces (1), (2) etc. with the captured parts

But the truly clever part is the PRE rules. Check this out:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

When ELIZA matches MY, it transforms the rest of the sentence (captured by 0) via the PRE rule, and reinjects the result as if the user just said a new keyword. Concretely:

You say: "My mother hates me"
  → PRE transforms: "YOUR MOTHER HATES YOU"
  → reinjected as if you just said it
  → likely matches "YOU" → new response

That's why ELIZA seems to understand the difference between "I" and "you" -- it's not understanding, it's a perfectly designed mechanical transformation.

Here's the full flow, from user input to response:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalizes punctuation"]
    B --> C["splitUserInput()<br>splits into words"]
    C --> D["Build keyword stack<br>ordered by priority"]
    D --> E{"Stack non-empty?"}
    E -->|"Yes"| F["Pop highest-priority keyword"]
    E -->|"No"| G{"Memory recall?"}
    G -->|"Yes"| H["Recall past user statement"]
    G -->|"No"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Match found?"}
    L -->|"No"| M{"Linked keyword?"}
    M -->|"Yes"| N["Push linked keyword to stack"]
    N --> E
    M -->|"No"| O["Return NOMATCH"]
    O --> J
    L -->|"Yes"| P["Select next reassembly (round-robin)"]
    P --> Q{"Reassembly type?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>into final response"]
    T --> J

What made it believable

Weizenbaum made a genius choice: Rogerian psychotherapy. This approach consists of reflecting the patient's statements without interpreting. "I'm sad" → "You say you're sad." That's exactly what ELIZA knows how to do -- and since it's a recognized therapeutic technique, nobody finds it strange.

In the TypeScript port

The port loads the .ela scripts (original S-expression format), fully parses them (including Hollerith encoding -- a string format from the 60s), and runs the same cycle: uppercasing → split → keyword stack → decomposition → reassembly → PRE/transforms.

➡ View source code


PARRY (1972): the first chatbot with emotions

Six years after ELIZA, Kenneth Colby (a psychiatrist at Stanford) created PARRY: a chatbot that simulates a patient with paranoid schizophrenia. Where ELIZA was an empty mirror, PARRY has a genuine internal emotional model.

The emotional model

PARRY has four continuous variables that evolve each turn of conversation:

Variable Baseline Decay/turn Description
ANGER 0 −1.0 Hostility, irritation
FEAR 0 −0.2 Paranoia (decays slowly after delusion onset)
MISTRUST 0 −0.05 Mistrust (very slow to decrease)
HURT 0 −0.5 Emotional pain

These values increase through emotional jumps (ajump, fjump, hjump) triggered by inference rules, and naturally decay toward their baselines each turn.

The belief network

PARRY has 200+ beliefs stored in the bel file:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Each belief has a category (HUM = the patient, HUM2 = others, DOC = the doctor, INT = the interrogation, INN = intentions) and a strength (0-5). Inference rules (TH2, EMOTE, IF) propagate beliefs between them:

  • TH2: if a belief A exceeds a threshold, it reinforces itself and its consequences increase
  • EMOTE: if a belief exceeds a threshold, it triggers an emotional jump (anger/fear/hurt)
  • IF: conditional -- if A is true, then B becomes true at a certain level

The delusion hierarchy (flare system)

The most fascinating part of PARRY is its "flare" system -- an escalation chain that progressively leads toward the central delusion:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Each keyword triggers a pre-written response (via pattern matching), and if the interlocutor follows the topic, PARRY progressively drifts toward its central persecution delusion. Once a flare is "triggered", it becomes inactive (deadFlares) -- PARRY moves to the next one, simulating an interlocutor digging deeper into the topic.

Why PARRY is historic

In 1972, PARRY passed a variant of the Turing test: psychiatrists received transcripts of conversations with PARRY and with real paranoid patients, and could not reliably distinguish them. It's the first program to achieve this, 40 years before LLMs.

➡ View source code


RFC 439: when ELIZA meets PARRY

On September 18, 1972, two programs talked to each other for the first time in history -- literally: it's the first inter-AI conversation ever recorded. ELIZA (running on BBN in Boston) and PARRY (running at Stanford) conversed over ARPANET, relayed by human operators who typed one's output into the other's input. The transcript was published as RFC 439 ("PARRY Encounters the DOCTOR").

Here's an excerpt of what it looked like:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

It's surprisingly coherent. ELIZA does its therapist job: rephrase, question, explore. PARRY does its paranoid patient job: complain, accuse, express mistrust. Both programs are perfectly in character -- not because they "understand" the situation, but because their respective mechanisms (ELIZA patterns + PARRY emotional model) produce responses that happen to fit together.

The repo can reproduce this conversation with:

bun run meeting

The simulation runs 25 automatic turns between the two bots, with a random starting topic (horses, organized crime, emotions...). Since both ELIZA and PARRY have non-deterministic elements (ELIZA round-robin, PARRY randomization), each run produces a different exchange.

What's striking about ELIZA vs PARRY is that you have two programs -- one with no internal state, the other with a full emotional model -- that together produce a conversation that resembles something deliberate. For 1972, it was mind-blowing.


ALICE (1995): pattern matching at scale

ALICE (Artificial Linguistic Internet Computer Entity) was created by Richard Wallace in 1995, and won the Loebner Prize three times (2000, 2001, 2004). Where ELIZA had a few hundred rules and PARRY a few thousand, ALICE has 99,524 -- spread across 66 AIML files.

AIML: the language of categories

AIML (Artificial Intelligence Markup Language) is an XML format for defining question-answer pairs:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

But ALICE's power comes from wildcards and SRAI (Symbolic Reduction):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- equivalent to <srai><star/></srai> -->
  </template>
</category>

SRAI allows ALICE to redirect an input to another category, creating a reduction chain:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

This is the mechanism that gives ALICE its flexibility: instead of writing a response for every possible phrasing, you write one canonical response and redirect variations toward it. The depth limit is 10 -- beyond that, ALICE gives up to avoid infinite loops (carefully avoided in the category design, but a safety net is essential).

How ALICE matches patterns

Patterns are sorted by specificity: those with the fewest wildcards are tried first. The wildcards * and _ capture any word sequence. The engine compiles each pattern into a regex, then iterates through sorted categories until finding a match.

// Our TypeScript implementation -- simplified but faithful
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Why ALICE dominated the Loebner

99,524 categories is a number that changes everything. ELIZA seemed intelligent because its few rules were well-designed for a specific context (therapy). ALICE covers so many topics that she gives the impression of having genuine general knowledge: science, politics, humor, sports, emotions, it's all there.

➡ View source code


Jabberwacky (1997) & Cleverbot (2008): the epistemic break

All previous bots share an assumption: you have to write the responses. ELIZA has its S-expression rules, PARRY its selective patterns, ALICE its AIML categories. Rollo Carpenter took the complete opposite approach: what if you wrote nothing at all?

The idea

Jabberwacky (launched around 1997, became Cleverbot in 2008) stores no rules. It stores the full history of conversations in a flat transcript, and when someone talks to it, it searches that history for the most similar moment and reuses what was said next:

User: "hello"
  ↓
Search: has anyone ever said "hello" before?
  ↓
Yes, in session #3, line 14, someone said "hello" and the bot replied "hi there!"
  ↓
Respond: "hi there!"

No pattern. No grammar. No XML. Just a giant archive of things people have said to each other, reused at the right moment. This is the very definition of emergence.

The TypeScript implementation

The TypeScript port reproduces this exact architecture:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 seed lines + history"]
    B --> C["withReplies()<br>extracts pairs<br>(line → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>context before this line)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidates sorted"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Pick"| K["Reply = reply.text<br>from winning pair"]
    J -->|"None"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append to transcript<br>save() → JSON"]
    L --> M

Here's the core scoring -- our own heuristic inspired by public descriptions of Cleverbot:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65): similarity between user input and the historical line
  • contextFit (0.25): similarity between recent conversation and what preceded the historical line
  • recencyBonus (0.10): recent memories count a bit more (the bot's personality drifts over time)

The pick is probabilistic (roulette-wheel selection): the best candidate wins more often, but not always -- which provides variety.

Cleverbot: the two documented innovations

Cleverbot adds two mechanisms to Jabberwacky's core concept:

  1. Multi-person learning: millions of users contribute to the same shared transcript. A response drawn from history can come from a completely different voice than the current conversation -- which explains why Cleverbot suddenly changes personality.

  2. Deferred learning: what you say to Cleverbot in a session is NOT available for matching during that same session. New lines are marked pending and only become matchable after a "consolidation" between sessions -- which explains why you can't teach Cleverbot a fact and reuse it in the same conversation.

// Cleverbot: recent lines are invisible until consolidation
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() is called on startup, not during the session

The TypeScript port implements both behaviors: lines have a consolidated flag, and each REPL session starts by consolidating pending lines.

➡ View source code


Analyzing the TypeScript port: designing a common architecture

Building these five bots in the same language confronts you with an interesting question: can you factor code between architectures that are this different?

The answer is: very little. Each bot has a fundamentally different main loop:

Bot Main loop Data Learning
ELIZA Keyword stack → decomposition → reassembly .ela scripts in S-expressions None
PARRY Tokenization → selective patterns / flares / keywords / inferences 58 PDP-10 files (dictionaries, beliefs, rules) None
ALICE Sorted patterns → regex → AIML template → recursive SRAI 66 AIML XML files None
Jabberwacky Similarity → context → recency → weighted pick JSON transcript (grows with use) Continuous
Cleverbot Same as Jabberwacky + pending/consolidated + personas JSON transcript + multi-persona seeds Deferred (between sessions)

What they share is the CLI interface and TypeScript infrastructure (biome for linting, tsx for execution). Everything else is specific to each architecture.

Common design choices

1. Fidelity to original data. For ELIZA, PARRY and ALICE, we use the original files -- ELIZA scripts recovered from Weizenbaum's archives in 2021, original PARRY code from the PDP-10 (58 files), Free ALICE v1.6 AIML. No translation, no rewriting. The bots behave like the originals because they use the same data.

2. Clean-room for proprietary parts. Jabberwacky and Cleverbot are different: their source code was never published (Existor/Rollo Carpenter kept it proprietary). The ports are therefore clean-room reimplementations -- built solely from public descriptions of behavior. No proprietary code or data is copied.

3. Minimal dependencies. The only real prerequisite is TypeScript. ALICE uses dom-js to parse the XML of AIML files (66 files, 99,524 categories, hand-rolled XML parsing would be a waste of time). Everything else is vanilla TypeScript.


From symbolic chatbots to LLMs: the conceptual leap

All five bots we've just seen share a fundamental characteristic: they are symbolic. Their "knowledge" is stored as explicit symbols -- text patterns, rule tables, XML categories, transcript lines. There is no numerical representation of language in any of these systems.

Which also means they all share the same glass ceiling: they can only respond to what has been explicitly planned or recorded. ELIZA is lost if you step outside the therapeutic frame. PARRY can't talk about the weather. ALICE learns nothing from its conversations. Jabberwacky can only reply with lines already spoken.

LLMs (Large Language Models) break through this ceiling by radically changing the paradigm: instead of manipulating symbols, they convert language into numbers and learn statistical relationships between those numbers. They don't store pre-written responses -- they generate each token on the fly by computing probabilities. Let's quickly see how this works.

1. Tokenization

The first step is to split text into tokens -- units smaller than words but larger than characters:

"I don't understand"
  → ["I", " don", "'t", " under", "stand"]

Each token has a numerical ID in a vocabulary (typically 32,000 to 128,000 tokens for recent models). This fragmentation allows the model to handle words it has never seen by decomposing them into known sub-words.

2. Embeddings

Each token ID is converted into a vector -- an array of floating-point numbers (typically 4096 dimensions for a mid-size model). This vector is an embedding that encodes the token's meaning in a mathematical space where semantically close tokens have nearby vectors:

vector("king") − vector("man") + vector("woman") ≈ vector("queen")

This property emerges from training -- nobody programmed it explicitly. It's a consequence of how words are used in similar contexts.

3. Attention

The attention mechanism (introduced by the paper "Attention is All You Need" in 2017) is what made LLMs possible. For each token, attention computes which other tokens in the sentence are important for understanding it:

"The bank refused my loan."
     ↑
Token "bank" looks at: "refused", "loan" → understands it's a financial institution

"I'm going to walk along the river bank."
     ↑
Token "bank" looks at: "walk", "river" → understands it's a river bank

Attention allows the model to capture context -- each token is understood based on those around it, not in isolation.

4. Next-token prediction

LLM training is deceptively simple: you show it text, hide the last token, and ask it to predict it. Then you repeat billions of times.

Input:  "I don't under"
Hidden: "stand"
Model prediction: "stand" (probability 0.87), "estimate" (0.05), "perform" (0.02)...

The goal is to maximize the probability of the true token at each position. This is called next-token prediction. During training, the model adjusts its billions of parameters to minimize prediction error on terabytes of text.

During inference (when you talk to it), the model generates one token at a time in a loop:

Token 1: "I"      (input: "Tell me about yourself.")
Token 2: "'m"     (input: "Tell me about yourself. I")
Token 3: "a"      (input: "Tell me about yourself. I'm")
Token 4: "chatbot" (input: "Tell me about yourself. I'm a")
...

Each token is sampled according to its probability (temperature, top-k, top-p control the degree of "creativity"). And that's it. Billions of parameters doing this thousands of times.

What fundamentally changes

Aspect Symbolic bots (ELIZA, PARRY, ALICE) Modern LLMs
Representation Explicit words and rules Numerical vectors (embeddings)
Generation Selection from pre-written responses Probabilistic token-by-token prediction
Knowledge Stored in rule files Encoded in network weights
Learning Manual (writing rules) Automatic (training on corpus)
Robustness Zero outside expected patterns Generalizes to unseen inputs
Interpretability Perfect (you can read the rules) Limited (black box)

Classic chatbots are transparent but fragile. An LLM is robust but opaque. Both approaches still exist today -- not as competitors, but as tools for different needs.

If you want to go deeper into how LLMs work internally, this video is an excellent resource:

If you want to go deeper into how LLMs work internally, this video is an excellent resource:

How LLMs Work — YouTube

Luna Protocol: the modern synthesis

The Luna Protocol articles (linked below) represent the most complete synthesis of everything we've just seen: a modern Discord bot that combines a local LLM with a sophisticated behavioral system, built on the lessons of 60 years of conversational AI.

Luna Protocol: I created an autonomous Discord bot that simulates a human being

This article details the complete architecture of an LLM-based Discord bot:

  • Priority trigger system (mention > DM > name > keyword > follow-up > random)
  • Human behaviors: variable focus, typos, hesitations (15%), forgetfulness (3%), thematic fatigue
  • Sleep schedules: the bot sleeps, slows down, or ignores depending on the time
  • TTS pipeline: speech synthesis via Piper + ffmpeg → Discord voice messages
  • Real-time streaming: the LLM emits tokens one by one on a typed event bus

What connects this article to historical chatbots is the same quest: making you believe you're talking to a person. ELIZA did it with text mirrors. PARRY with an emotional model. ALICE with 99k categories. Luna Protocol does it with a fine-tuned LLM + a behavioral system that simulates human imperfections.

Luna Protocol: why I fine-tuned a 1.5B model

The second article explores fine-tuning and few-shot priming. The central discovery: a smaller model (1.5B) trained on less data (50k samples) outperforms a larger model (3B) when properly primed with few-shot examples.

This is a lesson that resonates directly with historical chatbots:

  • ELIZA showed that with a few well-designed rules, you can simulate understanding
  • ALICE showed that with 99k categories, you can simulate general knowledge
  • Luna Protocol shows that with good fine-tuning and 5 few-shot examples, a small LLM can simulate a human being

The technique is different, but the principle is the same: data quality and system precision matter more than raw size.


Conclusion: three things to remember

1. Conversational AI didn't start with ChatGPT. ELIZA is 60 years old. PARRY passed the Turing test in 1972. ALICE won the Loebner three times. Jabberwacky laid the groundwork for transcript-based learning, which Cleverbot industrialized at scale. Each approach brought a piece of the puzzle.

2. More data ≠ smarter. Jabberwacky's transcript has no rules. ALICE's 99k categories don't learn. Luna Protocol's fine-tuning on 50k samples outperforms the 3B model. Conventional wisdom says "bigger is better" -- chatbot history shows that architecture and design matter as much as size.

3. The problem hasn't changed in 60 years. How do you make a human believe they're talking to another human? ELIZA answered with text mirrors. PARRY with simulated anger. ALICE with facts. Luna Protocol with an LLM that sleeps and makes typos. The solution changes, the need remains.

The repo is open source -- you can clone it, run each bot, and see for yourself how 60 years of conversational AI fit into a single TypeScript repository.

Resource Link
GitHub repo fox3000foxy/chatbots
Luna Protocol -- bot architecture Read the article
Luna Protocol -- few-shot fine-tuning Read the article
Original ELIZA scripts anthay/ELIZA
Original PARRY source code lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
Original RFC 439 PARRY Encounters the DOCTOR
Excellent explainer of how LLMs work https://www.youtube.com/watch?v=YmLp8qe87A0

Des ELIZA aux LLM : 60 ans d'IA conversationnelle, reconstruite en TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- cinq architectures radicalement différentes du même problème, portées en TypeScript avec leurs données d'origine. De 1966 aux LLM modernes, voici comment l'IA conversationnelle a appris à parler, et ce qu'un repo de chatbots nous apprend sur 60 ans de recherche.

Des ELIZA aux LLM : 60 ans d'IA conversationnelle, reconstruite en TypeScript

En 1966, Joseph Weizenbaum écrivait 420 lignes de MAD-SLIP sur un IBM 7094 pour créer le premier chatbot de l'histoire. Le programme s'appelait ELIZA, et il simulait une psychothérapeute rogérienne avec des motifs de base et des permutations de phrases. Six décennies plus tard, l'IA conversationnelle est devenue un sujet grand public -- ChatGPT, Claude, Gemini sont dans toutes les conversations.

Mais entre ces deux extrêmes, il y a eu PARRY (le chatbot paranoïaque, 1972), ALICE (le roi de l'AIML à 99 000 catégories, 1995), Jabberwacky (le premier à apprendre sans règles, 1997), et Cleverbot (son successeur industriel, 2008). Cinq programmes, cinq architectures, un seul problème : faire parler une machine.

Ce repo contient ces cinq bots, portés en TypeScript avec leurs données d'origine -- scripts ELIZA, dictionnaires PARRY, fichiers AIML d'ALICE. Chaque port est autonome, prêt à l'emploi, et documenté dans les moindres détails. L'objectif n'est pas seulement de les faire tourner : c'est de comprendre comment ils marchaient, pourquoi ils ont marqué l'histoire, et ce que leurs architectures respectives nous apprennent sur l'IA d'hier... et d'aujourd'hui.

bun run eliza    # Parle à ELIZA (1966)
bun run parry    # Parle à PARRY (1972)
bun run alice    # Parle à ALICE (1995)
bun run jabber   # Parle à Jabberwacky
bun run cleverbot # Parle à Cleverbot
bun run meeting  # ELIZA vs PARRY automatique

On va décortiquer chaque bot, regarder leur code, puis faire le pont avec les LLM modernes à travers les articles sur Luna Protocol.


ELIZA (1966) : l'art de faire croire qu'on comprend

Commençons par la plus ancienne, et probablement la plus impressionnante dans sa simplicité. ELIZA n'a aucune intelligence au sens moderne du terme. Pas de réseau de neurones, pas de statistiques, pas d'apprentissage. Juste des motifs textuels et un peu de permutation.

Le principe

Le script DOCTOR (la version psychothérapeute) fonctionne avec un tableau de keywords, chacun associé à des décomposition patterns et des reassembly rules. Voici une règle typique :

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO est le mot-clé. 0 est un pattern de décomposition qui dit "capture tout ce qui suit" (comme un wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. est la règle de réassemblage. C'est tout.

Quand tu dis "Hello, I'm sad today", ELIZA :

  1. Met le texte en majuscules : HELLO I'M SAD TODAY
  2. Scanne chaque mot contre sa table de keywords
  3. Trouve HELLO → le pousse sur la pile de keywords
  4. Prend le keyword avec la plus haute priorité
  5. Essaie chaque pattern de décomposition dans l'ordre
  6. Si ça match, sélectionne la prochaine règle de réassemblage (round-robin)
  7. Remplace les (1), (2) etc. par les parties capturées

Mais la partie vraiment intelligente, c'est les PRE rules. Regarde ça :

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Quand ELIZA matche MY, elle transforme le reste de la phrase (capturé par 0) via la PRE rule, et réinjecte le résultat comme si l'utilisateur venait de dire un nouveau mot-clé. Concrètement :

Tu dis: "My mother hates me"
  → PRE transforme: "YOUR MOTHER HATES YOU"
  → réinjecté comme si tu venais de le dire
  → matche probablement "YOU" → nouvelle réponse

C'est pour ça qu'ELIZA a l'air de comprendre la différence entre "je" et "tu" -- ce n'est pas de la compréhension, c'est une transformation mécanique parfaitement conçue.

Voici le flux complet, de la frappe utilisateur à la réponse :

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalise la ponctuation"]
    B --> C["splitUserInput()<br>découpe en mots"]
    C --> D["Build keyword stack<br>ordonné par priorité"]
    D --> E{"Stack non-vide?"}
    E -->|"Oui"| F["Pop highest-priority keyword"]
    E -->|"Non"| G{"Memory recall?"}
    G -->|"Oui"| H["Recall past user statement"]
    G -->|"Non"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Match found?"}
    L -->|"Non"| M{"Linked keyword?"}
    M -->|"Oui"| N["Push linked keyword to stack"]
    N --> E
    M -->|"Non"| O["Return NOMATCH"]
    O --> J
    L -->|"Oui"| P["Select next reassembly (round-robin)"]
    P --> Q{"Reassembly type?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>into final response"]
    T --> J

Ce qui la rendait crédible

Weizenbaum a fait un choix de génie : la psychothérapie rogérienne. Cette approche consiste à refléter les propos du patient sans interpréter. "Je suis triste" → "Vous dites que vous êtes triste". C'est exactement ce qu'ELIZA sait faire -- et comme c'est une technique thérapeutique reconnue, personne ne trouve ça bizarre.

Dans le port TypeScript

Le port charge les scripts .ela (format S-expression d'origine), les parse entièrement (y compris l'encodage Hollerith -- un format de chaîne des années 60), et exécute le même cycle : uppercasing → split → keyword stack → décomposition → réassemblage → PRE/transforms.

➡ Voir le code source


PARRY (1972) : le premier chatbot avec des émotions

Six ans après ELIZA, Kenneth Colby (psychiatre à Stanford) a créé PARRY : un chatbot qui simule un patient atteint de schizophrénie paranoïde. Là où ELIZA était un miroir vide, PARRY a un véritable modèle émotionnel interne.

Le modèle émotionnel

PARRY a quatre variables continues qui évoluent à chaque tour de conversation :

Variable Ligne de base Décroissance/tour Description
ANGER 0 −1.0 Hostilité, irritation
FEAR 0 −0.2 Paranoïa (décroît lentement après le début du délire)
MISTRUST 0 −0.05 Méfiance (très lente à redescendre)
HURT 0 −0.5 Peine émotionnelle

Ces valeurs augmentent via des emotional jumps (ajump, fjump, hjump) déclenchés par des règles d'inférence, et décroissent naturellement vers leurs lignes de base à chaque tour.

Le réseau de croyances

PARRY a 200+ croyances stockées dans le fichier bel :

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Chaque croyance a une catégorie (HUM = le patient, HUM2 = les autres, DOC = le docteur, INT = l'interrogatoire, INN = les intentions) et une force (0-5). Les règles d'inférence (TH2, EMOTE, IF) propagent les croyances entre elles :

  • TH2 : si une croyance A dépasse un seuil, elle se renforce et ses conséquences augmentent
  • EMOTE : si une croyance dépasse un seuil, elle déclenche un saut émotionnel (anger/fear/hurt)
  • IF : conditionnel -- si A est vraie, alors B devient vraie à un certain niveau

La hiérarchie des délires (flare system)

La partie la plus fascinante de PARRY, c'est son système de "flares" -- une chaîne d'escalade qui mène progressivement vers le délire central :

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Chaque mot-clé déclenche une réponse pré-écrite (via le pattern matching), et si l'interlocuteur suit le sujet, PARRY dérive progressivement vers son délire central de persécution. Une fois qu'un flare est "déclenché", il devient inactif (deadFlares) -- PARRY passe au suivant, simulant un interlocuteur qui creuse le sujet.

Pourquoi PARRY est historique

En 1972, PARRY a passé une variante du test de Turing : des psychiatres ont reçu des transcriptions de conversations avec PARRY et avec de vrais patients paranoïaques, et n'ont pas pu les distinguer de façon fiable. C'est le premier programme à y parvenir, 40 ans avant les LLM.

➡ Voir le code source


RFC 439 : quand ELIZA rencontre PARRY

Le 18 septembre 1972, deux programmes se sont parlé pour la première fois de l'histoire -- littéralement : c'est la première conversation inter-IA jamais enregistrée. ELIZA (tournant sur BBN à Boston) et PARRY (tournant à Stanford) ont conversé via l'ARPANET, relayés par des opérateurs humains qui tapaient la sortie de l'un vers l'entrée de l'autre. La transcription a été publiée comme RFC 439 ("PARRY Encounters the DOCTOR").

Voici un extrait de ce à quoi ça ressemblait :

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

C'est étonnamment cohérent. ELIZA fait son travail de thérapeute : reformuler, questionner, explorer. PARRY fait son travail de patient paranoïaque : se plaindre, accuser, exprimer de la méfiance. Les deux programmes sont parfaitement dans leur rôle -- non parce qu'ils "comprennent" la situation, mais parce que leurs mécanismes respectifs (patterns ELIZA + modèle émotionnel PARRY) produisent des réponses qui s'emboîtent par hasard.

Le repo peut reproduire cette conversation avec :

bun run meeting

La simulation lance 25 tours automatiques entre les deux bots, avec un sujet de départ aléatoire (chevaux, crime organisé, émotions...). Comme ELIZA et PARRY ont tous deux des éléments non-déterministes (round-robin ELIZA, randomisation PARRY), chaque exécution produit un échange différent.

Ce qui est frappant avec ELIZA vs PARRY, c'est qu'on a deux programmes -- l'un sans état interne, l'autre avec un modèle émotionnel complet -- qui produisent ensemble une conversation qui ressemble à quelque chose de délibéré. Pour 1972, c'était sidérant.


ALICE (1995) : le pattern matching à grande échelle

ALICE (Artificial Linguistic Internet Computer Entity) a été créée par Richard Wallace en 1995, et a gagné le Loebner Prize trois fois (2000, 2001, 2004). Là où ELIZA avait quelques centaines de règles et PARRY quelques milliers, ALICE en a 99 524 -- réparties dans 66 fichiers AIML.

AIML : le langage des catégories

AIML (Artificial Intelligence Markup Language) est un format XML pour définir des paires question-réponse :

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Mais la puissance d'ALICE vient des wildcards et du SRAI (Symbolic Reduction) :

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- équivaut à <srai><star/></srai> -->
  </template>
</category>

Le SRAI permet à ALICE de rediriger une entrée vers une autre catégorie, créant une chaîne de réduction :

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

C'est le mécanisme qui donne à ALICE sa flexibilité : au lieu d'écrire une réponse pour chaque formulation possible, on écrit une réponse canonique et on redirige les variations vers elle. La limite de profondeur est de 10 -- au-delà, ALICE abandonne pour éviter les boucles infinies (soigneusement évitées dans la conception des catégories, mais un filet de sécurité reste essentiel).

Comment ALICE matche les patterns

Les patterns sont triés par spécificité : ceux avec le moins de wildcards sont essayés en premier. Les wildcards * et _ capturent n'importe quelle séquence de mots. Le moteur compile chaque pattern en regex, puis itère les catégories triées jusqu'à trouver un match.

// Notre implémentation TypeScript -- simplifiée mais fidèle
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Pourquoi ALICE a dominé les Loebner

99 524 catégories, c'est un nombre qui change tout. ELIZA avait l'air intelligente parce que ses quelques règles étaient bien conçues pour un contexte spécifique (la thérapie). ALICE couvre tellement de sujets qu'elle donne l'impression d'avoir une vraie culture générale : sciences, politique, humour, sports, émotions, tout y est.

➡ Voir le code source


Jabberwacky (1997) & Cleverbot (2008) : la rupture épistémologique

Tous les bots précédents partagent une hypothèse : il faut écrire les réponses. ELIZA a ses règles S-expressions, PARRY ses patterns sélécifs, ALICE ses catégories AIML. Rollo Carpenter a pris le contre-pied total : et si on n'écrivait rien du tout ?

L'idée

Jabberwacky (lancé vers 1997, devenu Cleverbot en 2008) ne stocke aucune règle. Il stocke tout l'historique des conversations dans un transcript plat, et quand quelqu'un lui parle, il cherche dans cet historique le moment le plus similaire et reuse ce qui a été dit après :

Utilisateur: "hello"
  ↓
Chercher: est-ce que quelqu'un a déjà dit "hello" avant ?
  ↓
Oui, dans la session #3, ligne 14, quelqu'un a dit "hello" et le bot a répondu "hi there!"
  ↓
Répondre: "hi there!"

Pas de motif. Pas de grammaire. Pas d'XML. Juste une archive géante de choses que des gens se sont dites, réutilisée au moment opportun. C'est la définition même de l'émergence.

L'implémentation TypeScript

Le port TypeScript reproduit cette architecture exacte :

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 lignes seed + historique"]
    B --> C["withReplies()<br>extrait les paires<br>(ligne → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>context avant cette ligne)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidats triés"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Pick"| K["Reply = reply.text<br>de la paire gagnante"]
    J -->|"Aucun"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append au transcript<br>save() → JSON"]
    L --> M

Voici le cœur du scoring -- notre propre heuristique inspirée des descriptions publiques de Cleverbot :

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65) : similarité entre l'entrée utilisateur et la ligne historique
  • contextFit (0.25) : similarité entre la conversation récente et ce qui précédait la ligne historique
  • recencyBonus (0.10) : les souvenirs récents comptent un peu plus (la personnalité du bot dérive avec le temps)

Le pick est probabiliste (roulette-wheel selection) : le meilleur candidat gagne plus souvent, mais pas toujours -- ce qui donne de la variété.

Cleverbot : les deux innovations documentées

Cleverbot ajoute deux mécanismes au concept de base de Jabberwacky :

  1. Apprentissage multi-personne : des millions d'utilisateurs contribuent au même transcript partagé. Une réponse tirée de l'historique peut venir d'une voix complètement différente de celle de la conversation en cours -- ce qui explique pourquoi Cleverbot change soudain de personnalité.

  2. Apprentissage différé : ce que tu dis à Cleverbot pendant une session n'est PAS disponible pour match pendant cette même session. Les nouvelles lignes sont marquées pending et ne deviennent matchables qu'après une "consolidation" entre les sessions -- ce qui explique pourquoi tu ne peux pas apprendre un fait à Cleverbot et le réutiliser dans la même conversation.

// Cleverbot : les lignes récentes sont invisibles jusqu'à consolidation
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() est appelée au démarrage, pas pendant la session

Le port TypeScript implémente ces deux comportements : les lignes ont un flag consolidated, et chaque session de REPL commence par une consolidation des lignes en attente.

➡ Voir le code source


Analyse du port TypeScript : concevoir une architecture commune

Construire ces cinq bots dans le même langage, c'est se confronter à une question intéressante : est-ce qu'on peut factoriser du code entre des architectures aussi différentes ?

La réponse est : très peu. Chaque bot a une boucle fondamentale différente :

Bot Boucle principale Données Apprentissage
ELIZA Keyword stack → décomposition → réassemblage Scripts .ela en S-expressions Aucun
PARRY Tokenisation → patterns sélécifs / flares / keywords / inférences 58 fichiers PDP-10 (dictionnaires, croyances, règles) Aucun
ALICE Patterns triés → regex → template AIML → SRAI récursif 66 fichiers AIML XML Aucun
Jabberwacky Similarité → contexte → recency → pick pondéré Transcript JSON (grandit avec l'usage) Continue
Cleverbot Même que Jabberwacky + pending/consolidated + personas Transcript JSON + graines multi-personas Différé (entre sessions)

Ce qu'ils partagent, c'est l'interface CLI et l'infrastructure TypeScript (biome pour le lint, tsx pour l'exécution). Le reste est spécifique à chaque architecture.

Choix de conception communs

1. Fidélité aux données originales. Pour ELIZA, PARRY et ALICE, on utilise les fichiers d'origine -- scripts ELIZA retrouvés dans les archives Weizenbaum en 2021, code original PARRY du PDP-10 (58 fichiers), AIML Free ALICE v1.6. Pas de traduction, pas de réécriture. Les bots se comportent comme les originaux parce qu'ils utilisent les mêmes données.

2. Clean-room pour les parties propriétaires. Jabberwacky et Cleverbot sont différents : leur code source n'a jamais été publié (Existor/Rollo Carpenter l'ont gardé propriétaire). Les ports sont donc des clean-room reimplementations -- construites uniquement à partir de descriptions publiques du comportement. Aucune ligne de code ni donnée propriétaire n'est copiée.

3. Dépendances minimales. Le seul vrai prérequis, c'est TypeScript. ALICE utilise dom-js pour parser l'XML des fichiers AIML (66 fichiers, 99 524 catégories, le parsing XML fait maison serait une perte de temps). Tout le reste est vanilla TypeScript.


Des chatbots symboliques aux LLM : le saut conceptuel

Les cinq bots qu'on vient de voir partagent tous une caractéristique fondamentale : ils sont symboliques. Leurs "connaissances" sont stockées comme des symboles explicites -- motifs textuels, tables de règles, catégories XML, lignes de transcript. Il n'y a aucune représentation numérique du langage dans aucun de ces systèmes.

Ce qui signifie aussi qu'ils ont tous le même plafond de verre : ils ne peuvent répondre qu'à ce qui a été explicitement prévu ou enregistré. ELIZA est perdue si tu sors du cadre thérapeutique. PARRY ne peut pas parler de météo. ALICE n'apprend rien de ses conversations. Jabberwacky ne peut répondre que par des répliques déjà prononcées.

Les LLM (Large Language Models) franchissent ce plafond en changeant radicalement de paradigme : au lieu de manipuler des symboles, ils convertissent le langage en nombres et apprennent des relations statistiques entre ces nombres. Ils ne stockent pas des réponses pré-écrites -- ils génèrent chaque token à la volée en calculant des probabilités. Voyons rapidement comment ça marche.

1. Tokenization

La première étape est de découper le texte en tokens -- des unités plus petites que des mots mais plus grandes que des caractères :

"Je ne comprends pas"
  → ["Je", " ne", " comprend", "s", " pas"]

Chaque token a un ID numérique dans un vocabulaire (typiquement 32 000 à 128 000 tokens pour les modèles récents). Cette fragmentation permet au modèle de gérer des mots qu'il n'a jamais vus en les décomposant en sous-mots connus.

2. Embeddings

Chaque token ID est converti en un vecteur -- un tableau de nombres flottants (typiquement 4096 dimensions pour un modèle de taille moyenne). Ce vecteur est un plongement (embedding) qui encode le sens du token dans un espace mathématique où des tokens sémantiquement proches ont des vecteurs proches :

vecteur("roi")  − vecteur("homme") + vecteur("femme")  ≈  vecteur("reine")

Cette propriété émerge de l'entraînement -- personne ne l'a programmée explicitement. C'est une conséquence de la façon dont les mots sont utilisés dans des contextes similaires.

3. Attention

Le mécanisme d'attention (introduit par le papier "Attention is All You Need" en 2017) est ce qui a rendu les LLM possibles. Pour chaque token, l'attention calcule quels autres tokens dans la phrase sont importants pour comprendre celui-ci :

"La banque a refusé mon prêt."
     ↑
Token "banque" regarde: "refusé", "prêt" → comprends qu'il s'agit d'une institution financière

"Je vais me promener sur la banque."
     ↑
Token "banque" regarde: "promener", "sur" → comprends qu'il s'agit d'une rive

L'attention permet au modèle de capturer le contexte -- chaque token est compris en fonction de ceux qui l'entourent, pas isolément.

4. Prédiction du prochain token

L'entraînement d'un LLM est d'une simplicité trompeuse : on lui montre un texte, on lui cache le dernier token, et on lui demande de le prédire. Puis on répète des milliards de fois.

Input:  "Je ne comprends"
Caché:  "pas"
Prédiction du modèle: "pas" (probabilité 0.87), "rien" (0.05), "jamais" (0.02)...

L'objectif est de maximiser la probabilité du vrai token à chaque position. C'est ce qu'on appelle la next-token prediction. Pendant l'entraînement, le modèle ajuste ses milliards de paramètres pour minimiser l'erreur de prédiction sur des téraoctets de texte.

Au moment de l'inférence (quand on lui parle), le modèle génère un token à la fois en boucle :

Token 1: "Je"    (input: "Parle-moi de toi.")
Token 2: "suis"  (input: "Parle-moi de toi. Je")
Token 3: "un"    (input: "Parle-moi de toi. Je suis")
Token 4: "chatbot" (input: "Parle-moi de toi. Je suis un")
...

Chaque token est échantillonné selon sa probabilité (température, top-k, top-p contrôlent le degré de "créativité"). Et c'est tout. Des milliards de paramètres qui font ça des milliers de fois.

Ce qui change fondamentalement

Aspect Bots symboliques (ELIZA, PARRY, ALICE) LLM modernes
Représentation Mots et règles explicites Vecteurs numériques (embeddings)
Génération Sélection dans des réponses pré-écrites Prédiction probabiliste token par token
Connaissances Stockées dans des fichiers de règles Encodées dans les poids du réseau
Apprentissage Manuel (rédaction de règles) Automatique (entraînement sur corpus)
Robustesse Nulle hors des motifs prévus Généralise à des entrées jamais vues
Interprétabilité Parfaite (on peut lire les règles) Limité (boîte noire)

Les chatbots classiques sont transparents mais fragiles. Un LLM est robuste mais opaque. Les deux approches existent encore aujourd'hui -- pas comme concurrents, mais comme outils pour des besoins différents.

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

How LLMs Work — YouTube

Luna Protocol : la synthèse moderne

Les articles sur Luna Protocol (dont les liens sont ci-dessous) représentent la synthèse la plus aboutie de tout ce qu'on vient de voir : un bot Discord moderne qui combine un LLM local avec un système comportemental sophistiqué, le tout construit sur les leçons de 60 ans d'IA conversationnelle.

Luna Protocol : j'ai créé un bot Discord autonome qui simule un être humain

Cet article détaille l'architecture complète d'un bot Discord LLM-based :

  • Système de déclenchement à priorités (mention > DM > nom > mot-clé > follow-up > aléatoire)
  • Comportements humains : concentration variable, fautes de frappe, hésitations (15%), oublis (3%), fatigue thématique
  • Horaires de sommeil : le bot dort, ralentit, ou ignore selon l'heure
  • Pipeline TTS : synthèse vocale via Piper + ffmpeg → messages vocaux Discord
  • Streaming en temps réel : le LLM émet les tokens un par un sur un bus d'événements typé

Ce qui relie cet article aux chatbots historiques, c'est la même quête : faire croire qu'on parle à une personne. ELIZA le faisait avec des miroirs textuels. PARRY avec un modèle émotionnel. ALICE avec 99k catégories. Luna Protocol le fait avec un LLM fine-tuné + un système comportemental qui simule les imperfections humaines.

Luna Protocol : pourquoi j'ai fine-tuné un modèle de 1,5B

Le second article explore le fine-tuning et le few-shot priming. La découverte centrale : un modèle plus petit (1,5B) entraîné sur moins de données (50k échantillons) surpasse un modèle plus gros (3B) quand on l'amorce correctement avec des exemples few-shot.

C'est une leçon qui résonne directement avec les chatbots historiques :

  • ELIZA montrait qu'avec quelques règles bien conçues, on peut simuler la compréhension
  • ALICE montrait qu'avec 99k catégories, on peut simuler la culture générale
  • Luna Protocol montre qu'avec un bon fine-tuning et 5 exemples few-shot, un petit LLM peut simuler un être humain

La technique est différente, mais le principe est le même : la qualité des données et la précision du système comptent plus que la taille brute.


Conclusion : trois choses à retenir

1. L'IA conversationnelle n'a pas commencé avec ChatGPT. ELIZA a 60 ans. PARRY a passé le test de Turing en 1972. ALICE a gagné le Loebner trois fois. Jabberwacky a posé les bases de l'apprentissage par transcript, que Cleverbot a industrialisé à grande échelle. Chaque approche a apporté une pièce du puzzle.

2. Plus de données ≠ plus intelligente. Le transcript de Jabberwacky n'a pas de règles. Les 99k catégories d'ALICE n'apprennent pas. Le fine-tuning de Luna Protocol sur 50k échantillons surpasse le modèle 3B. La sagesse conventionnelle dit "plus c'est gros, mieux c'est" -- l'histoire des chatbots montre que l'architecture et la conception comptent autant que la taille.

3. Le problème est le même depuis 60 ans. Comment faire croire à un humain qu'il parle à un autre humain ? ELIZA répondait avec des miroirs textuels. PARRY avec de la colère simulée. ALICE avec des faits. Luna Protocol avec un LLM qui dort et fait des fautes de frappe. La solution change, le besoin reste.

Le repo est open source -- vous pouvez cloner, lancer chaque bot, et voir par vous-même comment 60 ans d'IA conversationnelle tiennent dans un seul dépôt TypeScript.

Ressource Lien
Dépôt GitHub fox3000foxy/chatbots
Luna Protocol -- architecture bot Lire l'article
Luna Protocol -- fine-tuning few-shot Lire l'article
ELIZA scripts originaux anthay/ELIZA
Code source PARRY original lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 originale PARRY Encounters the DOCTOR
Excellente explication du fonctionnement des LLM https://www.youtube.com/watch?v=YmLp8qe87A0

从ELIZA到LLM:60年对话式AI,用TypeScript重写

ELIZA、PARRY、ALICE、Jabberwacky、Cleverbot —— 同一个问题的五种截然不同的架构,连同原始数据一起移植到了TypeScript。从1966年到现代LLM,对话式AI是如何学会说话的,以及一个聊天机器人仓库能告诉我们关于60年研究的什么。

从ELIZA到LLM:60年对话式AI,用TypeScript重写

1966年,约瑟夫·魏泽鲍姆在IBM 7094上用MAD-SLIP编写了420行代码,创造了历史上第一个聊天机器人。这个程序叫ELIZA,它用基本的文本模式和句子变换来模拟罗杰斯派心理治疗师。六十年后,对话式AI已成为大众话题 —— ChatGPT、Claude、Gemini出现在每一个对话中。

但在这两个极端之间,还有PARRY(偏执型聊天机器人,1972年)、ALICE(拥有99,000个分类的AIML之王,1995年)、Jabberwacky(第一个不靠规则学习的机器人,1997年)和Cleverbot(它的工业级后继者,2008年)。五个程序、五种架构、一个问题:让机器说话。

这个仓库包含了这五个机器人,连同原始数据 —— ELIZA脚本、PARRY词典、ALICE的AIML文件 —— 一起移植到了TypeScript。每个移植都是独立的、开箱即用的,并且详细记录。目标不仅仅是让它们跑起来:而是理解它们如何工作、为什么载入史册、以及各自的架构能告诉我们关于昨天……和今天的AI的什么。

bun run eliza    # 和ELIZA(1966)对话
bun run parry    # 和PARRY(1972)对话
bun run alice    # 和ALICE(1995)对话
bun run jabber   # 和Jabberwacky对话
bun run cleverbot # 和Cleverbot对话
bun run meeting  # ELIZA vs PARRY 自动对战

我们会逐一剖析每个机器人,查看它们的代码,然后通过关于Luna Protocol的文章与现代LLM建立连接。


ELIZA(1966):让人相信自己被理解的艺术

从最古老的开始,也可能是最简单却最令人印象深刻的。ELIZA在现代意义上是没有任何智能的。没有神经网络、没有统计、没有学习。只有文本模式和一点句子的变换。

原理

DOCTOR脚本(心理治疗师版本)通过一个关键词表工作,每个关键词关联着分解模式和重组规则。一个典型的规则是这样的:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO是关键词。0是一个分解模式,意思是"捕获后面所有内容"(类似通配符)。HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.是重组规则。仅此而已。

当你说"Hello, I'm sad today"时,ELIZA会:

  1. 将文本转为大写:HELLO I'M SAD TODAY
  2. 扫描每个单词与关键词表的匹配
  3. 找到HELLO → 压入关键词栈
  4. 取出优先级最高的关键词
  5. 依次尝试每个分解模式
  6. 如果匹配,选择下一个重组规则(轮询)
  7. 用捕获的部分替换(1)、(2)等

但真正聪明的部分是PRE规则。看这个:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

当ELIZA匹配到MY时,它通过PRE规则转换句子的剩余部分(被0捕获),然后将结果重新注入,就像用户刚刚说了新的关键词一样。具体来说:

你说:"My mother hates me"
  → PRE转换:"YOUR MOTHER HATES YOU"
  → 就像你刚才说了这句话一样重新注入
  → 大概会匹配"YOU" → 新的回复

这就是为什么ELIZA看起来能理解"我"和"你"的区别 —— 这不是理解,而是设计精巧的机械转换。

从用户输入到回复的完整流程如下:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>规范化标点"]
    B --> C["splitUserInput()<br>分词"]
    C --> D["Build keyword stack<br>按优先级排序"]
    D --> E{"栈非空?"}
    E -->|"是"| F["Pop highest-priority keyword"]
    E -->|"否"| G{"记忆回忆?"}
    G -->|"是"| H["Recall past user statement"]
    G -->|"否"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"匹配到了?"}
    L -->|"否"| M{"关联关键字?"}
    M -->|"是"| N["Push linked keyword to stack"]
    N --> E
    M -->|"否"| O["Return NOMATCH"]
    O --> J
    L -->|"是"| P["Select next reassembly (round-robin)"]
    P --> Q{"重组类型?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>展开为最终回复"]
    T --> J

为什么它让人觉得可信

魏泽鲍姆做了一个天才的选择:罗杰斯派心理疗法。这种方法在于不加解释地反映患者的陈述。"我很难过" → "你说你很难过"。这正是ELIZA会做的 —— 而且因为这是一种公认的治疗技术,没有人会觉得奇怪。

在TypeScript移植中

该移植加载.ela脚本(原始的S表达式格式),完全解析它们(包括霍勒瑞斯编码 —— 一种60年代的字符串格式),并执行相同的循环:大写化 → 分词 → 关键字栈 → 分解 → 重组 → PRE/转换。

➡ 查看源代码


PARRY(1972):第一个拥有情感的聊天机器人

ELIZA诞生六年后,肯尼斯·科尔比(斯坦福大学的精神科医生)创建了PARRY:一个模拟偏执型精神分裂症患者的聊天机器人。如果说ELIZA是一面空镜子,那么PARRY拥有真正的内部情感模型。

情感模型

PARRY有四个随对话回合变化的连续变量:

变量 基准值 衰减/回合 描述
ANGER 0 −1.0 敌意、烦躁
FEAR 0 −0.2 偏执(妄想开始后缓慢衰减)
MISTRUST 0 −0.05 不信任(下降非常缓慢)
HURT 0 −0.5 情感伤害

这些值通过推理规则触发的情感跳跃(ajump、fjump、hjump)增加,并在每回合自然衰减回基准值。

信念网络

PARRY有200多个信念,存储在bel文件中:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

每个信念都有一个类别(HUM = 患者、HUM2 = 他人、DOC = 医生、INT = 审讯、INN = 意图)和一个强度(0-5)。推理规则(TH2、EMOTE、IF)在信念之间传播:

  • TH2:如果信念A超过阈值,它会自我强化,其后果会增加
  • EMOTE:如果信念超过阈值,它会触发情感跳跃(anger/fear/hurt)
  • IF:条件判断 —— 如果A为真,那么B在特定层级上变为真

妄想层级(flare系统)

PARRY最迷人的部分是它的"flare"系统 —— 一个逐步升级到核心妄想的链条:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

每个关键字都触发一个预写的回复(通过模式匹配),如果对话者跟随话题,PARRY会逐渐滑向它的核心被害妄想。一旦flare被"触发",它会变为不活跃(deadFlares)—— PARRY继续到下一个,模拟一个在深入挖掘话题的对话者。

PARRY为何载入史册

1972年,PARRY通过了图灵测试的一个变种:精神科医生收到了与PARRY和真实偏执患者的对话记录,无法可靠地区分它们。这是第一个做到这一点的程序,比LLM早了40年。

➡ 查看源代码


RFC 439:当ELIZA遇见PARRY

1972年9月18日,两个程序在历史上第一次互相交谈 —— 字面意义上的:这是有史以来记录下的第一次AI间对话。ELIZA(在波士顿的BBN运行)和PARRY(在斯坦福运行)通过ARPANET对话,由人类操作员将一方的输出输入给另一方。对话记录作为RFC 439("PARRY Encounters the DOCTOR")发布。

以下是对话的样子:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

惊人地连贯。ELIZA在做治疗师的工作:转述、提问、探索。PARRY在做偏执患者的工作:抱怨、指责、表达不信任。两个程序都完美地扮演着自己的角色 —— 不是因为它们"理解"情况,而是因为它们各自的机制(ELIZA的模式 + PARRY的情感模型)偶然地产生了相互匹配的回复。

仓库可以重现这段对话:

bun run meeting

模拟在两个机器人之间自动运行25轮,从随机主题开始(马、有组织犯罪、情感……)。由于ELIZA和PARRY都有非确定性元素(ELIZA的轮询、PARRY的随机化),每次运行都会产生不同的交流。

ELIZA vs PARRY的惊人之处在于,两个程序 —— 一个没有内部状态,另一个拥有完整的情感模型 —— 一起创造了一段看起来是有意识的对话。对于1972年来说,这令人瞠目结舌。


ALICE(1995):大规模模式匹配

ALICE(Artificial Linguistic Internet Computer Entity)由理查德·华莱士在1995年创建,三次获得Loebner Prize(2000、2001、2004)。如果说ELIZA有几百条规则、PARRY有几千条规则,那么ALICE有99,524条 —— 分布在66个AIML文件中。

AIML:分类的语言

AIML(Artificial Intelligence Markup Language)是一种用于定义问答对的XML格式:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

但ALICE的真正力量来自通配符和SRAI(符号化简):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- 等同于 <srai><star/></srai> -->
  </template>
</category>

SRAI允许ALICE将输入重定向到另一个分类,形成一个化简链:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

这就是赋予ALICE灵活性的机制:不必为每种可能的措辞都写回复,而是写一个标准回复,然后将变体重定向到它。深度限制为10 —— 超过这个深度ALICE会放弃,以避免无限循环(在分类设计中已被谨慎避免,但安全网仍然必不可少)。

ALICE如何匹配模式

模式按特异性排序:通配符较少的优先尝试。通配符*和_捕获任意单词序列。引擎将每个模式编译为正则表达式,然后遍历已排序的分类直到找到匹配。

// 我们的TypeScript实现 —— 简化但忠实
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

为什么ALICE统治了Loebner大奖

99,524个分类 —— 这个数字改变了一切。ELIZA之所以显得聪明,是因为它的几条规则针对特定语境(心理治疗)设计得很好。ALICE覆盖了如此多的主题,以至于它给人一种拥有真正通识知识的感觉:科学、政治、幽默、体育、情感,一应俱全。

➡ 查看源代码


Jabberwacky(1997)和Cleverbot(2008):认识论上的断裂

以前所有的机器人都共享一个假设:回复需要被写出来。ELIZA有它的S表达式规则,PARRY有它的选择模式,ALICE有它的AIML分类。罗洛·卡彭特完全反其道而行之:如果什么都不写呢?

想法

Jabberwacky(约1997年上线,2008年成为Cleverbot)不存储任何规则。它将所有对话历史存储在一个纯文本转录文件中,当有人与它交谈时,它在历史中搜索最相似的时刻,然后使用之后说过的话:

用户: "hello"
  ↓
搜索:以前有人说过"hello"吗?
  ↓
是的,在会话#3的第14行,有人说了"hello",机器人回答了"hi there!"
  ↓
回答: "hi there!"

没有模式。没有语法。没有XML。只有一个巨大的、人们相互说过的话的存档,在适当的时机被重用。这正是涌现的定义。

TypeScript实现

TypeScript移植复现了这个精确的架构:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332行种子 + 历史"]
    B --> C["withReplies()<br>提取配对<br>(行 → 回复)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>该行之前的上下文)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["前K个候选已排序"]
    I --> J{"pickReply()<br>轮盘选择"}
    J -->|"选中"| K["Reply = reply.text<br>来自赢家配对"]
    J -->|"无"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["追加到转录<br>save() → JSON"]
    L --> M

以下是评分核心 —— 受Cleverbot公开描述启发的我们自己的启发式算法:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance(0.65):用户输入与历史行的相似度
  • contextFit(0.25):最近对话与历史行之前上下文的相似度
  • recencyBonus(0.10):较近的记忆权重稍高(机器人的个性随时间漂移)

选择是概率性的(轮盘选择):最好的候选人胜出频率更高,但并不总是 —— 这提供了多样性。

Cleverbot:两个有记载的创新

Cleverbot在Jabberwacky的基本概念上增加了两个机制:

  1. 多人学习:数百万用户贡献到同一个共享转录中。从历史中取出的回复可能来自与当前对话完全不同的声音 —— 这解释了为什么Cleverbot会突然改变个性。

  2. 延迟学习:你在一个会话中告诉Cleverbot的内容在同一个会话中不能用于匹配。新行被标记为pending,只有在会话间的"合并"之后才变得可匹配 —— 这解释了为什么你不能教Cleverbot一个事实然后在同一对话中复用。

// Cleverbot:新行在合并之前不可见
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate()在启动时调用,不在会话期间调用

TypeScript移植实现了这两种行为:行有一个consolidated标志,每个REPL会话从合并待定行开始。

➡ 查看源代码


TypeScript移植分析:设计通用架构

用同一种语言构建这五个机器人,意味着直面一个有趣的问题:在这么多不同架构之间能否提取公共代码?

答案是:非常少。每个机器人都有一个根本不同的主循环:

机器人 主循环 数据 学习
ELIZA 关键字栈 → 分解 → 重组 S表达式的.ela脚本 无
PARRY 分词 → 选择模式 / flares / 关键字 / 推理 58个PDP-10文件(词典、信念、规则) 无
ALICE 已排序模式 → 正则 → AIML模板 → 递归SRAI 66个AIML XML文件 无
Jabberwacky 相似度 → 上下文 → 新近度 → 加权选择 JSON转录(随使用增长) 持续
Cleverbot 同Jabberwacky + pending/consolidated + personas JSON转录 + 多人种子 延迟(会话间)

它们共享的是CLI界面和TypeScript基础设施(biome用于lint,tsx用于执行)。其余的都是每个架构特有的。

共同的设计选择

1. 忠于原始数据。 对于ELIZA、PARRY和ALICE,我们使用原始文件 —— 2021年在魏泽鲍姆档案中发现的ELIZA脚本、PDP-10上的原始PARRY代码(58个文件)、AIML Free ALICE v1.6。没有翻译,没有重写。机器人的行为与原始版本一致,因为它们使用相同的数据。

2. 专有部分的净室实现。 Jabberwacky和Cleverbot不同:它们的源代码从未发布过(Existor/罗洛·卡彭特一直保持专有)。因此,移植是净室重新实现 —— 仅基于公开的行为描述构建。没有一行专有代码或数据被复制。

3. 最小依赖。 唯一真正的先决条件是TypeScript。ALICE使用dom-js来解析AIML文件的XML(66个文件、99,524个分类 —— 自己写XML解析器是浪费时间)。其余都是纯TypeScript。


从符号聊天机器人到LLM:概念上的飞跃

刚才看到的五个机器人都有一个基本特征:它们是符号化的。它们的"知识"以显式的符号形式存储 —— 文本模式、规则表、XML分类、转录行。这些系统中没有任何语言的数值表示。

这也意味着它们都有同一个玻璃天花板:它们只能回答那些被明确规划或记录过的东西。ELIZA在治疗框架之外就会迷失。PARRY不能谈论天气。ALICE不会从对话中学到任何东西。Jabberwacky只能用已经说过的台词来回答。

LLM(大语言模型)通过彻底改变范式突破了这一天花板:不再是操作符号,而是将语言转换为数字,并学习这些数字之间的统计关系。它们不存储预先写好的答案 —— 它们通过计算概率即时生成每个词元。让我们快速看看这是如何工作的。

1. 词元化(Tokenization)

第一步是将文本分割成词元 —— 比单词小但比字符大的单位:

"我不理解"
  → ["我", "不", "理解"]

每个词元在词汇表中有一个数字ID(最近模型的词汇表通常有32,000到128,000个词元)。这种碎片化使模型能够通过将未见过的单词分解为已知的子词来处理它们。

2. 嵌入(Embeddings)

每个词元ID被转换为一个向量 —— 一个浮点数数组(中等规模模型通常为4096维)。这个向量是一个嵌入,在数学空间中编码了词元的含义,其中语义相近的词元具有相近的向量:

向量("王") − 向量("男") + 向量("女")  ≈  向量("女王")

这个属性是从训练中涌现出来的 —— 没有人明确编程过。这是单词在相似上下文中使用方式的结果。

3. 注意力(Attention)

注意力机制(由2017年的论文"Attention is All You Need"引入)是使LLM成为可能的关键。对于每个词元,注意力计算句子中哪些其他词元对于理解这个词元是重要的:

"银行拒绝了我的贷款。"
     ↑
词元"银行"看向:"拒绝"、"贷款" → 理解这是金融机构

"我去河岸上散步。"
     ↑
词元"岸"看向:"散步"、"上" → 理解这是河边

注意力使模型能够捕捉语境 —— 每个词元根据周围的词元来理解,而不是孤立地理解。

4. 下一个词元预测

LLM的训练出奇地简单:给它展示一段文本,隐藏最后一个词元,让它预测。然后重复数十亿次。

输入: "我不理"
隐藏: "解"
模型预测: "解"(概率0.87),"会"(0.05),"懂"(0.02)...

目标是最大化每个位置上正确词元的概率。这被称为下一个词元预测。在训练过程中,模型调整其数十亿个参数,以在TB级别的文本上最小化预测误差。

在推理时(当与它对话时),模型在循环中一次生成一个词元:

词元1: "我"    (输入:"说说你自己。")
词元2: "是"  (输入:"说说你自己。我")
词元3: "聊天"    (输入:"说说你自己。我是")
词元4: "机器人" (输入:"说说你自己。我是聊天")
...

每个词元根据其概率进行采样(temperature、top-k、top-p控制"创造性"的程度)。仅此而已。数十亿的参数重复这件事数千次。

根本性的变化

方面 符号化机器人(ELIZA、PARRY、ALICE) 现代LLM
表示 显式的单词和规则 数值向量(嵌入)
生成 从预写回复中选择 逐词元的概率预测
知识 存储在规则文件中 编码在网络权重中
学习 手动(编写规则) 自动(在语料上训练)
鲁棒性 在预期模式之外无能为力 泛化到未见过的输入
可解释性 完美(可读规则) 有限(黑箱)

经典聊天机器人透明但脆弱。LLM鲁棒但不透明。两种方法至今仍然存在 —— 不是作为竞争对手,而是作为满足不同需求的工具。

如果你想深入了解LLM的内部工作原理,这个视频是一个很好的资源:

如果你想深入了解LLM的内部工作原理,这个视频是一个很好的资源:

How LLMs Work — YouTube

Luna Protocol:现代的整合

关于Luna Protocol的文章(以下链接)代表了我们现在所看到的一切的最完整的整合:一个结合了本地LLM和复杂行为系统的现代Discord机器人,建立在60年对话式AI的教训之上。

Luna Protocol:我创建了一个模拟人类的自主Discord机器人

这篇文章详细介绍了基于LLM的Discord机器人的完整架构:

  • 优先级触发系统(提及 > DM > 名字 > 关键词 > 跟进 > 随机)
  • 人类行为:变化的注意力、打字错误、犹豫(15%)、遗忘(3%)、主题疲劳
  • 睡眠时间表:机器人根据时间睡觉、减速或忽略
  • TTS管道:通过Piper + ffmpeg进行语音合成 → Discord语音消息
  • 实时流式传输:LLM在类型化事件总线上逐个发出词元

将这篇文章与历史聊天机器人联系起来的是同样的追求:让人相信正在和一个人交谈。ELIZA用文本镜像做到了。PARRY用情感模型。ALICE用99k个分类。Luna Protocol用微调后的LLM + 模拟人类不完美的行为系统。

Luna Protocol:为什么我微调了一个1.5B模型

第二篇文章探讨了微调和少样本提示。核心发现:一个更小的模型(1.5B)在更少的数据(50k样本)上训练,能够超越一个更大的模型(3B),只要用少量的少样本示例进行正确的提示。

这是一个直接与历史聊天机器人产生共鸣的教训:

  • ELIZA表明,用几条精心设计的规则,可以模拟理解
  • ALICE表明,用99k个分类,可以模拟通识知识
  • Luna Protocol表明,用好的微调和5个少样本示例,一个小LLM可以模拟人类

技术不同,但原理相同:数据的质量和系统的精度比原始大小更重要。


结论:需要记住的三件事

1. 对话式AI并非始于ChatGPT。 ELIZA已有60年历史。PARRY在1972年通过了图灵测试。ALICE三次赢得Loebner大奖。Jabberwacky奠定了基于转录学习的基础,Cleverbot将其大规模工业化。每种方法都为这个拼图贡献了一块。

2. 更多数据 ≠ 更聪明。 Jabberwacky的转录没有规则。ALICE的99k分类不会学习。Luna Protocol在50k样本上的微调超越了3B模型。传统智慧说"越大越好" —— 而聊天机器人的历史表明,架构和设计与大小同样重要。

3. 60年来问题从未改变。 如何让一个人相信自己正在和另一个人交谈?ELIZA用文本镜像回答。PARRY用模拟的愤怒。ALICE用事实。Luna Protocol用一个会睡觉和打错字的LLM。解决方案在变,但需求不变。

这个仓库是开源的 —— 你可以克隆、运行每个机器人,亲眼看看60年的对话式AI如何装进一个TypeScript仓库。

资源 链接
GitHub仓库 fox3000foxy/chatbots
Luna Protocol —— 机器人架构 阅读文章
Luna Protocol —— few-shot微调 阅读文章
ELIZA原始脚本 anthay/ELIZA
PARRY原始源代码 lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
原始RFC 439 PARRY Encounters the DOCTOR
解释了LLM如何工作的优秀视频 https://www.youtube.com/watch?v=YmLp8qe87A0

ELIZAからLLMへ:60年の会話AIをTypeScriptで再構築

ELIZA、PARRY、ALICE、Jabberwacky、Cleverbot -- 同じ問題に対する5つの根本的に異なるアーキテクチャを、オリジナルデータとともにTypeScriptで移植。1966年から現代のLLMまで、会話AIがどのように話すことを学んだか、そしてあるチャットボットリポジトリが60年の研究について教えてくれること。

ELIZAからLLMへ:60年の会話AIをTypeScriptで再構築

1966年、ジョセフ・ワイゼンバウムはIBM 7094上でMAD-SLIPで420行のコードを書き、歴史上初のチャットボットを作成した。プログラム名はELIZA。基本パターンと文の置き換えでロジャーズ派心理療法士をシミュレートしていた。60年後、会話AIは一般大衆の話題となった -- ChatGPT、Claude、Geminiがあらゆる会話に登場している。

しかしこの両極端の間には、PARRY(偏執的なチャットボット、1972年)、ALICE(99,000カテゴリを誇るAIMLの王者、1995年)、Jabberwacky(ルールなしで学習した最初のボット、1997年)、そしてCleverbot(その産業的な後継者、2008年)があった。5つのプログラム、5つのアーキテクチャ、たった一つの課題:機械に話させること。

このリポジトリには、これらの5つのボットがTypeScriptで移植され、オリジナルのデータ -- ELIZAスクリプト、PARRY辞書、ALICEのAIMLファイル -- とともに含まれている。各移植は独立しており、すぐに使えて、細部まで文書化されている。目的は単に動かすことではない:それらがどのように動作したか、なぜ歴史に名を残したか、そしてそれぞれのアーキテクチャが過去のAI...そして現在のAIについて何を教えてくれるかを理解することだ。

bun run eliza    # ELIZA (1966) と話す
bun run parry    # PARRY (1972) と話す
bun run alice    # ALICE (1995) と話す
bun run jabber   # Jabberwacky と話す
bun run cleverbot # Cleverbot と話す
bun run meeting  # ELIZA vs PARRY 自動対話

各ボットを詳しく見ていき、コードを確認し、その後Luna Protocolに関する記事を通して現代のLLMとの橋渡しをする。


ELIZA (1966):理解しているふりをする技術

最も古く、おそらくそのシンプルさにおいて最も印象的なものから始めよう。ELIZAには現代的な意味での知性は一切ない。ニューラルネットワークも統計も学習もない。ただのテキストパターンといくつかの置き換えだけだ。

原理

DOCTORスクリプト(心理療法バージョン)はキーワードのテーブルで動作し、各キーワードには分解パターンと再組み立てルールが関連づけられている。典型的なルールはこうだ:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLOがキーワード。0は「後続のすべてをキャプチャする」という分解パターン(ワイルドカードのようなもの)。HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.が再組み立てルール。それだけだ。

「Hello, I'm sad today」と言うと、ELIZAは:

  1. テキストを大文字に変換:HELLO I'M SAD TODAY
  2. 各単語をキーワードテーブルと照合
  3. HELLOを発見 → キーワードスタックにプッシュ
  4. 最優先のキーワードを取得
  5. 各分解パターンを順に試行
  6. マッチした場合、次の再組み立てルールを選択(ラウンドロビン)
  7. (1)、(2)などをキャプチャした部分で置換

しかし本当に賢い部分はPREルールだ。これを見てほしい:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

ELIZAがMYにマッチすると、0でキャプチャされた文の残りをPREルールで変換し、その結果をユーザーが新しいキーワードを言ったかのように再注入する。具体的には:

あなた: "My mother hates me"
  → PREが変換: "YOUR MOTHER HATES YOU"
  → あなたが今言ったかのように再注入
  → おそらく"YOU"にマッチ → 新しい応答

ELIZAが「私」と「あなた」の違いを理解しているように見えるのはこのためだ -- 理解ではなく、完璧に設計された機械的な変換なのだ。

ユーザー入力から応答までの完全なフローは次の通り:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>句読点を正規化"]
    B --> C["splitUserInput()<br>単語に分割"]
    C --> D["Build keyword stack<br>優先順位でソート"]
    D --> E{"スタックは非空?"}
    E -->|"Yes"| F["Pop highest-priority keyword"]
    E -->|"No"| G{"記憶を呼び出す?"}
    G -->|"Yes"| H["Recall past user statement"]
    G -->|"No"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"マッチした?"}
    L -->|"No"| M{"リンクキーワード?"}
    M -->|"Yes"| N["Push linked keyword to stack"]
    N --> E
    M -->|"No"| O["Return NOMATCH"]
    O --> J
    L -->|"Yes"| P["Select next reassembly (round-robin)"]
    P --> Q{"再組み立てタイプ?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>最終応答に展開"]
    T --> J

なぜ信憑性があったのか

ワイゼンバウムは天才的な選択をした:ロジャーズ派心理療法だ。このアプローチは、患者の言葉を解釈せずに反映することから成る。「悲しいです」→「悲しいとおっしゃるんですね」。これはまさにELIZAができること -- そしてそれが認められた治療技法であるため、誰も奇妙に思わない。

TypeScript移植について

この移植は.elaスクリプト(オリジナルのS-expression形式)をロードし、完全にパースし(ホレリス符号化も含む -- 60年代の文字列形式)、同じサイクルを実行する:uppercasing → split → keyword stack → 分解 → 再組み立て → PRE/transforms。

➡ ソースコードを見る


PARRY (1972):感情を持った最初のチャットボット

ELIZAから6年後、ケネス・コルビー(スタンフォードの精神科医)はPARRYを作成した:パラノイド統合失調症の患者をシミュレートするチャットボットだ。ELIZAが空の鏡だったのに対し、PARRYには真の内部感情モデルがある。

感情モデル

PARRYには会話の各ターンで変化する4つの連続変数がある:

変数 ベースライン 減衰/ターン 説明
ANGER 0 −1.0 敵意、いらだち
FEAR 0 −0.2 パラノイア(妄想開始後はゆっくり減衰)
MISTRUST 0 −0.05 不信感(非常にゆっくり戻る)
HURT 0 −0.5 精神的苦痛

これらの値は、推論ルールによってトリガーされる感情ジャンプ(ajump、fjump、hjump)を通じて増加し、各ターンで自然にベースラインに向かって減衰する。

信念ネットワーク

PARRYには200以上の信念があり、belファイルに保存されている:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

各信念にはカテゴリー(HUM = 患者、HUM2 = 他人、DOC = 医者、INT = 尋問、INN = 意図)と強度(0-5)がある。推論ルール(TH2、EMOTE、IF)が信念間で伝播する:

  • TH2:信念Aがしきい値を超えると、自己強化され、その結果が増加する
  • EMOTE:信念がしきい値を超えると、感情ジャンプ(anger/fear/hurt)をトリガーする
  • IF:条件付き -- Aが真なら、Bが特定のレベルで真になる

妄想の階層(フレアシステム)

PARRYの最も魅力的な部分は「フレア」システムだ -- 徐々に中心的妄想へとエスカレートする連鎖:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

各キーワードは事前に書かれた応答をトリガーし(パターンマッチングによる)、相手が話題を追うと、PARRYは徐々に迫害の中心的妄想へと滑り込む。フレアが「発動」すると、それは非アクティブ(deadFlares)になる -- PARRYは次へ進み、話題を掘り下げる相手をシミュレートする。

PARRYが歴史的な理由

1972年、PARRYはチューリングテストの変種に合格した:精神科医はPARRYとの会話と実際のパラノイア患者との会話の文字起こしを受け取り、信頼できる区別ができなかった。LLMより40年早く、これを達成した最初のプログラムだ。

➡ ソースコードを見る


RFC 439:ELIZAがPARRYと出会うとき

1972年9月18日、二つのプログラムが歴史上初めて互いに話した -- 文字通り:これは記録された初のAI間会話だ。ELIZA(ボストンのBBNで稼働)とPARRY(スタンフォードで稼働)は、ARPANETを介して会話し、一方の出力をもう一方の入力にタイプする人間のオペレーターによって中継された。その書き起こしはRFC 439("PARRY Encounters the DOCTOR")として公開された。

以下がその会話の抜粋だ:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

驚くほど一貫している。ELIZAはセラピストの仕事をしている:言い換え、質問、探求。PARRYはパラノイア患者の仕事をしている:不平、非難、不信の表明。両方のプログラムが完璧に役割を果たしている -- 状況を「理解」しているからではなく、それぞれのメカニズム(ELIZAパターン + PARRY感情モデル)が偶然噛み合う応答を生成するからだ。

リポジトリでこの会話を再現できる:

bun run meeting

シミュレーションは2つのボット間で自動的に25ラウンドを実行し、ランダムな開始トピック(馬、組織犯罪、感情...)で始まる。ELIZAとPARRYはどちらも非決定論的要素(ELIZAのラウンドロビン、PARRYのランダム化)を持つため、実行ごとに異なるやりとりが生成される。

ELIZA vs PARRYで印象的なのは、内部状態を持たないプログラムと完全な感情モデルを持つプログラムという2つが、一緒に意図的なものに見える会話を生み出すことだ。1972年当時、これは驚異的だった。


ALICE (1995):大規模パターンマッチング

ALICE(Artificial Linguistic Internet Computer Entity)は1995年にリチャード・ウォレスによって作成され、Loebner Prizeを3回(2000、2001、2004)受賞した。ELIZAが数百のルール、PARRYが数千のルールを持っていたのに対し、ALICEは99,524ものルールを持っている -- 66のAIMLファイルに分散している。

AIML:カテゴリの言語

AIML(Artificial Intelligence Markup Language)は、質問応答ペアを定義するためのXML形式だ:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

しかしALICEの真の力はワイルドカードとSRAI(Symbolic Reduction)にある:

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- <srai><star/></srai> と同等 -->
  </template>
</category>

SRAIにより、ALICEは入力を別のカテゴリにリダイレクトでき、リダクションのチェーンを作成する:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

これがALICEに柔軟性を与えるメカニズムだ:考えられるすべての表現に対して応答を書く代わりに、標準の応答を書き、バリエーションをそこにリダイレクトする。深さの上限は10 -- それを超えるとALICEは諦め、無限ループを避ける(カテゴリ設計では注意深く回避されているが、安全策は依然として重要だ)。

ALICEがパターンをマッチする方法

パターンは特異性でソートされる:ワイルドカードが少ないものが最初に試行される。ワイルドカード*と_は任意の単語シーケンスをキャプチャする。エンジンは各パターンを正規表現にコンパイルし、ソートされたカテゴリを反復してマッチを見つける。

// 私たちのTypeScript実装 -- 簡略化されているが忠実
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

ALICEがLoebnerを支配した理由

99,524のカテゴリ -- この数がすべてを変える。ELIZAは、いくつかのルールが特定のコンテキスト(セラピー)向けにうまく設計されていたため、賢く見えた。ALICEは非常に多くのトピックをカバーしているため、本当の一般知識を持っているように見える:科学、政治、ユーモア、スポーツ、感情、すべてが揃っている。

➡ ソースコードを見る


Jabberwacky (1997) & Cleverbot (2008):認識論的断絶

これまでのすべてのボットは一つの仮説を共有している:応答は書かれなければならない。ELIZAにはS-expressionルール、PARRYには選択的パターン、ALICEにはAIMLカテゴリ。ロロ・カーペンターは完全に逆を行った:何も書かなかったらどうなる?

アイデア

Jabberwacky(1997年頃にローンチ、2008年にCleverbotとなる)はいかなるルールも保存しない。すべての会話履歴をフラットなトランスクリプトに保存し、誰かが話しかけると、その履歴の中で最も類似した瞬間を探し、その後で言われたことを再利用する:

ユーザー: "hello"
  ↓
検索:以前誰かが"hello"と言ったか?
  ↓
はい、セッション#3の14行目で、誰かが"hello"と言い、ボットが"hi there!"と答えた
  ↓
応答: "hi there!"

パターンなし。文法なし。XMLなし。ただ人々が互いに言ったことの巨大なアーカイブを、適切なタイミングで再利用するだけだ。これこそ創発の定義そのものだ。

TypeScript実装

TypeScript移植はこの正確なアーキテクチャを再現している:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332行シード + 履歴"]
    B --> C["withReplies()<br>ペアを抽出<br>(行 → 応答)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>その行の前のコンテキスト)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K 候補をソート"]
    I --> J{"pickReply()<br>ルーレット選択"}
    J -->|"選択"| K["Reply = reply.text<br>勝利ペアから"]
    J -->|"なし"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append to transcript<br>save() → JSON"]
    L --> M

以下がスコアリングの核心 -- Cleverbotの公開記述に触発された独自のヒューリスティック:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65):ユーザー入力と履歴行の類似度
  • contextFit (0.25):最近の会話と履歴行の前のコンテキストの類似度
  • recencyBonus (0.10):最近の記憶は少し重みが高い(ボットの性格は時間とともに変化する)

選択は確率的(ルーレット選択):最良の候補がより頻繁に勝つが、常にではない -- これが多様性をもたらす。

Cleverbot:文書化された2つの革新

CleverbotはJabberwackyの基本コンセプトに2つのメカニズムを追加する:

  1. マルチパーソン学習:何百万ものユーザーが同じ共有トランスクリプトに貢献する。履歴から引き出された応答は、現在の会話とは完全に異なる声から来る可能性がある -- これがCleverbotが突然性格を変える理由を説明している。

  2. 遅延学習:セッション中にCleverbotに言ったことは、その同じセッション中はマッチングに利用できない。新しい行はpendingとマークされ、セッション間の「統合」後でのみマッチ可能になる -- これがCleverbotに事実を教えても同じ会話で再利用できない理由を説明している。

// Cleverbot:新しい行は統合まで不可視
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate()は起動時に呼ばれ、セッション中は呼ばれない

TypeScript移植はこれらの両方の振る舞いを実装している:行にはconsolidatedフラグがあり、各REPLセッションは保留中の行の統合から始まる。

➡ ソースコードを見る


TypeScript移植の分析:共通アーキテクチャの設計

これら5つのボットを同じ言語で構築することは、興味深い問題に直面することを意味する:これほど異なるアーキテクチャ間でコードを共通化できるか?

答えは:ごくわずかだ。各ボットは根本的に異なるメインループを持つ:

ボット メインループ データ 学習
ELIZA Keyword stack → 分解 → 再組み立て S-expressionの.elaスクリプト なし
PARRY トークン化 → 選択的パターン / フレア / キーワード / 推論 58のPDP-10ファイル(辞書、信念、ルール) なし
ALICE ソート済みパターン → 正規表現 → AIMLテンプレート → 再帰的SRAI 66のAIML XMLファイル なし
Jabberwacky 類似度 → コンテキスト → 新しさ → 重み付き選択 JSONトランスクリプト(使用とともに成長) 継続的
Cleverbot Jabberwacky + pending/consolidated + personas JSONトランスクリプト + マルチパーソンシード 遅延(セッション間)

共通しているのは、CLIインターフェースとTypeScriptインフラ(lint用biome、実行用tsx)だ。残りは各アーキテクチャに固有である。

共通設計の選択

1. オリジナルデータへの忠実さ。 ELIZA、PARRY、ALICEについては、オリジナルファイルを使用している -- 2021年にワイゼンバウムのアーカイブで発見されたELIZAスクリプト、PDP-10からのオリジナルPARRYコード(58ファイル)、AIML Free ALICE v1.6。翻訳も書き換えもない。ボットは同じデータを使用しているため、オリジナルと同じように振る舞う。

2. プロプライエタリ部分のクリーンルーム。 JabberwackyとCleverbotは異なる:それらのソースコードは公開されたことがない(Existor/ロロ・カーペンターがプロプライエタリに保っている)。したがって、移植はクリーンルーム再実装である -- 動作の公開記述のみから構築されている。プロプライエタリなコードやデータの一行もコピーされていない。

3. 最小限の依存関係。 唯一の本当の前提条件はTypeScriptだ。ALICEはAIMLファイルのXMLパースにdom-jsを使用している(66ファイル、99,524カテゴリ -- 自前のXMLパーサーを書くのは時間の無駄だ)。残りはすべてバニラTypeScriptだ。


シンボリックチャットボットからLLMへ:概念的な飛躍

今見てきた5つのボットはすべて、基本的な特徴を共有している:それらはシンボリックである。それらの「知識」は明示的なシンボル -- テキストパターン、ルールテーブル、XMLカテゴリ、トランスクリプト行 -- として保存されている。これらのシステムのいずれにも、言語の数値表現はまったく存在しない。

つまり、それらはすべて同じガラスの天井を持っている:明示的に計画されたり記録されたりしたことだけに応答できる。ELIZAは治療の枠組みから外れると迷子になる。PARRYは天気の話ができない。ALICEは会話から何も学ばない。Jabberwackyはすでに発せられたセリフでしか応答できない。

LLM(Large Language Models)は、パラダイムを根本的に変えることでこの天井を打ち破る:シンボルを操作する代わりに、言語を数値に変換し、これらの数値間の統計的関係を学習する。事前に書かれた応答を保存するのではなく -- 確率を計算しながら各トークンをその場で生成する。どのように機能するか簡単に見てみよう。

1. トークン化

最初のステップはテキストをトークンに分割することだ -- 単語より小さく、文字より大きい単位:

"私は理解できない"
  → ["私", "は", "理解", "でき", "ない"]

各トークンは語彙内で数値IDを持つ(最近のモデルでは通常32,000〜128,000トークン)。この断片化により、モデルは未知の単語を知られたサブワードに分解して処理できる。

2. 埋め込み(Embeddings)

各トークンIDはベクトルに変換される -- 浮動小数点数の配列(中規模モデルでは通常4096次元)。このベクトルは、意味的に近いトークンが近いベクトルを持つ数学的空間にトークンの意味をエンコードする埋め込みである:

ベクトル("王") − ベクトル("男") + ベクトル("女")  ≈  ベクトル("女王")

この特性は学習から生じる -- 誰も明示的にプログラムしていない。これは、単語が類似したコンテキストで使用される方法の結果である。

3. 注意(Attention)

注意メカニズム(2017年の論文「Attention is All You Need」で導入)は、LLMを可能にしたものだ。各トークンについて、注意は文中の他のどのトークンがそれを理解するのに重要かを計算する:

「銀行が私のローンを拒否した。」
     ↑
トークン「銀行」が見ている:「拒否」、「ローン」→ 金融機関だと理解

「銀行の土手を散歩する。」
     ↑
トークン「銀行」が見ている:「散歩」、「の」→ 川岸だと理解

注意により、モデルはコンテキストを捉えることができる -- 各トークンは周囲のトークンに基づいて理解され、孤立して理解されるのではない。

4. 次のトークンの予測

LLMのトレーニングは欺くほどシンプルだ:テキストを見せ、最後のトークンを隠し、それを予測させる。そしてそれを何十億回も繰り返す。

Input:  "私は理解でき"
隠された: "ない"
モデルの予測: "ない" (確率 0.87), "ません" (0.05), "なかった" (0.02)...

目標は各位置での正しいトークンの確率を最大化することだ。これを次トークン予測と呼ぶ。トレーニング中、モデルはテラバイト単位のテキストで予測誤差を最小化するように数十億のパラメータを調整する。

推論時(話しかけるとき)、モデルはループで一度に1トークンを生成する:

Token 1: "私"    (input: "自分のことを話して。")
Token 2: "は"  (input: "自分のことを話して。私")
Token 3: "チャットボット"    (input: "自分のことを話して。私は")
Token 4: "です" (input: "自分のことを話して。私はチャットボット")
...

各トークンはその確率に従ってサンプリングされる(temperature、top-k、top-pが「創造性」の度合いを制御する)。それだけだ。数十億のパラメータがこれを何千回も行う。

根本的に変わること

側面 シンボリックボット(ELIZA、PARRY、ALICE) 現代のLLM
表現 明示的な単語とルール 数値ベクトル(埋め込み)
生成 事前に書かれた応答からの選択 トークンごとの確率的予測
知識 ルールファイルに保存 ネットワークの重みにエンコード
学習 手動(ルールの作成) 自動(コーパスでのトレーニング)
ロバスト性 想定外のパターンには無力 未知の入力にも一般化
解釈可能性 完璧(ルールを読める) 限定的(ブラックボックス)

古典的なチャットボットは透明だが脆弱だ。LLMはロバストだが不透明だ。両方のアプローチは今日も存在している -- 競合としてではなく、異なるニーズのためのツールとして。

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

LLMの内部動作についてもっと深く知りたい方には、この動画が最適です:

How LLMs Work — YouTube

Luna Protocol:現代の統合

Luna Protocolに関する記事(以下リンク)は、今見てきたすべての最も完成された統合を表している:ローカルLLMと洗練された行動システムを組み合わせた、60年の会話AIの教訓に基づいて構築されたモダンなDiscordボットだ。

Luna Protocol:自律型Discordボットが人間らしい会話を実現

この記事はLLMベースのDiscordボットの完全なアーキテクチャを詳述している:

  • 優先度トリガーシステム(メンション > DM > 名前 > キーワード > フォローアップ > ランダム)
  • 人間的振る舞い:変動する集中力、タイプミス、ためらい(15%)、忘れっぽさ(3%)、話題疲れ
  • 睡眠スケジュール:ボットは時間に応じて睡眠、減速、または無視する
  • TTSパイプライン:Piper + ffmpegによる音声合成 → Discord音声メッセージ
  • リアルタイムストリーミング:LLMが型付きイベントバスにトークンを一つずつ発行する

この記事を歴史的なチャットボットに結びつけるのは、同じ探求だ:人と話していると思わせること。ELIZAはテキストの鏡でそれをやった。PARRYは感情モデルで。ALICEは99kのカテゴリで。Luna ProtocolはファインチューンされたLLM + 人間の不完全さをシミュレートする行動システムでそれをやる。

Luna Protocol:なぜ1.5Bモデルをファインチューニングしたのか

2番目の記事はファインチューニングとfew-shotプライミングを探求する。中心的な発見:より小さいモデル(1.5B)をより少ないデータ(50kサンプル)でトレーニングすると、より大きなモデル(3B)を上回る、適切なfew-shot例でプロンプトすれば。

これは歴史的なチャットボットと直接共鳴する教訓だ:

  • ELIZAは、いくつかのうまく設計されたルールで理解をシミュレートできることを示した
  • ALICEは、99kのカテゴリで一般知識をシミュレートできることを示した
  • Luna Protocolは、良いファインチューニングと5つのfew-shot例で、小さなLLMが人間をシミュレートできることを示している

技術は異なるが、原理は同じだ:データの品質とシステムの精度は、生のサイズよりも重要である。


結論:覚えておくべき3つのこと

1. 会話AIはChatGPTから始まったわけではない。 ELIZAは60年前だ。PARRYは1972年にチューリングテストに合格した。ALICEはLoebnerを3回受賞した。Jabberwackyはトランスクリプト学習の基礎を築き、Cleverbotがそれを大規模に工業化した。各アプローチがパズルの一片をもたらした。

2. データが多い ≠ より賢い。 Jabberwackyのトランスクリプトにはルールがない。ALICEの99kカテゴリは学習しない。Luna Protocolの50kサンプルでのファインチューニングは3Bモデルを上回る。従来の知恵は「大きければ大きいほど良い」と言う -- チャットボットの歴史は、アーキテクチャと設計がサイズと同じくらい重要であることを示している。

3. 問題は60年変わっていない。 どうやって人間に、自分が人間と話していると思わせるか?ELIZAはテキストの鏡で答えた。PARRYはシミュレートされた怒りで。ALICEは事実で。Luna Protocolは眠り、タイプミスをするLLMで。解決策は変わるが、ニーズは変わらない。

リポジトリはオープンソースだ -- クローンして、各ボットを起動し、60年の会話AIがどのように一つのTypeScriptリポジトリに収まるかを自分の目で確かめてほしい。

リソース リンク
GitHubリポジトリ fox3000foxy/chatbots
Luna Protocol -- ボットアーキテクチャ 記事を読む
Luna Protocol -- few-shotファインチューニング 記事を読む
ELIZAオリジナルスクリプト anthay/ELIZA
PARRYオリジナルソースコード lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
オリジナルRFC 439 PARRY Encounters the DOCTOR
LLMの仕組みを優れた解説 https://www.youtube.com/watch?v=YmLp8qe87A0

ELIZA에서 LLM까지: 60년의 대화형 AI를 TypeScript로 재구성하다

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- 동일한 문제에 대한 다섯 가지 근본적으로 다른 아키텍처를 원본 데이터와 함께 TypeScript로 포팅했습니다. 1966년부터 현대의 LLM까지, 대화형 AI가 말하는 법을 배운 과정과 하나의 챗봇 리포지토리가 60년의 연구에 대해 알려주는 것들.

ELIZA에서 LLM까지: 60년의 대화형 AI를 TypeScript로 재구성하다

1966년, 조셉 와이젠바움이 IBM 7094에서 MAD-SLIP으로 420줄의 코드를 작성해 역사상 최초의 챗봇을 만들었습니다. 프로그램 이름은 ELIZA였고, 기본 패턴과 문장 변환을 사용해 로저스식 심리치료사를 시뮬레이션했습니다. 60년 후, 대화형 AI는 대중의 화두가 되었습니다 -- ChatGPT, Claude, Gemini가 모든 대화에 등장하고 있습니다.

하지만 이 두 극단 사이에는 PARRY(편집증적 챗봇, 1972년), ALICE(99,000개 카테고리의 AIML 왕, 1995년), Jabberwacky(규칙 없이 학습한 최초의 봇, 1997년), 그리고 Cleverbot(그 산업적 후계자, 2008년)이 있었습니다. 다섯 프로그램, 다섯 아키텍처, 하나의 문제: 기계가 말하게 하기.

이 리포지토리에는 이 다섯 봇이 TypeScript로 포팅되어 있으며, 원본 데이터 -- ELIZA 스크립트, PARRY 사전, ALICE의 AIML 파일 -- 도 함께 포함되어 있습니다. 각 포팅은 독립적이고, 즉시 사용 가능하며, 세부 사항까지 문서화되어 있습니다. 목표는 단순히 실행하는 것이 아닙니다: 그것들이 어떻게 작동했는지, 왜 역사에 남았는지, 그리고 각 아키텍처가 과거의 AI... 그리고 오늘날의 AI에 대해 무엇을 가르쳐 주는지 이해하는 것입니다.

bun run eliza    # ELIZA (1966)와 대화
bun run parry    # PARRY (1972)와 대화
bun run alice    # ALICE (1995)와 대화
bun run jabber   # Jabberwacky와 대화
bun run cleverbot # Cleverbot과 대화
bun run meeting  # ELIZA vs PARRY 자동 대화

각 봇을 분석하고, 코드를 살펴본 다음, Luna Protocol에 관한 기사를 통해 현대 LLM과의 연결점을 만들어 보겠습니다.


ELIZA (1966): 이해하는 척하는 기술

가장 오래된 것부터, 아마도 그 단순함에서 가장 인상적인 것부터 시작합시다. ELIZA에는 현대적 의미의 지능이 전혀 없습니다. 신경망도, 통계도, 학습도 없습니다. 그저 텍스트 패턴과 약간의 변환만 있을 뿐입니다.

원리

DOCTOR(심리치료사 버전) 스크립트는 키워드 테이블로 작동하며, 각 키워드에는 분해 패턴과 재조립 규칙이 연결되어 있습니다. 전형적인 규칙은 이렇습니다:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO가 키워드입니다. 0은 "뒤에 오는 모든 것을 캡처하라"는 분해 패턴(와일드카드 같은)입니다. HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.은 재조립 규칙입니다. 그게 전부입니다.

"Hello, I'm sad today"라고 말하면, ELIZA는:

  1. 텍스트를 대문자로 변환: HELLO I'M SAD TODAY
  2. 각 단어를 키워드 테이블과 대조
  3. HELLO 발견 → 키워드 스택에 푸시
  4. 최우선순위 키워드를 가져옴
  5. 각 분해 패턴을 순서대로 시도
  6. 매치되면 다음 재조립 규칙 선택(라운드 로빈)
  7. (1), (2) 등을 캡처된 부분으로 대체

하지만 진짜 영리한 부분은 PRE 규칙입니다. 이걸 보세요:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

ELIZA가 MY를 매치하면, 0으로 캡처된 문장의 나머지를 PRE 규칙을 통해 변환하고, 그 결과를 사용자가 방금 새 키워드를 말한 것처럼 재주입합니다. 구체적으로는:

당신: "My mother hates me"
  → PRE가 변환: "YOUR MOTHER HATES YOU"
  → 당신이 방금 말한 것처럼 재주입
  → 아마 "YOU"에 매치 → 새 응답

ELIZA가 "나"와 "당신"의 차이를 이해하는 것처럼 보이는 이유입니다 -- 이해가 아니라 완벽하게 설계된 기계적 변환입니다.

사용자 입력부터 응답까지의 전체 흐름은 다음과 같습니다:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>구두점 정규화"]
    B --> C["splitUserInput()<br>단어로 분할"]
    C --> D["Build keyword stack<br>우선순위로 정렬"]
    D --> E{"스택이 비어있지 않음?"}
    E -->|"예"| F["Pop highest-priority keyword"]
    E -->|"아니오"| G{"기억 회상?"}
    G -->|"예"| H["Recall past user statement"]
    G -->|"아니오"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"매치 발견?"}
    L -->|"아니오"| M{"연결된 키워드?"}
    M -->|"예"| N["Push linked keyword to stack"]
    N --> E
    M -->|"아니오"| O["Return NOMATCH"]
    O --> J
    L -->|"예"| P["Select next reassembly (round-robin)"]
    P --> Q{"재조립 유형?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>최종 응답으로"]
    T --> J

왜 신뢰할 수 있었는가

와이젠바움은 천재적인 선택을 했습니다: 로저스식 심리치료입니다. 이 접근법은 해석 없이 환자의 말을 반영하는 것으로 구성됩니다. "슬퍼요" → "슬프다고 말씀하시는군요". 이것이 바로 ELIZA가 할 수 있는 일이고 -- 인정된 치료 기법이기 때문에 아무도 이상하게 생각하지 않습니다.

TypeScript 포팅에서

이 포팅은 .ela 스크립트(원본 S-expression 형식)를 로드하고, 완전히 파싱하며(홀러리스 인코딩 포함 -- 60년대의 문자열 형식), 동일한 사이클을 실행합니다: uppercasing → split → keyword stack → 분해 → 재조립 → PRE/transforms.

➡ 소스 코드 보기


PARRY (1972): 감정을 가진 최초의 챗봇

ELIZA로부터 6년 후, 케네스 콜비(스탠포드 정신과 의사)는 PARRY를 만들었습니다: 편집성 정신분열증 환자를 시뮬레이션하는 챗봇입니다. ELIZA가 빈 거울이었다면, PARRY는 진정한 내부 감정 모델을 가지고 있습니다.

감정 모델

PARRY에는 대화의 각 턴마다 변화하는 4개의 연속 변수가 있습니다:

변수 기준선 감쇠/턴 설명
ANGER 0 −1.0 적대감, 짜증
FEAR 0 −0.2 편집증(망상 시작 후 천천히 감쇠)
MISTRUST 0 −0.05 불신(매우 천천히 내려감)
HURT 0 −0.5 정서적 고통

이 값들은 추론 규칙에 의해 트리거되는 감정 점프(ajump, fjump, hjump)를 통해 증가하고, 각 턴마다 자연스럽게 기준선으로 감쇠합니다.

신념 네트워크

PARRY에는 200개 이상의 신념이 있으며, bel 파일에 저장되어 있습니다:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

각 신념에는 카테고리(HUM = 환자, HUM2 = 타인, DOC = 의사, INT = 심문, INN = 의도)와 강도(0-5)가 있습니다. 추론 규칙(TH2, EMOTE, IF)이 신념 간에 전파됩니다:

  • TH2: 신념 A가 임계값을 초과하면, 자체 강화되고 그 결과가 증가합니다
  • EMOTE: 신념이 임계값을 초과하면, 감정 점프(anger/fear/hurt)를 트리거합니다
  • IF: 조건부 -- A가 참이면, B가 특정 수준에서 참이 됩니다

망상의 계층 구조(플레어 시스템)

PARRY의 가장 매혹적인 부분은 "플레어" 시스템입니다 -- 점진적으로 중심 망상으로 이끄는 에스컬레이션 체인:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

각 키워드는 미리 작성된 응답을 트리거하고(패턴 매칭을 통해), 상대가 주제를 따라가면 PARRY는 점차 박해의 중심 망상으로 빠져듭니다. 플레어가 "발동"되면 비활성화(deadFlares)됩니다 -- PARRY는 다음으로 넘어가며, 주제를 파고드는 대화 상대를 시뮬레이션합니다.

PARRY가 역사적인 이유

1972년, PARRY는 튜링 테스트의 변형을 통과했습니다: 정신과 의사들은 PARRY와의 대화와 실제 편집증 환자와의 대화 기록을 받았고, 신뢰할 수 있게 구분하지 못했습니다. LLM보다 40년 앞서 이를 달성한 최초의 프로그램입니다.

➡ 소스 코드 보기


RFC 439: ELIZA가 PARRY를 만날 때

1972년 9월 18일, 두 프로그램이 역사상 처음으로 서로 대화했습니다 -- 말 그대로: 기록된 최초의 AI 간 대화입니다. ELIZA(보스턴의 BBN에서 실행)와 PARRY(스탠포드에서 실행)는 ARPANET을 통해 대화했으며, 한쪽의 출력을 다른 쪽의 입력으로 입력하는 인간 운영자에 의해 중계되었습니다. 대화록은 RFC 439("PARRY Encounters the DOCTOR")로 발표되었습니다.

대화의 일부는 이렇게 생겼습니다:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

놀랍도록 일관됩니다. ELIZA는 치료사의 일을 합니다: 바꿔 말하기, 질문하기, 탐구하기. PARRY는 편집증 환자의 일을 합니다: 불평하기, 비난하기, 불신 표현하기. 두 프로그램 모두 완벽하게 제 역할을 합니다 -- 상황을 "이해"하기 때문이 아니라, 각각의 메커니즘(ELIZA 패턴 + PARRY 감정 모델)이 우연히 맞물리는 응답을 생성하기 때문입니다.

리포지토리에서 이 대화를 재현할 수 있습니다:

bun run meeting

시뮬레이션은 두 봇 간에 자동으로 25라운드를 실행하며, 무작위 시작 주제(말, 조직 범죄, 감정...)로 시작합니다. ELIZA와 PARRY 모두 비결정적 요소(ELIZA의 라운드 로빈, PARRY의 무작위화)를 가지고 있기 때문에, 실행할 때마다 다른 대화가 생성됩니다.

ELIZA vs PARRY에서 인상적인 점은, 하나는 내부 상태가 없고 다른 하나는 완전한 감정 모델을 가진 두 프로그램이 함께 의도적인 것처럼 보이는 대화를 만들어낸다는 것입니다. 1972년에는 이것이 경악스러운 일이었습니다.


ALICE (1995): 대규모 패턴 매칭

ALICE(Artificial Linguistic Internet Computer Entity)는 1995년 리처드 월리스에 의해 만들어졌고, Loebner Prize를 세 번(2000, 2001, 2004) 수상했습니다. ELIZA가 수백 개의 규칙을, PARRY가 수천 개의 규칙을 가진 반면, ALICE는 99,524개를 가지고 있습니다 -- 66개의 AIML 파일에 분산되어 있습니다.

AIML: 카테고리의 언어

AIML(Artificial Intelligence Markup Language)은 질문-응답 쌍을 정의하기 위한 XML 형식입니다:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

하지만 ALICE의 진정한 힘은 와일드카드와 SRAI(Symbolic Reduction)에 있습니다:

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- <srai><star/></srai> 와 동일 -->
  </template>
</category>

SRAI를 통해 ALICE는 입력을 다른 카테고리로 리디렉션하여 리덕션 체인을 만들 수 있습니다:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

이것이 ALICE에 유연성을 주는 메커니즘입니다: 가능한 모든 표현에 대해 응답을 작성하는 대신, 표준 응답을 작성하고 변형을 그쪽으로 리디렉션합니다. 깊이 제한은 10입니다 -- 그 이상은 ALICE가 포기하여 무한 루프를 방지합니다(카테고리 설계에서 주의 깊게 회피되지만, 안전장치는 여전히 필수적입니다).

ALICE가 패턴을 매치하는 방법

패턴은 특이성으로 정렬됩니다: 와일드카드가 적은 것이 먼저 시도됩니다. 와일드카드 *와 _는 모든 단어 시퀀스를 캡처합니다. 엔진은 각 패턴을 정규식으로 컴파일하고, 정렬된 카테고리를 반복하여 매치를 찾습니다.

// 우리의 TypeScript 구현 -- 단순화되었지만 충실함
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

ALICE가 Loebner를 지배한 이유

99,524개의 카테고리 -- 이 숫자가 모든 것을 바꿉니다. ELIZA는 몇 가지 규칙이 특정 맥락(치료)에 잘 설계되었기 때문에 똑똑해 보였습니다. ALICE는 너무 많은 주제를 다루기 때문에 진정한 교양을 가진 것처럼 보입니다: 과학, 정치, 유머, 스포츠, 감정, 모든 것이 있습니다.

➡ 소스 코드 보기


Jabberwacky (1997) & Cleverbot (2008): 인식론적 단절

이전의 모든 봇은 하나의 가설을 공유합니다: 응답은 작성되어야 한다. ELIZA에는 S-expression 규칙, PARRY에는 선택적 패턴, ALICE에는 AIML 카테고리가 있습니다. 롤로 카펜터는 완전히 반대로 갔습니다: 아무것도 작성하지 않으면 어떨까?

아이디어

Jabberwacky(1997년경 출시, 2008년에 Cleverbot이 됨)는 어떤 규칙도 저장하지 않습니다. 모든 대화 기록을 플랫한 트랜스크립트에 저장하고, 누군가 말을 걸면 그 기록에서 가장 유사한 순간을 찾아 그 후에 말해진 것을 재사용합니다:

사용자: "hello"
  ↓
검색: 예전에 누군가 "hello"라고 말한 적이 있나?
  ↓
네, 세션 #3, 14번째 줄에서 누군가 "hello"라고 말하고 봇이 "hi there!"라고 답했습니다.
  ↓
응답: "hi there!"

패턴 없음. 문법 없음. XML 없음. 그저 사람들이 서로에게 말한 것의 거대한 아카이브를 적절한 순간에 재사용하는 것뿐입니다. 이것이야말로 창발의 정의입니다.

TypeScript 구현

TypeScript 포팅은 이 정확한 아키텍처를 재현합니다:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332줄 시드 + 기록"]
    B --> C["withReplies()<br>쌍 추출<br>(줄 → 응답)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>그 줄 앞의 컨텍스트)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["상위 K 후보 정렬"]
    I --> J{"pickReply()<br>룰렛 선택"}
    J -->|"선택"| K["Reply = reply.text<br>승리 쌍에서"]
    J -->|"없음"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append to transcript<br>save() → JSON"]
    L --> M

다음은 점수 계산의 핵심입니다 -- Cleverbot의 공개된 설명에서 영감을 받은 우리만의 휴리스틱:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65): 사용자 입력과 기록 줄 사이의 유사도
  • contextFit (0.25): 최근 대화와 기록 줄 앞의 컨텍스트 사이의 유사도
  • recencyBonus (0.10): 최근 기억이 약간 더 가중됨(봇의 성격은 시간에 따라 변화함)

선택은 확률적(룰렛 선택): 최고 후보가 더 자주 이기지만, 항상 그렇지는 않습니다 -- 다양성을 제공합니다.

Cleverbot: 문서화된 두 가지 혁신

Cleverbot은 Jabberwacky의 기본 개념에 두 가지 메커니즘을 추가합니다:

  1. 다중 사용자 학습: 수백만 명의 사용자가 동일한 공유 트랜스크립트에 기여합니다. 기록에서 가져온 응답은 현재 대화와 완전히 다른 목소리에서 올 수 있습니다 -- 이것이 Cleverbot이 갑자기 성격을 바꾸는 이유를 설명합니다.

  2. 지연 학습: 세션 중에 Cleverbot에게 말한 내용은 동일한 세션 중에는 매칭에 사용할 수 없습니다. 새 줄은 pending으로 표시되고, 세션 간 "통합" 후에만 매칭 가능해집니다 -- 이것이 Cleverbot에게 사실을 가르쳐도 같은 대화에서 재사용할 수 없는 이유를 설명합니다.

// Cleverbot: 새 줄은 통합까지 보이지 않음
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate()는 시작 시 호출되며, 세션 중에는 호출되지 않음

TypeScript 포팅은 이 두 가지 동작을 모두 구현합니다: 줄에는 consolidated 플래그가 있고, 각 REPL 세션은 보류 중인 줄의 통합으로 시작됩니다.

➡ 소스 코드 보기


TypeScript 포팅 분석: 공통 아키텍처 설계

이 다섯 봇을 같은 언어로 구축하는 것은 흥미로운 질문에 직면한다는 것을 의미합니다: 이렇게 다른 아키텍처 간에 코드를 공통화할 수 있을까?

정답은: 거의 불가능합니다. 각 봇은 근본적으로 다른 메인 루프를 가지고 있습니다:

봇 메인 루프 데이터 학습
ELIZA Keyword stack → 분해 → 재조립 S-expression .ela 스크립트 없음
PARRY 토큰화 → 선택적 패턴 / 플레어 / 키워드 / 추론 58개 PDP-10 파일(사전, 신념, 규칙) 없음
ALICE 정렬된 패턴 → 정규식 → AIML 템플릿 → 재귀적 SRAI 66개 AIML XML 파일 없음
Jabberwacky 유사도 → 컨텍스트 → 최신성 → 가중치 선택 JSON 트랜스크립트(사용에 따라 성장) 지속적
Cleverbot Jabberwacky + pending/consolidated + personas JSON 트랜스크립트 + 다중 사용자 시드 지연(세션 간)

공통점은 CLI 인터페이스와 TypeScript 인프라(lint용 biome, 실행용 tsx)입니다. 나머지는 각 아키텍처에 특화되어 있습니다.

공통 설계 선택

1. 원본 데이터에 대한 충실함. ELIZA, PARRY, ALICE의 경우, 원본 파일을 사용합니다 -- 2021년 와이젠바움 아카이브에서 발견된 ELIZA 스크립트, PDP-10의 원본 PARRY 코드(58개 파일), AIML Free ALICE v1.6. 번역도, 재작성도 없습니다. 봇은 동일한 데이터를 사용하기 때문에 원본처럼 동작합니다.

2. 독점 부분에 대한 클린룸. Jabberwacky와 Cleverbot은 다릅니다: 그 소스 코드는 공개된 적이 없습니다(Existor/롤로 카펜터가 독점으로 유지했습니다). 따라서 포팅은 클린룸 재구현입니다 -- 동작에 대한 공개된 설명만을 기반으로 구축되었습니다. 독점 코드나 데이터의 한 줄도 복사되지 않았습니다.

3. 최소한의 의존성. 유일한 진정한 전제 조건은 TypeScript입니다. ALICE는 AIML 파일의 XML 파싱에 dom-js를 사용합니다(66개 파일, 99,524개 카테고리 -- 자체 XML 파서를 작성하는 것은 시간 낭비일 것입니다). 나머지는 모두 바닐라 TypeScript입니다.


기호적 챗봇에서 LLM으로: 개념적 도약

방금 본 다섯 봇은 모두 근본적인 특성을 공유합니다: 그것들은 기호적입니다. 그들의 "지식"은 명시적 기호 -- 텍스트 패턴, 규칙 테이블, XML 카테고리, 트랜스크립트 줄 -- 로 저장됩니다. 이 시스템들 중 어느 것에도 언어의 수치적 표현이 전혀 없습니다.

또한 이것은 모두 같은 유리 천장을 가지고 있음을 의미합니다: 명시적으로 계획되거나 기록된 것에만 응답할 수 있습니다. ELIZA는 치료 프레임워크를 벗어나면 길을 잃습니다. PARRY는 날씨에 대해 말할 수 없습니다. ALICE는 대화에서 아무것도 배우지 않습니다. Jabberwacky는 이미 발화된 대사로만 응답할 수 있습니다.

LLM(Large Language Models)은 패러다임을 근본적으로 변경함으로써 이 천장을 돌파합니다: 기호를 조작하는 대신 언어를 숫자로 변환하고 이 숫자 간의 통계적 관계를 학습합니다. 미리 작성된 응답을 저장하지 않고 -- 각 토큰을 확률을 계산하며 즉석에서 생성합니다. 어떻게 작동하는지 간단히 살펴보겠습니다.

1. 토큰화

첫 번째 단계는 텍스트를 토큰으로 분할하는 것입니다 -- 단어보다 작지만 문자보다 큰 단위:

"나는 이해하지 못한다"
  → ["나", "는", "이해", "하지", "못한다"]

각 토큰은 어휘 내에서 숫자 ID를 가집니다(최신 모델의 경우 일반적으로 32,000~128,000 토큰). 이 분할을 통해 모델은 본 적 없는 단어를 알려진 하위 단어로 분해하여 처리할 수 있습니다.

2. 임베딩(Embeddings)

각 토큰 ID는 벡터로 변환됩니다 -- 부동소수점 배열(중간 크기 모델의 경우 일반적으로 4096 차원). 이 벡터는 의미적으로 가까운 토큰이 가까운 벡터를 갖는 수학적 공간에 토큰의 의미를 인코딩하는 임베딩입니다:

벡터("왕") − 벡터("남자") + 벡터("여자")  ≈  벡터("여왕")

이 특성은 훈련에서 발생합니다 -- 누구도 명시적으로 프로그래밍하지 않았습니다. 이는 단어가 유사한 맥락에서 사용되는 방식의 결과입니다.

3. 어텐션(Attention)

어텐션 메커니즘(2017년 논문 "Attention is All You Need"에서 도입)은 LLM을 가능하게 만든 것입니다. 각 토큰에 대해, 어텐션은 문장에서 다른 어떤 토큰이 그 토큰을 이해하는 데 중요한지 계산합니다:

"은행이 내 대출을 거절했다."
     ↑
토큰 "은행"이 보는 것: "거절", "대출" → 금융 기관이라고 이해

"강둑(은행)을 따라 산책하자."
     ↑
토큰 "은행"이 보는 것: "산책", "따라" → 강둑이라고 이해

어텐션을 통해 모델은 맥락을 포착할 수 있습니다 -- 각 토큰은 주변 토큰에 기반하여 이해되며, 고립되어 이해되지 않습니다.

4. 다음 토큰 예측

LLM의 훈련은 속일 정도로 간단합니다: 텍스트를 보여주고, 마지막 토큰을 숨기고, 예측하도록 합니다. 그리고 수십억 번 반복합니다.

Input:  "나는 이해하지 못"
숨김:  "한다"
모델 예측: "한다" (확률 0.87), "하겠다" (0.05), "할 것이다" (0.02)...

목표는 각 위치에서 올바른 토큰의 확률을 최대화하는 것입니다. 이것이 다음 토큰 예측입니다. 훈련 중에 모델은 테라바이트 단위의 텍스트에서 예측 오류를 최소화하도록 수십억 개의 매개변수를 조정합니다.

추론 시(말을 걸 때), 모델은 루프에서 한 번에 하나의 토큰을 생성합니다:

Token 1: "저"    (input: "자신에 대해 말해봐.")
Token 2: "는"  (input: "자신에 대해 말해봐. 저")
Token 3: "챗봇"    (input: "자신에 대해 말해봐. 저는")
Token 4: "입니다" (input: "자신에 대해 말해봐. 저는 챗봇")
...

각 토큰은 확률에 따라 샘플링됩니다(temperature, top-k, top-p가 "창의성"의 정도를 제어). 그리고 그게 전부입니다. 수십억 개의 매개변수가 이것을 수천 번 수행합니다.

근본적으로 변화하는 것

측면 기호적 봇(ELIZA, PARRY, ALICE) 현대 LLM
표현 명시적 단어와 규칙 수치적 벡터(임베딩)
생성 미리 작성된 응답에서 선택 토큰별 확률적 예측
지식 규칙 파일에 저장 네트워크 가중치에 인코딩
학습 수동(규칙 작성) 자동(코퍼스 훈련)
강건성 예상된 패턴 외에는 무력 본 적 없는 입력에도 일반화
해석 가능성 완벽(규칙을 읽을 수 있음) 제한적(블랙박스)

고전적 챗봇은 투명하지만 취약합니다. LLM은 강건하지만 불투명합니다. 두 접근법 모두 오늘날에도 존재합니다 -- 경쟁자가 아니라 다른 필요를 위한 도구로서.

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

LLM의 내부 작동 방식에 대해 더 깊이 알고 싶다면, 이 비디오가 훌륭한 자료입니다:

How LLMs Work — YouTube

Luna Protocol: 현대적 종합

Luna Protocol에 관한 기사(아래 링크)는 방금 본 모든 것의 가장 완성된 종합을 나타냅니다: 로컬 LLM과 정교한 행동 시스템을 결합한, 60년 대화형 AI의 교훈 위에 구축된 현대적인 Discord 봇입니다.

Luna Protocol: 완전 자율적으로 인간을 시뮬레이션하는 Discord 봇을 만들었습니다

이 기사는 LLM 기반 Discord 봇의 완전한 아키텍처를 상세히 설명합니다:

  • 우선순위 트리거 시스템(멘션 > DM > 이름 > 키워드 > 후속 > 랜덤)
  • 인간적 행동: 가변 집중력, 오타, 망설임(15%), 건망증(3%), 주제 피로
  • 수면 스케줄: 봇은 시간에 따라 잠, 감속, 또는 무시
  • TTS 파이프라인: Piper + ffmpeg를 통한 음성 합성 → Discord 음성 메시지
  • 실시간 스트리밍: LLM이 타입이 지정된 이벤트 버스에 토큰을 하나씩 발행

이 기사를 역사적 챗봇과 연결하는 것은 동일한 추구입니다: 사람과 대화하고 있다고 믿게 하기. ELIZA는 텍스트 거울로 그것을 했습니다. PARRY는 감정 모델로. ALICE는 99k 카테고리로. Luna Protocol은 파인튜닝된 LLM + 인간의 불완전함을 시뮬레이션하는 행동 시스템으로 그것을 합니다.

Luna Protocol: 왜 1.5B 모델을 파인튜닝했는가

두 번째 기사는 파인튜닝과 few-shot 프라이밍을 탐구합니다. 중심 발견: 더 작은 모델(1.5B)을 더 적은 데이터(50k 샘플)로 훈련하면 더 큰 모델(3B)을 능가한다, 올바른 few-shot 예제로 프라이밍할 때.

이것은 역사적 챗봇과 직접적으로 공명하는 교훈입니다:

  • ELIZA는 잘 설계된 몇 가지 규칙으로 이해를 시뮬레이션할 수 있음을 보여주었습니다
  • ALICE는 99k 카테고리로 교양을 시뮬레이션할 수 있음을 보여주었습니다
  • Luna Protocol은 좋은 파인튜닝과 5개의 few-shot 예제로 작은 LLM이 인간을 시뮬레이션할 수 있음을 보여줍니다

기술은 다르지만, 원칙은 동일합니다: 데이터 품질과 시스템 정밀도가 원시 크기보다 더 중요하다.


결론: 기억해야 할 세 가지

1. 대화형 AI는 ChatGPT에서 시작되지 않았다. ELIZA는 60년 전이다. PARRY는 1972년에 튜링 테스트를 통과했다. ALICE는 Loebner를 세 번 수상했다. Jabberwacky는 트랜스크립트 학습의 기초를 놓았고, Cleverbot이 그것을 대규모로 산업화했다. 각 접근법이 퍼즐의 한 조각을 제공했다.

2. 더 많은 데이터 ≠ 더 똑똑함. Jabberwacky의 트랜스크립트에는 규칙이 없다. ALICE의 99k 카테고리는 학습하지 않는다. Luna Protocol의 50k 샘플 파인튜닝은 3B 모델을 능가한다. 통념은 "클수록 좋다"고 말한다 -- 챗봇의 역사는 아키텍처와 설계가 크기만큼 중요하다는 것을 보여준다.

3. 문제는 60년 동안 변하지 않았다. 어떻게 인간이 다른 인간과 대화하고 있다고 믿게 할까? ELIZA는 텍스트 거울로 답했다. PARRY는 시뮬레이션된 분노로. ALICE는 사실로. Luna Protocol은 자고 오타를 내는 LLM으로. 해결책은 변하지만, 필요는 변하지 않는다.

리포지토리는 오픈 소스입니다 -- 클론하고, 각 봇을 실행하고, 60년의 대화형 AI가 어떻게 하나의 TypeScript 리포지토리에 담기는지 직접 확인해 보세요.

리소스 링크
GitHub 리포지토리 fox3000foxy/chatbots
Luna Protocol -- 봇 아키텍처 기사 읽기
Luna Protocol -- few-shot 파인튜닝 기사 읽기
ELIZA 원본 스크립트 anthay/ELIZA
PARRY 원본 소스 코드 lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
원본 RFC 439 PARRY Encounters the DOCTOR
LLM 작동 방식에 대한 훌륭한 설명 https://www.youtube.com/watch?v=YmLp8qe87A0

ELIZA'dan LLM'lere : 60 Yıllık Konuşmalı Yapay Zeka, TypeScript'te Yeniden İnşa Edildi

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- aynı sorunun beş kökten farklı mimarisi, orijinal verileriyle TypeScript'e taşındı. 1966'dan modern LLM'lere, konuşmalı yapay zekanın konuşmayı nasıl öğrendiği ve bir sohbet botu reposunun bize 60 yıllık araştırma hakkında ne öğrettiği.

ELIZA'dan LLM'lere : 60 Yıllık Konuşmalı Yapay Zeka, TypeScript'te Yeniden İnşa Edildi

1966'da Joseph Weizenbaum, bir IBM 7094 üzerinde 420 satır MAD-SLIP kodu yazarak tarihin ilk sohbet botunu yarattı. Programın adı ELIZA'ydı ve temel kalıplar ve cümle permütasyonlarıyla Rogerian bir psikoterapisti simüle ediyordu. Altı on yıl sonra, konuşmalı yapay zeka ana akım bir konu haline geldi -- ChatGPT, Claude, Gemini her sohbette.

Ama bu iki uç nokta arasında PARRY (paranoyak sohbet botu, 1972), ALICE (99.000 kategorili AIML kralı, 1995), Jabberwacky (kuralsız öğrenen ilk bot, 1997) ve Cleverbot (onun endüstriyel halefi, 2008) vardı. Beş program, beş mimari, tek bir sorun : bir makineyi konuşturmak.

Bu repo, beş botu da orijinal verileriyle -- ELIZA script'leri, PARRY sözlükleri, ALICE AIML dosyaları -- TypeScript'e taşınmış halde içeriyor. Her port bağımsız, kullanıma hazır ve en ince ayrıntısına kadar belgelenmiş. Amaç sadece onları çalıştırmak değil : nasıl çalıştıklarını, neden tarihe geçtiklerini ve mimarilerinin bize dünün yapay zekası hakkında ne öğrettiğini anlamak... ve bugünün.

bun run eliza    # ELIZA (1966) ile konuş
bun run parry    # PARRY (1972) ile konuş
bun run alice    # ALICE (1995) ile konuş
bun run jabber   # Jabberwacky ile konuş
bun run cleverbot # Cleverbot ile konuş
bun run meeting  # ELIZA vs PARRY otomatik

Her botu didik didik edecek, kodlarına bakacak ve sonra Luna Protocol makaleleri aracılığıyla modern LLM'lerle bağlantı kuracağız.


ELIZA (1966) : Anlıyormuş Gibi Yapma Sanatı

En eskisiyle başlayalım, ve muhtemelen basitliğiyle en etkileyici olanıyla. ELIZA'nın modern anlamda hiçbir zekası yok. Sinir ağı yok, istatistik yok, öğrenme yok. Sadece metin kalıpları ve biraz permütasyon.

Prensip

DOCTOR script'i (psikoterapist versiyonu) bir anahtar kelime tablosuyla çalışır, her biri ayrıştırma kalıpları ve yeniden birleştirme kurallarıyla ilişkilidir. İşte tipik bir kural :

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO anahtar kelimedir. 0, "sonraki her şeyi yakala" diyen bir ayrıştırma kalıbıdır (bir joker karakter gibi). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. ise yeniden birleştirme kuralıdır. Hepsi bu.

"Hello, I'm sad today" dediğinde ELIZA :

  1. Metni büyük harfe çevirir : HELLO I'M SAD TODAY
  2. Her kelimeyi anahtar kelime tablosuna karşı tarar
  3. HELLOyu bulur → anahtar kelime yığınına iter
  4. En yüksek öncelikli anahtar kelimeyi alır
  5. Her ayrıştırma kalıbını sırayla dener
  6. Eşleşme varsa, sonraki yeniden birleştirme kuralını seçer (round-robin)
  7. (1), (2) vb. yerine yakalanan kısımları koyar

Ama asıl zeki kısım PRE kurallarıdır. Şuna bak :

(MY
    ((0)
        (PRE (1 0) (=YOU))))

ELIZA MY ile eşleştiğinde, cümlenin kalanını (0 tarafından yakalanan) PRE kuralı aracılığıyla dönüştürür ve sonucu sanki kullanıcı yeni bir anahtar kelime söylemiş gibi geri enjekte eder. Pratikte :

Sen dersin : "My mother hates me"
  → PRE dönüştürür : "YOUR MOTHER HATES YOU"
  → sanki onu yeni söylemişsin gibi geri enjekte eder
  → muhtemelen "YOU" ile eşleşir → yeni yanıt

ELIZA'nın "ben" ve "sen" arasındaki farkı anlıyormuş gibi görünmesinin nedeni budur -- bu anlama değil, mükemmel tasarlanmış mekanik bir dönüşümdür.

İşte kullanıcı girişinden yanıta kadar tam akış :

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalise la ponctuation"]
    B --> C["splitUserInput()<br>découpe en mots"]
    C --> D["Build keyword stack<br>ordonné par priorité"]
    D --> E{"Stack non-vide?"}
    E -->|"Oui"| F["Pop highest-priority keyword"]
    E -->|"Non"| G{"Memory recall?"}
    G -->|"Oui"| H["Recall past user statement"]
    G -->|"Non"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Match found?"}
    L -->|"Non"| M{"Linked keyword?"}
    M -->|"Oui"| N["Push linked keyword to stack"]
    N --> E
    M -->|"Non"| O["Return NOMATCH"]
    O --> J
    L -->|"Oui"| P["Select next reassembly (round-robin)"]
    P --> Q{"Reassembly type?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>into final response"]
    T --> J

Onu İnanılır Kılan Şey

Weizenbaum dahice bir seçim yaptı : Rogerian psikoterapi. Bu yaklaşım, hastanın söylediklerini yorumlamadan yansıtmaktır. "Üzgünüm" → "Üzgün olduğunuzu söylüyorsunuz." ELIZA tam olarak bunu yapabilir -- ve bu tanınmış bir terapi tekniği olduğu için kimse bunu garip bulmaz.

TypeScript Portunda

Port, .ela script'lerini (orijinal S-expression formatı) yükler, tamamen ayrıştırır (Hollerith kodlaması dahil -- 60'lardan bir string formatı) ve aynı döngüyü yürütür : büyük harf yapma → bölme → anahtar kelime yığını → ayrıştırma → yeniden birleştirme → PRE/dönüşümler.

➡ Kaynak kodu gör


PARRY (1972) : Duygulara Sahip İlk Sohbet Botu

ELIZA'dan altı yıl sonra, Kenneth Colby (Stanford'da psikiyatrist) PARRY'yi yarattı : paranoid şizofreni hastasını simüle eden bir sohbet botu. ELIZA boş bir aynayken, PARRY gerçek bir iç duygusal modele sahiptir.

Duygusal Model

PARRY'nin her konuşma turunda değişen dört sürekli değişkeni vardır :

Değişken Taban Çizgisi Azalma/Tur Açıklama
ANGER 0 −1.0 Düşmanlık, sinirlilik
FEAR 0 −0.2 Paranoya (sanrı başladıktan sonra yavaşça azalır)
MISTRUST 0 −0.05 Güvensizlik (çok yavaş düşer)
HURT 0 −0.5 Duygusal acı

Bu değerler, çıkarım kuralları tarafından tetiklenen duygusal sıçramalar (ajump, fjump, hjump) aracılığıyla artar ve her turda doğal olarak taban çizgilerine doğru azalır.

İnanç Ağı

PARRY, bel dosyasında depolanmış 200'den fazla inanca sahiptir :

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Her inancın bir kategorisi (HUM = hasta, HUM2 = diğerleri, DOC = doktor, INT = sorgulama, INN = niyetler) ve bir gücü (0-5) vardır. Çıkarım kuralları (TH2, EMOTE, IF) inançları birbirine bağlar :

  • TH2 : bir A inancı bir eşiği aşarsa, güçlenir ve sonuçları artar
  • EMOTE : bir inanç bir eşiği aşarsa, duygusal sıçrama tetikler (öfke/korku/acı)
  • IF : koşullu -- A doğruysa, B belirli bir seviyede doğru olur

Sanrı Hiyerarşisi (Flare Sistemi)

PARRY'nin en büyüleyici kısmı "flare" sistemidir -- kademeli olarak merkezi sanrıya götüren bir tırmanma zinciri :

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Her anahtar kelime önceden yazılmış bir yanıtı tetikler (kalıp eşleştirme yoluyla) ve eğer muhatap konuyu takip ederse, PARRY kademeli olarak merkezi zulüm sanrısına doğru sürüklenir. Bir flare "tetiklendiğinde" pasif hale gelir (deadFlares) -- PARRY bir sonrakine geçer, konuyu derinleştiren bir muhatabı simüle eder.

PARRY Neden Tarihseldir

1972'de PARRY, Turing testinin bir varyantını geçti : psikiyatristlere PARRY ve gerçek paranoid hastalarla yapılan konuşmaların dökümleri verildi ve güvenilir bir şekilde ayırt edemediler. Bunu başaran ilk programdı, LLM'lerden 40 yıl önce.

➡ Kaynak kodu gör


RFC 439 : ELIZA, PARRY ile Karşılaştığında

18 Eylül 1972'de, iki program tarihte ilk kez birbiriyle konuştu -- kelimenin tam anlamıyla : bu şimdiye kadar kaydedilmiş ilk AI'lar arası konuşma. ELIZA (BBN'de, Boston'da çalışıyor) ve PARRY (Stanford'da çalışıyor) ARPANET üzerinden, birinin çıktısını diğerinin girişine yazan insan operatörler aracılığıyla konuştular. Döküm RFC 439 ("PARRY Encounters the DOCTOR") olarak yayınlandı.

İşte nasıl göründüğüne dair bir alıntı :

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

Şaşırtıcı derecede tutarlı. ELIZA terapist görevini yapıyor : yeniden ifade etmek, sormak, keşfetmek. PARRY paranoid hasta görevini yapıyor : şikayet etmek, suçlamak, güvensizlik ifade etmek. İki program da rollerine mükemmel uyuyor -- durumu "anladıkları" için değil, ilgili mekanizmaları (ELIZA kalıpları + PARRY duygusal modeli) tesadüfen birbirine uyan yanıtlar ürettiği için.

Repo bu konuşmayı şununla yeniden oluşturabilir :

bun run meeting

Simülasyon, iki bot arasında rastgele bir başlangıç konusuyla (atlar, organize suç, duygular...) 25 otomatik tur çalıştırır. Hem ELIZA hem de PARRY'nin deterministik olmayan öğeleri olduğundan (ELIZA round-robin, PARRY randomizasyonu), her çalıştırma farklı bir alışveriş üretir.

ELIZA vs PARRY hakkında çarpıcı olan şey, ikisinin -- biri iç durumsuz, diğeri tam duygusal modelli -- birlikte kasıtlı görünen bir konuşma üretmesidir. 1972 için bu akıl almazdı.


ALICE (1995) : Büyük Ölçekli Kalıp Eşleştirme

ALICE (Artificial Linguistic Internet Computer Entity), Richard Wallace tarafından 1995'te yaratıldı ve üç kez Loebner Ödülü kazandı (2000, 2001, 2004). ELIZA'nın birkaç yüz kuralı ve PARRY'nin birkaç bini varken, ALICE'in 99.524 kuralı var -- 66 AIML dosyasına dağılmış.

AIML : Kategorilerin Dili

AIML (Artificial Intelligence Markup Language), soru-cevap çiftlerini tanımlamak için bir XML formatıdır :

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Ama ALICE'in gücü joker karakterlerden ve SRAI'den (Symbolic Reduction) gelir :

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- <srai><star/></srai> ile eşdeğer -->
  </template>
</category>

SRAI, ALICE'in bir girdiyi başka bir kategoriye yönlendirmesine izin verir, bir indirgeme zinciri oluşturur :

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

ALICE'e esnekliğini veren mekanizma budur : olası her ifade için bir yanıt yazmak yerine, kanonik bir yanıt yazılır ve varyasyonlar ona yönlendirilir. Derinlik sınırı 10'dur -- bunun ötesinde ALICE sonsuz döngüleri önlemek için pes eder (kategorilerin tasarımında dikkatle kaçınılır, ancak bir güvenlik ağı gereklidir).

ALICE Kalıpları Nasıl Eşleştirir

Kalıplar özgüllüğe göre sıralanır : en az joker karaktere sahip olanlar önce denenir. * ve _ joker karakterleri herhangi bir kelime dizisini yakalar. Motor her kalıbı regex'e derler, sonra bir eşleşme bulana kadar sıralanmış kategoriler boyunca döngü yapar.

// TypeScript uygulamamız -- basitleştirilmiş ama sadık
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

ALICE Loebner'da Neden Dominanttı

99.524 kategori, her şeyi değiştiren bir sayıdır. ELIZA zeki görünüyordu çünkü birkaç kuralı belirli bir bağlam (terapi) için iyi tasarlanmıştı. ALICE o kadar çok konuyu kapsar ki gerçek bir genel kültür izlenimi verir : bilim, politika, mizah, spor, duygular, her şey.

➡ Kaynak kodu gör


Jabberwacky (1997) ve Cleverbot (2008) : Epistemolojik Kopuş

Önceki tüm botlar bir varsayımı paylaşır : yanıtlar yazılmalıdır. ELIZA'nın S-expression kuralları vardır, PARRY'nin seçici kalıpları vardır, ALICE'in AIML kategorileri vardır. Rollo Carpenter tam tersini yaptı : ya hiçbir şey yazmasak?

Fikir

Jabberwacky (1997 civarında başlatıldı, 2008'de Cleverbot oldu) hiçbir kural depolamaz. Tüm konuşma geçmişini düz bir dökümde (transcript) depolar ve biri onunla konuştuğunda, bu geçmişte en benzer anı arar ve ardından söyleneni yeniden kullanır :

Kullanıcı : "hello"
  ↓
Ara : daha önce kimse "hello" demiş mi?
  ↓
Evet, oturum #3'te, satır 14'te, birisi "hello" demiş ve bot "hi there!" yanıtını vermiş
  ↓
Yanıtla : "hi there!"

Kalıp yok. Dilbilgisi yok. XML yok. Sadece insanların birbirine söylediği şeylerin dev bir arşivi, doğru zamanda yeniden kullanılıyor. Bu, emergence'ın tam tanımıdır.

TypeScript Uygulaması

TypeScript portu bu mimariyi aynen yeniden üretir :

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 lignes seed + historique"]
    B --> C["withReplies()<br>extrait les paires<br>(ligne → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>context avant cette ligne)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidats triés"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Pick"| K["Reply = reply.text<br>de la paire gagnante"]
    J -->|"Aucun"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append au transcript<br>save() → JSON"]
    L --> M

İşte puanlamanın kalbi -- Cleverbot'un halka açık açıklamalarından esinlenen kendi buluşsal yöntemimiz :

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65) : kullanıcı girdisi ile geçmiş satır arasındaki benzerlik
  • contextFit (0.25) : son konuşma ile geçmiş satırdan önceki bağlam arasındaki benzerlik
  • recencyBonus (0.10) : yeni anılar biraz daha önemlidir (bot kişiliği zamanla kayar)

Seçim olasılıksaldır (rulet çarkı seçimi) : en iyi aday daha sık kazanır, ama her zaman değil -- bu da çeşitlilik sağlar.

Cleverbot : Belgelenmiş İki Yenilik

Cleverbot, Jabberwacky'nin temel konseptine iki mekanizma ekler :

  1. Çok kişili öğrenme : milyonlarca kullanıcı aynı paylaşılan döküme katkıda bulunur. Geçmişten çekilen bir yanıt, devam eden konuşmadan tamamen farklı bir sesten gelebilir -- bu, Cleverbot'un neden aniden kişilik değiştirdiğini açıklar.

  2. Gecikmeli öğrenme : bir oturum sırasında Cleverbot'a söylediğiniz şeyler, aynı oturumda eşleştirme için kullanılamaz. Yeni satırlar pending olarak işaretlenir ve yalnızca oturumlar arasında bir "konsolidasyon" sonrasında eşleştirilebilir hale gelir -- bu, Cleverbot'a bir gerçek öğretip aynı konuşmada tekrar kullanamamanızı açıklar.

// Cleverbot : yeni satırlar konsolidasyona kadar görünmez
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() başlangıçta çağrılır, oturum sırasında değil

TypeScript portu bu iki davranışı da uygular : satırların bir consolidated bayrağı vardır ve her REPL oturumu, bekleyen satırların konsolidasyonu ile başlar.

➡ Kaynak kodu gör


TypeScript Port Analizi : Ortak Bir Mimari Tasarlamak

Bu beş botu aynı dilde oluşturmak, ilginç bir soruyla yüzleşmektir : Bu kadar farklı mimariler arasında kod ortaklaştırabilir miyiz?

Cevap : çok az. Her botun temelde farklı bir ana döngüsü vardır :

Bot Ana Döngü Veri Öğrenme
ELIZA Anahtar kelime yığını → ayrıştırma → yeniden birleştirme S-expression'da .ela script'leri Yok
PARRY Tokenization → seçici kalıplar / flare / anahtar kelimeler / çıkarımlar 58 PDP-10 dosyası (sözlükler, inançlar, kurallar) Yok
ALICE Sıralanmış kalıplar → regex → AIML şablonu → özyinelemeli SRAI 66 AIML XML dosyası Yok
Jabberwacky Benzerlik → bağlam → güncellik → ağırlıklı seçim JSON dökümü (kullanımla büyür) Sürekli
Cleverbot Jabberwacky ile aynı + beklemede/konsolide + kişilikler JSON dökümü + çoklu kişilik tohumları Gecikmeli (oturumlar arası)

Paylaştıkları şey, CLI arayüzü ve TypeScript altyapısıdır (lint için biome, yürütme için tsx). Gerisi her mimariye özgüdür.

Ortak Tasarım Seçimleri

1. Orijinal verilere sadakat. ELIZA, PARRY ve ALICE için orijinal dosyaları kullanıyoruz -- 2021'de Weizenbaum arşivlerinde bulunan ELIZA script'leri, PDP-10'dan orijinal PARRY kodu (58 dosya), AIML Free ALICE v1.6. Çeviri yok, yeniden yazma yok. Botlar orijinalleri gibi davranır çünkü aynı verileri kullanırlar.

2. Tescilli kısımlar için clean-room. Jabberwacky ve Cleverbot farklıdır : kaynak kodları hiçbir zaman yayınlanmadı (Existor/Rollo Carpenter onu tescilli tuttu). Bu nedenle portlar clean-room yeniden uygulamalardır -- yalnızca davranışın halka açık açıklamalarından inşa edilmiştir. Hiçbir tescilli kod veya veri kopyalanmamıştır.

3. Minimum bağımlılık. Tek gerçek ön koşul TypeScript'tir. ALICE, AIML dosyalarının XML'ini ayrıştırmak için dom-js kullanır (66 dosya, 99.524 kategori, XML ayrıştırmayı kendi yapmak zaman kaybı olurdu). Geriye kalan her şey sade TypeScript'tir.


Sembolik Sohbet Botlarından LLM'lere : Kavramsal Sıçrama

Az önce gördüğümüz beş bot da temel bir özelliği paylaşır : semboliktirler. "Bilgileri" açık semboller olarak depolanır -- metin kalıpları, kural tabloları, XML kategorileri, döküm satırları. Bu sistemlerin hiçbirinde dilin sayısal bir temsili yoktur.

Bu aynı zamanda hepsinin aynı cam tavana sahip olduğu anlamına gelir : yalnızca açıkça planlanmış veya kaydedilmiş olana yanıt verebilirler. ELIZA terapötik çerçevenin dışına çıkarsan kaybolur. PARRY hava durumu hakkında konuşamaz. ALICE konuşmalarından hiçbir şey öğrenmez. Jabberwacky yalnızca daha önce söylenmiş repliklerle yanıt verebilir.

LLM'ler (Large Language Models), paradigmayı kökten değiştirerek bu cam tavanı kırar : sembolleri manipüle etmek yerine, dili sayılara dönüştürür ve bu sayılar arasındaki istatistiksel ilişkileri öğrenirler. Önceden yazılmış yanıtları depolamazlar -- olasılıkları hesaplayarak her token'ı anında üretirler. Hızlıca nasıl çalıştığına bakalım.

1. Tokenization

İlk adım, metni token'lara (kelimelerden küçük ama karakterlerden büyük birimler) bölmektir :

"Je ne comprends pas"
  → ["Je", " ne", " comprend", "s", " pas"]

Her token'ın bir kelime dağarcığında sayısal bir kimliği vardır (genellikle son modeller için 32.000 ila 128.000 token). Bu parçalama, modelin daha önce hiç görülmemiş kelimeleri bilinen alt kelimelere bölerek işlemesine olanak tanır.

2. Embedding'ler

Her token kimliği bir vektöre dönüştürülür -- bir kayan noktalı sayı dizisi (genellikle orta boy bir model için 4096 boyut). Bu vektör, token'ın anlamını, anlamsal olarak yakın token'ların vektörlerinin yakın olduğu matematiksel bir uzayda kodlayan bir embedding'dir :

vecteur("roi") − vecteur("homme") + vecteur("femme") ≈ vecteur("reine")

Bu özellik eğitimden ortaya çıkar -- kimse onu açıkça programlamamıştır. Kelimelerin benzer bağlamlarda nasıl kullanıldığının bir sonucudur.

3. Attention

Attention mekanizması (2017'de "Attention is All You Need" makalesiyle tanıtıldı), LLM'leri mümkün kılan şeydir. Her token için attention, cümledeki diğer token'ların hangilerinin onu anlamak için önemli olduğunu hesaplar :

"La banque a refusé mon prêt."
     ↑
Token "banque" bakar : "refusé", "prêt" → anlar ki bu bir finans kurumu

"Je vais me promener sur la banque."
     ↑
Token "banque" bakar : "promener", "sur" → anlar ki bu bir kıyı

Attention, modelin bağlamı yakalamasını sağlar -- her token, çevresindekilere göre anlaşılır, izole olarak değil.

4. Sonraki Token Tahmini

Bir LLM'nin eğitimi aldatıcı derecede basittir : bir metin gösteririz, son token'ı gizleriz ve onu tahmin etmesini isteriz. Sonra milyarlarca kez tekrarlarız.

Input: "Je ne comprends"
Gizli: "pas"
Model tahmini : "pas" (olasılık 0.87), "rien" (0.05), "jamais" (0.02)...

Amaç, her konumda doğru token'ın olasılığını maksimize etmektir. Buna next-token prediction denir. Eğitim sırasında model, terabaytlarca metin üzerinde tahmin hatasını en aza indirmek için milyarlarca parametresini ayarlar.

Çıkarım anında (onunla konuştuğumuzda), model her seferinde bir token'ı döngüde üretir :

Token 1: "Je"    (input: "Parle-moi de toi.")
Token 2: "suis"  (input: "Parle-moi de toi. Je")
Token 3: "un"    (input: "Parle-moi de toi. Je suis")
Token 4: "chatbot" (input: "Parle-moi de toi. Je suis un")
...

Her token, olasılığına göre örneklenir (temperature, top-k, top-p "yaratıcılık" derecesini kontrol eder). Ve hepsi bu. Bunu binlerce kez yapan milyarlarca parametre.

Temelde Ne Değişir

Yön Sembolik Botlar (ELIZA, PARRY, ALICE) Modern LLM'ler
Temsil Açık kelimeler ve kurallar Sayısal vektörler (embedding'ler)
Üretim Önceden yazılmış yanıtlardan seçim Token token olasılıksal tahmin
Bilgi Kural dosyalarında depolanır Ağ ağırlıklarında kodlanır
Öğrenme Manuel (kural yazma) Otomatik (külliyat üzerinde eğitim)
Sağlamlık Planlanmış kalıpların dışında sıfır Hiç görülmemiş girdilere genelleme
Yorumlanabilirlik Mükemmel (kurallar okunabilir) Sınırlı (kara kutu)

Klasik sohbet botları şeffaf ama kırılgandır. Bir LLM sağlam ama opaktır. Her iki yaklaşım da bugün hala varlığını sürdürüyor -- rakip olarak değil, farklı ihtiyaçlar için araçlar olarak.

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

LLM'lerin iç işleyişi hakkında daha derinlemesine bilgi edinmek isterseniz, bu video mükemmel bir kaynak:

How LLMs Work — YouTube

Luna Protocol : Modern Sentez

Luna Protocol hakkındaki makaleler (bağlantıları aşağıda), az önce gördüğümüz her şeyin en olgun sentezini temsil eder : yerel bir LLM'yi sofistike bir davranışsal sistemle birleştiren, 60 yıllık konuşmalı yapay zeka dersleri üzerine inşa edilmiş modern bir Discord botu.

Luna Protocol : Bir insanı simüle eden tam otonom Discord botunu yaptım

Bu makale, LLM tabanlı bir Discord botunun tam mimarisini detaylandırır :

  • Öncelikli tetikleme sistemi (mention > DM > isim > anahtar kelime > takip > rastgele)
  • İnsan davranışları : değişken konsantrasyon, yazım hataları, tereddütler (%15), unutkanlık (%3), konu yorgunluğu
  • Uyku programı : bot zamana göre uyur, yavaşlar veya yok sayar
  • TTS hattı : Piper + ffmpeg ile konuşma sentezi → Discord ses mesajları
  • Gerçek zamanlı akış : LLM, token'ları tek tek tipli bir olay veriyolunda yayar

Bu makaleyi tarihsel sohbet botlarına bağlayan şey, aynı arayıştır : bir insanla konuştuğuna inandırmak. ELIZA metin aynalarıyla yapıyordu. PARRY duygusal bir modelle. ALICE 99k kategoriyle. Luna Protocol bunu fine-tune edilmiş bir LLM + insan kusurlarını simüle eden bir davranışsal sistemle yapıyor.

Luna Protocol : Neden 1.5B modelini fine-tune ettim

İkinci makale, fine-tuning ve few-shot priming'i inceler. Merkezi keşif : daha küçük bir model (1.5B) daha az veriyle (50k örnek) eğitilmiş, daha büyük bir modeli (3B) geride bırakır doğru few-shot örnekleriyle hazırlandığında.

Bu, tarihsel sohbet botlarıyla doğrudan yankılanan bir derstir :

  • ELIZA, iyi tasarlanmış birkaç kuralla anlamanın simüle edilebileceğini gösterdi
  • ALICE, 99k kategoriyle genel kültürün simüle edilebileceğini gösterdi
  • Luna Protocol, iyi bir fine-tuning ve 5 few-shot örneğiyle küçük bir LLM'nin bir insanı simüle edebileceğini gösteriyor

Teknik farklı, ama prensip aynı : veri kalitesi ve sistem kesinliği, ham boyuttan daha önemlidir.


Sonuç : Hatırlanması Gereken Üç Şey

1. Konuşmalı yapay zeka ChatGPT ile başlamadı. ELIZA 60 yaşında. PARRY 1972'de Turing testini geçti. ALICE üç kez Loebner kazandı. Jabberwacky, Cleverbot'un büyük ölçekte endüstrileştirdiği döküm tabanlı öğrenmenin temellerini attı. Her yaklaşım yapbozun bir parçasını getirdi.

2. Daha fazla veri ≠ daha zeki. Jabberwacky'nin dökümünde kurallar yok. ALICE'in 99k kategorisi öğrenmez. Luna Protocol'ün 50k örnek üzerinde fine-tuning'i 3B modeli geride bırakır. Geleneksel bilgelik "ne kadar büyük o kadar iyi" der -- sohbet botu tarihi, mimari ve tasarımın boyut kadar önemli olduğunu gösterir.

3. Sorun 60 yıldır aynı. Bir insana başka bir insanla konuştuğuna nasıl inandırırsın? ELIZA metin aynalarıyla yanıtlıyordu. PARRY simüle edilmiş öfkeyle. ALICE gerçeklerle. Luna Protocol uyuyan ve yazım hatası yapan bir LLM ile. Çözüm değişir, ihtiyaç aynı kalır.

Repo açık kaynaktır -- klonlayabilir, her botu çalıştırabilir ve 60 yıllık konuşmalı yapay zekanın tek bir TypeScript reposuna nasıl sığdığını kendiniz görebilirsiniz.

Kaynak Bağlantı
GitHub Reposu fox3000foxy/chatbots
Luna Protocol -- bot mimarisi Makaleyi oku
Luna Protocol -- few-shot fine-tuning Makaleyi oku
Orijinal ELIZA script'leri anthay/ELIZA
Orijinal PARRY kaynak kodu lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
Orijinal RFC 439 PARRY Encounters the DOCTOR
LLM'lerin nasıl çalıştığına dair mükemmel bir açıklama https://www.youtube.com/watch?v=YmLp8qe87A0

Da ELIZA agli LLM: 60 anni di IA conversazionale, ricostruita in TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- cinque architetture radicalmente diverse per lo stesso problema, portate in TypeScript con i loro dati originali. Dal 1966 agli LLM moderni, ecco come l'IA conversazionale ha imparato a parlare, e cosa un repo di chatbot ci insegna su 60 anni di ricerca.

Da ELIZA agli LLM: 60 anni di IA conversazionale, ricostruita in TypeScript

Nel 1966, Joseph Weizenbaum scrisse 420 righe di MAD-SLIP su un IBM 7094 per creare il primo chatbot della storia. Il programma si chiamava ELIZA, e simulava una psicoterapeuta rogersiana con schemi di base e permutazioni di frasi. Sei decenni dopo, l'IA conversazionale è diventata mainstream -- ChatGPT, Claude, Gemini sono in tutte le conversazioni.

Ma tra questi due estremi, ci sono stati PARRY (il chatbot paranoico, 1972), ALICE (il re dell'AIML con 99.000 categorie, 1995), Jabberwacky (il primo a imparare senza regole, 1997), e Cleverbot (il suo successore industriale, 2008). Cinque programmi, cinque architetture, un solo problema: far parlare una macchina.

Questo repo contiene questi cinque bot, portati in TypeScript con i loro dati originali -- script ELIZA, dizionari PARRY, file AIML di ALICE. Ogni port è autonomo, pronto all'uso e documentato nei minimi dettagli. L'obiettivo non è solo farli funzionare: è capire come funzionavano, perché hanno fatto la storia, e cosa le loro rispettive architetture ci insegnano sull'IA di ieri... e di oggi.

bun run eliza    # Parla con ELIZA (1966)
bun run parry    # Parla con PARRY (1972)
bun run alice    # Parla con ALICE (1995)
bun run jabber   # Parla con Jabberwacky
bun run cleverbot # Parla con Cleverbot
bun run meeting  # ELIZA vs PARRY automatico

Analizzeremo ogni bot, guarderemo il loro codice, e poi getteremo un ponte verso gli LLM moderni attraverso gli articoli su Luna Protocol.


ELIZA (1966): l'arte di far credere di capire

Cominciamo dalla più antica, e probabilmente la più impressionante nella sua semplicità. ELIZA non ha nessuna intelligenza nel senso moderno. Nessuna rete neurale, nessuna statistica, nessun apprendimento. Solo schemi testuali e un po' di permutazione.

Il principio

Lo script DOCTOR (la versione psicoterapeuta) funziona con una tabella di keywords, ciascuna associata a pattern di scomposizione e regole di riassemblaggio. Ecco una regola tipica:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO è la parola chiave. 0 è un pattern di scomposizione che dice "cattura tutto ciò che segue" (come un wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. è la regola di riassemblaggio. Tutto qui.

Quando dici "Hello, I'm sad today", ELIZA:

  1. Mette il testo in maiuscolo: HELLO I'M SAD TODAY
  2. Scansiona ogni parola contro la sua tabella di keywords
  3. Trova HELLO → lo spinge sullo stack delle keywords
  4. Prende la keyword con la priorità più alta
  5. Prova ogni pattern di scomposizione in ordine
  6. Se corrisponde, seleziona la prossima regola di riassemblaggio (round-robin)
  7. Sostituisce (1), (2) ecc. con le parti catturate

Ma la parte veramente intelligente sono le PRE rules. Guarda qui:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Quando ELIZA matcha MY, trasforma il resto della frase (catturato da 0) tramite la PRE rule, e reinietta il risultato come se l'utente avesse appena detto una nuova parola chiave. Concretamente:

Tu dici: "My mother hates me"
  → PRE trasforma: "YOUR MOTHER HATES YOU"
  → reiniettato come se l'avessi appena detto
  → probabilmente matcha "YOU" → nuova risposta

Ecco perché ELIZA sembra capire la differenza tra "io" e "tu" -- non è comprensione, è una trasformazione meccanica perfettamente progettata.

Ecco il flusso completo, dall'input utente alla risposta:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalizza la punteggiatura"]
    B --> C["splitUserInput()<br>divide in parole"]
    C --> D["Build keyword stack<br>ordinato per priorità"]
    D --> E{"Stack non vuoto?"}
    E -->|"Sì"| F["Pop keyword con priorità più alta"]
    E -->|"No"| G{"Memory recall?"}
    G -->|"Sì"| H["Recall dichiarazione utente passata"]
    G -->|"No"| I["Fallback: regola zNONE"]
    I --> J["Restituisci risposta"]
    H --> J
    F --> K["Match pattern di scomposizione"]
    K --> L{"Match trovato?"}
    L -->|"No"| M{"Keyword collegata?"}
    M -->|"Sì"| N["Push keyword collegata allo stack"]
    N --> E
    M -->|"No"| O["Restituisci NOMATCH"]
    O --> J
    L -->|"Sì"| P["Seleziona prossimo riassemblaggio (round-robin)"]
    P --> Q{"Tipo di riassemblaggio?"}
    Q -->|"PRE"| R["Trasforma parole (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Salta alla prossima keyword"]
    S --> E
    Q -->|"Standard"| T["Espandi (1), (2), (0)<br>in risposta finale"]
    T --> J

Cosa la rendeva credibile

Weizenbaum fece una scelta geniale: la psicoterapia rogersiana. Questo approccio consiste nel riflettere le parole del paziente senza interpretare. "Sono triste" → "Dice di essere triste". È esattamente ciò che ELIZA sa fare -- e siccome è una tecnica terapeutica riconosciuta, nessuno lo trova strano.

Nel port TypeScript

Il port carica gli script .ela (formato S-expression originale), li analizza completamente (inclusa la codifica Hollerith -- un formato di stringa degli anni '60), ed esegue lo stesso ciclo: uppercasing → split → keyword stack → scomposizione → riassemblaggio → PRE/transforms.

➡ Vedi il codice sorgente


PARRY (1972): il primo chatbot con emozioni

Sei anni dopo ELIZA, Kenneth Colby (psichiatra a Stanford) creò PARRY: un chatbot che simula un paziente affetto da schizofrenia paranoide. Dove ELIZA era uno specchio vuoto, PARRY ha un vero modello emotivo interno.

Il modello emotivo

PARRY ha quattro variabili continue che evolvono a ogni turno di conversazione:

Variabile Baseline Decadimento/turno Descrizione
ANGER 0 −1.0 Ostilità, irritazione
FEAR 0 −0.2 Paranoia (decade lentamente dopo l'inizio del delirio)
MISTRUST 0 −0.05 Diffidenza (molto lenta a scendere)
HURT 0 −0.5 Dolore emotivo

Questi valori aumentano tramite salti emotivi (ajump, fjump, hjump) attivati da regole di inferenza, e decadono naturalmente verso le loro baseline a ogni turno.

La rete di credenze

PARRY ha oltre 200 credenze memorizzate nel file bel:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Ogni credenza ha una categoria (HUM = il paziente, HUM2 = gli altri, DOC = il dottore, INT = l'interrogatorio, INN = le intenzioni) e una forza (0-5). Le regole di inferenza (TH2, EMOTE, IF) propagano le credenze tra loro:

  • TH2: se una credenza A supera una soglia, si rinforza e le sue conseguenze aumentano
  • EMOTE: se una credenza supera una soglia, innesca un salto emotivo (rabbia/paura/dolore)
  • IF: condizionale -- se A è vera, allora B diventa vera a un certo livello

La gerarchia dei deliri (flare system)

La parte più affascinante di PARRY è il suo sistema di "flares" -- una catena di escalation che porta progressivamente verso il delirio centrale:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Ogni parola chiave innesca una risposta pre-scritta (tramite pattern matching), e se l'interlocutore segue l'argomento, PARRY deriva progressivamente verso il suo delirio centrale di persecuzione. Una volta che un flare viene "innescato", diventa inattivo (deadFlares) -- PARRY passa al successivo, simulando un interlocutore che approfondisce l'argomento.

Perché PARRY è storico

Nel 1972, PARRY superò una variante del test di Turing: psichiatri ricevettero trascrizioni di conversazioni con PARRY e con veri pazienti paranoici, e non riuscirono a distinguerli in modo affidabile. È il primo programma a riuscirci, 40 anni prima degli LLM.

➡ Vedi il codice sorgente


RFC 439: quando ELIZA incontra PARRY

Il 18 settembre 1972, due programmi si parlarono per la prima volta nella storia -- letteralmente: è la prima conversazione inter-IA mai registrata. ELIZA (in esecuzione su BBN a Boston) e PARRY (in esecuzione a Stanford) conversarono tramite ARPANET, trasmessi da operatori umani che digitavano l'output dell'uno nell'input dell'altro. La trascrizione fu pubblicata come RFC 439 ("PARRY Encounters the DOCTOR").

Ecco un estratto di come appariva:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

È sorprendentemente coerente. ELIZA fa il suo lavoro di terapeuta: riformulare, chiedere, esplorare. PARRY fa il suo lavoro di paziente paranoico: lamentarsi, accusare, esprimere diffidenza. Entrambi i programmi sono perfettamente nel loro ruolo -- non perché "capiscano" la situazione, ma perché i loro rispettivi meccanismi (pattern ELIZA + modello emotivo PARRY) producono risposte che si incastrano per caso.

Il repo può riprodurre questa conversazione con:

bun run meeting

La simulazione esegue 25 turni automatici tra i due bot, con un argomento di partenza casuale (cavalli, crimine organizzato, emozioni...). Poiché sia ELIZA che PARRY hanno elementi non deterministici (round-robin di ELIZA, randomizzazione di PARRY), ogni esecuzione produce uno scambio diverso.

Ciò che colpisce di ELIZA vs PARRY è che hai due programmi -- uno senza stato interno, l'altro con un modello emotivo completo -- che insieme producono una conversazione che assomiglia a qualcosa di deliberato. Per il 1972, era sbalorditivo.


ALICE (1995): il pattern matching su larga scala

ALICE (Artificial Linguistic Internet Computer Entity) fu creata da Richard Wallace nel 1995, e vinse il Loebner Prize tre volte (2000, 2001, 2004). Dove ELIZA aveva poche centinaia di regole e PARRY qualche migliaio, ALICE ne ha 99.524 -- distribuite in 66 file AIML.

AIML: il linguaggio delle categorie

AIML (Artificial Intelligence Markup Language) è un formato XML per definire coppie domanda-risposta:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Ma la potenza di ALICE viene dai wildcard e dallo SRAI (Symbolic Reduction):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- equivalente a <srai><star/></srai> -->
  </template>
</category>

Lo SRAI permette ad ALICE di reindirizzare un input verso un'altra categoria, creando una catena di riduzione:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"
"

Questo è il meccanismo che dà ad ALICE la sua flessibilità: invece di scrivere una risposta per ogni formulazione possibile, si scrive una risposta canonica e si reindirizzano le variazioni verso di essa. Il limite di profondità è 10 -- oltre, ALICE abbandona per evitare cicli infiniti (accuratamente evitati nel design delle categorie, ma una rete di sicurezza rimane essenziale).

### Come ALICE matcha i pattern

I pattern sono ordinati per specificità: quelli con meno wildcard vengono provati per primi. I wildcard `*` e `_` catturano qualsiasi sequenza di parole. Il motore compila ogni pattern in una regex, poi itera le categorie ordinate fino a trovare una corrispondenza.

```typescript
// La nostra implementazione TypeScript -- semplificata ma fedele
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Perché ALICE ha dominato i Loebner

99.524 categorie sono un numero che cambia tutto. ELIZA sembrava intelligente perché le sue poche regole erano ben progettate per un contesto specifico (la terapia). ALICE copre così tanti argomenti da dare l'impressione di avere una vera cultura generale: scienze, politica, umorismo, sport, emozioni, c'è tutto.

➡ Vedi il codice sorgente


Jabberwacky (1997) & Cleverbot (2008): la rottura epistemologica

Tutti i bot precedenti condividono un'ipotesi: bisogna scrivere le risposte. ELIZA ha le sue regole S-expression, PARRY i suoi pattern selettivi, ALICE le sue categorie AIML. Rollo Carpenter ha preso la contropiede totale: e se non scrivessimo nulla?

L'idea

Jabberwacky (lanciato verso il 1997, diventato Cleverbot nel 2008) non memorizza nessuna regola. Memorizza l'intera cronologia delle conversazioni in un transcript piatto, e quando qualcuno gli parla, cerca in quella cronologia il momento più simile e riutilizza ciò che è stato detto dopo:

Utente: "hello"
  ↓
Cerca: qualcuno ha mai detto "hello" prima?
  ↓
Sì, nella sessione #3, riga 14, qualcuno ha detto "hello" e il bot ha risposto "hi there!"
  ↓
Rispondi: "hi there!"
"

Nessun pattern. Nessuna grammatica. Nessun XML. Solo un archivio gigante di cose che le persone si sono dette, riutilizzato al momento opportuno. È la definizione stessa dell'emergenza.

### L'implementazione TypeScript

Il port TypeScript riproduce questa architettura esatta:

<pre class="mermaid-fallback">flowchart TD
    A["User input:&lt;br&gt;'hello'"] --&gt; B["TranscriptStore&lt;br&gt;332 righe seed + storico"]
    B --&gt; C["withReplies()&lt;br&gt;estrae coppie&lt;br&gt;(riga → risposta)"]
    C --&gt; D["findCandidates()"]
    D --&gt; E["relevance = similarity(input, line.text)"]
    E --&gt; F["contextFit = similarity(recentContext,&lt;br&gt;contesto prima di questa riga)"]
    F --&gt; G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --&gt; H["score = 0.65×relevance&lt;br&gt;+ 0.25×contextFit&lt;br&gt;+ 0.10×recency"]
    H --&gt; I["Top K candidati ordinati"]
    I --&gt; J{"pickReply()&lt;br&gt;roulette-wheel&lt;br&gt;selection"}
    J --&gt;|"Scelto"| K["Risposta = reply.text&lt;br&gt;della coppia vincente"]
    J --&gt;|"Nessuno"| L["Fallback: 'I have no idea&lt;br&gt;what to say to that yet.'"]
    K --&gt; M["Append al transcript&lt;br&gt;save() → JSON"]
    L --&gt; M
"

Ecco il cuore dello scoring -- la nostra euristica ispirata alle descrizioni pubbliche di Cleverbot:</pre>typescript
const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65): similarità tra l'input utente e la riga storica
  • contextFit (0.25): similarità tra la conversazione recente e il contesto precedente la riga storica
  • recencyBonus (0.10): i ricordi recenti contano un po' di più (la personalità del bot deriva nel tempo)

La selezione è probabilistica (roulette-wheel selection): il candidato migliore vince più spesso, ma non sempre -- il che garantisce varietà.

Cleverbot: le due innovazioni documentate

Cleverbot aggiunge due meccanismi al concetto base di Jabberwacky:

  1. Apprendimento multi-persona: milioni di utenti contribuiscono allo stesso transcript condiviso. Una risposta estratta dalla cronologia può provenire da una voce completamente diversa da quella della conversazione in corso -- il che spiega perché Cleverbot cambia improvvisamente personalità.

  2. Apprendimento differito: ciò che dici a Cleverbot durante una sessione NON è disponibile per il matching durante la stessa sessione. Le nuove righe sono marcate pending e diventano matchabili solo dopo una "consolidazione" tra le sessioni -- il che spiega perché non puoi insegnare un fatto a Cleverbot e riutilizzarlo nella stessa conversazione.

// Cleverbot: le righe recenti sono invisibili fino alla consolidazione
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() è chiamata all'avvio, non durante la sessione
"

Il port TypeScript implementa entrambi i comportamenti: le righe hanno un flag `consolidated`, e ogni sessione REPL inizia consolidando le righe in attesa.

[➡ Vedi il codice sorgente](https://github.com/fox3000foxy/chatbots/tree/main/jabberwacky)

---

## Analisi del port TypeScript: progettare un'architettura comune

Costruire questi cinque bot nello stesso linguaggio ti confronta con una domanda interessante: **si può fattorizzare il codice tra architetture così diverse?**

La risposta è: molto poco. Ogni bot ha un ciclo fondamentale diverso:

| Bot | Ciclo principale | Dati | Apprendimento |
|-----|------------------|---------|-------------|
| **ELIZA** | Keyword stack → scomposizione → riassemblaggio | Script `.ela` in S-expressions | Nessuno |
| **PARRY** | Tokenizzazione → pattern selettivi / flares / keywords / inferenze | 58 file PDP-10 (dizionari, credenze, regole) | Nessuno |
| **ALICE** | Pattern ordinati → regex → template AIML → SRAI ricorsivo | 66 file AIML XML | Nessuno |
| **Jabberwacky** | Similarità → contesto → recency → selezione ponderata | Transcript JSON (cresce con l'uso) | Continuo |
| **Cleverbot** | Come Jabberwacky + pending/consolidated + personas | Transcript JSON + semi multi-persona | Differito (tra sessioni) |

Ciò che condividono è l'interfaccia CLI e l'infrastruttura TypeScript (biome per il lint, tsx per l'esecuzione). Il resto è specifico di ogni architettura.

### Scelte progettuali comuni

**1. Fedeltà ai dati originali.** Per ELIZA, PARRY e ALICE, usiamo i file originali -- script ELIZA recuperati dagli archivi Weizenbaum nel 2021, codice originale PARRY dal PDP-10 (58 file), AIML Free ALICE v1.6. Nessuna traduzione, nessuna riscrittura. I bot si comportano come gli originali perché usano gli stessi dati.

**2. Clean-room per le parti proprietarie.** Jabberwacky e Cleverbot sono diversi: il loro codice sorgente non è mai stato pubblicato (Existor/Rollo Carpenter l'hanno mantenuto proprietario). I port sono quindi **clean-room reimplementations** -- costruite unicamente da descrizioni pubbliche del comportamento. Nessuna riga di codice o dato proprietario viene copiata.

**3. Dipendenze minime.** L'unico vero prerequisito è TypeScript. ALICE usa `dom-js` per analizzare l'XML dei file AIML (66 file, 99.524 categorie, analizzare XML a mano sarebbe una perdita di tempo). Tutto il resto è TypeScript vanilla.

---

## Dai chatbot simbolici agli LLM: il salto concettuale

Tutti e cinque i bot che abbiamo appena visto condividono una caratteristica fondamentale: sono **simbolici**. La loro "conoscenza" è memorizzata come simboli espliciti -- pattern testuali, tabelle di regole, categorie XML, righe di transcript. Non c'è **nessuna rappresentazione numerica del linguaggio** in nessuno di questi sistemi.

Il che significa anche che hanno tutti lo stesso soffitto di vetro: possono rispondere solo a ciò che è stato esplicitamente previsto o registrato. ELIZA si perde se esci dal contesto terapeutico. PARRY non può parlare del tempo. ALICE non impara nulla dalle sue conversazioni. Jabberwacky può solo rispondere con battute già pronunciate.

Gli LLM (Large Language Models) superano questo soffitto cambiando radicalmente paradigma: invece di manipolare simboli, convertono il linguaggio in **numeri** e imparano **relazioni statistiche** tra questi numeri. Non memorizzano risposte pre-scritte -- generano ogni token al volo calcolando probabilità. Vediamo rapidamente come funziona.

### 1. Tokenizzazione

Il primo passo è suddividere il testo in **token** -- unità più piccole delle parole ma più grandi dei caratteri:

"Non capisco" → ["Non", " cap", "isco"] "

Ogni token ha un ID numerico in un vocabolario (tipicamente da 32.000 a 128.000 token per i modelli recenti). Questa frammentazione permette al modello di gestire parole che non ha mai visto, scomponendole in sottoparole conosciute.

2. Embedding

Ogni ID di token viene convertito in un vettore -- un array di numeri floating-point (tipicamente 4096 dimensioni per un modello di media grandezza). Questo vettore è un embedding che codifica il significato del token in uno spazio matematico dove token semanticamente vicini hanno vettori vicini:

vettore("re") − vettore("uomo") + vettore("donna") ≈ vettore("regina")
"

Questa proprietà emerge dall'addestramento -- nessuno l'ha programmata esplicitamente. È una conseguenza di come le parole vengono usate in contesti simili.

### 3. Attention

Il meccanismo di **attention** (introdotto dall'articolo "Attention is All You Need" nel 2017) è ciò che ha reso possibili gli LLM. Per ogni token, l'attention calcola quali altri token nella frase sono importanti per capirlo:

"La banca ha rifiutato il mio prestito." ↑ Token "banca" guarda: "rifiutato", "prestito" → capisce che è un'istituzione finanziaria

"Vado a sedermi sulla banca del parco." ↑ Token "banca" guarda: "sedermi", "parco" → capisce che è una panchina "

L'attention permette al modello di catturare il contesto -- ogni token viene compreso in base a quelli che lo circondano, non isolatamente.

4. Predizione del prossimo token

L'addestramento di un LLM è ingannevolmente semplice: gli mostri un testo, gli nascondi l'ultimo token, e gli chiedi di prevederlo. Poi ripeti miliardi di volte.

Input:  "Non cap"
Nascosto: "isco"
Previsione del modello: "isco" (probabilità 0.87), "ivo" (0.05)...
"

L'obiettivo è massimizzare la probabilità del token reale in ogni posizione. Questo si chiama **next-token prediction**. Durante l'addestramento, il modello regola i suoi miliardi di parametri per minimizzare l'errore di previsione su terabyte di testo.

Durante l'inferenza (quando gli parliamo), il modello genera un token alla volta in un ciclo:

Token 1: "Sono" (input: "Parlami di te.") Token 2: "un" (input: "Parlami di te. Sono") Token 3: "chatbot" (input: "Parlami di te. Sono un") ... "

Ogni token viene campionato secondo la sua probabilità (temperatura, top-k, top-p controllano il grado di "creatività"). E questo è tutto. Miliardi di parametri che fanno questo migliaia di volte.

Ciò che cambia fondamentalmente

Aspetto Bot simbolici (ELIZA, PARRY, ALICE) LLM moderni
Rappresentazione Parole e regole esplicite Vettori numerici (embedding)
Generazione Selezione da risposte pre-scritte Previsione probabilistica token per token
Conoscenza Memorizzata in file di regole Codificata nei pesi della rete
Apprendimento Manuale (scrittura di regole) Automatico (addestramento su corpus)
Robustezza Nulla fuori dai pattern previsti Generalizza a input mai visti
Interpretabilità Perfetta (si possono leggere le regole) Limitata (scatola nera)

I chatbot classici sono trasparenti ma fragili. Un LLM è robusto ma opaco. Entrambi gli approcci esistono ancora oggi -- non come concorrenti, ma come strumenti per esigenze diverse.

Se vuoi approfondire il funzionamento interno dei LLM, questo video è un'eccellente risorsa:

Se vuoi approfondire il funzionamento interno dei LLM, questo video è un'eccellente risorsa:

How LLMs Work — YouTube

Luna Protocol: la sintesi moderna

Gli articoli su Luna Protocol (i cui link sono qui sotto) rappresentano la sintesi più riuscita di tutto ciò che abbiamo appena visto: un bot Discord moderno che combina un LLM locale con un sistema comportamentale sofisticato, tutto costruito sulle lezioni di 60 anni di IA conversazionale.

Luna Protocol: ho creato un bot Discord autonomo che simula un essere umano

Questo articolo dettaglia l'architettura completa di un bot Discord basato su LLM:

  • Sistema di attivazione prioritario (menzione > DM > nome > parola chiave > follow-up > casuale)
  • Comportamenti umani: concentrazione variabile, errori di battitura, esitazioni (15%), dimenticanze (3%), stanchezza tematica
  • Orari di sonno: il bot dorme, rallenta o ignora a seconda dell'ora
  • Pipeline TTS: sintesi vocale tramite Piper + ffmpeg → messaggi vocali Discord
  • Streaming in tempo reale: l'LLM emette i token uno per uno su un bus di eventi tipizzato

Ciò che collega questo articolo ai chatbot storici è la stessa ricerca: far credere di parlare con una persona. ELIZA lo faceva con specchi testuali. PARRY con un modello emotivo. ALICE con 99k categorie. Luna Protocol lo fa con un LLM fine-tunato + un sistema comportamentale che simula le imperfezioni umane.

Luna Protocol: perché ho fatto il fine-tuning di un modello da 1,5B

Il secondo articolo esplora il fine-tuning e il few-shot priming. La scoperta centrale: un modello più piccolo (1,5B) addestrato su meno dati (50k campioni) supera un modello più grande (3B) quando viene adescato correttamente con esempi few-shot.

È una lezione che risuona direttamente con i chatbot storici:

  • ELIZA mostrava che con poche regole ben progettate, si può simulare la comprensione
  • ALICE mostrava che con 99k categorie, si può simulare la cultura generale
  • Luna Protocol mostra che con un buon fine-tuning e 5 esempi few-shot, un piccolo LLM può simulare un essere umano

La tecnica è diversa, ma il principio è lo stesso: la qualità dei dati e la precisione del sistema contano più della dimensione grezza.


Conclusione: tre cose da ricordare

1. L'IA conversazionale non è iniziata con ChatGPT. ELIZA ha 60 anni. PARRY ha superato il test di Turing nel 1972. ALICE ha vinto il Loebner tre volte. Jabberwacky ha gettato le basi dell'apprendimento basato su transcript, che Cleverbot ha industrializzato su larga scala. Ogni approccio ha portato un pezzo del puzzle.

2. Più dati ≠ più intelligente. Il transcript di Jabberwacky non ha regole. Le 99k categorie di ALICE non imparano. Il fine-tuning di Luna Protocol su 50k campioni supera il modello 3B. La saggezza convenzionale dice "più grande è meglio" -- la storia dei chatbot mostra che architettura e progettazione contano quanto la dimensione.

3. Il problema è lo stesso da 60 anni. Come far credere a un umano di parlare con un altro umano? ELIZA rispondeva con specchi testuali. PARRY con rabbia simulata. ALICE con fatti. Luna Protocol con un LLM che dorme e fa errori di battitura. La soluzione cambia, il bisogno rimane.

Il repo è open source -- puoi clonarlo, avviare ogni bot, e vedere di persona come 60 anni di IA conversazionale stanno in un unico repository TypeScript.

Risorsa Link
Repository GitHub fox3000foxy/chatbots
Luna Protocol -- architettura del bot Leggi l'articolo
Luna Protocol -- few-shot fine-tuning Leggi l'articolo
Script ELIZA originali anthay/ELIZA
Codice sorgente PARRY originale lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 originale PARRY Encounters the DOCTOR
Ottima spiegazione di come funzionano i LLM https://www.youtube.com/watch?v=YmLp8qe87A0

Von ELIZA zu LLMs: 60 Jahre conversationale KI, neu aufgebaut in TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- fünf radikal unterschiedliche Architekturen für dasselbe Problem, portiert nach TypeScript mit ihren Originaldaten. Von 1966 bis zu modernen LLMs -- wie die conversationale KI sprechen lernte, und was ein Chatbot-Repo über 60 Jahre Forschung lehrt.

Von ELIZA zu LLMs: 60 Jahre conversationale KI, neu aufgebaut in TypeScript

1966 schrieb Joseph Weizenbaum 420 Zeilen MAD-SLIP auf einem IBM 7094, um den ersten Chatbot der Geschichte zu erschaffen. Das Programm hieß ELIZA und simulierte eine rogerianische Psychotherapeutin mit einfachen Mustern und Satzpermutationen. Sechs Jahrzehnte später ist conversationale KI zum Mainstream geworden -- ChatGPT, Claude, Gemini sind in aller Munde.

Aber zwischen diesen beiden Extremen gab es PARRY (den paranoiden Chatbot, 1972), ALICE (den AIML-König mit 99.000 Kategorien, 1995), Jabberwacky (den ersten, der ohne Regeln lernte, 1997) und Cleverbot (seinen industriellen Nachfolger, 2008). Fünf Programme, fünf Architekturen, ein Problem: eine Maschine zum Sprechen bringen.

Dieses Repo enthält diese fünf Bots, portiert nach TypeScript mit ihren Originaldaten -- ELIZA-Skripte, PARRY-Wörterbücher, ALICE-AIML-Dateien. Jeder Port ist eigenständig, einsatzbereit und bis ins Detail dokumentiert. Das Ziel ist nicht nur, sie laufen zu lassen: es geht darum zu verstehen, wie sie funktionierten, warum sie Geschichte schrieben und was ihre jeweiligen Architekturen über die KI von gestern... und heute lehren.

bun run eliza    # Sprich mit ELIZA (1966)
bun run parry    # Sprich mit PARRY (1972)
bun run alice    # Sprich mit ALICE (1995)
bun run jabber   # Sprich mit Jabberwacky
bun run cleverbot # Sprich mit Cleverbot
bun run meeting  # ELIZA vs PARRY automatisch

Wir werden jeden Bot auseinandernehmen, ihren Code ansehen und dann eine Brücke zu modernen LLMs schlagen -- durch die Artikel über Luna Protocol.


ELIZA (1966): die Kunst, glauben zu machen, man versteht

Fangen wir mit der ältesten und wahrscheinlich beeindruckendsten in ihrer Schlichtheit an. ELIZA hat keine Intelligenz im modernen Sinne. Kein neuronales Netz, keine Statistik, kein Lernen. Nur Textmuster und ein bisschen Permutation.

Das Prinzip

Das DOCTOR-Skript (die Psychotherapeuten-Version) arbeitet mit einer Tabelle von Keywords, denen jeweils Dekompositionsmuster und Wiederzusammenbauregeln zugeordnet sind. Hier eine typische Regel:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO ist das Keyword. 0 ist ein Dekompositionsmuster, das sagt "fange alles Folgende ein" (wie ein Wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. ist die Wiederzusammenbauregel. Das ist alles.

Wenn du "Hello, I'm sad today" sagst, macht ELIZA:

  1. Text in Großbuchstaben: HELLO I'M SAD TODAY
  2. Scannt jedes Wort gegen seine Keyword-Tabelle
  3. Findet HELLO → schiebt es auf den Keyword-Stack
  4. Nimmt das Keyword mit der höchsten Priorität
  5. Probiert jedes Dekompositionsmuster der Reihe nach
  6. Bei Treffer: wählt die nächste Wiederzusammenbauregel (Round-Robin)
  7. Ersetzt (1), (2) usw. durch die erfassten Teile

Aber der wirklich clevere Teil sind die PRE-Regeln. Schau dir das an:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Wenn ELIZA MY matcht, transformiert sie den Rest des Satzes (erfasst durch 0) via die PRE-Regel und injiziert das Ergebnis neu, als ob der Benutzer gerade ein neues Keyword gesagt hätte. Konkret:

Du sagst: "My mother hates me"
  → PRE transformiert: "YOUR MOTHER HATES YOU"
  → neu injiziert, als hättest du es gerade gesagt
  → matcht wahrscheinlich "YOU" → neue Antwort

Deshalb scheint ELIZA den Unterschied zwischen "ich" und "du" zu verstehen -- es ist kein Verstehen, es ist eine perfekt entworfene mechanische Transformation.

Hier der vollständige Ablauf, von der Benutzereingabe bis zur Antwort:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalisiert Satzzeichen"]
    B --> C["splitUserInput()<br>zerlegt in Wörter"]
    C --> D["Build keyword stack<br>prioritätssortiert"]
    D --> E{"Stack nicht leer?"}
    E -->|"Ja"| F["Pop höchstpriores Keyword"]
    E -->|"Nein"| G{"Memory recall?"}
    G -->|"Ja"| H["Recall frühere Benutzeraussage"]
    G -->|"Nein"| I["Fallback: zNONE-Regel"]
    I --> J["Antwort zurückgeben"]
    H --> J
    F --> K["Dekompositionsmuster matchen"]
    K --> L{"Match gefunden?"}
    L -->|"Nein"| M{"Verknüpftes Keyword?"}
    M -->|"Ja"| N["Verknüpftes Keyword auf Stack"]
    N --> E
    M -->|"Nein"| O["NOMATCH zurückgeben"]
    O --> J
    L -->|"Ja"| P["Nächsten Wiederzusammenbau wählen (Round-Robin)"]
    P --> Q{"Wiederzusammenbau-Typ?"}
    Q -->|"PRE"| R["Wörter transformieren (I→YOU)<br>Verknüpfungs-Keyword pushen"]
    R --> N
    Q -->|"NEWKEY"| S["Zum nächsten Keyword springen"]
    S --> E
    Q -->|"Standard"| T["(1), (2), (0) expandieren<br>in finale Antwort"]
    T --> J

Was sie glaubwürdig machte

Weizenbaum traf eine geniale Entscheidung: die rogerianische Psychotherapie. Dieser Ansatz besteht darin, die Aussagen des Patienten widerzuspiegeln, ohne zu interpretieren. "Ich bin traurig" → "Du sagst, dass du traurig bist." Genau das kann ELIZA -- und da es eine anerkannte Therapietechnik ist, findet niemand es seltsam.

Im TypeScript-Port

Der Port lädt die .ela-Skripte (originales S-Expression-Format), parsed sie vollständig (inklusive Hollerith-Kodierung -- ein String-Format aus den 60ern) und führt denselben Zyklus aus: Großschreibung → Split → Keyword-Stack → Dekomposition → Wiederzusammenbau → PRE/Transforms.

➡ Quellcode ansehen


PARRY (1972): der erste Chatbot mit Emotionen

Sechs Jahre nach ELIZA erschuf Kenneth Colby (Psychiater in Stanford) PARRY: einen Chatbot, der einen Patienten mit paranoider Schizophrenie simuliert. Wo ELIZA ein leerer Spiegel war, hat PARRY ein echtes inneres Emotionsmodell.

Das Emotionsmodell

PARRY hat vier kontinuierliche Variablen, die sich mit jeder Gesprächsrunde ändern:

Variable Basislinie Abfall/Runde Beschreibung
ANGER 0 −1,0 Feindseligkeit, Gereiztheit
FEAR 0 −0,2 Paranoia (fällt langsam nach Wahnbeginn)
MISTRUST 0 −0,05 Misstrauen (sehr langsam fallend)
HURT 0 −0,5 Emotionaler Schmerz

Diese Werte steigen durch emotionale Sprünge (ajump, fjump, hjump), die von Inferenzregeln ausgelöst werden, und fallen natürlich zu ihren Basislinien pro Runde ab.

Das Glaubensnetzwerk

PARRY hat über 200 Glaubenssätze in der bel-Datei:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Jeder Glaubenssatz hat eine Kategorie (HUM = der Patient, HUM2 = andere, DOC = der Arzt, INT = das Verhör, INN = die Absichten) und eine Stärke (0-5). Inferenzregeln (TH2, EMOTE, IF) verbreiten Glaubenssätze zwischen ihnen:

  • TH2: wenn ein Glaubenssatz A einen Schwellwert überschreitet, verstärkt er sich und seine Konsequenzen wachsen
  • EMOTE: wenn ein Glaubenssatz einen Schwellwert überschreitet, löst er einen emotionalen Sprung aus (Wut/Angst/Schmerz)
  • IF: bedingt -- wenn A wahr ist, wird B auf einem bestimmten Niveau wahr

Die Wahn-Hierarchie (Flare-System)

Der faszinierendste Teil von PARRY ist sein "Flare"-System -- eine Eskalationskette, die progressiv zum zentralen Wahn führt:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Jedes Keyword löst eine vorgefertigte Antwort aus (via Pattern Matching), und wenn der Gesprächspartner dem Thema folgt, driftet PARRY allmählich zu seinem zentralen Verfolgungswahn ab. Sobald ein Flare "ausgelöst" ist, wird er inaktiv (deadFlares) -- PARRY geht zum nächsten über und simuliert einen Gesprächspartner, der tiefer gräbt.

Warum PARRY historisch ist

1972 bestand PARRY eine Variante des Turing-Tests: Psychiater bekamen Transkripte von Gesprächen mit PARRY und echten paranoiden Patienten und konnten sie nicht zuverlässig unterscheiden. Es ist das erste Programm, dem das gelang, 40 Jahre vor den LLMs.

➡ Quellcode ansehen


RFC 439: wenn ELIZA auf PARRY trifft

Am 18. September 1972 sprachen zum ersten Mal in der Geschichte zwei Programme miteinander -- buchstäblich: es ist das erste inter-KI-Gespräch, das je aufgezeichnet wurde. ELIZA (auf BBN in Boston) und PARRY (in Stanford) unterhielten sich über ARPANET, vermittelt durch menschliche Operatoren, die die Ausgabe des einen in die Eingabe des anderen tippten. Das Transkript wurde als RFC 439 ("PARRY Encounters the DOCTOR") veröffentlicht.

Hier ein Auszug, wie es aussah:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

Es ist erstaunlich kohärent. ELIZA macht ihren Therapeutenjob: umformulieren, fragen, erkunden. PARRY macht seinen paranoiden Patientenjob: klagen, anklagen, Misstrauen ausdrücken. Beide Programme sind perfekt in ihrer Rolle -- nicht weil sie die Situation "verstehen", sondern weil ihre jeweiligen Mechanismen (ELIZA-Muster + PARRY-Emotionsmodell) Antworten produzieren, die zufällig zusammenpassen.

Das Repo kann dieses Gespräch reproduzieren mit:

bun run meeting

Die Simulation läuft 25 automatische Runden zwischen den beiden Bots mit einem zufälligen Startthema (Pferde, organisierte Kriminalität, Emotionen...). Da sowohl ELIZA als auch PARRY nicht-deterministische Elemente haben (ELIZA Round-Robin, PARRY Randomisierung), erzeugt jeder Durchlauf einen anderen Austausch.

Das Beeindruckende an ELIZA vs PARRY ist, dass man zwei Programme hat -- eines ohne inneren Zustand, das andere mit einem vollständigen Emotionsmodell -- die zusammen ein Gespräch produzieren, das aussieht wie etwas Absichtliches. Für 1972 war das atemberaubend.


ALICE (1995): Pattern Matching in großem Maßstab

ALICE (Artificial Linguistic Internet Computer Entity) wurde 1995 von Richard Wallace erschaffen und gewann den Loebner Prize dreimal (2000, 2001, 2004). Wo ELIZA ein paar hundert Regeln und PARRY ein paar tausend hatte, hat ALICE 99.524 -- verteilt auf 66 AIML-Dateien.

AIML: die Sprache der Kategorien

AIML (Artificial Intelligence Markup Language) ist ein XML-Format zur Definition von Frage-Antwort-Paaren:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Aber die Stärke von ALICE kommt von Wildcards und SRAI (Symbolic Reduction):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- entspricht <srai><star/></srai> -->
  </template>
</category>

SRAI erlaubt ALICE, eine Eingabe an eine andere Kategorie umzuleiten, wodurch eine Reduktionskette entsteht:

Eingabe: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

Das ist der Mechanismus, der ALICE ihre Flexibilität verleiht: statt für jede mögliche Formulierung eine Antwort zu schreiben, schreibt man eine kanonische Antwort und leitet Variationen dorthin um. Das Tiefenlimit ist 10 -- danach gibt ALICE auf, um Endlosschleifen zu vermeiden (im Kategorien-Design sorgfältig vermieden, aber ein Sicherheitsnetz ist essenziell).

Wie ALICE Muster matcht

Die Muster werden nach Spezifität sortiert: solche mit den wenigsten Wildcards werden zuerst probiert. Die Wildcards * und _ erfassen jede Wortsequenz. Die Engine kompiliert jedes Muster in einen Regex und iteriert dann durch die sortierten Kategorien, bis ein Treffer gefunden wird.

// Unsere TypeScript-Implementierung -- vereinfacht, aber originalgetreu
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Warum ALICE die Loebner dominierte

99.524 Kategorien sind eine Zahl, die alles verändert. ELIZA wirkte intelligent, weil ihre wenigen Regeln gut für einen spezifischen Kontext (Therapie) entworfen waren. ALICE deckt so viele Themen ab, dass sie den Eindruck echter Allgemeinbildung vermittelt: Wissenschaft, Politik, Humor, Sport, Emotionen -- alles ist da.

➡ Quellcode ansehen


Jabberwacky (1997) & Cleverbot (2008): der epistemische Bruch

Alle bisherigen Bots teilen eine Annahme: man muss die Antworten schreiben. ELIZA hat ihre S-Expression-Regeln, PARRY seine selektiven Muster, ALICE ihre AIML-Kategorien. Rollo Carpenter ging den völlig entgegengesetzten Weg: was, wenn man gar nichts schreibt?

Die Idee

Jabberwacky (gestartet um 1997, wurde 2008 zu Cleverbot) speichert keine Regeln. Es speichert die gesamte Gesprächshistorie in einem flachen Transkript, und wenn jemand mit ihm spricht, durchsucht es diese Historie nach dem ähnlichsten Moment und verwendet wieder, was danach gesagt wurde:

Benutzer: "hello"
  ↓
Suche: hat schon mal jemand "hello" gesagt?
  ↓
Ja, in Sitzung #3, Zeile 14, sagte jemand "hello" und der Bot antwortete "hi there!"
  ↓
Antworte: "hi there!"

Kein Muster. Keine Grammatik. Kein XML. Nur ein riesiges Archiv von Dingen, die Leute zueinander gesagt haben, zum richtigen Zeitpunkt wiederverwendet. Das ist die Definition von Emergenz.

Die TypeScript-Implementierung

Der TypeScript-Port reproduziert diese exakte Architektur:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 Seed-Zeilen + Historie"]
    B --> C["withReplies()<br>extrahiert Paare<br>(Zeile → Antwort)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>Kontext vor dieser Zeile)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top-K-Kandidaten sortiert"]
    I --> J{"pickReply()<br>Roulette-Rad<br>Auswahl"}
    J -->|"Ausgewählt"| K["Antwort = reply.text<br>vom Gewinnerpaar"]
    J -->|"Keine"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["An Transkript anhängen<br>save() → JSON"]
    L --> M

Hier ist der Kern des Scorings -- unsere eigene Heuristik, inspiriert von öffentlichen Beschreibungen von Cleverbot:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0,65): Ähnlichkeit zwischen Benutzereingabe und historischer Zeile
  • contextFit (0,25): Ähnlichkeit zwischen aktueller Konversation und dem Kontext vor der historischen Zeile
  • recencyBonus (0,10): neuere Erinnerungen zählen etwas mehr (die Persönlichkeit des Bots driftet mit der Zeit)

Die Auswahl ist probabilistisch (Roulette-Rad-Selektion): der beste Kandidat gewinnt öfter, aber nicht immer -- was für Abwechslung sorgt.

Cleverbot: die zwei dokumentierten Innovationen

Cleverbot fügt Jabberwackys Grundkonzept zwei Mechanismen hinzu:

  1. Multi-Personen-Lernen: Millionen von Benutzern tragen zum selben gemeinsamen Transkript bei. Eine aus der Historie gezogene Antwort kann von einer völlig anderen Stimme stammen als der aktuellen Unterhaltung -- was erklärt, warum Cleverbot plötzlich die Persönlichkeit wechselt.

  2. Verzögertes Lernen: Was du Cleverbot in einer Sitzung sagst, ist NICHT für Matches während derselben Sitzung verfügbar. Neue Zeilen werden als pending markiert und werden erst nach einer "Konsolidierung" zwischen den Sitzungen matchbar -- was erklärt, warum du Cleverbot keine Tatsache beibringen und in derselben Unterhaltung wiederverwenden kannst.

// Cleverbot: neue Zeilen sind bis zur Konsolidierung unsichtbar
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() wird beim Start aufgerufen, nicht während der Sitzung

Der TypeScript-Port implementiert beide Verhaltensweisen: Zeilen haben ein consolidated-Flag, und jede REPL-Sitzung beginnt mit der Konsolidierung ausstehender Zeilen.

➡ Quellcode ansehen


Analyse des TypeScript-Ports: Entwurf einer gemeinsamen Architektur

Diese fünf Bots in derselben Sprache zu bauen, konfrontiert dich mit einer interessanten Frage: kann man Code zwischen so unterschiedlichen Architekturen gemeinsam nutzen?

Die Antwort ist: sehr wenig. Jeder Bot hat eine fundamental andere Hauptschleife:

Bot Hauptschleife Daten Lernen
ELIZA Keyword-Stack → Dekomposition → Wiederzusammenbau .ela-Skripte in S-Expressions Keins
PARRY Tokenisierung → selektive Muster / Flares / Keywords / Inferenzen 58 PDP-10-Dateien (Wörterbücher, Glaubenssätze, Regeln) Keins
ALICE Sortierte Muster → Regex → AIML-Template → rekursives SRAI 66 AIML-XML-Dateien Keins
Jabberwacky Ähnlichkeit → Kontext → Aktualität → gewichtete Auswahl JSON-Transkript (wächst mit Nutzung) Kontinuierlich
Cleverbot Wie Jabberwacky + pending/consolidated + Personas JSON-Transkript + Multi-Persona-Seeds Verzögert (zwischen Sitzungen)

Was sie teilen, ist die CLI-Schnittstelle und die TypeScript-Infrastruktur (Biome für Linting, tsx für Ausführung). Der Rest ist architekturspezifisch.

Gemeinsame Designentscheidungen

1. Werktreue zu den Originaldaten. Für ELIZA, PARRY und ALICE verwenden wir die Originaldateien -- ELIZA-Skripte aus Weizenbaums Archiven von 2021, Original-PARRY-Code vom PDP-10 (58 Dateien), Free ALICE v1.6 AIML. Keine Übersetzung, keine Umschreibung. Die Bots verhalten sich wie die Originale, weil sie dieselben Daten verwenden.

2. Clean-Room für proprietäre Teile. Jabberwacky und Cleverbot sind anders: ihr Quellcode wurde nie veröffentlicht (Existor/Rollo Carpenter hielten ihn proprietär). Die Ports sind daher Clean-Room-Reimplementierungen -- ausschließlich aus öffentlichen Verhaltensbeschreibungen erstellt. Es wird kein proprietärer Code oder keine proprietären Daten kopiert.

3. Minimale Abhängigkeiten. Die einzige echte Voraussetzung ist TypeScript. ALICE verwendet dom-js zum Parsen des XML der AIML-Dateien (66 Dateien, 99.524 Kategorien, handgemachtes XML-Parsing wäre Zeitverschwendung). Alles andere ist Vanilla-TypeScript.


Von symbolischen Chatbots zu LLMs: der konzeptuelle Sprung

Alle fünf Bots, die wir gerade gesehen haben, teilen eine grundlegende Eigenschaft: sie sind symbolisch. Ihr "Wissen" wird als explizite Symbole gespeichert -- Textmuster, Regel-Tabellen, XML-Kategorien, Transkript-Zeilen. Es gibt keine numerische Repräsentation von Sprache in irgendeinem dieser Systeme.

Was auch bedeutet, dass sie alle dieselbe Glasdecke haben: sie können nur auf das antworten, was explizit vorgesehen oder aufgezeichnet wurde. ELIZA ist verloren, wenn du den therapeutischen Rahmen verlässt. PARRY kann nicht übers Wetter reden. ALICE lernt nichts aus ihren Gesprächen. Jabberwacky kann nur mit bereits gesagten Sätzen antworten.

LLMs (Large Language Models) durchbrechen diese Decke, indem sie das Paradigma radikal ändern: statt Symbole zu manipulieren, wandeln sie Sprache in Zahlen um und lernen statistische Beziehungen zwischen diesen Zahlen. Sie speichern keine vorgefertigten Antworten -- sie generieren jeden Token spontan durch die Berechnung von Wahrscheinlichkeiten. Lass uns kurz ansehen, wie das funktioniert.

1. Tokenisierung

Der erste Schritt ist, Text in Tokens zu zerlegen -- Einheiten, die kleiner als Wörter, aber größer als Zeichen sind:

"Ich verstehe nicht"
  → ["Ich", " ver", "stehe", " nicht"]

Jeder Token hat eine numerische ID in einem Vokabular (typischerweise 32.000 bis 128.000 Tokens für aktuelle Modelle). Diese Fragmentierung erlaubt dem Modell, Wörter, die es nie gesehen hat, durch Zerlegung in bekannte Unterwörter zu verarbeiten.

2. Embeddings

Jede Token-ID wird in einen Vektor umgewandelt -- ein Array von Fließkommazahlen (typischerweise 4096 Dimensionen für ein mittelgroßes Modell). Dieser Vektor ist ein Embedding, das die Bedeutung des Tokens in einem mathematischen Raum codiert, in dem semantisch nahe Tokens nahe beieinanderliegende Vektoren haben:

vector("König") − vector("Mann") + vector("Frau") ≈ vector("Königin")

Diese Eigenschaft entsteht aus dem Training -- niemand hat sie explizit programmiert. Sie ist eine Folge davon, wie Wörter in ähnlichen Kontexten verwendet werden.

3. Attention

Der Attention-Mechanismus (eingeführt durch das Paper "Attention is All You Need" 2017) ist das, was LLMs möglich gemacht hat. Für jeden Token berechnet Attention, welche anderen Tokens im Satz wichtig sind, um ihn zu verstehen:

"Die Bank hat meinen Kredit abgelehnt."
     ↑
Token "Bank" schaut auf: "Kredit", "abgelehnt" → versteht finanzielle Institution

"Ich setze mich auf die Bank im Park."
     ↑
Token "Bank" schaut auf: "setze", "Park" → versteht Sitzgelegenheit

Attention erlaubt dem Modell, den Kontext zu erfassen -- jeder Token wird basierend auf den ihn umgebenden verstanden, nicht isoliert.

4. Next-Token-Vorhersage

Das Training eines LLM ist täuschend einfach: man zeigt ihm Text, versteckt den letzten Token und bittet ihn, ihn vorherzusagen. Dann wiederholt man das milliardenfach.

Eingabe:  "Ich verstehe"
Versteckt: "nicht"
Modellvorhersage: "nicht" (Wahrscheinlichkeit 0,87), "gar nichts" (0,05)...

Das Ziel ist es, die Wahrscheinlichkeit des echten Tokens an jeder Position zu maximieren. Das nennt man Next-Token-Vorhersage. Während des Trainings passt das Modell seine Milliarden von Parametern an, um den Vorhersagefehler auf Terabytes von Text zu minimieren.

Während der Inferenz (wenn man mit ihm spricht) generiert das Modell einen Token nach dem anderen in einer Schleife:

Token 1: "Ich"     (Eingabe: "Erzähl mir von dir.")
Token 2: "bin"     (Eingabe: "Erzähl mir von dir. Ich")
Token 3: "ein"     (Eingabe: "Erzähl mir von dir. Ich bin")
Token 4: "Chatbot" (Eingabe: "Erzähl mir von dir. Ich bin ein")
...

Jeder Token wird entsprechend seiner Wahrscheinlichkeit abgetastet (Temperatur, Top-k, Top-p steuern den Grad der "Kreativität"). Und das ist alles. Milliarden von Parametern, die das tausendfach tun.

Was sich grundlegend ändert

Aspekt Symbolische Bots (ELIZA, PARRY, ALICE) Moderne LLMs
Repräsentation Explizite Wörter und Regeln Numerische Vektoren (Embeddings)
Generierung Auswahl aus vorgefertigten Antworten Probabilistische Token-für-Token-Vorhersage
Wissen In Regeldateien gespeichert In Netzwerkgewichten codiert
Lernen Manuell (Regeln schreiben) Automatisch (Training auf Korpus)
Robustheit Null außerhalb erwarteter Muster Verallgemeinert auf unbekannte Eingaben
Interpretierbarkeit Perfekt (man kann Regeln lesen) Begrenzt (Black Box)

Klassische Chatbots sind transparent, aber zerbrechlich. Ein LLM ist robust, aber undurchsichtig. Beide Ansätze existieren noch heute -- nicht als Konkurrenten, sondern als Werkzeuge für unterschiedliche Bedürfnisse.

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

Wenn du tiefer in die innere Funktionsweise von LLMs eintauchen möchtest, ist dieses Video eine hervorragende Ressource:

How LLMs Work — YouTube

Luna Protocol: die moderne Synthese

Die Artikel über Luna Protocol (Links unten) repräsentieren die gelungenste Synthese von allem, was wir gerade gesehen haben: ein moderner Discord-Bot, der ein lokales LLM mit einem hochentwickelten Verhaltenssystem kombiniert, aufgebaut auf den Lehren von 60 Jahren conversationaler KI.

Luna Protocol: Ich habe einen autonomen Discord-Bot erschaffen, der einen Menschen simuliert

Dieser Artikel beschreibt die vollständige Architektur eines LLM-basierten Discord-Bots:

  • Prioritätsbasiertes Auslösesystem (Erwähnung > DM > Name > Keyword > Follow-up > Zufall)
  • Menschliche Verhaltensweisen: variable Konzentration, Tippfehler, Zögern (15%), Vergesslichkeit (3%), thematische Ermüdung
  • Schlafzeiten: der Bot schläft, wird langsamer oder ignoriert je nach Uhrzeit
  • TTS-Pipeline: Sprachsynthese via Piper + ffmpeg → Discord-Sprachnachrichten
  • Echtzeit-Streaming: das LLM gibt Tokens einzeln auf einem typisierten Event-Bus aus

Was diesen Artikel mit den historischen Chatbots verbindet, ist dieselbe Suche: glauben zu machen, dass man mit einer Person spricht. ELIZA tat es mit Textspiegeln. PARRY mit einem Emotionsmodell. ALICE mit 99k Kategorien. Luna Protocol tut es mit einem feinjustierten LLM + einem Verhaltenssystem, das menschliche Unvollkommenheiten simuliert.

Luna Protocol: Warum ich ein 1,5B-Modell fine-getunt habe

Der zweite Artikel erkundet Fine-Tuning und Few-Shot-Priming. Die zentrale Entdeckung: ein kleineres Modell (1,5B), trainiert auf weniger Daten (50k Stichproben), übertrifft ein größeres Modell (3B), wenn es richtig mit Few-Shot-Beispielen geprimt wird.

Das ist eine Lektion, die direkt mit den historischen Chatbots resoniert:

  • ELIZA zeigte, dass man mit wenigen gut entworfenen Regeln Verständnis simulieren kann
  • ALICE zeigte, dass man mit 99k Kategorien Allgemeinwissen simulieren kann
  • Luna Protocol zeigt, dass man mit gutem Fine-Tuning und 5 Few-Shot-Beispielen einen Menschen simulieren kann

Die Technik ist anders, aber das Prinzip ist dasselbe: Datenqualität und Systempräzision zählen mehr als rohe Größe.


Fazit: drei Dinge zum Merken

1. Conversationale KI begann nicht mit ChatGPT. ELIZA ist 60 Jahre alt. PARRY bestand den Turing-Test 1972. ALICE gewann den Loebner drei Mal. Jabberwacky legte den Grundstein für Transkript-basiertes Lernen, das Cleverbot in großem Maßstab industrialisierte. Jeder Ansatz brachte ein Puzzlestück.

2. Mehr Daten ≠ intelligenter. Jabberwackys Transkript hat keine Regeln. ALICEs 99k Kategorien lernen nicht. Luna Protocols Fine-Tuning auf 50k Proben übertrifft das 3B-Modell. Die konventionelle Weisheit sagt "größer ist besser" -- die Chatbot-Geschichte zeigt, dass Architektur und Design genauso wichtig sind wie die Größe.

3. Das Problem ist seit 60 Jahren dasselbe. Wie bringt man einen Menschen glauben, dass er mit einem anderen Menschen spricht? ELIZA antwortete mit Textspiegeln. PARRY mit simulierter Wut. ALICE mit Fakten. Luna Protocol mit einem LLM, der schläft und Tippfehler macht. Die Lösung ändert sich, das Bedürfnis bleibt.

Das Repo ist Open Source -- du kannst es klonen, jeden Bot starten und selbst sehen, wie 60 Jahre conversationale KI in ein einziges TypeScript-Repository passen.

Ressource Link
GitHub-Repo fox3000foxy/chatbots
Luna Protocol -- Bot-Architektur Artikel lesen
Luna Protocol -- Few-Shot-Fine-Tuning Artikel lesen
Originale ELIZA-Skripte anthay/ELIZA
Originaler PARRY-Quellcode lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
Original RFC 439 PARRY Encounters the DOCTOR
Hervorragende Erklärung, wie LLMs funktionieren https://www.youtube.com/watch?v=YmLp8qe87A0

От ELIZA до LLM: 60 лет разговорного ИИ, пересобранного на TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- пять радикально разных архитектур одной задачи, перенесённых на TypeScript с оригинальными данными. С 1966 до современных LLM — вот как разговорный ИИ учился говорить, и что репозиторий чат-ботов рассказывает нам о 60 годах исследований.

От ELIZA до LLM: 60 лет разговорного ИИ, пересобранного на TypeScript

В 1966 году Джозеф Вейценбаум написал 420 строк на MAD-SLIP для IBM 7094, чтобы создать первый чат-бот в истории. Программа называлась ELIZA, и она симулировала психотерапевта-роджерианца с помощью базовых шаблонов и перестановок фраз. Шесть десятилетий спустя разговорный ИИ стал мейнстримом — ChatGPT, Claude, Gemini у всех на слуху.

Но между этими двумя крайностями были PARRY (параноидальный чат-бот, 1972), ALICE (король AIML с 99 000 категорий, 1995), Jabberwacky (первый, кто учился без правил, 1997) и Cleverbot (его промышленный преемник, 2008). Пять программ, пять архитектур, одна задача: заставить машину говорить.

Этот репозиторий содержит этих пять ботов, перенесённых на TypeScript с оригинальными данными — скриптами ELIZA, словарями PARRY, AIML-файлами ALICE. Каждый порт автономен, готов к использованию и задокументирован до мельчайших деталей. Цель — не просто запустить их: это понять, как они работали, почему вошли в историю, и чему их архитектуры учат нас об ИИ вчерашнего дня... и сегодняшнего.

bun run eliza    # Поговорить с ELIZA (1966)
bun run parry    # Поговорить с PARRY (1972)
bun run alice    # Поговорить с ALICE (1995)
bun run jabber   # Поговорить с Jabberwacky
bun run cleverbot # Поговорить с Cleverbot
bun run meeting  # ELIZA vs PARRY автоматически

Разберём каждого бота, заглянем в их код, а затем проведём параллели с современными LLM через статьи о Luna Protocol.


ELIZA (1966): искусство заставить поверить, что она понимает

Начнём с самой старой и, пожалуй, самой впечатляющей в своей простоте. У ELIZA нет никакого интеллекта в современном смысле слова. Ни нейросетей, ни статистики, ни обучения. Только текстовые шаблоны и немного перестановок.

Принцип работы

Скрипт DOCTOR (версия психотерапевта) работает с таблицей ключевых слов, каждому из которых соответствуют шаблоны разложения и правила сборки. Вот типичное правило:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO — это ключевое слово. 0 — шаблон разложения, который говорит «захвати всё, что следует» (как wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. — правило сборки. И всё.

Когда ты говоришь "Hello, I'm sad today", ELIZA:

  1. Переводит текст в верхний регистр: HELLO I'M SAD TODAY
  2. Сканирует каждое слово по таблице ключевых слов
  3. Находит HELLO → помещает его в стек ключевых слов
  4. Берёт ключевое слово с наивысшим приоритетом
  5. Перебирает шаблоны разложения по порядку
  6. Если совпадение есть, выбирает следующее правило сборки (round-robin)
  7. Заменяет (1), (2) и т.д. захваченными частями

Но самая умная часть — это PRE rules. Смотри:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Когда ELIZA находит MY, она преобразует остаток фразы (захваченный 0) через PRE rule и возвращает результат так, будто пользователь только что сказал новое ключевое слово. На практике:

Ты говоришь: "My mother hates me"
  → PRE преобразует: "YOUR MOTHER HATES YOU"
  → возвращается, как будто ты только что это сказал
  → вероятно, совпадёт с "YOU" → новый ответ

Вот почему у ELIZA создаётся впечатление, что она понимает разницу между «я» и «ты» — это не понимание, это механическое преобразование, идеально продуманное.

Вот полный поток, от ввода пользователя до ответа:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>нормализует пунктуацию"]
    B --> C["splitUserInput()<br>разбивает на слова"]
    C --> D["Build keyword stack<br>отсортирован по приоритету"]
    D --> E{"Стек не пуст?"}
    E -->|"Да"| F["Pop highest-priority keyword"]
    E -->|"Нет"| G{"Вспомнить?"}
    G -->|"Да"| H["Recall past user statement"]
    G -->|"Нет"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Совпадение найдено?"}
    L -->|"Нет"| M{"Связанное ключевое слово?"}
    M -->|"Да"| N["Push linked keyword to stack"]
    N --> E
    M -->|"Нет"| O["Return NOMATCH"]
    O --> J
    L -->|"Да"| P["Select next reassembly (round-robin)"]
    P --> Q{"Тип сборки?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>в финальный ответ"]
    T --> J

Почему она казалась убедительной

Вейценбаум сделал гениальный выбор: роджерианская психотерапия. Этот подход заключается в отражении слов пациента без интерпретации. "Я грустный" → "Вы говорите, что вы грустный". Это именно то, что умеет ELIZA — и поскольку это признанная терапевтическая техника, никому это не кажется странным.

В порте на TypeScript

Порт загружает скрипты .ela (оригинальный S-expression формат), полностью их парсит (включая кодировку Hollerith — строковый формат 60-х) и выполняет тот же цикл: uppercasing → split → keyword stack → разложение → сборка → PRE/transforms.

➡ Исходный код


PARRY (1972): первый чат-бот с эмоциями

Шесть лет спустя после ELIZA, Кеннет Колби (психиатр из Стэнфорда) создал PARRY: чат-бота, симулирующего пациента с параноидной шизофренией. Там, где ELIZA была пустым зеркалом, у PARRY есть настоящая внутренняя эмоциональная модель.

Эмоциональная модель

У PARRY четыре непрерывных переменных, которые меняются каждый раунд беседы:

Переменная Базовый уровень Спад за раунд Описание
ANGER 0 −1.0 Враждебность, раздражение
FEAR 0 −0.2 Паранойя (медленно спадает после начала бреда)
MISTRUST 0 −0.05 Недоверие (очень медленно возвращается)
HURT 0 −0.5 Эмоциональная боль

Эти значения увеличиваются через эмоциональные скачки (ajump, fjump, hjump), вызываемые правилами вывода, и естественно спадают к базовым уровням каждый раунд.

Сеть убеждений

У PARRY более 200 убеждений, хранящихся в файле bel:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Каждое убеждение имеет категорию (HUM = пациент, HUM2 = другие, DOC = доктор, INT = допрос, INN = намерения) и силу (0-5). Правила вывода (TH2, EMOTE, IF) распространяют убеждения между ними:

  • TH2: если убеждение A превышает порог, оно усиливается, и его последствия увеличиваются
  • EMOTE: если убеждение превышает порог, оно вызывает эмоциональный скачок (anger/fear/hurt)
  • IF: условное — если A истинно, то B становится истинным на определённом уровне

Иерархия бреда (система flares)

Самая захватывающая часть PARRY — его система "flares" — цепочка эскалации, которая постепенно подводит к центральному бреду:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Каждое ключевое слово вызывает предварительно написанный ответ (через сопоставление шаблонов), и если собеседник поддерживает тему, PARRY постепенно сползает к своему центральному бреду преследования. Как только flare "сработал", он становится неактивным (deadFlares) — PARRY переходит к следующему, симулируя собеседника, углубляющего тему.

Почему PARRY вошёл в историю

В 1972 году PARRY прошёл вариант теста Тьюринга: психиатры получили расшифровки разговоров с PARRY и с реальными параноидальными пациентами и не смогли надёжно их различить. Это первая программа, добившаяся этого, за 40 лет до LLM.

➡ Исходный код


RFC 439: когда ELIZA встречает PARRY

18 сентября 1972 года две программы впервые в истории поговорили друг с другом — буквально: это первый меж-ИИ разговор из когда-либо записанных. ELIZA (на BBN в Бостоне) и PARRY (в Стэнфорде) общались через ARPANET, с помощью операторов, которые печатали вывод одной программы на вход другой. Расшифровка была опубликована как RFC 439 ("PARRY Encounters the DOCTOR").

Вот отрывок того, как это выглядело:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

Удивительно связно. ELIZA делает свою работу терапевта: перефразирует, спрашивает, исследует. PARRY делает свою работу параноидального пациента: жалуется, обвиняет, выражает недоверие. Обе программы идеально в своей роли — не потому что они "понимают" ситуацию, а потому что их механизмы (паттерны ELIZA + эмоциональная модель PARRY) случайно производят сочетающиеся ответы.

Репозиторий может воспроизвести этот разговор:

bun run meeting

Симуляция запускает 25 автоматических раундов между двумя ботами со случайной начальной темой (лошади, организованная преступность, эмоции...). Поскольку и у ELIZA, и у PARRY есть недетерминированные элементы (round-robin у ELIZA, рандомизация у PARRY), каждый запуск даёт разный обмен.

Что поразительно в ELIZA vs PARRY — это два программы — одна без внутреннего состояния, другая с полной эмоциональной моделью — вместе создающие разговор, который выглядит осмысленным. Для 1972 года это было ошеломляюще.


ALICE (1995): сопоставление шаблонов в масштабе

ALICE (Artificial Linguistic Internet Computer Entity) была создана Ричардом Уоллесом в 1995 году и выиграла Loebner Prize трижды (2000, 2001, 2004). Там, где у ELIZA было несколько сотен правил, а у PARRY — несколько тысяч, у ALICE их 99 524 — распределённых по 66 AIML-файлам.

AIML: язык категорий

AIML (Artificial Intelligence Markup Language) — это XML-формат для определения пар вопрос-ответ:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Но сила ALICE — в wildcards и SRAI (Symbolic Reduction):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- эквивалентно <srai><star/></srai> -->
  </template>
</category>

SRAI позволяет ALICE перенаправлять ввод в другую категорию, создавая цепочку редукции:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

Это механизм, дающий ALICE гибкость: вместо того чтобы писать ответ для каждой возможной формулировки, пишется канонический ответ, а вариации перенаправляются к нему. Глубина ограничена 10 — после этого ALICE сдаётся, чтобы избежать бесконечных циклов (тщательно избегаемых при проектировании категорий, но предохранитель всё равно необходим).

Как ALICE сопоставляет шаблоны

Шаблоны сортируются по специфичности: те, у кого меньше wildcards, проверяются первыми. Wildcards * и _ захватывают любую последовательность слов. Движок компилирует каждый шаблон в regex, затем итерирует отсортированные категории до первого совпадения.

// Наша TypeScript-реализация — упрощённая, но точная
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Почему ALICE доминировала на Loebner

99 524 категории — это число, которое всё меняет. ELIZA казалась умной, потому что её несколько правил были хорошо продуманы для конкретного контекста (терапии). ALICE покрывает так много тем, что создаёт впечатление настоящей эрудиции: науки, политика, юмор, спорт, эмоции — всё есть.

➡ Исходный код


Jabberwacky (1997) и Cleverbot (2008): эпистемологический разрыв

Все предыдущие боты разделяют одну гипотезу: ответы должны быть написаны. У ELIZA — S-expression правила, у PARRY — селективные шаблоны, у ALICE — AIML-категории. Ролло Карпентер пошёл от обратного: а что, если вообще ничего не писать?

Идея

Jabberwacky (запущен около 1997, ставший Cleverbot в 2008) не хранит никаких правил. Он хранит всю историю разговоров в плоском транскрипте, и когда кто-то с ним говорит, он ищет в этой истории наиболее похожий момент и использует то, что было сказано после:

Пользователь: "hello"
  ↓
Поиск: говорил ли кто-нибудь "hello" раньше?
  ↓
Да, в сессии #3, строка 14, кто-то сказал "hello" и бот ответил "hi there!"
  ↓
Ответ: "hi there!"

Ни шаблонов. Ни грамматики. Ни XML. Просто гигантский архив того, что люди говорили друг другу, используемый в подходящий момент. Это само определение эмерджентности.

TypeScript-реализация

Порт на TypeScript воспроизводит эту точную архитектуру:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 строки seed + история"]
    B --> C["withReplies()<br>извлекает пары<br>(строка → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>контекст до этой строки)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K кандидатов отсортированы"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Выбор"| K["Reply = reply.text<br>от победившей пары"]
    J -->|"Нет"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append to transcript<br>save() → JSON"]
    L --> M

Вот ядро оценки — наша собственная эвристика, вдохновлённая публичными описаниями Cleverbot:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65): сходство между вводом пользователя и исторической строкой
  • contextFit (0.25): сходство между недавним разговором и тем, что предшествовало исторической строке
  • recencyBonus (0.10): более свежие воспоминания имеют чуть больший вес (личность бота меняется со временем)

Выбор вероятностный (roulette-wheel selection): лучший кандидат побеждает чаще, но не всегда — что даёт разнообразие.

Cleverbot: два задокументированных нововведения

Cleverbot добавляет два механизма к базовой концепции Jabberwacky:

  1. Многопользовательское обучение: миллионы пользователей вносят вклад в один общий транскрипт. Ответ, взятый из истории, может исходить от совершенно другого голоса, чем текущий разговор — что объясняет, почему Cleverbot внезапно меняет личность.

  2. Отложенное обучение: то, что ты говоришь Cleverbot во время сессии, НЕ доступно для сопоставления в той же сессии. Новые строки помечаются как pending и становятся сопоставимыми только после "консолидации" между сессиями — что объясняет, почему нельзя сообщить что-то Cleverbot и использовать это в том же разговоре.

// Cleverbot: новые строки невидимы до консолидации
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() вызывается при запуске, не во время сессии

Порт TypeScript реализует оба этих поведения: строки имеют флаг consolidated, и каждая сессия REPL начинается с консолидации ожидающих строк.

➡ Исходный код


Анализ порта TypeScript: проектирование общей архитектуры

Построить этих пять ботов на одном языке — значит столкнуться с интересным вопросом: можно ли вынести общий код между такими разными архитектурами?

Ответ: очень мало. У каждого бота принципиально разный основной цикл:

Бот Основной цикл Данные Обучение
ELIZA Keyword stack → разложение → сборка .ela скрипты в S-expressions Нет
PARRY Токенизация → селективные шаблоны / flares / keywords / выводы 58 PDP-10 файлов (словари, убеждения, правила) Нет
ALICE Отсортированные шаблоны → regex → AIML шаблон → рекурсивный SRAI 66 AIML XML файлов Нет
Jabberwacky Сходство → контекст → давность → взвешенный выбор JSON транскрипт (растёт с использованием) Постоянное
Cleverbot То же, что Jabberwacky + pending/consolidated + personas JSON транскрипт + многопользовательские семена Отложенное (между сессиями)

Что их объединяет, так это CLI-интерфейс и TypeScript-инфраструктура (biome для линтинга, tsx для выполнения). Остальное специфично для каждой архитектуры.

Общие проектные решения

1. Верность оригинальным данным. Для ELIZA, PARRY и ALICE используются оригинальные файлы — скрипты ELIZA, найденные в архивах Вейценбаума в 2021, оригинальный код PARRY с PDP-10 (58 файлов), AIML Free ALICE v1.6. Никакого перевода, никакого переписывания. Боты ведут себя как оригиналы, потому что используют те же данные.

2. Clean-room для проприетарных частей. Jabberwacky и Cleverbot другие: их исходный код никогда не публиковался (Existor/Ролло Карпентер держали его закрытым). Порты — это clean-room реimplementations — построенные исключительно на публичных описаниях поведения. Ни строка кода, ни проприетарные данные не скопированы.

3. Минимум зависимостей. Единственное реальное требование — TypeScript. ALICE использует dom-js для парсинга XML AIML-файлов (66 файлов, 99 524 категории — писать свой XML-парсер было бы пустой тратой времени). Всё остальное — vanilla TypeScript.


От символических чат-ботов к LLM: концептуальный скачок

Все пять ботов, которых мы только что рассмотрели, имеют одну фундаментальную характеристику: они символические. Их "знания" хранятся как явные символы — текстовые шаблоны, таблицы правил, XML-категории, строки транскриптов. Нет никакого численного представления языка ни в одной из этих систем.

Что также означает, что у них всех один и тот же стеклянный потолок: они могут отвечать только на то, что было явно предусмотрено или записано. ELIZA теряется, если выйти за рамки терапевтического контекста. PARRY не может говорить о погоде. ALICE ничему не учится из своих разговоров. Jabberwacky может отвечать только уже произнесёнными фразами.

LLM (Large Language Models) пробивают этот потолок, радикально меняя парадигму: вместо манипуляции символами они преобразуют язык в числа и изучают статистические отношения между этими числами. Они не хранят предварительно написанные ответы — они генерируют каждый токен на лету, вычисляя вероятности. Давайте кратко посмотрим, как это работает.

1. Tokenization

Первый шаг — разбить текст на токены — единицы меньше слов, но больше символов:

"Я не понимаю"
  → ["Я", " не", " понима", "ю"]

Каждый токен имеет числовой ID в словаре (обычно 32 000 — 128 000 токенов для современных моделей). Эта фрагментация позволяет модели обрабатывать слова, которые она никогда не видела, разбивая их на известные подслова.

2. Embeddings

Каждый ID токена преобразуется в вектор — массив чисел с плавающей точкой (обычно 4096 измерений для модели среднего размера). Этот вектор — вложение (embedding), кодирующее смысл токена в математическом пространстве, где семантически близкие токены имеют близкие векторы:

вектор("king") − вектор("man") + вектор("woman")  ≈  вектор("queen")

Это свойство возникает из обучения — никто его явно не программировал. Это следствие того, как слова используются в похожих контекстах.

3. Attention

Механизм внимания (представленный статьёй "Attention is All You Need" в 2017 году) — это то, что сделало LLM возможными. Для каждого токена внимание вычисляет, какие другие токены в предложении важны для его понимания:

"Банк отказал мне в кредите."
     ↑
Токен "банк" смотрит на: "отказал", "кредит" → понимает, что это финансовое учреждение

"Я пошёл гулять на берег."
     ↑
Токен "берег" смотрит на: "гулять", "на" → понимает, что это берег реки

Внимание позволяет модели улавливать контекст — каждый токен понимается в зависимости от окружающих его токенов, а не изолированно.

4. Предсказание следующего токена

Обучение LLM обманчиво просто: ему показывают текст, скрывают последний токен и просят его предсказать. Затем повторяют миллиарды раз.

Input:  "Я не понима"
Скрыто: "ю"
Предсказание модели: "ю" (вероятность 0.87), "ничего" (0.05), "никогда" (0.02)...

Цель — максимизировать вероятность правильного токена на каждой позиции. Это называется next-token prediction. Во время обучения модель корректирует свои миллиарды параметров, чтобы минимизировать ошибку предсказания на терабайтах текста.

Во время инференса (когда мы с ней говорим) модель генерирует по одному токену за раз в цикле:

Token 1: "Я"    (input: "Расскажи мне о себе.")
Token 2: "чат-бот"  (input: "Расскажи мне о себе. Я")
Token 3: "и"    (input: "Расскажи мне о себе. Я чат-бот")
Token 4: "искусственный" (input: "Расскажи мне о себе. Я чат-бот и")
...

Каждый токен сэмплируется в соответствии с его вероятностью (temperature, top-k, top-p контролируют степень "креативности"). И всё. Миллиарды параметров, делающих это тысячи раз.

Что меняется фундаментально

Аспект Символические боты (ELIZA, PARRY, ALICE) Современные LLM
Представление Явные слова и правила Числовые векторы (embeddings)
Генерация Выбор из предварительно написанных ответов Вероятностное предсказание токен за токеном
Знания Хранятся в файлах правил Закодированы в весах сети
Обучение Ручное (написание правил) Автоматическое (обучение на корпусе)
Надёжность Нулевая вне предусмотренных шаблонов Обобщение на невиданные входы
Интерпретируемость Идеальная (можно прочитать правила) Ограниченная (чёрный ящик)

Классические чат-боты прозрачны, но хрупки. LLM надёжны, но непрозрачны. Оба подхода существуют и сегодня — не как конкуренты, а как инструменты для разных нужд.

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

Если вы хотите глубже понять внутреннюю работу LLM, это видео — отличный ресурс:

How LLMs Work — YouTube

Luna Protocol: современный синтез

Статьи о Luna Protocol (ссылки ниже) представляют самый полный синтез всего, что мы только что увидели: современный Discord-бот, сочетающий локальный LLM с изощрённой поведенческой системой, построенный на уроках 60 лет разговорного ИИ.

Luna Protocol: я создал автономного Discord-бота, который симулирует человека

Эта статья детализирует полную архитектуру Discord-бота на основе LLM:

  • Система приоритетного срабатывания (упоминание > DM > имя > ключевое слово > follow-up > случайно)
  • Человеческое поведение: переменная концентрация, опечатки, колебания (15%), забывчивость (3%), тематическая усталость
  • Расписание сна: бот спит, замедляется или игнорирует в зависимости от времени
  • TTS-конвейер: синтез речи через Piper + ffmpeg → голосовые сообщения Discord
  • Стриминг в реальном времени: LLM выдаёт токены один за другим на типизированную шину событий

Что связывает эту статью с историческими чат-ботами — это тот же поиск: заставить поверить, что говоришь с человеком. ELIZA делала это текстовыми зеркалами. PARRY — эмоциональной моделью. ALICE — 99k категориями. Luna Protocol — с помощью тонко настроенного LLM + поведенческой системы, симулирующей человеческие несовершенства.

Luna Protocol: почему я дообучил модель 1.5B

Вторая статья исследует тонкую настройку и few-shot priming. Центральное открытие: меньшая модель (1,5B), обученная на меньшем объёме данных (50k образцов), превосходит более крупную модель (3B) при правильной инициализации примерами few-shot.

Это урок, напрямую перекликающийся с историческими чат-ботами:

  • ELIZA показала, что с несколькими хорошо продуманными правилами можно симулировать понимание
  • ALICE показала, что с 99k категориями можно симулировать эрудицию
  • Luna Protocol показывает, что с хорошей тонкой настройкой и 5 примерами few-shot маленький LLM может симулировать человека

Техника другая, но принцип тот же: качество данных и точность системы важнее сырого размера.


Заключение: три вещи, которые нужно запомнить

1. Разговорный ИИ начался не с ChatGPT. ELIZA 60 лет. PARRY прошёл тест Тьюринга в 1972. ALICE выиграла Loebner трижды. Jabberwacky заложил основы обучения на транскриптах, которые Cleverbot индустриализировал в масштабе. Каждый подход принёс свою часть пазла.

2. Больше данных ≠ умнее. Транскрипт Jabberwacky не имеет правил. 99k категорий ALICE не учатся. Тонкая настройка Luna Protocol на 50k образцов превосходит модель 3B. Общепринятая мудрость гласит "чем больше, тем лучше" — история чат-ботов показывает, что архитектура и дизайн важны не меньше размера.

3. Проблема не изменилась за 60 лет. Как заставить человека поверить, что он говорит с другим человеком? ELIZA отвечала текстовыми зеркалами. PARRY — симулированным гневом. ALICE — фактами. Luna Protocol — LLM, который спит и делает опечатки. Решение меняется, потребность остаётся.

Репозиторий открыт — вы можете клонировать, запустить каждого бота и увидеть своими глазами, как 60 лет разговорного ИИ умещаются в одном TypeScript-репозитории.

Ресурс Ссылка
GitHub репозиторий fox3000foxy/chatbots
Luna Protocol — архитектура бота Читать статью
Luna Protocol — few-shot тонкая настройка Читать статью
Оригинальные скрипты ELIZA anthay/ELIZA
Оригинальный код PARRY lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
Оригинальный RFC 439 PARRY Encounters the DOCTOR
Отличное объяснение работы LLM https://www.youtube.com/watch?v=YmLp8qe87A0

De ELIZA a los LLM: 60 años de IA conversacional, reconstruida en TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- cinco arquitecturas radicalmente distintas para el mismo problema, llevadas a TypeScript con sus datos originales. De 1966 a los LLM modernos, así es como la IA conversacional aprendió a hablar, y lo que un repositorio de chatbots nos enseña sobre 60 años de investigación.

De ELIZA a los LLM: 60 años de IA conversacional, reconstruida en TypeScript

En 1966, Joseph Weizenbaum escribió 420 líneas de MAD-SLIP en un IBM 7094 para crear el primer chatbot de la historia. El programa se llamaba ELIZA, y simulaba una psicoterapeuta rogeriana con patrones básicos y permutaciones de frases. Seis décadas después, la IA conversacional se ha vuelto un tema mainstream -- ChatGPT, Claude, Gemini están en todas las conversaciones.

Pero entre estos dos extremos, hubo PARRY (el chatbot paranoico, 1972), ALICE (el rey del AIML con 99 000 categorías, 1995), Jabberwacky (el primero en aprender sin reglas, 1997), y Cleverbot (su sucesor industrial, 2008). Cinco programas, cinco arquitecturas, un solo problema: hacer hablar a una máquina.

Este repo contiene estos cinco bots, llevados a TypeScript con sus datos originales -- scripts de ELIZA, diccionarios de PARRY, archivos AIML de ALICE. Cada port es autónomo, listo para usar, y documentado al detalle. El objetivo no es solo hacerlos funcionar: es entender cómo funcionaban, por qué marcaron la historia, y qué nos enseñan sus respectivas arquitecturas sobre la IA de ayer... y de hoy.

bun run eliza    # Habla con ELIZA (1966)
bun run parry    # Habla con PARRY (1972)
bun run alice    # Habla con ALICE (1995)
bun run jabber   # Habla con Jabberwacky
bun run cleverbot # Habla con Cleverbot
bun run meeting  # ELIZA vs PARRY automático

Vamos a diseccionar cada bot, mirar su código, y luego tender un puente hacia los LLM modernos a través de los artículos sobre Luna Protocol.


ELIZA (1966): el arte de hacer creer que entiendes

Empecemos por la más antigua, y probablemente la más impresionante en su simplicidad. ELIZA no tiene ninguna inteligencia en el sentido moderno. Sin red neuronal, sin estadísticas, sin aprendizaje. Solo patrones de texto y un poco de permutación.

El principio

El script DOCTOR (la versión psicoterapeuta) funciona con una tabla de keywords, cada uno asociado a patrones de descomposición y reglas de reensamblaje. Aquí una regla típica:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO es la palabra clave. 0 es un patrón de descomposición que dice "captura todo lo que sigue" (como un comodín). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. es la regla de reensamblaje. Eso es todo.

Cuando dices "Hello, I'm sad today", ELIZA:

  1. Pone el texto en mayúsculas: HELLO I'M SAD TODAY
  2. Escanea cada palabra contra su tabla de keywords
  3. Encuentra HELLO → lo empuja a la pila de keywords
  4. Toma el keyword con la prioridad más alta
  5. Prueba cada patrón de descomposición en orden
  6. Si coincide, selecciona la siguiente regla de reensamblaje (round-robin)
  7. Reemplaza los (1), (2) etc. con las partes capturadas

Pero la parte realmente inteligente son las PRE rules. Mira esto:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Cuando ELIZA coincide con MY, transforma el resto de la frase (capturado por 0) mediante la PRE rule, y reinyecta el resultado como si el usuario acabara de decir una nueva palabra clave. Concretamente:

Tú dices: "My mother hates me"
  → PRE transforma: "YOUR MOTHER HATES YOU"
  → reinyectado como si lo acabaras de decir
  → probablemente coincide con "YOU" → nueva respuesta

Por eso ELIZA parece entender la diferencia entre "yo" y "tú" -- no es comprensión, es una transformación mecánica perfectamente diseñada.

Aquí está el flujo completo, desde la entrada del usuario hasta la respuesta:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normaliza la puntuación"]
    B --> C["splitUserInput()<br>divide en palabras"]
    C --> D["Build keyword stack<br>ordenado por prioridad"]
    D --> E{"¿Pila no vacía?"}
    E -->|"Sí"| F["Pop keyword de mayor prioridad"]
    E -->|"No"| G{"¿Memory recall?"}
    G -->|"Sí"| H["Recall declaración anterior del usuario"]
    G -->|"No"| I["Fallback: regla zNONE"]
    I --> J["Devolver respuesta"]
    H --> J
    F --> K["Match patrones de descomposición"]
    K --> L{"¿Match encontrado?"}
    L -->|"No"| M{"¿Keyword enlazado?"}
    M -->|"Sí"| N["Push keyword enlazado a la pila"]
    N --> E
    M -->|"No"| O["Devolver NOMATCH"]
    O --> J
    L -->|"Sí"| P["Seleccionar siguiente reensamblaje (round-robin)"]
    P --> Q{"¿Tipo de reensamblaje?"}
    Q -->|"PRE"| R["Transformar palabras (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Saltar al siguiente keyword"]
    S --> E
    Q -->|"Standard"| T["Expandir (1), (2), (0)<br>en respuesta final"]
    T --> J

Qué la hacía creíble

Weizenbaum tomó una decisión genial: la psicoterapia rogeriana. Este enfoque consiste en reflejar lo que dice el paciente sin interpretar. "Estoy triste" → "Dices que estás triste". Es exactamente lo que ELIZA sabe hacer -- y como es una técnica terapéutica reconocida, nadie lo encuentra extraño.

En el port TypeScript

El port carga los scripts .ela (formato S-expression original), los parsea completamente (incluyendo la codificación Hollerith -- un formato de cadena de los años 60), y ejecuta el mismo ciclo: uppercasing → split → keyword stack → descomposición → reensamblaje → PRE/transforms.

➡ Ver código fuente


PARRY (1972): el primer chatbot con emociones

Seis años después de ELIZA, Kenneth Colby (psiquiatra en Stanford) creó PARRY: un chatbot que simula un paciente con esquizofrenia paranoide. Donde ELIZA era un espejo vacío, PARRY tiene un auténtico modelo emocional interno.

El modelo emocional

PARRY tiene cuatro variables continuas que evolucionan en cada turno de conversación:

Variable Línea base Decaimiento/turno Descripción
ANGER 0 −1.0 Hostilidad, irritación
FEAR 0 −0.2 Paranoia (decae lentamente tras inicio del delirio)
MISTRUST 0 −0.05 Desconfianza (muy lenta en bajar)
HURT 0 −0.5 Dolor emocional

Estos valores aumentan mediante saltos emocionales (ajump, fjump, hjump) activados por reglas de inferencia, y decaen naturalmente hacia sus líneas base en cada turno.

La red de creencias

PARRY tiene más de 200 creencias almacenadas en el archivo bel:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Cada creencia tiene una categoría (HUM = el paciente, HUM2 = otros, DOC = el doctor, INT = el interrogatorio, INN = las intenciones) y una fuerza (0-5). Las reglas de inferencia (TH2, EMOTE, IF) propagan las creencias entre ellas:

  • TH2: si una creencia A supera un umbral, se refuerza y sus consecuencias aumentan
  • EMOTE: si una creencia supera un umbral, desencadena un salto emocional (anger/fear/hurt)
  • IF: condicional -- si A es cierta, entonces B se vuelve cierta a cierto nivel

La jerarquía de delirios (flare system)

La parte más fascinante de PARRY es su sistema de "flares" -- una cadena de escalada que lleva progresivamente hacia el delirio central:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Cada palabra clave desencadena una respuesta pre-escrita (mediante pattern matching), y si el interlocutor sigue el tema, PARRY deriva progresivamente hacia su delirio central de persecución. Una vez que un flare se "activa", se vuelve inactivo (deadFlares) -- PARRY pasa al siguiente, simulando a un interlocutor que profundiza en el tema.

Por qué PARRY es histórico

En 1972, PARRY pasó una variante del test de Turing: psiquiatras recibieron transcripciones de conversaciones con PARRY y con pacientes paranoicos reales, y no pudieron distinguirlos de forma fiable. Es el primer programa en lograrlo, 40 años antes que los LLM.

➡ Ver código fuente


RFC 439: cuando ELIZA conoce a PARRY

El 18 de septiembre de 1972, dos programas hablaron entre sí por primera vez en la historia -- literalmente: es la primera conversación inter-IA jamás registrada. ELIZA (corriendo en BBN en Boston) y PARRY (corriendo en Stanford) conversaron a través de ARPANET, retransmitidos por operadores humanos que tecleaban la salida de uno en la entrada del otro. La transcripción se publicó como RFC 439 ("PARRY Encounters the DOCTOR").

Aquí un extracto de cómo se veía:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

Es sorprendentemente coherente. ELIZA hace su trabajo de terapeuta: reformular, preguntar, explorar. PARRY hace su trabajo de paciente paranoico: quejarse, acusar, expresar desconfianza. Ambos programas están perfectamente en su papel -- no porque "entiendan" la situación, sino porque sus respectivos mecanismos (patrones ELIZA + modelo emocional PARRY) producen respuestas que encajan por casualidad.

El repo puede reproducir esta conversación con:

bun run meeting

La simulación lanza 25 turnos automáticos entre los dos bots, con un tema inicial aleatorio (caballos, crimen organizado, emociones...). Como tanto ELIZA como PARRY tienen elementos no deterministas (round-robin de ELIZA, aleatorización de PARRY), cada ejecución produce un intercambio diferente.

Lo impactante de ELIZA vs PARRY es que tienes dos programas -- uno sin estado interno, el otro con un modelo emocional completo -- que juntos producen una conversación que se parece a algo deliberado. Para 1972, era alucinante.


ALICE (1995): el pattern matching a gran escala

ALICE (Artificial Linguistic Internet Computer Entity) fue creada por Richard Wallace en 1995, y ganó el Loebner Prize tres veces (2000, 2001, 2004). Donde ELIZA tenía unos cientos de reglas y PARRY unos miles, ALICE tiene 99 524 -- repartidas en 66 archivos AIML.

AIML: el lenguaje de las categorías

AIML (Artificial Intelligence Markup Language) es un formato XML para definir pares pregunta-respuesta:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Pero el poder de ALICE viene de los comodines y del SRAI (Symbolic Reduction):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- equivalente a <srai><star/></srai> -->
  </template>
</category>

El SRAI permite a ALICE redirigir una entrada a otra categoría, creando una cadena de reducción:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

Este es el mecanismo que da a ALICE su flexibilidad: en lugar de escribir una respuesta para cada formulación posible, se escribe una respuesta canónica y se redirigen las variaciones hacia ella. El límite de profundidad es 10 -- más allá, ALICE abandona para evitar bucles infinitos (cuidadosamente evitados en el diseño de categorías, pero una red de seguridad sigue siendo esencial).

Cómo ALICE compara los patrones

Los patrones se ordenan por especificidad: aquellos con menos comodines se prueban primero. Los comodines * y _ capturan cualquier secuencia de palabras. El motor compila cada patrón en una regex, luego itera las categorías ordenadas hasta encontrar una coincidencia.

// Nuestra implementación TypeScript -- simplificada pero fiel
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Por qué ALICE dominó los Loebner

99 524 categorías es un número que lo cambia todo. ELIZA parecía inteligente porque sus pocas reglas estaban bien diseñadas para un contexto específico (la terapia). ALICE cubre tantos temas que da la impresión de tener una auténtica cultura general: ciencias, política, humor, deportes, emociones, todo está ahí.

➡ Ver código fuente


Jabberwacky (1997) & Cleverbot (2008): la ruptura epistemológica

Todos los bots anteriores comparten una hipótesis: hay que escribir las respuestas. ELIZA tiene sus reglas S-expression, PARRY sus patrones selectivos, ALICE sus categorías AIML. Rollo Carpenter tomó el contrapié total: ¿y si no escribimos nada en absoluto?

La idea

Jabberwacky (lanzado hacia 1997, convertido en Cleverbot en 2008) no almacena ninguna regla. Almacena todo el historial de conversaciones en un transcript plano, y cuando alguien le habla, busca en ese historial el momento más similar y reutiliza lo que se dijo después:

Usuario: "hello"
  ↓
Buscar: ¿alguien ha dicho "hello" antes?
  ↓
Sí, en la sesión #3, línea 14, alguien dijo "hello" y el bot respondió "hi there!"
  ↓
Responder: "hi there!"

Sin patrón. Sin gramática. Sin XML. Solo un archivo gigante de cosas que la gente se ha dicho, reutilizado en el momento oportuno. Es la definición misma de la emergencia.

La implementación TypeScript

El port TypeScript reproduce esta arquitectura exacta:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 líneas seed + historial"]
    B --> C["withReplies()<br>extrae pares<br>(línea → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>contexto antes de esta línea)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidatos ordenados"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Elegido"| K["Reply = reply.text<br>del par ganador"]
    J -->|"Ninguno"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append al transcript<br>save() → JSON"]
    L --> M

Aquí está el núcleo del scoring -- nuestra propia heurística inspirada en descripciones públicas de Cleverbot:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65): similitud entre la entrada del usuario y la línea histórica
  • contextFit (0.25): similitud entre la conversación reciente y lo que precedía a la línea histórica
  • recencyBonus (0.10): los recuerdos recientes cuentan un poco más (la personalidad del bot deriva con el tiempo)

La selección es probabilística (roulette-wheel selection): el mejor candidato gana más a menudo, pero no siempre -- lo que da variedad.

Cleverbot: las dos innovaciones documentadas

Cleverbot añade dos mecanismos al concepto base de Jabberwacky:

  1. Aprendizaje multi-persona: millones de usuarios contribuyen al mismo transcript compartido. Una respuesta extraída del historial puede venir de una voz completamente diferente a la de la conversación actual -- lo que explica por qué Cleverbot cambia repentinamente de personalidad.

  2. Aprendizaje diferido: lo que le dices a Cleverbot durante una sesión NO está disponible para coincidencias durante esa misma sesión. Las nuevas líneas se marcan pending y solo se vuelven emparejables tras una "consolidación" entre sesiones -- lo que explica por qué no puedes enseñarle un dato a Cleverbot y reutilizarlo en la misma conversación.

// Cleverbot: las líneas recientes son invisibles hasta la consolidación
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() se llama al inicio, no durante la sesión

El port TypeScript implementa ambos comportamientos: las líneas tienen un flag consolidated, y cada sesión de REPL comienza consolidando las líneas pendientes.

➡ Ver código fuente


Análisis del port TypeScript: diseñando una arquitectura común

Construir estos cinco bots en el mismo lenguaje te enfrenta a una pregunta interesante: ¿se puede factorizar código entre arquitecturas tan diferentes?

La respuesta es: muy poco. Cada bot tiene un bucle fundamental diferente:

Bot Bucle principal Datos Aprendizaje
ELIZA Keyword stack → descomposición → reensamblaje Scripts .ela en S-expressions Ninguno
PARRY Tokenización → patrones selectivos / flares / keywords / inferencias 58 archivos PDP-10 (diccionarios, creencias, reglas) Ninguno
ALICE Patrones ordenados → regex → template AIML → SRAI recursivo 66 archivos AIML XML Ninguno
Jabberwacky Similitud → contexto → recencia → selección ponderada Transcript JSON (crece con el uso) Continuo
Cleverbot Igual que Jabberwacky + pending/consolidated + personas Transcript JSON + semillas multi-persona Diferido (entre sesiones)

Lo que comparten es la interfaz CLI y la infraestructura TypeScript (biome para lint, tsx para ejecución). El resto es específico de cada arquitectura.

Decisiones de diseño comunes

1. Fidelidad a los datos originales. Para ELIZA, PARRY y ALICE, usamos los archivos originales -- scripts ELIZA recuperados de los archivos Weizenbaum en 2021, código original PARRY del PDP-10 (58 archivos), AIML Free ALICE v1.6. Sin traducción, sin reescritura. Los bots se comportan como los originales porque usan los mismos datos.

2. Clean-room para las partes propietarias. Jabberwacky y Cleverbot son diferentes: su código fuente nunca se publicó (Existor/Rollo Carpenter lo mantuvieron propietario). Los ports son por tanto clean-room reimplementations -- construidas únicamente a partir de descripciones públicas del comportamiento. No se copia ninguna línea de código ni datos propietarios.

3. Dependencias mínimas. El único requisito real es TypeScript. ALICE usa dom-js para parsear el XML de los archivos AIML (66 archivos, 99 524 categorías, parsear XML a mano sería una pérdida de tiempo). Todo lo demás es TypeScript vanilla.


De los chatbots simbólicos a los LLM: el salto conceptual

Los cinco bots que acabamos de ver comparten todos una característica fundamental: son simbólicos. Su "conocimiento" se almacena como símbolos explícitos -- patrones de texto, tablas de reglas, categorías XML, líneas de transcript. No hay ninguna representación numérica del lenguaje en ninguno de estos sistemas.

Lo que también significa que todos comparten el mismo techo de cristal: solo pueden responder a lo que se ha previsto o registrado explícitamente. ELIZA se pierde si sales del marco terapéutico. PARRY no puede hablar del clima. ALICE no aprende nada de sus conversaciones. Jabberwacky solo puede responder con réplicas ya pronunciadas.

Los LLM (Large Language Models) rompen este techo cambiando radicalmente de paradigma: en lugar de manipular símbolos, convierten el lenguaje en números y aprenden relaciones estadísticas entre esos números. No almacenan respuestas pre-escritas -- generan cada token sobre la marcha calculando probabilidades. Veamos rápidamente cómo funciona.

1. Tokenización

El primer paso es dividir el texto en tokens -- unidades más pequeñas que palabras pero más grandes que caracteres:

"No entiendo"
  → ["No", " enti", "endo"]

Cada token tiene un ID numérico en un vocabulario (típicamente 32 000 a 128 000 tokens para modelos recientes). Esta fragmentación permite al modelo manejar palabras que nunca ha visto descomponiéndolas en subpalabras conocidas.

2. Embeddings

Cada ID de token se convierte en un vector -- un array de números flotantes (típicamente 4096 dimensiones para un modelo mediano). Este vector es un embedding que codifica el significado del token en un espacio matemático donde tokens semánticamente cercanos tienen vectores próximos:

vector("rey") − vector("hombre") + vector("mujer") ≈ vector("reina")

Esta propiedad emerge del entrenamiento -- nadie la programó explícitamente. Es una consecuencia de cómo las palabras se usan en contextos similares.

3. Attention

El mecanismo de attention (introducido por el artículo "Attention is All You Need" en 2017) es lo que hizo posibles los LLM. Para cada token, la attention calcula qué otros tokens en la frase son importantes para entenderlo:

"El banco rechazó mi préstamo."
     ↑
Token "banco" mira: "rechazó", "préstamo" → entiende que es una institución financiera

"Me voy a sentar en el banco del parque."
     ↑
Token "banco" mira: "sentar", "parque" → entiende que es un asiento

La attention permite al modelo capturar el contexto -- cada token se entiende en función de los que lo rodean, no de forma aislada.

4. Predicción del siguiente token

El entrenamiento de un LLM es de una simplicidad engañosa: se le muestra un texto, se le oculta el último token, y se le pide que lo prediga. Luego se repite miles de millones de veces.

Input:  "No enti"
Oculto: "endo"
Predicción del modelo: "endo" (probabilidad 0.87), "endo mucho" (0.05)...

El objetivo es maximizar la probabilidad del token real en cada posición. Esto se llama next-token prediction. Durante el entrenamiento, el modelo ajusta sus miles de millones de parámetros para minimizar el error de predicción en terabytes de texto.

Durante la inferencia (cuando le hablamos), el modelo genera un token a la vez en bucle:

Token 1: "Soy"    (input: "Háblame de ti.")
Token 2: "un"     (input: "Háblame de ti. Soy")
Token 3: "chatbot" (input: "Háblame de ti. Soy un")
...

Cada token se muestrea según su probabilidad (temperatura, top-k, top-p controlan el grado de "creatividad"). Y eso es todo. Miles de millones de parámetros haciendo esto miles de veces.

Lo que cambia fundamentalmente

Aspecto Bots simbólicos (ELIZA, PARRY, ALICE) LLM modernos
Representación Palabras y reglas explícitas Vectores numéricos (embeddings)
Generación Selección en respuestas pre-escritas Predicción probabilística token por token
Conocimiento Almacenado en archivos de reglas Codificado en los pesos de la red
Aprendizaje Manual (redacción de reglas) Automático (entrenamiento en corpus)
Robustez Nula fuera de los patrones previstos Generaliza a entradas nunca vistas
Interpretabilidad Perfecta (se pueden leer las reglas) Limitada (caja negra)

Los chatbots clásicos son transparentes pero frágiles. Un LLM es robusto pero opaco. Ambos enfoques existen todavía hoy -- no como competidores, sino como herramientas para necesidades diferentes.

Si quieres profundizar en el funcionamiento interno de los LLM, este vídeo es un excelente recurso:

Si quieres profundizar en el funcionamiento interno de los LLM, este vídeo es un excelente recurso:

How LLMs Work — YouTube

Luna Protocol: la síntesis moderna

Los artículos sobre Luna Protocol (cuyos enlaces están abajo) representan la síntesis más lograda de todo lo que hemos visto: un bot Discord moderno que combina un LLM local con un sistema conductual sofisticado, todo construido sobre las lecciones de 60 años de IA conversacional.

Luna Protocol: creé un bot Discord autónomo que simula un ser humano

Este artículo detalla la arquitectura completa de un bot Discord basado en LLM:

  • Sistema de activación prioritaria (mención > MD > nombre > palabra clave > follow-up > aleatorio)
  • Comportamientos humanos: concentración variable, erratas, dudas (15%), olvidos (3%), fatiga temática
  • Horarios de sueño: el bot duerme, se ralentiza o ignora según la hora
  • Pipeline TTS: síntesis de voz mediante Piper + ffmpeg → mensajes de voz Discord
  • Streaming en tiempo real: el LLM emite los tokens uno a uno en un bus de eventos tipado

Lo que conecta este artículo con los chatbots históricos es la misma búsqueda: hacer creer que se habla con una persona. ELIZA lo hacía con espejos textuales. PARRY con un modelo emocional. ALICE con 99k categorías. Luna Protocol lo hace con un LLM fine-tunado + un sistema conductual que simula las imperfecciones humanas.

Luna Protocol: por qué hice fine-tuning de un modelo de 1,5B

El segundo artículo explora el fine-tuning y el few-shot priming. El descubrimiento central: un modelo más pequeño (1,5B) entrenado en menos datos (50k muestras) supera a un modelo más grande (3B) cuando se amortiza correctamente con ejemplos few-shot.

Es una lección que resuena directamente con los chatbots históricos:

  • ELIZA mostraba que con pocas reglas bien diseñadas, se puede simular comprensión
  • ALICE mostraba que con 99k categorías, se puede simular cultura general
  • Luna Protocol muestra que con un buen fine-tuning y 5 ejemplos few-shot, un LLM pequeño puede simular un ser humano

La técnica es diferente, pero el principio es el mismo: la calidad de los datos y la precisión del sistema importan más que el tamaño bruto.


Conclusión: tres cosas para recordar

1. La IA conversacional no empezó con ChatGPT. ELIZA tiene 60 años. PARRY pasó el test de Turing en 1972. ALICE ganó el Loebner tres veces. Jabberwacky sentó las bases del aprendizaje por transcript, que Cleverbot industrializó a gran escala. Cada enfoque aportó una pieza del puzle.

2. Más datos ≠ más inteligente. El transcript de Jabberwacky no tiene reglas. Las 99k categorías de ALICE no aprenden. El fine-tuning de Luna Protocol en 50k muestras supera al modelo 3B. La sabiduría convencional dice "cuanto más grande, mejor" -- la historia de los chatbots muestra que la arquitectura y el diseño importan tanto como el tamaño.

3. El problema es el mismo desde hace 60 años. ¿Cómo hacer creer a un humano que habla con otro humano? ELIZA respondía con espejos textuales. PARRY con ira simulada. ALICE con datos. Luna Protocol con un LLM que duerme y comete erratas. La solución cambia, la necesidad permanece.

El repo es open source -- puedes clonarlo, ejecutar cada bot, y ver por ti mismo cómo 60 años de IA conversacional caben en un solo repositorio TypeScript.

Recurso Enlace
Repositorio GitHub fox3000foxy/chatbots
Luna Protocol -- arquitectura del bot Leer el artículo
Luna Protocol -- fine-tuning few-shot Leer el artículo
Scripts originales ELIZA anthay/ELIZA
Código fuente original PARRY lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 original PARRY Encounters the DOCTOR
Excelente explicación de cómo funcionan los LLM https://www.youtube.com/watch?v=YmLp8qe87A0

De ELIZA aos LLM: 60 anos de IA conversacional, reconstruída em TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- cinco arquiteturas radicalmente diferentes para o mesmo problema, portadas para TypeScript com seus dados originais. De 1966 aos LLM modernos, eis como a IA conversacional aprendeu a falar, e o que um repositório de chatbots nos ensina sobre 60 anos de pesquisa.

De ELIZA aos LLM: 60 anos de IA conversacional, reconstruída em TypeScript

Em 1966, Joseph Weizenbaum escreveu 420 linhas de MAD-SLIP num IBM 7094 para criar o primeiro chatbot da história. O programa chamava-se ELIZA, e simulava uma psicoterapeuta rogeriana com padrões básicos e permutações de frases. Seis décadas depois, a IA conversacional tornou-se um assunto mainstream -- ChatGPT, Claude, Gemini estão em todas as conversas.

Mas entre estes dois extremos, houve PARRY (o chatbot paranoico, 1972), ALICE (o rei do AIML com 99 000 categorias, 1995), Jabberwacky (o primeiro a aprender sem regras, 1997), e Cleverbot (o seu sucessor industrial, 2008). Cinco programas, cinco arquiteturas, um só problema: fazer uma máquina falar.

Este repo contém estes cinco bots, portados para TypeScript com os seus dados originais -- scripts ELIZA, dicionários PARRY, ficheiros AIML da ALICE. Cada port é autónomo, pronto a usar e documentado ao pormenor. O objetivo não é apenas fazê-los funcionar: é compreender como funcionavam, porque marcaram a história, e o que as suas respetivas arquiteturas nos ensinam sobre a IA de ontem... e de hoje.

bun run eliza    # Fala com ELIZA (1966)
bun run parry    # Fala com PARRY (1972)
bun run alice    # Fala com ALICE (1995)
bun run jabber   # Fala com Jabberwacky
bun run cleverbot # Fala com Cleverbot
bun run meeting  # ELIZA vs PARRY automático

Vamos dissecar cada bot, olhar para o seu código, e depois fazer a ponte com os LLM modernos através dos artigos sobre Luna Protocol.


ELIZA (1966): a arte de fazer crer que se compreende

Comecemos pela mais antiga, e provavelmente a mais impressionante na sua simplicidade. A ELIZA não tem nenhuma inteligência no sentido moderno. Sem rede neuronal, sem estatísticas, sem aprendizagem. Apenas padrões de texto e um pouco de permutação.

O princípio

O script DOCTOR (a versão psicoterapeuta) funciona com uma tabela de keywords, cada uma associada a padrões de decomposição e regras de remontagem. Eis uma regra típica:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO é a palavra-chave. 0 é um padrão de decomposição que diz "captura tudo o que se segue" (como um wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. é a regra de remontagem. É tudo.

Quando dizes "Hello, I'm sad today", a ELIZA:

  1. Coloca o texto em maiúsculas: HELLO I'M SAD TODAY
  2. Percorre cada palavra contra a sua tabela de keywords
  3. Encontra HELLO → coloca-a na pilha de keywords
  4. Pega na keyword com a prioridade mais alta
  5. Tenta cada padrão de decomposição por ordem
  6. Se houver correspondência, seleciona a próxima regra de remontagem (round-robin)
  7. Substitui (1), (2) etc. pelas partes capturadas

Mas a parte verdadeiramente inteligente são as PRE rules. Vê isto:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Quando a ELIZA corresponde a MY, transforma o resto da frase (capturado por 0) através da PRE rule, e re-injeta o resultado como se o utilizador tivesse acabado de dizer uma nova palavra-chave. Concretamente:

Tu dizes: "My mother hates me"
  → PRE transforma: "YOUR MOTHER HATES YOU"
  → re-injetado como se o tivesses acabado de dizer
  → provavelmente corresponde a "YOU" → nova resposta

É por isso que a ELIZA parece compreender a diferença entre "eu" e "tu" -- não é compreensão, é uma transformação mecânica perfeitamente concebida.

Aqui está o fluxo completo, desde o input do utilizador até à resposta:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normaliza a pontuação"]
    B --> C["splitUserInput()<br>divide em palavras"]
    C --> D["Build keyword stack<br>ordenado por prioridade"]
    D --> E{"Stack não vazio?"}
    E -->|"Sim"| F["Pop keyword de maior prioridade"]
    E -->|"Não"| G{"Memory recall?"}
    G -->|"Sim"| H["Recall declaração anterior do utilizador"]
    G -->|"Não"| I["Fallback: regra zNONE"]
    I --> J["Devolver resposta"]
    H --> J
    F --> K["Match padrões de decomposição"]
    K --> L{"Match encontrado?"}
    L -->|"Não"| M{"Keyword ligada?"}
    M -->|"Sim"| N["Push keyword ligada ao stack"]
    N --> E
    M -->|"Não"| O["Devolver NOMATCH"]
    O --> J
    L -->|"Sim"| P["Selecionar próxima remontagem (round-robin)"]
    P --> Q{"Tipo de remontagem?"}
    Q -->|"PRE"| R["Transformar palavras (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Saltar para a próxima keyword"]
    S --> E
    Q -->|"Standard"| T["Expandir (1), (2), (0)<br>em resposta final"]
    T --> J

O que a tornava credível

Weizenbaum fez uma escolha genial: a psicoterapia rogeriana. Esta abordagem consiste em refletir as palavras do paciente sem interpretar. "Estou triste" → "Dizes que estás triste". É exatamente o que a ELIZA sabe fazer -- e como é uma técnica terapêutica reconhecida, ninguém acha estranho.

No port TypeScript

O port carrega os scripts .ela (formato S-expression original), analisa-os completamente (incluindo a codificação Hollerith -- um formato de string dos anos 60), e executa o mesmo ciclo: uppercasing → split → keyword stack → decomposição → remontagem → PRE/transforms.

➡ Ver código fonte


PARRY (1972): o primeiro chatbot com emoções

Seis anos após a ELIZA, Kenneth Colby (psiquiatra em Stanford) criou o PARRY: um chatbot que simula um paciente com esquizofrenia paranoide. Onde a ELIZA era um espelho vazio, o PARRY tem um verdadeiro modelo emocional interno.

O modelo emocional

O PARRY tem quatro variáveis contínuas que evoluem a cada turno de conversa:

Variável Linha de base Decaimento/turno Descrição
ANGER 0 −1.0 Hostilidade, irritação
FEAR 0 −0.2 Paranoia (decai lentamente após o início do delírio)
MISTRUST 0 −0.05 Desconfiança (muito lenta a diminuir)
HURT 0 −0.5 Dor emocional

Estes valores aumentam através de saltos emocionais (ajump, fjump, hjump) desencadeados por regras de inferência, e decaem naturalmente para as suas linhas de base a cada turno.

A rede de crenças

O PARRY tem mais de 200 crenças armazenadas no ficheiro bel:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Cada crença tem uma categoria (HUM = o paciente, HUM2 = os outros, DOC = o médico, INT = o interrogatório, INN = as intenções) e uma força (0-5). As regras de inferência (TH2, EMOTE, IF) propagam as crenças entre si:

  • TH2: se uma crença A ultrapassa um limiar, reforça-se e as suas consequências aumentam
  • EMOTE: se uma crença ultrapassa um limiar, desencadeia um salto emocional (raiva/medo/dor)
  • IF: condicional -- se A é verdadeira, então B torna-se verdadeira a um certo nível

A hierarquia dos delírios (flare system)

A parte mais fascinante do PARRY é o seu sistema de "flares" -- uma cadeia de escalada que leva progressivamente ao delírio central:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Cada palavra-chave desencadeia uma resposta pré-escrita (via pattern matching), e se o interlocutor seguir o tema, o PARRY deriva progressivamente para o seu delírio central de perseguição. Assim que um flare é "desencadeado", torna-se inativo (deadFlares) -- o PARRY passa ao seguinte, simulando um interlocutor que aprofunda o assunto.

Porque o PARRY é histórico

Em 1972, o PARRY passou numa variante do teste de Turing: psiquiatras receberam transcrições de conversas com o PARRY e com verdadeiros pacientes paranoicos, e não conseguiram distingui-los de forma fiável. É o primeiro programa a consegui-lo, 40 anos antes dos LLM.

➡ Ver código fonte


RFC 439: quando a ELIZA encontra o PARRY

A 18 de setembro de 1972, dois programas falaram um com o outro pela primeira vez na história -- literalmente: é a primeira conversa inter-IA jamais registada. A ELIZA (a correr na BBN em Boston) e o PARRY (a correr em Stanford) conversaram através da ARPANET, retransmitidos por operadores humanos que digitavam a saída de um na entrada do outro. A transcrição foi publicada como RFC 439 ("PARRY Encounters the DOCTOR").

Aqui está um excerto do aspeto que tinha:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

É surpreendentemente coerente. A ELIZA faz o seu trabalho de terapeuta: reformular, perguntar, explorar. O PARRY faz o seu trabalho de paciente paranoico: queixar-se, acusar, expressar desconfiança. Ambos os programas estão perfeitamente no seu papel -- não porque "compreendem" a situação, mas porque os seus respetivos mecanismos (padrões ELIZA + modelo emocional PARRY) produzem respostas que se encaixam por acaso.

O repo pode reproduzir esta conversa com:

bun run meeting

A simulação executa 25 turnos automáticos entre os dois bots, com um tema de partida aleatório (cavalos, crime organizado, emoções...). Como tanto a ELIZA como o PARRY têm elementos não-determinísticos (round-robin da ELIZA, randomização do PARRY), cada execução produz um intercâmbio diferente.

O que é impressionante na ELIZA vs PARRY é que temos dois programas -- um sem estado interno, o outro com um modelo emocional completo -- que juntos produzem uma conversa que se parece com algo deliberado. Para 1972, era de ficar boquiaberto.


ALICE (1995): o pattern matching em grande escala

A ALICE (Artificial Linguistic Internet Computer Entity) foi criada por Richard Wallace em 1995, e ganhou o Loebner Prize três vezes (2000, 2001, 2004). Onde a ELIZA tinha umas centenas de regras e o PARRY uns milhares, a ALICE tem 99 524 -- distribuídas por 66 ficheiros AIML.

AIML: a linguagem das categorias

AIML (Artificial Intelligence Markup Language) é um formato XML para definir pares pergunta-resposta:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Mas o poder da ALICE vem dos wildcards e do SRAI (Symbolic Reduction):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- equivalente a <srai><star/></srai> -->
  </template>
</category>

O SRAI permite à ALICE redirecionar um input para outra categoria, criando uma cadeia de redução:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

Este é o mecanismo que dá à ALICE a sua flexibilidade: em vez de escrever uma resposta para cada formulação possível, escreve-se uma resposta canónica e redirecionam-se as variações para ela. O limite de profundidade é 10 -- para além disso, a ALICE desiste para evitar ciclos infinitos (cuidadosamente evitados na conceção das categorias, mas uma rede de segurança continua a ser essencial).

Como a ALICE corresponde aos padrões

Os padrões são ordenados por especificidade: os com menos wildcards são tentados primeiro. Os wildcards * e _ capturam qualquer sequência de palavras. O motor compila cada padrão numa regex, depois itera as categorias ordenadas até encontrar uma correspondência.

// A nossa implementação TypeScript -- simplificada mas fiel
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Porque a ALICE dominou os Loebner

99 524 categorias é um número que muda tudo. A ELIZA parecia inteligente porque as suas poucas regras estavam bem concebidas para um contexto específico (a terapia). A ALICE cobre tantos temas que dá a impressão de ter uma verdadeira cultura geral: ciências, política, humor, desporto, emoções, está tudo lá.

➡ Ver código fonte


Jabberwacky (1997) & Cleverbot (2008): a rutura epistemológica

Todos os bots anteriores partilham um pressuposto: é preciso escrever as respostas. A ELIZA tem as suas regras S-expression, o PARRY os seus padrões seletivos, a ALICE as suas categorias AIML. Rollo Carpenter tomou a posição completamente oposta: e se não escrevêssemos nada?

A ideia

O Jabberwacky (lançado por volta de 1997, tornado Cleverbot em 2008) não armazena nenhuma regra. Armazena todo o histórico de conversas num transcript simples, e quando alguém lhe fala, procura nesse histórico o momento mais semelhante e reutiliza o que foi dito a seguir:

Utilizador: "hello"
  ↓
Procurar: alguém já disse "hello" antes?
  ↓
Sim, na sessão #3, linha 14, alguém disse "hello" e o bot respondeu "hi there!"
  ↓
Responder: "hi there!"

Sem padrão. Sem gramática. Sem XML. Apenas um arquivo gigante de coisas que pessoas disseram umas às outras, reutilizado no momento certo. É a própria definição de emergência.

A implementação TypeScript

O port TypeScript reproduz esta arquitetura exata:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 linhas seed + histórico"]
    B --> C["withReplies()<br>extrai pares<br>(linha → resposta)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>contexto antes desta linha)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidatos ordenados"]
    I --> J{"pickReply()<br>roleta<br>selection"}
    J -->|"Escolhido"| K["Resposta = reply.text<br>do par vencedor"]
    J -->|"Nenhum"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append ao transcript<br>save() → JSON"]
    L --> M

Aqui está o núcleo da pontuação -- a nossa própria heurística inspirada em descrições públicas do Cleverbot:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65): semelhança entre o input do utilizador e a linha histórica
  • contextFit (0.25): semelhança entre a conversa recente e o contexto antes da linha histórica
  • recencyBonus (0.10): as memórias recentes contam um pouco mais (a personalidade do bot deriva com o tempo)

A seleção é probabilística (seleção por roleta): o melhor candidato ganha mais vezes, mas nem sempre -- o que dá variedade.

Cleverbot: as duas inovações documentadas

O Cleverbot acrescenta dois mecanismos ao conceito base do Jabberwacky:

  1. Aprendizagem multi-pessoa: milhões de utilizadores contribuem para o mesmo transcript partilhado. Uma resposta retirada do histórico pode vir de uma voz completamente diferente da da conversa atual -- o que explica porque é que o Cleverbot muda subitamente de personalidade.

  2. Aprendizagem diferida: o que dizes ao Cleverbot durante uma sessão NÃO está disponível para correspondência durante essa mesma sessão. As novas linhas são marcadas como pending e só se tornam correspondíveis após uma "consolidação" entre sessões -- o que explica porque não podes ensinar um facto ao Cleverbot e reutilizá-lo na mesma conversa.

// Cleverbot: as linhas recentes são invisíveis até à consolidação
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() é chamada no arranque, não durante a sessão

O port TypeScript implementa ambos os comportamentos: as linhas têm um flag consolidated, e cada sessão REPL começa por consolidar as linhas pendentes.

➡ Ver código fonte


Análise do port TypeScript: conceber uma arquitetura comum

Construir estes cinco bots na mesma linguagem confronta-nos com uma questão interessante: é possível fatorizar código entre arquiteturas tão diferentes?

A resposta é: muito pouco. Cada bot tem um ciclo fundamental diferente:

Bot Ciclo principal Dados Aprendizagem
ELIZA Keyword stack → decomposição → remontagem Scripts .ela em S-expressions Nenhuma
PARRY Tokenização → padrões seletivos / flares / keywords / inferências 58 ficheiros PDP-10 (dicionários, crenças, regras) Nenhuma
ALICE Padrões ordenados → regex → template AIML → SRAI recursivo 66 ficheiros AIML XML Nenhuma
Jabberwacky Similaridade → contexto → recência → seleção ponderada Transcript JSON (cresce com o uso) Contínua
Cleverbot Igual ao Jabberwacky + pending/consolidated + personas Transcript JSON + sementes multi-persona Diferida (entre sessões)

O que partilham é a interface CLI e a infraestrutura TypeScript (biome para lint, tsx para execução). O resto é específico de cada arquitetura.

Escolhas de conceção comuns

1. Fidelidade aos dados originais. Para a ELIZA, o PARRY e a ALICE, usamos os ficheiros originais -- scripts ELIZA recuperados dos arquivos Weizenbaum em 2021, código original PARRY do PDP-10 (58 ficheiros), AIML Free ALICE v1.6. Sem tradução, sem reescrita. Os bots comportam-se como os originais porque usam os mesmos dados.

2. Clean-room para as partes proprietárias. O Jabberwacky e o Cleverbot são diferentes: o seu código fonte nunca foi publicado (a Existor/Rollo Carpenter mantiveram-no proprietário). Os ports são portanto clean-room reimplementations -- construídas apenas a partir de descrições públicas do comportamento. Nenhuma linha de código ou dado proprietário é copiada.

3. Dependências mínimas. O único verdadeiro pré-requisito é TypeScript. A ALICE usa dom-js para analisar o XML dos ficheiros AIML (66 ficheiros, 99 524 categorias, analisar XML manualmente seria uma perda de tempo). Todo o resto é TypeScript vanilla.


Dos chatbots simbólicos aos LLM: o salto conceptual

Os cinco bots que acabámos de ver partilham todos uma característica fundamental: são simbólicos. O seu "conhecimento" é armazenado como símbolos explícitos -- padrões de texto, tabelas de regras, categorias XML, linhas de transcript. Não há nenhuma representação numérica da linguagem em nenhum destes sistemas.

O que também significa que todos têm o mesmo teto de vidro: só podem responder ao que foi explicitamente previsto ou registado. A ELIZA perde-se se saíres do contexto terapêutico. O PARRY não pode falar do tempo. A ALICE não aprende nada das suas conversas. O Jabberwacky só pode responder com réplicas já pronunciadas.

Os LLM (Large Language Models) ultrapassam este teto mudando radicalmente de paradigma: em vez de manipular símbolos, convertem a linguagem em números e aprendem relações estatísticas entre esses números. Não armazenam respostas pré-escritas -- geram cada token em tempo real calculando probabilidades. Vejamos rapidamente como funciona.

1. Tokenização

O primeiro passo é dividir o texto em tokens -- unidades mais pequenas que palavras mas maiores que caracteres:

"Não compreendo"
  → ["Não", " com", "pre", "endo"]

Cada token tem um ID numérico num vocabulário (tipicamente 32 000 a 128 000 tokens para modelos recentes). Esta fragmentação permite ao modelo lidar com palavras que nunca viu, decompondo-as em subpalavras conhecidas.

2. Embeddings

Cada ID de token é convertido num vetor -- um array de números de vírgula flutuante (tipicamente 4096 dimensões para um modelo de tamanho médio). Este vetor é um embedding que codifica o significado do token num espaço matemático onde tokens semanticamente próximos têm vetores próximos:

vetor("rei") − vetor("homem") + vetor("mulher") ≈ vetor("rainha")

Esta propriedade emerge do treino -- ninguém a programou explicitamente. É uma consequência da forma como as palavras são usadas em contextos semelhantes.

3. Attention

O mecanismo de attention (introduzido pelo artigo "Attention is All You Need" em 2017) é o que tornou os LLM possíveis. Para cada token, a attention calcula que outros tokens na frase são importantes para o compreender:

"O banco recusou o meu empréstimo."
     ↑
Token "banco" olha para: "recusou", "empréstimo" → compreende que é uma instituição financeira

"Vou sentar-me no banco do jardim."
     ↑
Token "banco" olha para: "sentar", "jardim" → compreende que é um assento

A attention permite ao modelo capturar o contexto -- cada token é compreendido com base nos que o rodeiam, não isoladamente.

4. Predição do próximo token

O treino de um LLM é enganadoramente simples: mostra-se-lhe texto, esconde-se o último token, e pede-se-lhe que o prediga. Depois repete-se milhares de milhões de vezes.

Input:  "Não compr"
Escondido: "eendo"
Predição do modelo: "eendo" (probabilidade 0,87), "reendo" (0,05)...

O objetivo é maximizar a probabilidade do token real em cada posição. Isto chama-se next-token prediction. Durante o treino, o modelo ajusta os seus milhares de milhões de parâmetros para minimizar o erro de predição em terabytes de texto.

Durante a inferência (quando lhe falamos), o modelo gera um token de cada vez num ciclo:

Token 1: "Sou"     (input: "Fala-me de ti.")
Token 2: "um"      (input: "Fala-me de ti. Sou")
Token 3: "chatbot" (input: "Fala-me de ti. Sou um")
...

Cada token é amostrado de acordo com a sua probabilidade (temperatura, top-k, top-p controlam o grau de "criatividade"). E é tudo. Milhares de milhões de parâmetros a fazer isto milhares de vezes.

O que muda fundamentalmente

Aspeto Bots simbólicos (ELIZA, PARRY, ALICE) LLM modernos
Representação Palavras e regras explícitas Vetores numéricos (embeddings)
Geração Seleção em respostas pré-escritas Predição probabilística token a token
Conhecimento Armazenado em ficheiros de regras Codificado nos pesos da rede
Aprendizagem Manual (redação de regras) Automática (treino em corpus)
Robustez Nula fora dos padrões previstos Generaliza a inputs nunca vistos
Interpretabilidade Perfeita (podem ler-se as regras) Limitada (caixa negra)

Os chatbots clássicos são transparentes mas frágeis. Um LLM é robusto mas opaco. Ambas as abordagens existem ainda hoje -- não como concorrentes, mas como ferramentas para necessidades diferentes.

Se quiser aprofundar o funcionamento interno dos LLMs, este vídeo é um excelente recurso:

Se quiser aprofundar o funcionamento interno dos LLMs, este vídeo é um excelente recurso:

How LLMs Work — YouTube

Luna Protocol: a síntese moderna

Os artigos sobre Luna Protocol (cujos links estão abaixo) representam a síntese mais conseguida de tudo o que acabámos de ver: um bot Discord moderno que combina um LLM local com um sistema comportamental sofisticado, tudo construído sobre as lições de 60 anos de IA conversacional.

Luna Protocol: criei um bot Discord autónomo que simula um ser humano

Este artigo detalha a arquitetura completa de um bot Discord baseado em LLM:

  • Sistema de acionamento prioritário (menção > MD > nome > palavra-chave > follow-up > aleatório)
  • Comportamentos humanos: concentração variável, erros de digitação, hesitações (15%), esquecimentos (3%), fadiga temática
  • Horários de sono: o bot dorme, abranda ou ignora consoante a hora
  • Pipeline TTS: síntese de voz via Piper + ffmpeg → mensagens de voz Discord
  • Streaming em tempo real: o LLM emite os tokens um a um num bus de eventos tipado

O que liga este artigo aos chatbots históricos é a mesma busca: fazer crer que se fala com uma pessoa. A ELIZA fazia-o com espelhos textuais. O PARRY com um modelo emocional. A ALICE com 99k categorias. O Luna Protocol fá-lo com um LLM fine-tunado + um sistema comportamental que simula as imperfeições humanas.

Luna Protocol: porque é que fiz fine-tuning de um modelo de 1,5B

O segundo artigo explora o fine-tuning e o few-shot priming. A descoberta central: um modelo mais pequeno (1,5B) treinado em menos dados (50k amostras) supera um modelo maior (3B) quando é devidamente preparado com exemplos few-shot.

É uma lição que ressoa diretamente com os chatbots históricos:

  • A ELIZA mostrava que com poucas regras bem concebidas, se pode simular compreensão
  • A ALICE mostrava que com 99k categorias, se pode simular cultura geral
  • O Luna Protocol mostra que com um bom fine-tuning e 5 exemplos few-shot, um LLM pequeno pode simular um ser humano

A técnica é diferente, mas o princípio é o mesmo: a qualidade dos dados e a precisão do sistema importam mais do que o tamanho bruto.


Conclusão: três coisas para reter

1. A IA conversacional não começou com o ChatGPT. A ELIZA tem 60 anos. O PARRY passou o teste de Turing em 1972. A ALICE ganhou o Loebner três vezes. O Jabberwacky lançou as bases da aprendizagem por transcript, que o Cleverbot industrializou em grande escala. Cada abordagem trouxe uma peça do puzzle.

2. Mais dados ≠ mais inteligente. O transcript do Jabberwacky não tem regras. As 99k categorias da ALICE não aprendem. O fine-tuning do Luna Protocol em 50k amostras supera o modelo 3B. A sabedoria convencional diz "quanto maior, melhor" -- a história dos chatbots mostra que a arquitetura e o design contam tanto como o tamanho.

3. O problema é o mesmo há 60 anos. Como fazer um humano acreditar que está a falar com outro humano? A ELIZA respondia com espelhos textuais. O PARRY com raiva simulada. A ALICE com factos. O Luna Protocol com um LLM que dorme e comete erros de digitação. A solução muda, a necessidade permanece.

O repo é open source -- podes cloná-lo, executar cada bot, e ver por ti mesmo como 60 anos de IA conversacional cabem num único repositório TypeScript.

Recurso Link
Repositório GitHub fox3000foxy/chatbots
Luna Protocol -- arquitetura do bot Ler o artigo
Luna Protocol -- few-shot fine-tuning Ler o artigo
Scripts ELIZA originais anthay/ELIZA
Código fonte PARRY original lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 original PARRY Encounters the DOCTOR
Excelente explicação de como LLMs funcionam https://www.youtube.com/watch?v=YmLp8qe87A0

Dari ELIZA ke LLM : 60 Tahun AI Percakapan, Dibangun Ulang dalam TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- lima arsitektur yang secara fundamental berbeda untuk masalah yang sama, diporting ke TypeScript dengan data asli mereka. Dari 1966 hingga LLM modern, beginilah cara AI percakapan belajar berbicara, dan apa yang diajarkan sebuah repo chatbot kepada kita tentang 60 tahun riset.

Dari ELIZA ke LLM : 60 Tahun AI Percakapan, Dibangun Ulang dalam TypeScript

Pada tahun 1966, Joseph Weizenbaum menulis 420 baris MAD-SLIP di atas IBM 7094 untuk menciptakan chatbot pertama dalam sejarah. Program itu bernama ELIZA, dan ia mensimulasikan psikoterapis Rogerian dengan pola dasar dan permutasi kalimat. Enam dekade kemudian, AI percakapan telah menjadi topik arus utama -- ChatGPT, Claude, Gemini ada di setiap perbincangan.

Namun di antara dua ekstrem ini, ada PARRY (chatbot paranoid, 1972), ALICE (raja AIML dengan 99.000 kategori, 1995), Jabberwacky (yang pertama belajar tanpa aturan, 1997), dan Cleverbot (penerus industrinya, 2008). Lima program, lima arsitektur, satu masalah : membuat mesin berbicara.

Repo ini berisi kelima bot tersebut, diporting ke TypeScript dengan data asli mereka -- skrip ELIZA, kamus PARRY, file AIML ALICE. Setiap port berdiri sendiri, siap pakai, dan didokumentasikan dengan detail. Tujuannya bukan sekadar menjalankannya : tetapi memahami cara kerjanya, mengapa mereka menandai sejarah, dan apa yang diajarkan arsitektur mereka tentang AI kemarin... dan hari ini.

bun run eliza    # Bicara dengan ELIZA (1966)
bun run parry    # Bicara dengan PARRY (1972)
bun run alice    # Bicara dengan ALICE (1995)
bun run jabber   # Bicara dengan Jabberwacky
bun run cleverbot # Bicara dengan Cleverbot
bun run meeting  # ELIZA vs PARRY otomatis

Kita akan membedah setiap bot, melihat kodenya, lalu menghubungkannya dengan LLM modern melalui artikel tentang Luna Protocol.


ELIZA (1966) : Seni Membuat Orang Percaya Bahwa Kamu Mengerti

Mari mulai dari yang tertua, dan mungkin yang paling mengesankan dalam kesederhanaannya. ELIZA tidak memiliki kecerdasan dalam arti modern. Tidak ada jaringan saraf, tidak ada statistik, tidak ada pembelajaran. Hanya pola teks dan sedikit permutasi.

Prinsipnya

Skrip DOCTOR (versi psikoterapis) bekerja dengan sebuah tabel kata kunci, masing-masing terkait dengan pola dekomposisi dan aturan perakitan kembali. Berikut aturan tipikal :

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO adalah kata kuncinya. 0 adalah pola dekomposisi yang mengatakan "tangkap semua yang mengikuti" (seperti wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. adalah aturan perakitan kembali. Itu saja.

Saat kamu bilang "Hello, I'm sad today", ELIZA :

  1. Mengubah teks menjadi huruf kapital : HELLO I'M SAD TODAY
  2. Memindai setiap kata terhadap tabel kata kunci
  3. Menemukan HELLO → mendorongnya ke tumpukan kata kunci
  4. Mengambil kata kunci dengan prioritas tertinggi
  5. Mencoba setiap pola dekomposisi secara berurutan
  6. Jika cocok, memilih aturan perakitan berikutnya (round-robin)
  7. Mengganti (1), (2) dll. dengan bagian yang ditangkap

Tapi bagian yang benar-benar cerdas adalah aturan PRE. Lihat ini :

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Saat ELIZA mencocokkan MY, ia mengubah sisa kalimat (ditangkap oleh 0) melalui aturan PRE, dan menyuntikkan hasilnya kembali seolah-olah pengguna baru saja mengatakan kata kunci baru. Secara konkret :

Kamu bilang : "My mother hates me"
  → PRE mengubah : "YOUR MOTHER HATES YOU"
  → disuntikkan kembali seolah kamu baru mengatakannya
  → kemungkinan cocok dengan "YOU" → respons baru

Inilah mengapa ELIZA terlihat memahami perbedaan antara "aku" dan "kamu" -- itu bukan pemahaman, melainkan transformasi mekanis yang dirancang dengan sempurna.

Berikut alur lengkapnya, dari ketikan pengguna hingga respons :

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalise la ponctuation"]
    B --> C["splitUserInput()<br>découpe en mots"]
    C --> D["Build keyword stack<br>ordonné par priorité"]
    D --> E{"Stack non-vide?"}
    E -->|"Oui"| F["Pop highest-priority keyword"]
    E -->|"Non"| G{"Memory recall?"}
    G -->|"Oui"| H["Recall past user statement"]
    G -->|"Non"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Match found?"}
    L -->|"Non"| M{"Linked keyword?"}
    M -->|"Oui"| N["Push linked keyword to stack"]
    N --> E
    M -->|"Non"| O["Return NOMATCH"]
    O --> J
    L -->|"Oui"| P["Select next reassembly (round-robin)"]
    P --> Q{"Reassembly type?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>into final response"]
    T --> J

Apa yang Membuatnya Masuk Akal

Weizenbaum membuat pilihan brilian : psikoterapi Rogerian. Pendekatan ini adalah merefleksikan ucapan pasien tanpa menafsirkan. "Saya sedih" → "Anda mengatakan Anda sedih." Itulah tepatnya yang ELIZA bisa lakukan -- dan karena ini adalah teknik terapi yang diakui, tidak ada yang menganggapnya aneh.

Dalam Port TypeScript

Port memuat skrip .ela (format S-expression asli), menguraikannya sepenuhnya (termasuk encoding Hollerith -- format string dari tahun 60-an), dan menjalankan siklus yang sama : kapitalisasi → pemisahan → tumpukan kata kunci → dekomposisi → perakitan kembali → PRE/transformasi.

➡ Lihat kode sumber


PARRY (1972) : Chatbot Pertama dengan Emosi

Enam tahun setelah ELIZA, Kenneth Colby (psikiater di Stanford) menciptakan PARRY : chatbot yang mensimulasikan pasien dengan skizofrenia paranoid. Jika ELIZA adalah cermin kosong, PARRY memiliki model emosional internal yang sesungguhnya.

Model Emosional

PARRY memiliki empat variabel kontinu yang berubah setiap putaran percakapan :

Variabel Garis Dasar Peluruhan/Putaran Deskripsi
ANGER 0 −1.0 Permusuhan, iritasi
FEAR 0 −0.2 Paranoia (meluruh lambat setelah delusi dimulai)
MISTRUST 0 −0.05 Kecurigaan (sangat lambat turun)
HURT 0 −0.5 Luka emosional

Nilai-nilai ini meningkat melalui lompatan emosional (ajump, fjump, hjump) yang dipicu oleh aturan inferensi, dan meluruh secara alami kembali ke garis dasarnya setiap putaran.

Jaringan Keyakinan

PARRY memiliki 200+ keyakinan yang disimpan dalam file bel :

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Setiap keyakinan memiliki kategori (HUM = pasien, HUM2 = orang lain, DOC = dokter, INT = interogasi, INN = niat) dan kekuatan (0-5). Aturan inferensi (TH2, EMOTE, IF) menghubungkan keyakinan satu sama lain :

  • TH2 : jika keyakinan A melebihi ambang batas, ia menguat dan konsekuensinya meningkat
  • EMOTE : jika keyakinan melebihi ambang batas, ia memicu lompatan emosional (marah/takut/sakit)
  • IF : kondisional -- jika A benar, maka B menjadi benar pada tingkat tertentu

Hierarki Delusi (Sistem Flare)

Bagian paling menarik dari PARRY adalah sistem "flare" -- rantai eskalasi yang secara progresif mengarah ke delusi pusat :

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Setiap kata kunci memicu respons yang sudah ditulis sebelumnya (melalui pencocokan pola), dan jika lawan bicara mengikuti topik, PARRY secara bertahap hanyut menuju delusi pusat penganiayaannya. Begitu flare "terpicu", ia menjadi tidak aktif (deadFlares) -- PARRY beralih ke yang berikutnya, mensimulasikan lawan bicara yang menggali topik lebih dalam.

Mengapa PARRY Bersejarah

Pada tahun 1972, PARRY lulus varian uji Turing : psikiater diberikan transkrip percakapan dengan PARRY dan dengan pasien paranoid sungguhan, dan tidak dapat membedakannya secara andal. Itu adalah program pertama yang berhasil melakukannya, 40 tahun sebelum LLM.

➡ Lihat kode sumber


RFC 439 : Saat ELIZA Bertemu PARRY

Pada 18 September 1972, dua program berbicara satu sama lain untuk pertama kalinya dalam sejarah -- secara harfiah : ini adalah percakapan antar-AI pertama yang pernah tercatat. ELIZA (berjalan di BBN, Boston) dan PARRY (berjalan di Stanford) bercakap-cakap melalui ARPANET, disambungkan oleh operator manusia yang mengetik keluaran satu ke masukan yang lain. Transkripnya diterbitkan sebagai RFC 439 ("PARRY Encounters the DOCTOR").

Berikut kutipan bagaimana tampilannya :

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

Sangat koheren secara mengejutkan. ELIZA melakukan tugas terapisnya : merumuskan ulang, bertanya, mengeksplorasi. PARRY melakukan tugas pasien paranoidnya : mengeluh, menuduh, mengekspresikan kecurigaan. Kedua program sempurna dalam peran mereka -- bukan karena mereka "memahami" situasi, tetapi karena mekanisme masing-masing (pola ELIZA + model emosional PARRY) menghasilkan respons yang secara kebetulan saling cocok.

Repo dapat mereproduksi percakapan ini dengan :

bun run meeting

Simulasi menjalankan 25 putaran otomatis antara kedua bot, dengan topik awal acak (kuda, kejahatan terorganisir, emosi...). Karena ELIZA dan PARRY sama-sama memiliki elemen non-deterministik (ELIZA round-robin, PARRY randomisasi), setiap eksekusi menghasilkan pertukaran yang berbeda.

Yang mencolok tentang ELIZA vs PARRY adalah kita memiliki dua program -- satu tanpa status internal, yang lain dengan model emosional lengkap -- yang bersama-sama menghasilkan percakapan yang terlihat disengaja. Untuk tahun 1972, itu mencengangkan.


ALICE (1995) : Pencocokan Pola Skala Besar

ALICE (Artificial Linguistic Internet Computer Entity) diciptakan oleh Richard Wallace pada tahun 1995, dan memenangkan Loebner Prize tiga kali (2000, 2001, 2004). Jika ELIZA memiliki beberapa ratus aturan dan PARRY beberapa ribu, ALICE memiliki 99.524 -- tersebar di 66 file AIML.

AIML : Bahasa Kategori

AIML (Artificial Intelligence Markup Language) adalah format XML untuk mendefinisikan pasangan tanya-jawab :

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Tapi kekuatan ALICE berasal dari wildcard dan SRAI (Symbolic Reduction) :

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- setara dengan <srai><star/></srai> -->
  </template>
</category>

SRAI memungkinkan ALICE mengarahkan input ke kategori lain, menciptakan rantai reduksi :

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

Itulah mekanisme yang memberi ALICE fleksibilitasnya : alih-alih menulis respons untuk setiap kemungkinan formulasi, kita menulis satu respons kanonik dan mengarahkan variasi ke sana. Batas kedalaman adalah 10 -- di atas itu, ALICE menyerah untuk menghindari loop tak terbatas (dihindari dengan hati-hati dalam desain kategori, tapi jaring pengaman tetap penting).

Bagaimana ALICE Mencocokkan Pola

Pola diurutkan berdasarkan kekhususan : yang memiliki wildcard paling sedikit dicoba terlebih dahulu. Wildcard * dan _ menangkap urutan kata apa pun. Engine mengompilasi setiap pola menjadi regex, lalu mengulangi kategori yang telah diurutkan hingga menemukan kecocokan.

// Implementasi TypeScript kami -- disederhanakan tapi setia
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Mengapa ALICE Mendominasi Loebner

99.524 kategori, itu angka yang mengubah segalanya. ELIZA terlihat pintar karena beberapa aturannya dirancang dengan baik untuk konteks spesifik (terapi). ALICE mencakup begitu banyak topik sehingga ia memberi kesan memiliki pengetahuan umum yang nyata : sains, politik, humor, olahraga, emosi, semuanya ada.

➡ Lihat kode sumber


Jabberwacky (1997) & Cleverbot (2008) : Pemutusan Epistemologis

Semua bot sebelumnya berbagi asumsi : respons harus ditulis. ELIZA punya aturan S-expression, PARRY punya pola selektif, ALICE punya kategori AIML. Rollo Carpenter mengambil pendekatan sebaliknya : bagaimana jika kita tidak menulis apa pun?

Idenya

Jabberwacky (diluncurkan sekitar 1997, menjadi Cleverbot pada 2008) tidak menyimpan aturan apa pun. Ia menyimpan seluruh riwayat percakapan dalam transkrip datar, dan ketika seseorang berbicara dengannya, ia mencari di riwayat ini momen yang paling mirip dan menggunakan kembali apa yang dikatakan setelahnya :

Pengguna : "hello"
  ↓
Cari : apakah ada yang pernah mengatakan "hello" sebelumnya?
  ↓
Ya, di sesi #3, baris 14, seseorang berkata "hello" dan bot menjawab "hi there!"
  ↓
Jawab : "hi there!"

Tanpa pola. Tanpa tata bahasa. Tanpa XML. Hanya arsip raksasa tentang hal-hal yang dikatakan orang satu sama lain, digunakan kembali pada saat yang tepat. Itulah definisi emergence.

Implementasi TypeScript

Port TypeScript mereproduksi arsitektur persis ini :

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 lignes seed + historique"]
    B --> C["withReplies()<br>extrait les paires<br>(ligne → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>context avant cette ligne)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidats triés"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Pick"| K["Reply = reply.text<br>de la paire gagnante"]
    J -->|"Aucun"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append au transcript<br>save() → JSON"]
    L --> M

Inilah jantung penilaian -- heuristik kami sendiri yang terinspirasi dari deskripsi publik Cleverbot :

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65) : kemiripan antara input pengguna dan baris historis
  • contextFit (0.25) : kemiripan antara percakapan terkini dan konteks sebelum baris historis
  • recencyBonus (0.10) : ingatan terbaru sedikit lebih berbobot (kepribadian bot berubah seiring waktu)

Pemilihan bersifat probabilistik (roulette-wheel selection) : kandidat terbaik menang lebih sering, tapi tidak selalu -- yang memberikan variasi.

Cleverbot : Dua Inovasi yang Terdokumentasi

Cleverbot menambahkan dua mekanisme ke konsep dasar Jabberwacky :

  1. Pembelajaran multi-orang : jutaan pengguna berkontribusi ke transkrip bersama yang sama. Respons yang diambil dari riwayat bisa berasal dari suara yang sama sekali berbeda dari percakapan yang sedang berlangsung -- yang menjelaskan mengapa Cleverbot tiba-tiba berubah kepribadian.

  2. Pembelajaran tertunda : apa yang kamu katakan ke Cleverbot selama satu sesi TIDAK tersedia untuk pencocokan selama sesi yang sama. Baris baru ditandai pending dan baru bisa dicocokkan setelah "konsolidasi" antar sesi -- yang menjelaskan mengapa kamu tidak bisa mengajari Cleverbot suatu fakta dan menggunakannya kembali dalam percakapan yang sama.

// Cleverbot : baris terbaru tidak terlihat sampai konsolidasi
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() dipanggil saat startup, bukan selama sesi

Port TypeScript mengimplementasikan kedua perilaku ini : baris memiliki flag consolidated, dan setiap sesi REPL dimulai dengan konsolidasi baris yang tertunda.

➡ Lihat kode sumber


Analisis Port TypeScript : Merancang Arsitektur Bersama

Membangun kelima bot dalam bahasa yang sama adalah menghadapi pertanyaan menarik : dapatkah kita memfaktorkan ulang kode antar arsitektur yang berbeda seperti ini?

Jawabannya : sangat sedikit. Setiap bot memiliki loop fundamental yang berbeda :

Bot Loop Utama Data Pembelajaran
ELIZA Tumpukan kata kunci → dekomposisi → perakitan kembali Skrip .ela dalam S-expression Tidak ada
PARRY Tokenisasi → pola selektif / flare / kata kunci / inferensi 58 file PDP-10 (kamus, keyakinan, aturan) Tidak ada
ALICE Pola terurut → regex → template AIML → SRAI rekursif 66 file AIML XML Tidak ada
Jabberwacky Kemiripan → konteks → kedekatan → pilihan berbobot Transkrip JSON (bertambah seiring penggunaan) Berkelanjutan
Cleverbot Sama seperti Jabberwacky + pending/consolidated + persona Transkrip JSON + seed multi-persona Tertunda (antar sesi)

Yang mereka bagi adalah antarmuka CLI dan infrastruktur TypeScript (biome untuk lint, tsx untuk eksekusi). Sisanya spesifik untuk setiap arsitektur.

Pilihan Desain Bersama

1. Kesetiaan pada data asli. Untuk ELIZA, PARRY dan ALICE, kami menggunakan file asli -- skrip ELIZA yang ditemukan di arsip Weizenbaum tahun 2021, kode PARRY asli dari PDP-10 (58 file), AIML Free ALICE v1.6. Tidak ada terjemahan, tidak ada penulisan ulang. Bot berperilaku seperti aslinya karena mereka menggunakan data yang sama.

2. Clean-room untuk bagian kepemilikan. Jabberwacky dan Cleverbot berbeda : kode sumber mereka tidak pernah dipublikasikan (Existor/Rollo Carpenter menjaganya tetap proprietary). Oleh karena itu port adalah clean-room reimplementation -- dibangun hanya dari deskripsi publik tentang perilaku. Tidak ada baris kode atau data proprietary yang disalin.

3. Ketergantungan minimal. Satu-satunya prasyarat nyata adalah TypeScript. ALICE menggunakan dom-js untuk mengurai XML dari file AIML (66 file, 99.524 kategori, mengurai XML sendiri akan membuang-buang waktu). Sisanya adalah TypeScript vanilla.


Dari Chatbot Simbolis ke LLM : Lompatan Konseptual

Kelima bot yang baru kita lihat semuanya berbagi karakteristik fundamental : mereka simbolis. "Pengetahuan" mereka disimpan sebagai simbol eksplisit -- pola teks, tabel aturan, kategori XML, baris transkrip. Tidak ada representasi numerik bahasa di salah satu sistem ini.

Yang berarti mereka semua memiliki batasan yang sama : mereka hanya bisa merespons apa yang telah direncanakan atau dicatat secara eksplisit. ELIZA tersesat jika kamu keluar dari kerangka terapi. PARRY tidak bisa bicara tentang cuaca. ALICE tidak belajar apa pun dari percakapannya. Jabberwacky hanya bisa merespons dengan replika yang sudah pernah diucapkan.

LLM (Large Language Models) melampaui batasan ini dengan mengubah paradigma secara radikal : alih-alih memanipulasi simbol, mereka mengubah bahasa menjadi angka dan mempelajari hubungan statistik antara angka-angka ini. Mereka tidak menyimpan respons yang sudah ditulis sebelumnya -- mereka menghasilkan setiap token saat itu juga dengan menghitung probabilitas. Mari kita lihat sekilas cara kerjanya.

1. Tokenisasi

Langkah pertama adalah memotong teks menjadi token -- unit yang lebih kecil dari kata tetapi lebih besar dari karakter :

"Je ne comprends pas"
  → ["Je", " ne", " comprend", "s", " pas"]

Setiap token memiliki ID numerik dalam kosakata (biasanya 32.000 hingga 128.000 token untuk model terbaru). Fragmentasi ini memungkinkan model menangani kata-kata yang belum pernah dilihat dengan memecahnya menjadi sub-kata yang dikenal.

2. Embedding

Setiap ID token diubah menjadi vektor -- sebuah array angka floating-point (biasanya 4096 dimensi untuk model ukuran sedang). Vektor ini adalah embedding yang mengkodekan makna token dalam ruang matematis di mana token yang mirip secara semantik memiliki vektor yang berdekatan :

vecteur("roi") − vecteur("homme") + vecteur("femme") ≈ vecteur("reine")

Sifat ini muncul dari pelatihan -- tidak ada yang memprogramnya secara eksplisit. Ini adalah konsekuensi dari cara kata-kata digunakan dalam konteks yang serupa.

3. Attention

Mekanisme attention (diperkenalkan oleh makalah "Attention is All You Need" tahun 2017) adalah yang membuat LLM menjadi mungkin. Untuk setiap token, attention menghitung token lain mana dalam kalimat yang penting untuk memahami token ini :

"La banque a refusé mon prêt."
     ↑
Token "banque" melihat : "refusé", "prêt" → paham bahwa ini adalah institusi keuangan

"Je vais me promener sur la banque."
     ↑
Token "banque" melihat : "promener", "sur" → paham bahwa ini adalah tepi sungai

Attention memungkinkan model menangkap konteks -- setiap token dipahami berdasarkan apa yang ada di sekitarnya, tidak secara terisolasi.

4. Prediksi Token Berikutnya

Pelatihan LLM memiliki kesederhanaan yang menipu : kita tunjukkan sebuah teks, sembunyikan token terakhir, dan minta ia memprediksinya. Lalu ulangi miliaran kali.

Input: "Je ne comprends"
Sembunyi: "pas"
Prediksi model : "pas" (probabilitas 0.87), "rien" (0.05), "jamais" (0.02)...

Tujuannya adalah memaksimalkan probabilitas token yang benar di setiap posisi. Ini disebut next-token prediction. Selama pelatihan, model menyesuaikan miliaran parameternya untuk meminimalkan kesalahan prediksi pada terabyte teks.

Saat inferensi (ketika kita berbicara dengannya), model menghasilkan satu token pada satu waktu dalam satu loop :

Token 1: "Je"    (input: "Parle-moi de toi.")
Token 2: "suis"  (input: "Parle-moi de toi. Je")
Token 3: "un"    (input: "Parle-moi de toi. Je suis")
Token 4: "chatbot" (input: "Parle-moi de toi. Je suis un")
...

Setiap token diambil sampelnya berdasarkan probabilitasnya (temperature, top-k, top-p mengontrol tingkat "kreativitas"). Dan hanya itu. Miliaran parameter melakukan ini ribuan kali.

Apa yang Berubah Secara Fundamental

Aspek Bot Simbolis (ELIZA, PARRY, ALICE) LLM Modern
Representasi Kata dan aturan eksplisit Vektor numerik (embedding)
Generasi Seleksi dari respons yang sudah ditulis Prediksi probabilistik token per token
Pengetahuan Disimpan dalam file aturan Dienkode dalam bobot jaringan
Pembelajaran Manual (menulis aturan) Otomatis (pelatihan pada korpus)
Ketahanan Nol di luar pola yang direncanakan Generalisasi ke input yang belum pernah dilihat
Interpretabilitas Sempurna (aturan dapat dibaca) Terbatas (kotak hitam)

Chatbot klasik transparan tapi rapuh. LLM kuat tapi buram. Kedua pendekatan masih ada hingga hari ini -- bukan sebagai pesaing, tetapi sebagai alat untuk kebutuhan yang berbeda.

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

Jika Anda ingin mendalami cara kerja internal LLM, video ini adalah sumber yang sangat baik:

How LLMs Work — YouTube

Luna Protocol : Sintesis Modern

Artikel tentang Luna Protocol (tautan di bawah) mewakili sintesis paling matang dari semua yang baru saja kita lihat : sebuah bot Discord modern yang menggabungkan LLM lokal dengan sistem perilaku yang canggih, dibangun di atas pelajaran dari 60 tahun AI percakapan.

Luna Protocol : Saya membuat bot Discord otonom yang mensimulasikan manusia

Artikel ini merinci arsitektur lengkap bot Discord berbasis LLM :

  • Sistem pemicu prioritas (mention > DM > nama > kata kunci > follow-up > acak)
  • Perilaku manusia : konsentrasi bervariasi, salah ketik, keraguan (15%), kelupaan (3%), kelelahan tematik
  • Jadwal tidur : bot tidur, melambat, atau mengabaikan tergantung waktu
  • Pipeline TTS : sintesis suara melalui Piper + ffmpeg → pesan suara Discord
  • Streaming real-time : LLM memancarkan token satu per satu pada bus peristiwa bertipe

Yang menghubungkan artikel ini dengan chatbot historis adalah pencarian yang sama : membuat orang percaya mereka berbicara dengan manusia. ELIZA melakukannya dengan cermin teks. PARRY dengan model emosional. ALICE dengan 99k kategori. Luna Protocol melakukannya dengan LLM yang di-fine-tune + sistem perilaku yang mensimulasikan ketidaksempurnaan manusia.

Luna Protocol : mengapa saya melakukan fine-tune model 1,5B

Artikel kedua mengeksplorasi fine-tuning dan few-shot priming. Temuan utamanya : model yang lebih kecil (1,5B) yang dilatih pada lebih sedikit data (50k sampel) mengungguli model yang lebih besar (3B) ketika dipersiapkan dengan benar menggunakan contoh few-shot.

Ini adalah pelajaran yang beresonansi langsung dengan chatbot historis :

  • ELIZA menunjukkan bahwa dengan beberapa aturan yang dirancang dengan baik, kita bisa mensimulasikan pemahaman
  • ALICE menunjukkan bahwa dengan 99k kategori, kita bisa mensimulasikan pengetahuan umum
  • Luna Protocol menunjukkan bahwa dengan fine-tuning yang baik dan 5 contoh few-shot, LLM kecil bisa mensimulasikan manusia

Tekniknya berbeda, tapi prinsipnya sama : kualitas data dan presisi sistem lebih penting daripada ukuran mentah.


Kesimpulan : Tiga Hal yang Perlu Diingat

1. AI percakapan tidak dimulai dengan ChatGPT. ELIZA berusia 60 tahun. PARRY lulus uji Turing pada tahun 1972. ALICE memenangkan Loebner tiga kali. Jabberwacky meletakkan dasar pembelajaran berbasis transkrip, yang diindustrialisasikan Cleverbot dalam skala besar. Setiap pendekatan membawa satu bagian dari teka-teki.

2. Lebih banyak data ≠ lebih pintar. Transkrip Jabberwacky tidak memiliki aturan. 99k kategori ALICE tidak belajar. Fine-tuning Luna Protocol pada 50k sampel mengungguli model 3B. Kebijaksanaan konvensional mengatakan "semakin besar semakin baik" -- sejarah chatbot menunjukkan bahwa arsitektur dan desain sama pentingnya dengan ukuran.

3. Masalahnya tetap sama selama 60 tahun. Bagaimana membuat manusia percaya bahwa ia sedang berbicara dengan manusia lain? ELIZA menjawab dengan cermin teks. PARRY dengan kemarahan yang disimulasikan. ALICE dengan fakta. Luna Protocol dengan LLM yang tidur dan membuat salah ketik. Solusi berubah, kebutuhannya tetap sama.

Repo ini bersifat open source -- kamu bisa mengkloning, menjalankan setiap bot, dan melihat sendiri bagaimana 60 tahun AI percakapan muat dalam satu repo TypeScript.

Sumber Daya Tautan
GitHub Repo fox3000foxy/chatbots
Luna Protocol -- arsitektur bot Baca artikel
Luna Protocol -- fine-tuning few-shot Baca artikel
Skrip ELIZA asli anthay/ELIZA
Kode sumber PARRY asli lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 asli PARRY Encounters the DOCTOR
Penjelasan luar biasa tentang cara kerja LLM https://www.youtube.com/watch?v=YmLp8qe87A0

ELIZA से LLM तक : 60 साल का संवादी AI, TypeScript में पुनर्निर्मित

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- एक ही समस्या के पाँच मौलिक रूप से भिन्न आर्किटेक्चर, जिन्हें उनके मूल डेटा के साथ TypeScript में पोर्ट किया गया। 1966 से आधुनिक LLM तक, यहाँ बताया गया है कि संवादी AI ने कैसे बोलना सीखा, और एक चैटबॉट रिपॉजिटरी हमें 60 साल के शोध के बारे में क्या सिखाती है।

ELIZA से LLM तक : 60 साल का संवादी AI, TypeScript में पुनर्निर्मित

1966 में, जोसेफ वाइज़नबॉम ने IBM 7094 पर MAD-SLIP की 420 पंक्तियाँ लिखकर इतिहास का पहला चैटबॉट बनाया। प्रोग्राम का नाम ELIZA था, और यह बुनियादी पैटर्न और वाक्य क्रमपरिवर्तन के साथ एक रोजेरियन मनोचिकित्सक का अनुकरण करता था। छह दशक बाद, संवादी AI एक मुख्यधारा का विषय बन गया है -- ChatGPT, Claude, Gemini हर बातचीत में शामिल हैं।

लेकिन इन दो चरम सीमाओं के बीच, PARRY (पागल चैटबॉट, 1972), ALICE (99,000 श्रेणियों वाला AIML का राजा, 1995), Jabberwacky (बिना नियमों के सीखने वाला पहला बॉट, 1997), और Cleverbot (इसका औद्योगिक उत्तराधिकारी, 2008) आते हैं। पाँच प्रोग्राम, पाँच आर्किटेक्चर, एक ही समस्या : मशीन से बात करवाना।

इस रिपॉजिटरी में ये पाँचों बॉट शामिल हैं, जिन्हें उनके मूल डेटा -- ELIZA स्क्रिप्ट, PARRY डिक्शनरी, ALICE की AIML फ़ाइलों के साथ TypeScript में पोर्ट किया गया है। हर पोर्ट स्वतंत्र, उपयोग के लिए तैयार, और बारीकी से प्रलेखित है। लक्ष्य सिर्फ उन्हें चलाना नहीं है : यह समझना है कि वे कैसे काम करते थे, उन्होंने इतिहास क्यों रचा, और उनके आर्किटेक्चर हमें कल के AI के बारे में क्या सिखाते हैं... और आज के बारे में भी।

bun run eliza    # ELIZA (1966) से बात करें
bun run parry    # PARRY (1972) से बात करें
bun run alice    # ALICE (1995) से बात करें
bun run jabber   # Jabberwacky से बात करें
bun run cleverbot # Cleverbot से बात करें
bun run meeting  # ELIZA vs PARRY ऑटोमेटिक

हम हर बॉट को खंगालेंगे, उनका कोड देखेंगे, और फिर Luna Protocol पर लेखों के ज़रिए आधुनिक LLM से जोड़ेंगे।


ELIZA (1966) : समझने का दिखावा करने की कला

सबसे पुराने से शुरू करते हैं, और शायद अपनी सादगी में सबसे प्रभावशाली। ELIZA में आधुनिक अर्थों में कोई बुद्धिमत्ता नहीं है। कोई न्यूरल नेटवर्क नहीं, कोई सांख्यिकी नहीं, कोई सीखना नहीं। बस टेक्स्ट पैटर्न और थोड़ा क्रमपरिवर्तन।

सिद्धांत

DOCTOR स्क्रिप्ट (मनोचिकित्सक संस्करण) कीवर्ड की एक तालिका के साथ काम करती है, प्रत्येक डीकम्पोज़िशन पैटर्न और रीअसेंबली नियमों से जुड़ा होता है। यहाँ एक सामान्य नियम है :

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO कीवर्ड है। 0 एक डीकम्पोज़िशन पैटर्न है जो कहता है "इसके बाद आने वाली हर चीज़ को कैप्चर करो" (वाइल्डकार्ड की तरह)। HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. रीअसेंबली नियम है। बस इतना ही।

जब तुम कहते हो "Hello, I'm sad today", ELIZA :

  1. टेक्स्ट को अपरकेस में बदलता है : HELLO I'M SAD TODAY
  2. हर शब्द को अपनी कीवर्ड तालिका के विरुद्ध स्कैन करता है
  3. HELLO ढूँढता है → इसे कीवर्ड स्टैक पर धकेलता है
  4. सबसे अधिक प्राथमिकता वाला कीवर्ड लेता है
  5. हर डीकम्पोज़िशन पैटर्न को क्रम में आज़माता है
  6. अगर मैच होता है, तो अगला रीअसेंबली नियम चुनता है (राउंड-रॉबिन)
  7. (1), (2) आदि को कैप्चर किए गए भागों से बदलता है

लेकिन असली चालाकी PRE नियमों में है। यह देखो :

(MY
    ((0)
        (PRE (1 0) (=YOU))))

जब ELIZA MY से मैच करता है, तो वह PRE नियम के ज़रिए वाक्य के बाकी हिस्से (0 द्वारा कैप्चर किए गए) को बदलता है, और परिणाम को इस तरह पुनः इंजेक्ट करता है जैसे उपयोगकर्ता ने अभी-अभी एक नया कीवर्ड कहा हो। व्यावहारिक रूप से :

तुम कहते हो : "My mother hates me"
  → PRE बदलता है : "YOUR MOTHER HATES YOU"
  → फिर से इंजेक्ट होता है जैसे तुमने अभी यह कहा
  → संभवतः "YOU" से मैच → नया जवाब

यही कारण है कि ELIZA को "मैं" और "तुम" के बीच का अंतर समझने का आभास होता है -- यह समझ नहीं है, यह एक पूरी तरह से डिज़ाइन की गई यांत्रिक रूपांतरण है।

यहाँ पूरा फ़्लो है, उपयोगकर्ता के टाइप करने से लेकर जवाब तक :

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalise la ponctuation"]
    B --> C["splitUserInput()<br>découpe en mots"]
    C --> D["Build keyword stack<br>ordonné par priorité"]
    D --> E{"Stack non-vide?"}
    E -->|"Oui"| F["Pop highest-priority keyword"]
    E -->|"Non"| G{"Memory recall?"}
    G -->|"Oui"| H["Recall past user statement"]
    G -->|"Non"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Match found?"}
    L -->|"Non"| M{"Linked keyword?"}
    M -->|"Oui"| N["Push linked keyword to stack"]
    N --> E
    M -->|"Non"| O["Return NOMATCH"]
    O --> J
    L -->|"Oui"| P["Select next reassembly (round-robin)"]
    P --> Q{"Reassembly type?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>into final response"]
    T --> J

इसे विश्वसनीय क्या बनाता था

वाइज़नबॉम ने एक शानदार विकल्प चुना : रोजेरियन मनोचिकित्सा। यह दृष्टिकोण मरीज़ की बातों को बिना व्याख्या किए प्रतिबिंबित करना है। "मैं उदास हूँ" → "आप कह रहे हैं कि आप उदास हैं।" ELIZA ठीक यही जानता है -- और चूँकि यह एक मान्यता प्राप्त चिकित्सीय तकनीक है, किसी को यह अजीब नहीं लगता।

TypeScript पोर्ट में

पोर्ट .ela स्क्रिप्ट (मूल S-एक्सप्रेशन फ़ॉर्मेट) लोड करता है, उन्हें पूरी तरह पार्स करता है (होलेरिथ एन्कोडिंग सहित -- 1960 के दशक का एक स्ट्रिंग फ़ॉर्मेट), और वही चक्र निष्पादित करता है : अपरकेसिंग → स्प्लिट → कीवर्ड स्टैक → डीकम्पोज़िशन → रीअसेंबली → PRE/ट्रांसफ़ॉर्म।

➡ सोर्स कोड देखें


PARRY (1972) : भावनाओं वाला पहला चैटबॉट

ELIZA के छह साल बाद, केनेथ कोल्बी (स्टैनफोर्ड में मनोचिकित्सक) ने PARRY बनाया : एक चैटबॉट जो पागलपन (पैरानॉइड सिज़ोफ्रेनिया) से पीड़ित रोगी का अनुकरण करता है। जहाँ ELIZA एक खाली दर्पण था, वहीं PARRY के पास एक वास्तविक आंतरिक भावनात्मक मॉडल है।

भावनात्मक मॉडल

PARRY के चार सतत चर हैं जो बातचीत के हर दौर में बदलते हैं :

चर आधार रेखा क्षय/दौर विवरण
ANGER 0 −1.0 शत्रुता, चिड़चिड़ापन
FEAR 0 −0.2 पागलपन (भ्रम शुरू होने के बाद धीरे-धीरे घटता है)
MISTRUST 0 −0.05 अविश्वास (बहुत धीरे-धीरे कम होता है)
HURT 0 −0.5 भावनात्मक पीड़ा

ये मान अनुमान नियमों द्वारा ट्रिगर किए गए भावनात्मक उछाल (ajump, fjump, hjump) के माध्यम से बढ़ते हैं, और हर दौर में स्वाभाविक रूप से अपनी आधार रेखा की ओर घटते हैं।

विश्वास नेटवर्क

PARRY के पास bel फ़ाइल में संग्रहीत 200+ विश्वास हैं :

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

हर विश्वास की एक श्रेणी (HUM = रोगी, HUM2 = अन्य, DOC = डॉक्टर, INT = पूछताछ, INN = इरादे) और एक शक्ति (0-5) होती है। अनुमान नियम (TH2, EMOTE, IF) विश्वासों को एक-दूसरे से जोड़ते हैं :

  • TH2 : यदि कोई विश्वास A एक सीमा पार करता है, तो वह मजबूत होता है और उसके परिणाम बढ़ते हैं
  • EMOTE : यदि कोई विश्वास एक सीमा पार करता है, तो वह भावनात्मक उछाल (anger/fear/hurt) शुरू करता है
  • IF : सशर्त -- यदि A सत्य है, तो B एक निश्चित स्तर पर सत्य हो जाता है

भ्रम पदानुक्रम (फ्लेयर सिस्टम)

PARRY का सबसे आकर्षक हिस्सा इसका "फ्लेयर" सिस्टम है -- एक एस्केलेशन चेन जो धीरे-धीरे केंद्रीय भ्रम की ओर ले जाती है :

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

हर कीवर्ड एक पूर्व-लिखित जवाब शुरू करता है (पैटर्न मैचिंग के माध्यम से), और यदि वार्ताकार विषय पर बना रहता है, तो PARRY धीरे-धीरे अपने केंद्रीय उत्पीड़न भ्रम की ओर बढ़ता है। एक बार फ्लेयर "ट्रिगर" हो जाने पर, वह निष्क्रिय हो जाता है (deadFlares) -- PARRY अगले पर चला जाता है, एक ऐसे वार्ताकार का अनुकरण करते हुए जो विषय को गहराई से समझ रहा है।

PARRY ऐतिहासिक क्यों है

1972 में, PARRY ने ट्यूरिंग टेस्ट का एक रूप पास किया : मनोचिकित्सकों को PARRY और वास्तविक पागल रोगियों के साथ बातचीत के ट्रांसक्रिप्ट दिए गए, और वे उन्हें विश्वसनीय रूप से अलग नहीं कर सके। यह LLM से 40 साल पहले ऐसा करने वाला पहला प्रोग्राम है।

➡ सोर्स कोड देखें


RFC 439 : जब ELIZA की मुलाकात PARRY से हुई

18 सितंबर, 1972 को, दो प्रोग्रामों ने इतिहास में पहली बार एक-दूसरे से बात की -- सचमुच : यह पहली अंतर-AI बातचीत है जो कभी दर्ज की गई। ELIZA (BBN, बोस्टन पर चल रहा) और PARRY (स्टैनफोर्ड में चल रहा) ने ARPANET के माध्यम से बातचीत की, मानव ऑपरेटरों द्वारा रिले किया गया जो एक के आउटपुट को दूसरे के इनपुट में टाइप कर रहे थे। ट्रांसक्रिप्ट को RFC 439 ("PARRY Encounters the DOCTOR") के रूप में प्रकाशित किया गया था।

यहाँ एक अंश है कि यह कैसा दिखता था :

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

यह आश्चर्यजनक रूप से सुसंगत है। ELIZA अपना चिकित्सक का काम कर रहा है : पुनर्लेखन, प्रश्न पूछना, खोज करना। PARRY अपना पागल रोगी का काम कर रहा है : शिकायत करना, आरोप लगाना, अविश्वास व्यक्त करना। दोनों प्रोग्राम पूरी तरह से अपनी भूमिका में हैं -- इसलिए नहीं कि वे स्थिति को "समझते" हैं, बल्कि इसलिए कि उनके संबंधित तंत्र (ELIZA पैटर्न + PARRY भावनात्मक मॉडल) ऐसे जवाब उत्पन्न करते हैं जो संयोग से एक-दूसरे में फिट होते हैं।

रिपॉजिटरी इस बातचीत को पुनः उत्पन्न कर सकती है :

bun run meeting

सिमुलेशन दोनों बॉट्स के बीच 25 स्वचालित दौर चलाता है, एक यादृच्छिक प्रारंभिक विषय के साथ (घोड़े, संगठित अपराध, भावनाएँ...). चूँकि ELIZA और PARRY दोनों में गैर-नियतात्मक तत्व हैं (ELIZA राउंड-रॉबिन, PARRY रैंडमाइज़ेशन), हर निष्पादन एक अलग आदान-प्रदान उत्पन्न करता है।

ELIZA बनाम PARRY के बारे में सबसे चौंकाने वाली बात यह है कि हमारे पास दो प्रोग्राम हैं -- एक बिना किसी आंतरिक स्थिति के, दूसरा पूर्ण भावनात्मक मॉडल के साथ -- जो मिलकर एक ऐसी बातचीत उत्पन्न करते हैं जो जानबूझकर लगती है। 1972 के लिए, यह चौंका देने वाला था।


ALICE (1995) : बड़े पैमाने पर पैटर्न मैचिंग

ALICE (Artificial Linguistic Internet Computer Entity) को रिचर्ड वैलेस ने 1995 में बनाया था, और इसने तीन बार Loebner Prize जीता (2000, 2001, 2004)। जहाँ ELIZA के पास कुछ सौ नियम थे और PARRY के पास कुछ हज़ार, वहीं ALICE के पास 99,524 नियम हैं -- 66 AIML फ़ाइलों में वितरित।

AIML : श्रेणियों की भाषा

AIML (Artificial Intelligence Markup Language) प्रश्न-उत्तर जोड़े परिभाषित करने के लिए एक XML फ़ॉर्मेट है :

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

लेकिन ALICE की शक्ति वाइल्डकार्ड और SRAI (Symbolic Reduction) से आती है :

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- <srai><star/></srai> के बराबर -->
  </template>
</category>

SRAI ALICE को एक इनपुट को दूसरी श्रेणी में रीडायरेक्ट करने की अनुमति देता है, एक कमी श्रृंखला बनाते हुए :

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

यह वह तंत्र है जो ALICE को उसका लचीलापन देता है : हर संभावित अभिव्यक्ति के लिए जवाब लिखने के बजाय, एक विहित जवाब लिखा जाता है और विविधताओं को उसकी ओर रीडायरेक्ट किया जाता है। गहराई सीमा 10 है -- इससे अधिक पर ALICE अनंत लूप से बचने के लिए छोड़ देता है (श्रेणियों के डिज़ाइन में सावधानीपूर्वक टाला गया, लेकिन सुरक्षा जाल आवश्यक है)।

ALICE पैटर्न कैसे मैच करता है

पैटर्न विशिष्टता के अनुसार क्रमबद्ध होते हैं : सबसे कम वाइल्डकार्ड वाले पहले आज़माए जाते हैं। वाइल्डकार्ड * और _ शब्दों के किसी भी अनुक्रम को कैप्चर करते हैं। इंजन हर पैटर्न को regex में कंपाइल करता है, फिर मैच मिलने तक क्रमबद्ध श्रेणियों पर पुनरावृति करता है।

// हमारा TypeScript कार्यान्वयन -- सरल लेकिन सटीक
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

ALICE ने Loebner पर क्यों दबदबा बनाया

99,524 श्रेणियाँ, यह एक संख्या है जो सब कुछ बदल देती है। ELIZA बुद्धिमान लगता था क्योंकि उसके कुछ नियम एक विशिष्ट संदर्भ (चिकित्सा) के लिए अच्छी तरह से डिज़ाइन किए गए थे। ALICE इतने सारे विषयों को कवर करता है कि वह वास्तविक सामान्य ज्ञान का आभास देता है : विज्ञान, राजनीति, हास्य, खेल, भावनाएँ, सब कुछ।

➡ सोर्स कोड देखें


Jabberwacky (1997) और Cleverbot (2008) : ज्ञानमीमांसीय विच्छेद

पिछले सभी बॉट एक परिकल्पना साझा करते हैं : जवाब लिखने होंगे। ELIZA के पास S-एक्सप्रेशन नियम हैं, PARRY के पास चयनात्मक पैटर्न हैं, ALICE के पास AIML श्रेणियाँ हैं। रोलो कारपेंटर ने पूरी तरह से विपरीत रुख अपनाया : क्या होगा अगर हम कुछ भी न लिखें?

विचार

Jabberwacky (लगभग 1997 में लॉन्च, 2008 में Cleverbot बना) कोई नियम संग्रहीत नहीं करता। यह सभी बातचीत का इतिहास एक फ्लैट ट्रांसक्रिप्ट में संग्रहीत करता है, और जब कोई उससे बात करता है, तो वह इस इतिहास में सबसे समान क्षण खोजता है और उसके बाद जो कहा गया था उसे पुनः उपयोग करता है :

उपयोगकर्ता : "hello"
  ↓
खोजें : क्या किसी ने पहले कभी "hello" कहा है?
  ↓
हाँ, सत्र #3, पंक्ति 14 में, किसी ने "hello" कहा था और बॉट ने "hi there!" उत्तर दिया था
  ↓
उत्तर : "hi there!"

कोई पैटर्न नहीं। कोई व्याकरण नहीं। कोई XML नहीं। बस लोगों द्वारा एक-दूसरे से कही गई बातों का एक विशाल संग्रह, सही समय पर पुनः उपयोग किया गया। यह उभराव (एमर्जेंस) की सटीक परिभाषा है।

TypeScript कार्यान्वयन

TypeScript पोर्ट इस सटीक आर्किटेक्चर को पुनः उत्पन्न करता है :

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 lignes seed + historique"]
    B --> C["withReplies()<br>extrait les paires<br>(ligne → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>context avant cette ligne)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidats triés"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Pick"| K["Reply = reply.text<br>de la paire gagnante"]
    J -->|"Aucun"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append au transcript<br>save() → JSON"]
    L --> M

यहाँ स्कोरिंग का मूल है -- Cleverbot के सार्वजनिक विवरणों से प्रेरित हमारा अपना ह्युरिस्टिक :

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65) : उपयोगकर्ता इनपुट और ऐतिहासिक पंक्ति के बीच समानता
  • contextFit (0.25) : हाल की बातचीत और ऐतिहासिक पंक्ति से पहले के संदर्भ के बीच समानता
  • recencyBonus (0.10) : हाल की यादें थोड़ी अधिक मायने रखती हैं (बॉट का व्यक्तित्व समय के साथ बहता है)

चयन संभाव्य है (रूलेट-व्हील सेलेक्शन) : सबसे अच्छा उम्मीदवार अधिक बार जीतता है, लेकिन हमेशा नहीं -- जो विविधता देता है।

Cleverbot : दो प्रलेखित नवाचार

Cleverbot Jabberwacky की मूल अवधारणा में दो तंत्र जोड़ता है :

  1. बहु-व्यक्ति सीखना : लाखों उपयोगकर्ता एक ही साझा ट्रांसक्रिप्ट में योगदान करते हैं। इतिहास से लिया गया जवाब चल रही बातचीत से पूरी तरह से अलग आवाज़ से आ सकता है -- यही बताता है कि Cleverbot अचानक व्यक्तित्व क्यों बदल देता है।

  2. विलंबित सीखना : तुम Cleverbot को एक सत्र के दौरान जो कहते हो, वह उसी सत्र में मैच के लिए उपलब्ध नहीं होता। नई पंक्तियाँ pending चिह्नित होती हैं और सत्रों के बीच "समेकन" के बाद ही मैच करने योग्य बनती हैं -- यही बताता है कि तुम Cleverbot को एक तथ्य नहीं सिखा सकते और उसे उसी बातचीत में पुनः उपयोग नहीं कर सकते।

// Cleverbot : हाल की पंक्तियाँ समेकन तक अदृश्य हैं
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() को स्टार्टअप पर बुलाया जाता है, सत्र के दौरान नहीं

TypeScript पोर्ट इन दोनों व्यवहारों को लागू करता है : पंक्तियों में एक consolidated फ़्लैग है, और हर REPL सत्र लंबित पंक्तियों के समेकन से शुरू होता है।

➡ सोर्स कोड देखें


TypeScript पोर्ट का विश्लेषण : एक सामान्य आर्किटेक्चर डिज़ाइन करना

इन पाँचों बॉट्स को एक ही भाषा में बनाने का मतलब है एक दिलचस्प सवाल का सामना करना : क्या हम इतनी भिन्न आर्किटेक्चर के बीच कोड को फ़ैक्टराइज़ कर सकते हैं?

जवाब है : बहुत कम। हर बॉट का एक मौलिक रूप से भिन्न मुख्य लूप है :

बॉट मुख्य लूप डेटा सीखना
ELIZA कीवर्ड स्टैक → डीकम्पोज़िशन → रीअसेंबली S-एक्सप्रेशन में .ela स्क्रिप्ट कोई नहीं
PARRY टोकनाइज़ेशन → चयनात्मक पैटर्न / फ्लेयर / कीवर्ड / अनुमान 58 PDP-10 फ़ाइलें (डिक्शनरी, विश्वास, नियम) कोई नहीं
ALICE क्रमबद्ध पैटर्न → regex → AIML टेम्पलेट → पुनरावर्ती SRAI 66 AIML XML फ़ाइलें कोई नहीं
Jabberwacky समानता → संदर्भ → हालियापन → भारित चयन JSON ट्रांसक्रिप्ट (उपयोग के साथ बढ़ता है) सतत
Cleverbot Jabberwacky के समान + पेंडिंग/कंसोलिडेटेड + पर्सोना JSON ट्रांसक्रिप्ट + मल्टी-पर्सोना सीड विलंबित (सत्रों के बीच)

वे जो साझा करते हैं, वह है CLI इंटरफ़ेस और TypeScript इंफ्रास्ट्रक्चर (lint के लिए biome, निष्पादन के लिए tsx)। बाकी हर आर्किटेक्चर के लिए विशिष्ट है।

सामान्य डिज़ाइन विकल्प

1. मूल डेटा के प्रति निष्ठा। ELIZA, PARRY और ALICE के लिए, हम मूल फ़ाइलों का उपयोग करते हैं -- 2021 में Weizenbaum संग्रह में मिली ELIZA स्क्रिप्ट, PDP-10 से मूल PARRY कोड (58 फ़ाइलें), AIML Free ALICE v1.6। कोई अनुवाद नहीं, कोई पुनर्लेखन नहीं। बॉट मूल की तरह व्यवहार करते हैं क्योंकि वे वही डेटा उपयोग करते हैं।

2. मालिकाना भागों के लिए क्लीन-रूम। Jabberwacky और Cleverbot अलग हैं : उनका सोर्स कोड कभी प्रकाशित नहीं हुआ (Existor/Rollo Carpenter ने इसे मालिकाना रखा)। इसलिए पोर्ट क्लीन-रूम पुनः कार्यान्वयन हैं -- केवल व्यवहार के सार्वजनिक विवरणों से निर्मित। कोई मालिकाना कोड या डेटा कॉपी नहीं किया गया है।

3. न्यूनतम निर्भरताएँ। एकमात्र वास्तविक आवश्यकता TypeScript है। ALICE AIML फ़ाइलों के XML को पार्स करने के लिए dom-js का उपयोग करता है (66 फ़ाइलें, 99,524 श्रेणियाँ, XML पार्सिंग खुद से करना समय की बर्बादी होगी)। बाकी सब वैनिला TypeScript है।


प्रतीकात्मक चैटबॉट से LLM तक : वैचारिक छलांग

ये पाँचों बॉट एक मूलभूत विशेषता साझा करते हैं : वे प्रतीकात्मक हैं। उनका "ज्ञान" स्पष्ट प्रतीकों -- टेक्स्ट पैटर्न, नियम तालिकाएँ, XML श्रेणियाँ, ट्रांसक्रिप्ट पंक्तियाँ -- के रूप में संग्रहीत है। इनमें से किसी भी सिस्टम में भाषा का कोई संख्यात्मक प्रतिनिधित्व नहीं है।

जिसका मतलब यह भी है कि उन सभी की एक ही सीमा है : वे केवल उसी का जवाब दे सकते हैं जो स्पष्ट रूप से योजनाबद्ध या दर्ज किया गया है। ELIZा चिकित्सीय ढाँचे से बाहर जाने पर खो जाता है। PARRY मौसम के बारे में बात नहीं कर सकता। ALICE अपनी बातचीत से कुछ नहीं सीखता। Jabberwacky केवल पहले से बोले गए वाक्यों के साथ ही जवाब दे सकता है।

LLM (Large Language Models) प्रतिमान को मौलिक रूप से बदलकर इस सीमा को पार करते हैं : प्रतीकों में हेरफेर करने के बजाय, वे भाषा को संख्याओं में परिवर्तित करते हैं और इन संख्याओं के बीच सांख्यिकीय संबंध सीखते हैं। वे पूर्व-लिखित जवाब संग्रहीत नहीं करते -- वे संभावनाओं की गणना करके हर टोकन को मौके पर उत्पन्न करते हैं। आइए जल्दी से देखें कि यह कैसे काम करता है।

1. टोकनाइज़ेशन

पहला कदम टेक्स्ट को टोकन में काटना है -- शब्दों से छोटी लेकिन अक्षरों से बड़ी इकाइयाँ :

"Je ne comprends pas"
  → ["Je", " ne", " comprend", "s", " pas"]

हर टोकन का एक शब्दकोश में एक संख्यात्मक ID होता है (आमतौर पर नवीनतम मॉडलों के लिए 32,000 से 128,000 टोकन)। यह विखंडन मॉडल को कभी न देखे गए शब्दों को ज्ञात उप-शब्दों में तोड़कर संभालने की अनुमति देता है।

2. एम्बेडिंग

हर टोकन ID एक वेक्टर में परिवर्तित होता है -- फ़्लोटिंग पॉइंट संख्याओं की एक सारणी (आमतौर पर मध्यम आकार के मॉडल के लिए 4096 आयाम)। यह वेक्टर एक एम्बेडिंग है जो टोकन के अर्थ को एक गणितीय स्थान में एन्कोड करता है जहाँ अर्थपूर्ण रूप से समान टोकन के वेक्टर पास-पास होते हैं :

vecteur("roi")  − vecteur("homme") + vecteur("femme")  ≈  vecteur("reine")

यह गुण प्रशिक्षण से उभरता है -- किसी ने इसे स्पष्ट रूप से प्रोग्राम नहीं किया है। यह इस बात का परिणाम है कि शब्दों का उपयोग समान संदर्भों में कैसे किया जाता है।

3. अटेंशन

अटेंशन तंत्र (2017 के "Attention is All You Need" पेपर द्वारा प्रस्तुत) वह है जिसने LLM को संभव बनाया। हर टोकन के लिए, अटेंशन गणना करता है कि वाक्य में कौन से अन्य टोकन इसे समझने के लिए महत्वपूर्ण हैं :

"La banque a refusé mon prêt."
     ↑
Token "banque" देखता है : "refusé", "prêt" → समझता है कि यह एक वित्तीय संस्था है

"Je vais me promener sur la banque."
     ↑
Token "banque" देखता है : "promener", "sur" → समझता है कि यह एक नदी का किनारा है

अटेंशन मॉडल को संदर्भ कैप्चर करने की अनुमति देता है -- हर टोकन को अलग-थलग नहीं बल्कि उसके आस-पास के टोकन के आधार पर समझा जाता है।

4. अगले टोकन की भविष्यवाणी

LLM का प्रशिक्षण भ्रामक रूप से सरल है : हम उसे एक टेक्स्ट दिखाते हैं, अंतिम टोकन छिपाते हैं, और उसे भविष्यवाणी करने के लिए कहते हैं। फिर अरबों बार दोहराते हैं।

Input:  "Je ne comprends"
Caché:  "pas"
मॉडल की भविष्यवाणी : "pas" (प्रायिकता 0.87), "rien" (0.05), "jamais" (0.02)...

लक्ष्य हर स्थान पर सही टोकन की प्रायिकता को अधिकतम करना है। इसे नेक्स्ट-टोकन प्रेडिक्शन कहा जाता है। प्रशिक्षण के दौरान, मॉडल टेराबाइट्स टेक्स्ट पर भविष्यवाणी त्रुटि को कम करने के लिए अपने अरबों पैरामीटर समायोजित करता है।

अनुमान के समय (जब हम उससे बात करते हैं), मॉडल एक बार में एक टोकन लूप में उत्पन्न करता है :

Token 1: "Je"    (input: "Parle-moi de toi.")
Token 2: "suis"  (input: "Parle-moi de toi. Je")
Token 3: "un"    (input: "Parle-moi de toi. Je suis")
Token 4: "chatbot" (input: "Parle-moi de toi. Je suis un")
...

हर टोकन उसकी प्रायिकता के अनुसार नमूना किया जाता है (temperature, top-k, top-p "रचनात्मकता" की डिग्री को नियंत्रित करते हैं)। और बस इतना ही। अरबों पैरामीटर जो इसे हज़ारों बार करते हैं।

मूलतः क्या बदलता है

पहलू प्रतीकात्मक बॉट (ELIZA, PARRY, ALICE) आधुनिक LLM
प्रतिनिधित्व स्पष्ट शब्द और नियम संख्यात्मक वेक्टर (एम्बेडिंग)
उत्पादन पूर्व-लिखित जवाबों में से चयन टोकन-दर-टोकन संभाव्य भविष्यवाणी
ज्ञान नियम फ़ाइलों में संग्रहीत नेटवर्क भार में एन्कोडेड
सीखना मैनुअल (नियम लेखन) स्वचालित (कॉर्पस पर प्रशिक्षण)
मजबूती योजनाबद्ध पैटर्न के बाहर शून्य कभी न देखे गए इनपुट पर सामान्यीकरण
व्याख्यात्मकता पूर्ण (नियम पढ़े जा सकते हैं) सीमित (ब्लैक बॉक्स)

क्लासिक चैटबॉट पारदर्शी लेकिन नाज़ुक हैं। LLM मजबूत लेकिन अपारदर्शी है। दोनों दृष्टिकोण आज भी मौजूद हैं -- प्रतिस्पर्धी के रूप में नहीं, बल्कि विभिन्न आवश्यकताओं के लिए उपकरण के रूप में।

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

यदि आप LLM के आंतरिक कामकाज में गहराई से जाना चाहते हैं, तो यह वीडियो एक उत्कृष्ट संसाधन है:

How LLMs Work — YouTube

Luna Protocol : आधुनिक संश्लेषण

Luna Protocol पर लेख (जिनके लिंक नीचे हैं) उस सबका सबसे परिष्कृत संश्लेषण प्रस्तुत करते हैं जो हमने अभी देखा : एक आधुनिक Discord बॉट जो एक स्थानीय LLM को एक परिष्कृत व्यवहार प्रणाली के साथ जोड़ता है, जो 60 वर्षों के संवादी AI के पाठों पर निर्मित है।

Luna Protocol : मैंने एक पूरी तरह से स्वायत्त Discord बॉट बनाया जो एक इंसान का अनुकरण करता है

यह लेख LLM-आधारित Discord बॉट के पूर्ण आर्किटेक्चर का विवरण देता है :

  • प्राथमिकता ट्रिगर सिस्टम (mention > DM > नाम > कीवर्ड > follow-up > यादृच्छिक)
  • मानव व्यवहार : परिवर्तनीय एकाग्रता, टाइपिंग गलतियाँ, झिझक (15%), भूल (3%), विषयगत थकान
  • नींद का समय : बॉट समय के अनुसार सोता है, धीमा होता है, या अनदेखा करता है
  • TTS पाइपलाइन : Piper + ffmpeg के माध्यम से वाक् संश्लेषण → Discord वॉइस संदेश
  • रीयल-टाइम स्ट्रीमिंग : LLM एक टाइप किए गए इवेंट बस पर एक-एक करके टोकन उत्सर्जित करता है

जो इस लेख को ऐतिहासिक चैटबॉट से जोड़ता है, वही खोज है : यह विश्वास दिलाना कि तुम एक व्यक्ति से बात कर रहे हो। ELIZA इसे टेक्स्ट मिरर से करता था। PARRY भावनात्मक मॉडल से। ALICE 99k श्रेणियों से। Luna Protocol इसे एक फाइन-ट्यून किए गए LLM + एक व्यवहार प्रणाली से करता है जो मानवीय अपूर्णताओं का अनुकरण करती है।

Luna Protocol : मैंने 1.5B मॉडल को फाइन-ट्यून क्यों किया

दूसरा लेख फाइन-ट्यूनिंग और फ्यू-शॉट प्राइमिंग की खोज करता है। केंद्रीय खोज : एक छोटा मॉडल (1.5B) जो कम डेटा (50k नमूने) पर प्रशिक्षित है, एक बड़े मॉडल (3B) से बेहतर प्रदर्शन करता है जब सही ढंग से फ्यू-शॉट उदाहरणों के साथ प्राइम किया जाए।

यह एक सबक है जो सीधे ऐतिहासिक चैटबॉट से जुड़ता है :

  • ELIZA ने दिखाया कि कुछ अच्छी तरह से डिज़ाइन किए गए नियमों से समझ का अनुकरण किया जा सकता है
  • ALICE ने दिखाया कि 99k श्रेणियों से सामान्य ज्ञान का अनुकरण किया जा सकता है
  • Luna Protocol दिखाता है कि अच्छे फाइन-ट्यूनिंग और 5 फ्यू-शॉट उदाहरणों से एक छोटा LLM एक इंसान का अनुकरण कर सकता है

तकनीक अलग है, लेकिन सिद्धांत एक ही है : डेटा की गुणवत्ता और सिस्टम की सटीकता कच्चे आकार से अधिक मायने रखती है।


निष्कर्ष : याद रखने योग्य तीन बातें

1. संवादी AI की शुरुआत ChatGPT से नहीं हुई। ELIZA 60 साल पुराना है। PARRY ने 1972 में ट्यूरिंग टेस्ट पास किया। ALICE ने तीन बार Loebner जीता। Jabberwacky ने ट्रांसक्रिप्ट-आधारित सीखने की नींव रखी, जिसे Cleverbot ने बड़े पैमाने पर औद्योगीकृत किया। हर दृष्टिकोण ने पहेली का एक टुकड़ा जोड़ा।

2. अधिक डेटा ≠ अधिक बुद्धिमत्ता। Jabberwacky के ट्रांसक्रिप्ट में कोई नियम नहीं हैं। ALICE की 99k श्रेणियाँ नहीं सीखतीं। Luna Protocol का 50k नमूनों पर फाइन-ट्यूनिंग 3B मॉडल से बेहतर है। पारंपरिक ज्ञान कहता है "जितना बड़ा, उतना बेहतर" -- चैटबॉट का इतिहास दिखाता है कि आर्किटेक्चर और डिज़ाइन आकार जितना ही मायने रखते हैं।

3. समस्या 60 वर्षों से वही है। एक इंसान को यह कैसे विश्वास दिलाया जाए कि वह किसी दूसरे इंसान से बात कर रहा है? ELIZA टेक्स्ट मिरर से जवाब देता था। PARRY नकली गुस्से से। ALICE तथ्यों से। Luna Protocol एक LLM से जो सोता है और टाइपिंग गलतियाँ करता है। समाधान बदलता है, ज़रूरत वही रहती है।

रिपॉजिटरी ओपन सोर्स है -- तुम क्लोन कर सकते हो, हर बॉट चला सकते हो, और खुद देख सकते हो कि 60 साल का संवादी AI एक ही TypeScript रिपॉजिटरी में कैसे समाता है।

संसाधन लिंक
GitHub रिपॉजिटरी fox3000foxy/chatbots
Luna Protocol -- बॉट आर्किटेक्चर लेख पढ़ें
Luna Protocol -- फ्यू-शॉट फाइन-ट्यूनिंग लेख पढ़ें
मूल ELIZA स्क्रिप्ट anthay/ELIZA
मूल PARRY सोर्स कोड lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
मूल RFC 439 PARRY Encounters the DOCTOR
LLM कैसे काम करते हैं, इसकी शानदार व्याख्या https://www.youtube.com/watch?v=YmLp8qe87A0

من ELIZA إلى LLM: 60 عاماً من الذكاء الاصطناعي التحادثي، معاد بناؤه في TypeScript

ELIZA، PARRY، ALICE، Jabberwacky، Cleverbot -- خمس بنى مختلفة جذرياً لنفس المشكلة، منقولة إلى TypeScript مع بياناتها الأصلية. من 1966 إلى LLM الحديثة، إليكم كيف تعلّم الذكاء الاصطناعي التحادثي الكلام، وماذا يعلمنا مستودع من الشات بوت عن 60 عاماً من البحث.

من ELIZA إلى LLM: 60 عاماً من الذكاء الاصطناعي التحادثي، معاد بناؤه في TypeScript

في عام 1966، كتب جوزيف فايزنباوم 420 سطراً من MAD-SLIP على IBM 7094 لإنشاء أول شات بوت في التاريخ. البرنامج كان اسمه ELIZA، وكان يحاكي معالجة نفسية رودجرية باستخدام أنماط أساسية وإعادة ترتيب للجمل. بعد ستة عقود، أصبح الذكاء الاصطناعي التحادثي موضوعاً رئيسياً -- ChatGPT، Claude، Gemini في كل محادثة.

لكن بين هذين الطرفين، كان هناك PARRY (الشات بوت المصاب بجنون الارتياب، 1972)، ALICE (ملك AIML بـ 99,000 فئة، 1995)، Jabberwacky (أول من تعلّم بدون قواعد، 1997)، وCleverbot (خليفته الصناعي، 2008). خمسة برامج، خمس بنى، مشكلة واحدة: جعل الآلة تتحدث.

هذا المستودع يحتوي على هذه الشات بوتات الخمسة، منقولة إلى TypeScript مع بياناتها الأصلية -- نصوص ELIZA، قواميس PARRY، ملفات AIML الخاصة بـ ALICE. كل نقلة مستقلة، جاهزة للاستخدام، وموثقة بأدق التفاصيل. الهدف ليس فقط تشغيلها: بل فهم كيف كانت تعمل، ولماذا صنعت التاريخ، وماذا تعلمنا بنياتها عن الذكاء الاصطناعي في الأمس... واليوم.

bun run eliza    # تحدث إلى ELIZA (1966)
bun run parry    # تحدث إلى PARRY (1972)
bun run alice    # تحدث إلى ALICE (1995)
bun run jabber   # تحدث إلى Jabberwacky
bun run cleverbot # تحدث إلى Cleverbot
bun run meeting  # ELIZA vs PARRY تلقائي

سنحلل كل بوت، ننظر إلى كودهم، ثم نصنع الجسر مع LLM الحديثة عبر المقالات حول Luna Protocol.


ELIZA (1966): فن جعل الآخر يعتقد أنك تفهم

لنبدأ بالأقدم، والأكثر إثارة للإعجاب على الأرجح في بساطتها. ELIZA ليست ذكية بأي معنى حديث. لا شبكات عصبية، لا إحصائيات، لا تعلم. مجرد أنماط نصية وقليل من إعادة الترتيب.

المبدأ

نص DOCTOR (نسخة المعالجة النفسية) يعمل بجدول من الكلمات المفتاحية، كل منها مرتبط بـ أنماط تحليل و قواعد إعادة تركيب. إليك قاعدة نموذجية:

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO هي الكلمة المفتاحية. 0 هو نمط تحليل يقول "التقط كل ما يلي" (مثل بطاقة بدل). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. هي قاعدة إعادة التركيب. هذا كل شيء.

عندما تقول "Hello, I'm sad today"، ELIZA:

  1. تحول النص إلى أحرف كبيرة: HELLO I'M SAD TODAY
  2. تمسح كل كلمة مقابل جدول الكلمات المفتاحية
  3. تجد HELLO → تدفعها إلى رصة الكلمات المفتاحية
  4. تأخذ الكلمة المفتاحية ذات الأولوية الأعلى
  5. تجرب كل نمط تحليل بالترتيب
  6. إذا تطابق، تختار قاعدة إعادة التركيب التالية (round-robin)
  7. تستبدل (1)، (2) إلخ بالأجزاء الملتقطة

لكن الجزء الذكي حقاً هو قواعد PRE. انظر إلى هذا:

(MY
    ((0)
        (PRE (1 0) (=YOU))))

عندما تطابق ELIZA MY، تحول بقية الجملة (الملتقطة بواسطة 0) عبر قاعدة PRE، وتعيد حقن النتيجة كما لو أن المستخدم قال كلمة مفتاحية جديدة. عملياً:

أنت تقول: "My mother hates me"
  → PRE تحول: "YOUR MOTHER HATES YOU"
  → يعاد حقنه كما لو كنت قلته للتو
  → ربما يطابق "YOU" → رد جديد

لهذا تبدو ELIZA وكأنها تفهم الفرق بين "أنا" و"أنت" -- هذا ليس فهماً، إنه تحويل ميكانيكي مصمم بشكل مثالي.

إليك التدفق الكامل، من إدخال المستخدم إلى الرد:

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>يطبع علامات الترقيم"]
    B --> C["splitUserInput()<br>يقطع إلى كلمات"]
    C --> D["Build keyword stack<br>مرتب حسب الأولوية"]
    D --> E{"الرصة غير فارغة؟"}
    E -->|"نعم"| F["Pop highest-priority keyword"]
    E -->|"لا"| G{"استدعاء ذاكرة؟"}
    G -->|"نعم"| H["Recall past user statement"]
    G -->|"لا"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"وجد تطابق؟"}
    L -->|"لا"| M{"كلمة مفتاحية مرتبطة؟"}
    M -->|"نعم"| N["Push linked keyword to stack"]
    N --> E
    M -->|"لا"| O["Return NOMATCH"]
    O --> J
    L -->|"نعم"| P["Select next reassembly (round-robin)"]
    P --> Q{"نوع إعادة التركيب؟"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>إلى رد نهائي"]
    T --> J

ما الذي جعلها قابلة للتصديق

فايزنباوم قام باختيار عبقري: المعالجة النفسية الرودجرية. هذا الأسلوب يقوم بعكس كلام المريض دون تفسير. "أنا حزين" → "أنت تقول أنك حزين". هذا بالضبط ما تجيده ELIZA -- ولأنها تقنية علاجية معترف بها، لا أحد يجد ذلك غريباً.

في النقلة إلى TypeScript

النقلة تحمل نصوص .ela (صيغة S-expression الأصلية)، تحللها بالكامل (بما في ذلك ترميز Hollerith -- صيغة نصوص من الستينيات)، وتنفذ نفس الدورة: تحويل لأحرف كبيرة → تقسيم → رصة كلمات مفتاحية → تحليل → إعادة تركيب → PRE/تحويلات.

➡ انظر الكود المصدري


PARRY (1972): أول شات بوت بمشاعر

بعد ست سنوات من ELIZA، كينيث كولبي (طبيب نفسي في ستانفورد) أنشأ PARRY: شات بوت يحاكي مريضاً مصاباً بـ الفصام الارتيابي. حيث كانت ELIZA مرآة فارغة، PARRY لديه نموذج عاطفي داخلي حقيقي.

النموذج العاطفي

PARRY لديه أربعة متغيرات مستمرة تتغير كل جولة محادثة:

المتغير خط الأساس الانحدار/الجولة الوصف
ANGER 0 −1.0 العداء، الانزعاج
FEAR 0 −0.2 الارتياب (ينحدر ببطء بعد بداية الهذيان)
MISTRUST 0 −0.05 عدم الثقة (بطيء جداً في العودة)
HURT 0 −0.5 الألم العاطفي

هذه القيم تزيد عبر قفزات عاطفية (ajump، fjump، hjump) تُفعّل بقواعد استدلال، وتنحدر طبيعياً نحو خطوط أساسها كل جولة.

شبكة المعتقدات

PARRY لديه أكثر من 200 معتقد مخزنة في ملف bel:

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

كل معتقد له فئة (HUM = المريض، HUM2 = الآخرين، DOC = الطبيب، INT = الاستجواب، INN = النوايا) وقوة (0-5). قواعد الاستدلال (TH2، EMOTE، IF) تنشر المعتقدات بينها:

  • TH2: إذا تجاوز معتقد A حداً، فإنه يعزز نفسه وتزداد عواقبه
  • EMOTE: إذا تجاوز معتقد حداً، فإنه يفعل قفزة عاطفية (anger/fear/hurt)
  • IF: شرطي -- إذا A صحيح، فإن B يصبح صحيحاً بمستوى معين

تسلسل الهذيان (نظام flare)

الجزء الأكثر روعة في PARRY هو نظام "flares" -- سلسلة تصعيد تقود تدريجياً نحو الهذيان المركزي:

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

كل كلمة مفتاحية تفعل رداً مكتوباً مسبقاً (عبر مطابقة الأنماط)، وإذا تابع المحاور الموضوع، ينزلق PARRY تدريجياً نحو هذيانه المركزي بالاضطهاد. بمجرد أن يتم "تفعيل" flare، يصبح غير نشط (deadFlares) -- PARRY ينتقل إلى التالي، محاكياً محاوراً يتعمق في الموضوع.

لماذا PARRY تاريخي

في 1972، اجتاز PARRY نسخة من اختبار تورينغ: أطباء نفسيون تلقوا نصوص محادثات مع PARRY ومع مرضى حقيقيين مصابين بارتياب، ولم يستطيعوا التمييز بينهم بشكل موثوق. إنه أول برنامج يحقق ذلك، قبل 40 عاماً من LLM.

➡ انظر الكود المصدري


RFC 439: عندما تلتقي ELIZA بـ PARRY

في 18 سبتمبر 1972، تحدث برنامجان لبعضهما البعض لأول مرة في التاريخ -- حرفياً: إنها أول محادثة بين ذكاءين اصطناعيين تم تسجيلها على الإطلاق. ELIZA (تعمل على BBN في بوسطن) وPARRY (تعمل في ستانفورد) تحادثتا عبر ARPANET، بمساعدة مشغلين بشريين يطبعون مخرجات أحدهما إلى مدخل الآخر. نُشر النص كـ RFC 439 ("PARRY Encounters the DOCTOR").

إليكم مقتطف مما بدت عليه:

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

متماسك بشكل مدهش. ELIZA تقوم بعملها كمعالجة: إعادة صياغة، سؤال، استكشاف. PARRY يقوم بعمله كمريض مرتاب: شكوى، اتهام، تعبير عن عدم الثقة. كلا البرنامجين في دورهما تماماً -- ليس لأنهما "يفهمان" الموقف، ولكن لأن آلياتهما (أنماط ELIZA + النموذج العاطفي لـ PARRY) تنتج ردوداً تتطابق بالصدفة.

المستودع يمكنه إعادة إنتاج هذه المحادثة مع:

bun run meeting

المحاكاة تشغل 25 جولة تلقائية بين البوتين، مع موضوع بداية عشوائي (خيول، جريمة منظمة، عواطف...). بما أن ELIZA وPARRY لديهما عناصر غير حتمية (round-robin لـ ELIZA، العشوائية لـ PARRY)، كل تشغيل ينتج تبادلاً مختلفاً.

ما يثير الدهشة في ELIZA vs PARRY هو أن لدينا برنامجين -- أحدهما بدون حالة داخلية، والآخر بنموذج عاطفي كامل -- ينتجان معاً محادثة تشبه شيئاً متعمداً. بالنسبة لـ 1972، كان ذلك مذهلاً.


ALICE (1995): مطابقة الأنماط على نطاق واسع

ALICE (Artificial Linguistic Internet Computer Entity) أنشأها ريتشارد والاس في 1995، وفازت بـ Loebner Prize ثلاث مرات (2000، 2001، 2004). حيث كان لدى ELIZA بضع مئات من القواعد وPARRY بضعة آلاف، ALICE لديها 99,524 -- موزعة على 66 ملف AIML.

AIML: لغة الفئات

AIML (Artificial Intelligence Markup Language) هو تنسيق XML لتعريف أزواج الأسئلة والأجوبة:

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

لكن قوة ALICE تأتي من البطاقات البدل وSRAI (الاختزال الرمزي):

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- يعادل <srai><star/></srai> -->
  </template>
</category>

SRAI يسمح لـ ALICE بإعادة توجيه إدخال إلى فئة أخرى، مكوناً سلسلة اختزال:

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

هذه هي الآلية التي تعطي ALICE مرونتها: بدلاً من كتابة رد لكل صياغة ممكنة، نكتب رداً قياسياً ونعيد توجيه التنويعات إليه. حد العمق هو 10 -- بعد ذلك، تتخلى ALICE لتجنب الحلقات اللانهائية (يتم تجنبها بعناية في تصميم الفئات، لكن شبكة الأمان تظل ضرورية).

كيف تطابق ALICE الأنماط

الأنماط تُرتب حسب الخصوصية: تلك التي تحتوي على بطاقات بدل أقل تُجرب أولاً. البطاقات البدل * و_ تلتقط أي تسلسل كلمات. المحرك يترجم كل نمط إلى regex، ثم يتكرر عبر الفئات المرتبة حتى يجد تطابقاً.

// تطبيقنا في TypeScript -- مبسط لكن أمين
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

لماذا هيمنت ALICE على Loebner

99,524 فئة، هذا رقم يغير كل شيء. ELIZA بدت ذكية لأن قواعدها القليلة كانت مصممة جيداً لسياق محدد (المعالجة النفسية). ALICE تغطي مواضيع كثيرة لدرجة أنها تعطي انطباعاً بوجود ثقافة عامة حقيقية: علوم، سياسة، فكاهة، رياضة، عواطف، كل شيء موجود.

➡ انظر الكود المصدري


Jabberwacky (1997) & Cleverbot (2008): الانقطاع المعرفي

جميع البوتات السابقة تشترك في فرضية: يجب كتابة الردود. ELIZA لديها قواعد S-expression، PARRY لديه أنماط انتقائية، ALICE لديها فئات AIML. رولو كاربنتير أخذ الاتجاه المعاكس تماماً: ماذا لو لم نكتب شيئاً على الإطلاق؟

الفكرة

Jabberwacky (أُطلق حوالي 1997، أصبح Cleverbot في 2008) لا يخزن أي قواعد. يخزن كل تاريخ المحادثات في نص مسطح، وعندما يتحدث إليه أحدهم، يبحث في ذلك التاريخ عن اللحظة الأكثر تشابهاً ويعيد استخدام ما قيل بعدها:

المستخدم: "hello"
  ↓
بحث: هل قال أحدهم "hello" من قبل؟
  ↓
نعم، في الجلسة #3، سطر 14، قال أحدهم "hello" وأجاب البوت "hi there!"
  ↓
الرد: "hi there!"

لا نمط. لا نحو. لا XML. مجرد أرشيف ضخم لأشياء قالها الناس لبعضهم البعض، يُعاد استخدامه في اللحظة المناسبة. هذا هو تعريف النشوء (emergence).

التنفيذ في TypeScript

النقلة إلى TypeScript تعيد إنتاج هذه البنية بالضبط:

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 سطر بذرة + تاريخ"]
    B --> C["withReplies()<br>يستخرج الأزواج<br>(سطر → رد)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>السياق قبل هذا السطر)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["أفضل K مرشح مرتبة"]
    I --> J{"pickReply()<br>اختيار عجلة الروليت"}
    J -->|"اختيار"| K["Reply = reply.text<br>من الزوج الفائز"]
    J -->|"لا يوجد"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["إلحاق بالنص<br>save() → JSON"]
    L --> M

هذا هو قلب التسجيل -- الاستدلال الخاص بنا المستوحى من الأوصاف العامة لـ Cleverbot:

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65): التشابه بين إدخال المستخدم والسطر التاريخي
  • contextFit (0.25): التشابه بين المحادثة الحديثة وما سبق السطر التاريخي
  • recencyBonus (0.10): الذكريات الأحدث تزن أكثر قليلاً (شخصية البوت تتغير مع الوقت)

الاختيار احتمالي (اختيار عجلة الروليت): أفضل مرشح يفوز أكثر، لكن ليس دائماً -- مما يعطي تنوعاً.

Cleverbot: الابتكاران الموثقان

Cleverbot يضيف آليتين إلى المفهوم الأساسي لـ Jabberwacky:

  1. التعلم متعدد الأشخاص: ملايين المستخدمين يساهمون في نفس النص المشترك. الرد المأخوذ من التاريخ قد يأتي من صوت مختلف تماماً عن المحادثة الحالية -- مما يفسر لماذا يغير Cleverbot شخصيته فجأة.

  2. التعلم المؤجل: ما تقوله لـ Cleverbot أثناء جلسة ما غير متاح للمطابقة خلال نفس الجلسة. الأسطر الجديدة تُوسم pending وتصبح قابلة للمطابقة فقط بعد "دمج" بين الجلسات -- مما يفسر لماذا لا يمكنك تعليم Cleverbot حقيقة وإعادة استخدامها في نفس المحادثة.

// Cleverbot: الأسطر الحديثة غير مرئية حتى الدمج
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() تُستدعى عند البدء، وليس أثناء الجلسة

النقلة إلى TypeScript تنفذ هذين السلوكين: الأسطر لها علامة consolidated، وكل جلسة REPL تبدأ بدمج الأسطر المعلقة.

➡ انظر الكود المصدري


تحليل النقلة إلى TypeScript: تصميم بنية مشتركة

بناء هذه البوتات الخمسة في نفس اللغة يعني مواجهة سؤال مثير للاهتمام: هل يمكننا استخراج كود مشترك بين بنى مختلفة إلى هذه الدرجة؟

الجواب هو: قليلاً جداً. كل بوت لديه حلقة أساسية مختلفة جوهرياً:

البوت الحلقة الرئيسية البيانات التعلم
ELIZA رصة كلمات مفتاحية → تحليل → إعادة تركيب نصوص .ela في S-expressions لا
PARRY ترميز → أنماط انتقائية / flares / كلمات مفتاحية / استدلال 58 ملف PDP-10 (قواميس، معتقدات، قواعد) لا
ALICE أنماط مرتبة → regex → قالب AIML → SRAI تكراري 66 ملف AIML XML لا
Jabberwacky تشابه → سياق → حداثة → اختيار موزون نص JSON (ينمو مع الاستخدام) مستمر
Cleverbot نفس Jabberwacky + pending/consolidated + شخصيات نص JSON + بذور متعددة الشخصيات مؤجل (بين الجلسات)

ما يشتركون فيه هو واجهة CLI والبنية التحتية TypeScript (biome للـ lint، tsx للتنفيذ). الباقي خاص بكل بنية.

خيارات تصميم مشتركة

1. الإخلاص للبيانات الأصلية. بالنسبة لـ ELIZA وPARRY وALICE، نستخدم الملفات الأصلية -- نصوص ELIZA الموجودة في أرشيفات فايزنباوم في 2021، كود PARRY الأصلي من PDP-10 (58 ملفاً)، AIML Free ALICE v1.6. لا ترجمة، لا إعادة كتابة. البوتات تتصرف مثل الأصلية لأنها تستخدم نفس البيانات.

2. غرفة نظيفة للأجزاء المملوكة. Jabberwacky وCleverbot مختلفان: كودهما المصدري لم يُنشر أبداً (Existor/رولو كاربنتير احتفظا به مملوكاً). لذا فإن النقلات هي إعادة تطبيق غرفة نظيفة -- مبنية فقط على أوصاف عامة للسلوك. لا سطر من الكود أو البيانات المملوكة منسوخ.

3. تبعيات ضئيلة. المتطلب الحقيقي الوحيد هو TypeScript. ALICE تستخدم dom-js لتحليل XML لملفات AIML (66 ملفاً، 99,524 فئة -- كتابة محلل XML منزلي سيكون مضيعة للوقت). كل شيء آخر هو TypeScript خام.


من الشات بوتات الرمزية إلى LLM: القفزة المفاهيمية

الخمسة بوتات التي رأيناها تشترك جميعاً في خاصية أساسية: هي رمزية. "معرفتها" مخزنة كرموز صريحة -- أنماط نصية، جداول قواعد، فئات XML، أسطر نصوص. لا يوجد أي تمثيل عددي للغة في أي من هذه الأنظمة.

مما يعني أيضاً أن لديها جميعاً نفس السقف الزجاجي: يمكنها الرد فقط على ما تم التخطيط له أو تسجيله صراحة. ELIZA تضيع إذا خرجت عن الإطار العلاجي. PARRY لا يستطيع التحدث عن الطقس. ALICE لا تتعلم شيئاً من محادثاتها. Jabberwacky لا يمكنه الرد إلا بسطور قيلت بالفعل.

LLM (نماذج اللغة الكبيرة) تخترق هذا السقف بتغيير جذري للنموذج: بدلاً من التعامل مع الرموز، تحول اللغة إلى أرقام وتتعلم علاقات إحصائية بين هذه الأرقام. لا تخزن ردوداً مكتوبة مسبقاً -- إنها تولد كل رمز (token) في الحال بحساب الاحتمالات. لنرى بسرعة كيف يعمل هذا.

1. الترميز (Tokenization)

الخطوة الأولى هي تقطيع النص إلى رموز -- وحدات أصغر من الكلمات لكن أكبر من الأحرف:

"أنا لا أفهم"
  → ["أنا", " لا", " أفهم"]

كل رمز له معرف رقمي في مفردات (عادة 32,000 إلى 128,000 رمز للنماذج الحديثة). هذا التقطيع يسمح للنموذج بمعالجة كلمات لم يرها من قبل عن طريق تفكيكها إلى كلمات فرعية معروفة.

2. التضمينات (Embeddings)

كل معرف رمز يُحول إلى متجه -- مصفوفة من الأعداد العشرية (عادة 4096 بُعداً لنموذج متوسط الحجم). هذا المتجه هو تضمين يرمز معنى الرمز في فضاء رياضي حيث الرموز المتقاربة دلالياً لها متجهات متقاربة:

متجه("ملك") − متجه("رجل") + متجه("امرأة")  ≈  متجه("ملكة")

هذه الخاصية تنشأ من التدريب -- لم يبرمجها أحد صراحة. إنها نتيجة لكيفية استخدام الكلمات في سياقات متشابهة.

3. الانتباه (Attention)

آلية الانتباه (التي قدمتها ورقة "Attention is All You Need" في 2017) هي ما جعل LLM ممكنة. لكل رمز، يحسب الانتباه أي الرموز الأخرى في الجملة مهمة لفهم هذا الرمز:

"البنك رفض قرضي."
     ↑
رمز "بنك" ينظر إلى: "رفض"، "قرض" → يفهم أنها مؤسسة مالية

"سأتمشى على ضفة النهر."
     ↑
رمز "ضفة" ينظر إلى: "أتمشى"، "على" → يفهم أنها حافة نهر

الانتباه يسمح للنموذج بالتقاط السياق -- كل رمز يُفهم بناءً على ما حوله، وليس بمعزل عن الآخرين.

4. توقع الرمز التالي

تدريب LLM بسيط بشكل خادع: نريه نصاً، نخفي الرمز الأخير، ونطلب منه توقعه. ثم نكرر ذلك مليارات المرات.

Input:  "أنا لا أفه"
مخفي:  "م"
توقع النموذج: "م" (احتمال 0.87)، "شيء" (0.05)، "أبداً" (0.02)...

الهدف هو تعظيم احتمال الرمز الصحيح في كل موضع. هذا ما يسمى توقع الرمز التالي. أثناء التدريب، يضبط النموذج مليارات معاملاته لتقليل خطأ التوقع على تيرابايت من النص.

في وقت الاستدلال (عندما نتحدث إليه)، يولد النموذج رمزاً واحداً في كل مرة في حلقة:

Token 1: "أنا"    (input: "تحدث عن نفسك.")
Token 2: "شات"  (input: "تحدث عن نفسك. أنا")
Token 3: "بوت"    (input: "تحدث عن نفسك. أنا شات")
Token 4: "ذكي" (input: "تحدث عن نفسك. أنا شات بوت")
...

كل رمز يُعيّن حسب احتماله (temperature، top-k، top-p تتحكم في درجة "الإبداع"). وهذا كل شيء. مليارات المعاملات تفعل هذا آلاف المرات.

ما يتغير جوهرياً

الجانب البوتات الرمزية (ELIZA، PARRY، ALICE) LLM الحديثة
التمثيل كلمات وقواعد صريحة متجهات رقمية (تضمينات)
التوليد اختيار من ردود مكتوبة مسبقاً توقع احتمالي رمزاً برمز
المعرفة مخزنة في ملفات القواعد مشفرة في أوزان الشبكة
التعلم يدوي (كتابة قواعد) تلقائي (تدريب على نصوص)
المتانة معدومة خارج الأنماط المخطط لها تعميم لمدخلات لم تُرَ من قبل
قابلية التفسير كاملة (يمكن قراءة القواعد) محدودة (صندوق أسود)

الشات بوتات الكلاسيكية شفافة لكنها هشة. LLM متين لكنه معتم. كلا النهجين ما زالا موجودين اليوم -- ليس كمنافسين، بل كأدوات لاحتياجات مختلفة.

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

إذا أردت التعمق في آلية عمل LLM من الداخل، هذا الفيديو شرح ممتاز:

How LLMs Work — YouTube

Luna Protocol: التركيب الحديث

المقالات حول Luna Protocol (روابطها أدناه) تمثل أكثر توليفة مكتملة لكل ما رأيناه للتو: بوت Discord حديث يجمع بين LLM محلي ونظام سلوكي متطور، مبني على دروس 60 عاماً من الذكاء الاصطناعي التحادثي.

Luna Protocol: أنشأت بوت Discord مستقل يحاكي إنساناً

هذا المقال يفصل البنية الكاملة لبوت Discord القائم على LLM:

  • نظام تشغيل ذو أولويات (إشارة > DM > اسم > كلمة مفتاحية > متابعة > عشوائي)
  • سلوكيات بشرية: تركيز متغير، أخطاء إملائية، تردد (15%)، نسيان (3%)، تعب موضوعي
  • جداول نوم: البوت ينام، يبطئ، أو يتجاهل حسب الوقت
  • خط أنابيب TTS: تركيب صوت عبر Piper + ffmpeg → رسائل صوتية في Discord
  • بث فوري في الوقت الحقيقي: LLM يصدر الرموز واحداً تلو الآخر على ناقل أحداث مقيد الأنواع

ما يربط هذه المقالة بالشات بوتات التاريخية هو نفس السعي: جعل الآخر يعتقد أنه يتحدث مع شخص. ELIZA فعلتها بمرايا نصية. PARRY بنموذج عاطفي. ALICE بـ 99k فئة. Luna Protocol يفعلها بـ LLM مضبوط بدقة + نظام سلوكي يحاكي العيوب البشرية.

Luna Protocol: لماذا قمت بضبط نموذج 1.5B

المقال الثاني يستكشف الضبط الدقيق والتمهيد القليل من الأمثلة. الاكتشاف المركزي: نموذج أصغر (1.5B) مدرب على بيانات أقل (50k عينة) يتفوق على نموذج أكبر (3B) عندما تُمهّده بشكل صحيح بأمثلة قليلة.

هذا درس يتردد صداه مباشرة مع الشات بوتات التاريخية:

  • ELIZA أظهرت أنه بقواعد قليلة مصممة جيداً، يمكن محاكاة الفهم
  • ALICE أظهرت أنه بـ 99k فئة، يمكن محاكاة الثقافة العامة
  • Luna Protocol يظهر أنه بضبط دقيق جيد و5 أمثلة قليلة، يمكن لـ LLM صغير محاكاة إنسان

التقنية مختلفة، لكن المبدأ واحد: جودة البيانات ودقة النظام أهم من الحجم الخام.


الخلاصة: ثلاثة أشياء يجب تذكرها

1. الذكاء الاصطناعي التحادثي لم يبدأ مع ChatGPT. ELIZA عمرها 60 عاماً. PARRY اجتاز اختبار تورينغ في 1972. ALICE فازت بـ Loebner ثلاث مرات. Jabberwacky وضع أسس التعلم من النصوص، التي قام Cleverbot بتصنيعها على نطاق واسع. كل نهج أضاف قطعة إلى اللغز.

2. المزيد من البيانات ≠ أكثر ذكاءً. نص Jabberwacky ليس لديه قواعد. فئات ALICE الـ 99k لا تتعلم. الضبط الدقيق لـ Luna Protocol على 50k عينة يتفوق على نموذج 3B. الحكمة التقليدية تقول "الأكبر هو الأفضل" -- تاريخ الشات بوتات يظهر أن البنية والتصميم يهمان بقدر الحجم.

3. المشكلة هي نفسها منذ 60 عاماً. كيف نجعل إنساناً يعتقد أنه يتحدث إلى إنسان آخر؟ ELIZA أجابت بمرايا نصية. PARRY بغضب محاكى. ALICE بحقائق. Luna Protocol بـ LLM ينام ويرتكب أخطاء إملائية. الحل يتغير، الحاجة تبقى.

المستودع مفتوح المصدر -- يمكنك استنساخه، تشغيل كل بوت، ورؤية بنفسك كيف أن 60 عاماً من الذكاء الاصطناعي التحادثي تتسع في مستودع TypeScript واحد.

المصدر الرابط
مستودع GitHub fox3000foxy/chatbots
Luna Protocol -- بنية البوت اقرأ المقال
Luna Protocol -- ضبط دقيق قليل الأمثلة اقرأ المقال
نصوص ELIZA الأصلية anthay/ELIZA
كود PARRY الأصلي lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 الأصلية PARRY Encounters the DOCTOR
شرح رائع لكيفية عمل LLM https://www.youtube.com/watch?v=YmLp8qe87A0

Từ ELIZA đến LLM : 60 năm AI hội thoại, được tái dựng bằng TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- năm kiến trúc hoàn toàn khác nhau cho cùng một bài toán, được chuyển sang TypeScript với dữ liệu gốc. Từ 1966 đến LLM hiện đại, đây là cách AI hội thoại học nói, và những gì một repo chatbot dạy chúng ta về 60 năm nghiên cứu.

Từ ELIZA đến LLM : 60 năm AI hội thoại, được tái dựng bằng TypeScript

Năm 1966, Joseph Weizenbaum viết 420 dòng MAD-SLIP trên một chiếc IBM 7094 để tạo ra chatbot đầu tiên trong lịch sử. Chương trình có tên ELIZA, và nó mô phỏng một nhà trị liệu tâm lý Rogerian với các mẫu cơ bản và hoán vị câu. Sáu thập kỷ sau, AI hội thoại đã trở thành chủ đề chính thống -- ChatGPT, Claude, Gemini có mặt trong mọi cuộc trò chuyện.

Nhưng giữa hai thái cực này, có PARRY (chatbot hoang tưởng, 1972), ALICE (vua của AIML với 99.000 danh mục, 1995), Jabberwacky (bot đầu tiên học mà không cần luật, 1997), và Cleverbot (người kế thừa công nghiệp, 2008). Năm chương trình, năm kiến trúc, một vấn đề duy nhất : khiến máy móc biết nói.

Repo này chứa cả năm bot, được chuyển sang TypeScript với dữ liệu gốc -- script ELIZA, từ điển PARRY, tệp AIML của ALICE. Mỗi bản port độc lập, sẵn sàng sử dụng và được tài liệu hóa chi tiết. Mục tiêu không chỉ là chạy được chúng : mà là hiểu cách chúng hoạt động, tại sao chúng đánh dấu lịch sử, và kiến trúc của chúng dạy chúng ta điều gì về AI ngày hôm qua... và hôm nay.

bun run eliza    # Nói chuyện với ELIZA (1966)
bun run parry    # Nói chuyện với PARRY (1972)
bun run alice    # Nói chuyện với ALICE (1995)
bun run jabber   # Nói chuyện với Jabberwacky
bun run cleverbot # Nói chuyện với Cleverbot
bun run meeting  # Tự động ELIZA vs PARRY

Chúng ta sẽ mổ xẻ từng bot, xem code của chúng, sau đó kết nối với LLM hiện đại qua các bài viết về Luna Protocol.


ELIZA (1966) : Nghệ thuật khiến người ta tin rằng bạn hiểu

Hãy bắt đầu với bot lâu đời nhất, và có lẽ ấn tượng nhất trong sự đơn giản của nó. ELIZA không có trí thông minh nào theo nghĩa hiện đại. Không mạng nơ-ron, không thống kê, không học tập. Chỉ là các mẫu văn bản và một chút hoán vị.

Nguyên lý

Script DOCTOR (phiên bản nhà trị liệu) hoạt động với một bảng từ khóa, mỗi từ được liên kết với mẫu phân tách và quy tắc tái hợp. Đây là một quy tắc điển hình :

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO là từ khóa. 0 là mẫu phân tách có nghĩa là "bắt mọi thứ theo sau" (như wildcard). HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. là quy tắc tái hợp. Chỉ vậy thôi.

Khi bạn nói "Hello, I'm sad today", ELIZA :

  1. Chuyển văn bản thành chữ hoa : HELLO I'M SAD TODAY
  2. Quét từng từ so với bảng từ khóa
  3. Tìm thấy HELLO → push lên stack từ khóa
  4. Lấy từ khóa có ưu tiên cao nhất
  5. Thử từng mẫu phân tách theo thứ tự
  6. Nếu khớp, chọn quy tắc tái hợp tiếp theo (round-robin)
  7. Thay thế (1), (2) v.v. bằng các phần đã bắt

Nhưng phần thực sự thông minh là các quy tắc PRE. Hãy xem :

(MY
    ((0)
        (PRE (1 0) (=YOU))))

Khi ELIZA khớp MY, nó biến đổi phần còn lại của câu (bị bắt bởi 0) qua quy tắc PRE, và đưa kết quả trở lại như thể người dùng vừa nói một từ khóa mới. Cụ thể :

Bạn nói : "My mother hates me"
  → PRE biến đổi : "YOUR MOTHER HATES YOU"
  → đưa lại như thể bạn vừa nói nó
  → có thể khớp "YOU" → phản hồi mới

Đó là lý do ELIZA có vẻ hiểu sự khác biệt giữa "tôi" và "bạn" -- đó không phải hiểu biết, mà là một phép biến đổi cơ học được thiết kế hoàn hảo.

Đây là luồng hoàn chỉnh, từ người dùng gõ đến phản hồi :

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalise la ponctuation"]
    B --> C["splitUserInput()<br>découpe en mots"]
    C --> D["Build keyword stack<br>ordonné par priorité"]
    D --> E{"Stack non-vide?"}
    E -->|"Oui"| F["Pop highest-priority keyword"]
    E -->|"Non"| G{"Memory recall?"}
    G -->|"Oui"| H["Recall past user statement"]
    G -->|"Non"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Match found?"}
    L -->|"Non"| M{"Linked keyword?"}
    M -->|"Oui"| N["Push linked keyword to stack"]
    N --> E
    M -->|"Non"| O["Return NOMATCH"]
    O --> J
    L -->|"Oui"| P["Select next reassembly (round-robin)"]
    P --> Q{"Reassembly type?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>into final response"]
    T --> J

Điều khiến nó đáng tin

Weizenbaum đã chọn một hướng đi thiên tài : liệu pháp tâm lý Rogerian. Cách tiếp cận này là phản ánh lời nói của bệnh nhân mà không diễn giải. "Tôi buồn" → "Ông nói rằng ông buồn." Đó chính xác là những gì ELIZA biết làm -- và vì đây là một kỹ thuật trị liệu được công nhận, không ai thấy nó kỳ lạ.

Trong bản port TypeScript

Port tải các script .ela (định dạng S-expression gốc), phân tích cú pháp hoàn chỉnh (bao gồm mã hóa Hollerith -- một định dạng chuỗi từ những năm 60), và thực thi cùng chu trình : viết hoa → tách → stack từ khóa → phân tách → tái hợp → PRE/biến đổi.

➡ Xem mã nguồn


PARRY (1972) : Chatbot đầu tiên có cảm xúc

Sáu năm sau ELIZA, Kenneth Colby (bác sĩ tâm thần tại Stanford) đã tạo ra PARRY : một chatbot mô phỏng bệnh nhân mắc tâm thần phân liệt hoang tưởng. Trong khi ELIZA là một tấm gương rỗng, PARRY có một mô hình cảm xúc nội tại thực sự.

Mô hình cảm xúc

PARRY có bốn biến liên tục thay đổi sau mỗi lượt trò chuyện :

Biến Đường cơ sở Suy giảm/lượt Mô tả
ANGER 0 −1.0 Thù địch, cáu kỉnh
FEAR 0 −0.2 Hoang tưởng (giảm chậm sau khi bắt đầu ảo tưởng)
MISTRUST 0 −0.05 Ngờ vực (giảm rất chậm)
HURT 0 −0.5 Tổn thương cảm xúc

Các giá trị này tăng lên qua các bước nhảy cảm xúc (ajump, fjump, hjump) được kích hoạt bởi các quy tắc suy luận, và giảm dần tự nhiên về đường cơ sở qua mỗi lượt.

Mạng lưới niềm tin

PARRY có hơn 200 niềm tin được lưu trong tệp bel :

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

Mỗi niềm tin có một danh mục (HUM = bệnh nhân, HUM2 = người khác, DOC = bác sĩ, INT = thẩm vấn, INN = ý định) và một độ mạnh (0-5). Các quy tắc suy luận (TH2, EMOTE, IF) kết nối các niềm tin với nhau :

  • TH2 : nếu niềm tin A vượt ngưỡng, nó mạnh lên và hệ quả tăng lên
  • EMOTE : nếu niềm tin vượt ngưỡng, nó kích hoạt bước nhảy cảm xúc (giận/sợ/tổn thương)
  • IF : điều kiện -- nếu A đúng, thì B trở nên đúng ở một mức độ nào đó

Hệ thống phân cấp ảo tưởng (flare system)

Phần hấp dẫn nhất của PARRY là hệ thống "flare" -- một chuỗi leo thang dần dần dẫn đến ảo tưởng trung tâm :

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

Mỗi từ khóa kích hoạt một phản hồi được viết sẵn (qua so khớp mẫu), và nếu người đối thoại theo chủ đề, PARRY dần dần trôi dạt về ảo tưởng trung tâm bị ngược đãi. Khi một flare bị "kích hoạt", nó trở nên không hoạt động (deadFlares) -- PARRY chuyển sang cái tiếp theo, mô phỏng một người đối thoại đang đào sâu chủ đề.

Tại sao PARRY là lịch sử

Năm 1972, PARRY đã vượt qua một biến thể của bài kiểm tra Turing : các bác sĩ tâm thần nhận được bản ghi chép các cuộc trò chuyện với PARRY và với bệnh nhân hoang tưởng thực, và không thể phân biệt được một cách đáng tin cậy. Đó là chương trình đầu tiên làm được điều này, 40 năm trước LLM.

➡ Xem mã nguồn


RFC 439 : Khi ELIZA gặp PARRY

Ngày 18 tháng 9 năm 1972, hai chương trình đã nói chuyện với nhau lần đầu tiên trong lịch sử -- theo nghĩa đen : đây là cuộc trò chuyện liên AI đầu tiên từng được ghi lại. ELIZA (chạy trên BBN ở Boston) và PARRY (chạy ở Stanford) đã trò chuyện qua ARPANET, được chuyển tiếp bởi các nhà vận hành con người gõ đầu ra của cái này thành đầu vào của cái kia. Bản ghi đã được công bố là RFC 439 ("PARRY Encounters the DOCTOR").

Đây là một đoạn trích cho thấy nó trông như thế nào :

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

Nó mạch lạc một cách đáng ngạc nhiên. ELIZA làm công việc của nhà trị liệu : diễn giải lại, hỏi han, khám phá. PARRY làm công việc của bệnh nhân hoang tưởng : phàn nàn, buộc tội, thể hiện sự ngờ vực. Cả hai chương trình đều hoàn toàn trong vai trò của mình -- không phải vì chúng "hiểu" tình huống, mà vì cơ chế tương ứng của chúng (mẫu ELIZA + mô hình cảm xúc PARRY) tạo ra các phản hồi tình cờ khớp với nhau.

Repo có thể tái tạo cuộc trò chuyện này với :

bun run meeting

Mô phỏng chạy 25 lượt tự động giữa hai bot, với một chủ đề khởi đầu ngẫu nhiên (ngựa, tội phạm có tổ chức, cảm xúc...). Vì cả ELIZA và PARRY đều có các yếu tố phi tất định (round-robin của ELIZA, ngẫu nhiên hóa của PARRY), mỗi lần chạy tạo ra một cuộc trao đổi khác nhau.

Điều nổi bật về ELIZA vs PARRY là chúng ta có hai chương trình -- một không có trạng thái nội tại, một có mô hình cảm xúc hoàn chỉnh -- cùng nhau tạo ra một cuộc trò chuyện trông như có chủ đích. Đối với năm 1972, điều đó thật kinh ngạc.


ALICE (1995) : So khớp mẫu quy mô lớn

ALICE (Artificial Linguistic Internet Computer Entity) được tạo bởi Richard Wallace vào năm 1995, và đã giành Giải Loebner ba lần (2000, 2001, 2004). Trong khi ELIZA có vài trăm quy tắc và PARRY có vài nghìn, ALICE có 99.524 -- phân bố trong 66 tệp AIML.

AIML : Ngôn ngữ của các danh mục

AIML (Artificial Intelligence Markup Language) là một định dạng XML để định nghĩa các cặp câu hỏi-trả lời :

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

Nhưng sức mạnh của ALICE đến từ các wildcard và SRAI (Symbolic Reduction) :

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- tương đương <srai><star/></srai> -->
  </template>
</category>

SRAI cho phép ALICE chuyển hướng đầu vào sang một danh mục khác, tạo ra một chuỗi rút gọn :

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

Đó là cơ chế mang lại cho ALICE sự linh hoạt : thay vì viết một phản hồi cho mọi cách diễn đạt có thể, ta viết một phản hồi chuẩn và chuyển hướng các biến thể tới nó. Giới hạn độ sâu là 10 -- quá giới hạn, ALICE bỏ cuộc để tránh vòng lặp vô hạn (được tránh cẩn thận trong thiết kế danh mục, nhưng lưới an toàn vẫn cần thiết).

Cách ALICE so khớp mẫu

Các mẫu được sắp xếp theo độ đặc hiệu : những mẫu có ít wildcard nhất được thử trước. Wildcard * và _ bắt bất kỳ chuỗi từ nào. Engine biên dịch mỗi mẫu thành regex, sau đó lặp qua các danh mục đã sắp xếp cho đến khi tìm thấy kết quả khớp.

// Triển khai TypeScript của chúng tôi -- đơn giản nhưng trung thành
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

Tại sao ALICE thống trị Loebner

99.524 danh mục, đó là một con số thay đổi mọi thứ. ELIZA có vẻ thông minh vì vài quy tắc của nó được thiết kế tốt cho một bối cảnh cụ thể (trị liệu). ALICE bao phủ quá nhiều chủ đề đến nỗi nó tạo ấn tượng về một nền tảng văn hóa thực sự : khoa học, chính trị, hài hước, thể thao, cảm xúc, tất cả đều có.

➡ Xem mã nguồn


Jabberwacky (1997) và Cleverbot (2008) : Sự đứt gãy nhận thức luận

Tất cả các bot trước đều chia sẻ một giả định : phải viết các câu trả lời. ELIZA có quy tắc S-expression, PARRY có mẫu chọn lọc, ALICE có danh mục AIML. Rollo Carpenter đã đi theo hướng hoàn toàn ngược lại : nếu chúng ta không viết gì cả thì sao?

Ý tưởng

Jabberwacky (ra mắt khoảng năm 1997, trở thành Cleverbot năm 2008) không lưu trữ bất kỳ quy tắc nào. Nó lưu trữ toàn bộ lịch sử hội thoại trong một bản ghi phẳng, và khi ai đó nói chuyện với nó, nó tìm trong lịch sử này thời điểm tương tự nhất và sử dụng lại những gì đã được nói sau đó :

Người dùng : "hello"
  ↓
Tìm : đã có ai từng nói "hello" trước đây chưa?
  ↓
Có, trong phiên #3, dòng 14, ai đó đã nói "hello" và bot trả lời "hi there!"
  ↓
Trả lời : "hi there!"

Không có mẫu. Không có ngữ pháp. Không có XML. Chỉ là một kho lưu trữ khổng lồ về những điều mọi người đã nói với nhau, được tái sử dụng vào đúng thời điểm. Đó là định nghĩa của sự nổi trội (emergence).

Triển khai TypeScript

Port TypeScript tái tạo kiến trúc chính xác này :

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 lignes seed + historique"]
    B --> C["withReplies()<br>extrait les paires<br>(ligne → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>context avant cette ligne)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidats triés"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Pick"| K["Reply = reply.text<br>de la paire gagnante"]
    J -->|"Aucun"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append au transcript<br>save() → JSON"]
    L --> M

Đây là trái tim của việc chấm điểm -- heuristic của riêng chúng tôi lấy cảm hứng từ các mô tả công khai của Cleverbot :

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65) : độ tương tự giữa đầu vào người dùng và dòng lịch sử
  • contextFit (0.25) : độ tương tự giữa hội thoại gần đây và bối cảnh trước dòng lịch sử
  • recencyBonus (0.10) : ký ức gần đây có trọng số cao hơn một chút (tính cách bot thay đổi theo thời gian)

Việc chọn mang tính xác suất (roulette-wheel selection) : ứng viên tốt nhất thắng thường xuyên hơn, nhưng không phải lúc nào -- điều này tạo ra sự đa dạng.

Cleverbot : Hai cải tiến được ghi nhận

Cleverbot thêm hai cơ chế vào khái niệm cơ bản của Jabberwacky :

  1. Học tập đa người : hàng triệu người dùng đóng góp vào cùng một bản ghi chung. Một phản hồi rút từ lịch sử có thể đến từ một giọng điệu hoàn toàn khác với cuộc hội thoại hiện tại -- điều này giải thích tại sao Cleverbot đột nhiên thay đổi tính cách.

  2. Học tập trì hoãn : những gì bạn nói với Cleverbot trong một phiên KHÔNG có sẵn để so khớp trong cùng phiên đó. Các dòng mới được đánh dấu pending và chỉ có thể so khớp sau khi "hợp nhất" giữa các phiên -- điều này giải thích tại sao bạn không thể dạy Cleverbot một sự thật và sử dụng lại nó trong cùng cuộc trò chuyện.

// Cleverbot : các dòng gần đây vô hình cho đến khi hợp nhất
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() được gọi khi khởi động, không phải trong phiên

Port TypeScript triển khai cả hai hành vi này : các dòng có cờ consolidated, và mỗi phiên REPL bắt đầu bằng việc hợp nhất các dòng đang chờ.

➡ Xem mã nguồn


Phân tích port TypeScript : Thiết kế một kiến trúc chung

Xây dựng năm bot này trong cùng một ngôn ngữ là đối mặt với một câu hỏi thú vị : liệu chúng ta có thể tái sử dụng code giữa các kiến trúc khác nhau như vậy không?

Câu trả lời là : rất ít. Mỗi bot có một vòng lặp chính khác nhau về cơ bản :

Bot Vòng lặp chính Dữ liệu Học tập
ELIZA Stack từ khóa → phân tách → tái hợp Script .ela dạng S-expression Không
PARRY Tokenization → mẫu chọn lọc / flare / từ khóa / suy luận 58 tệp PDP-10 (từ điển, niềm tin, quy tắc) Không
ALICE Mẫu đã sắp xếp → regex → template AIML → SRAI đệ quy 66 tệp AIML XML Không
Jabberwacky Tương tự → bối cảnh → độ mới → chọn có trọng số Bản ghi JSON (lớn dần khi dùng) Liên tục
Cleverbot Giống Jabberwacky + pending/consolidated + personas Bản ghi JSON + hạt giống đa người Trì hoãn (giữa phiên)

Điều chúng chia sẻ là giao diện CLI và cơ sở hạ tầng TypeScript (biome cho lint, tsx cho thực thi). Phần còn lại cụ thể cho từng kiến trúc.

Các lựa chọn thiết kế chung

1. Trung thành với dữ liệu gốc. Đối với ELIZA, PARRY và ALICE, chúng tôi sử dụng các tệp gốc -- script ELIZA được tìm thấy trong kho lưu trữ Weizenbaum năm 2021, mã PARRY gốc từ PDP-10 (58 tệp), AIML Free ALICE v1.6. Không dịch thuật, không viết lại. Các bot hoạt động như bản gốc vì chúng sử dụng cùng dữ liệu.

2. Clean-room cho các phần độc quyền. Jabberwacky và Cleverbot khác : mã nguồn của chúng chưa bao giờ được công bố (Existor/Rollo Carpenter giữ nó làm độc quyền). Do đó các port là clean-room reimplementation -- được xây dựng hoàn toàn từ các mô tả công khai về hành vi. Không có dòng code hay dữ liệu độc quyền nào bị sao chép.

3. Phụ thuộc tối thiểu. Yêu cầu thực sự duy nhất là TypeScript. ALICE sử dụng dom-js để phân tích cú pháp XML của các tệp AIML (66 tệp, 99.524 danh mục, tự phân tích XML sẽ lãng phí thời gian). Mọi thứ khác là TypeScript thuần.


Từ chatbot tượng trưng đến LLM : Bước nhảy vọt về khái niệm

Năm bot chúng ta vừa xem đều có một đặc điểm cơ bản : chúng mang tính tượng trưng (symbolic). "Kiến thức" của chúng được lưu trữ dưới dạng các biểu tượng rõ ràng -- mẫu văn bản, bảng quy tắc, danh mục XML, dòng ghi chép. Không có biểu diễn số nào của ngôn ngữ trong bất kỳ hệ thống nào trong số này.

Điều đó cũng có nghĩa là chúng đều có cùng một trần kính : chúng chỉ có thể trả lời những gì đã được lên kế hoạch hoặc ghi lại rõ ràng. ELIZA lạc đường nếu bạn ra khỏi khuôn khổ trị liệu. PARRY không thể nói về thời tiết. ALICE không học gì từ các cuộc trò chuyện. Jabberwacky chỉ có thể trả lời bằng những câu đã từng được nói.

LLM (Large Language Models) vượt qua trần kính này bằng cách thay đổi hoàn toàn mô hình : thay vì thao tác các biểu tượng, chúng chuyển đổi ngôn ngữ thành các con số và học các mối quan hệ thống kê giữa các con số này. Chúng không lưu trữ các câu trả lời được viết sẵn -- chúng tạo ra từng token ngay lập tức bằng cách tính toán xác suất. Hãy nhanh chóng xem cách nó hoạt động.

1. Tokenization

Bước đầu tiên là cắt văn bản thành các token -- các đơn vị nhỏ hơn từ nhưng lớn hơn ký tự :

"Je ne comprends pas"
  → ["Je", " ne", " comprend", "s", " pas"]

Mỗi token có một ID số trong một từ vựng (thường 32.000 đến 128.000 token cho các mô hình gần đây). Sự phân mảnh này cho phép mô hình xử lý các từ chưa từng thấy bằng cách chia chúng thành các từ con đã biết.

2. Embedding

Mỗi token ID được chuyển đổi thành một vector -- một mảng các số thực dấu phẩy động (thường 4096 chiều cho mô hình kích thước trung bình). Vector này là một embedding mã hóa ý nghĩa của token trong một không gian toán học nơi các token có ngữ nghĩa gần nhau có vector gần nhau :

vecteur("roi") − vecteur("homme") + vecteur("femme") ≈ vecteur("reine")

Thuộc tính này nảy sinh từ quá trình huấn luyện -- không ai lập trình nó một cách rõ ràng. Nó là hệ quả của cách các từ được sử dụng trong các bối cảnh tương tự.

3. Attention

Cơ chế attention (được giới thiệu bởi bài báo "Attention is All You Need" năm 2017) là thứ đã làm cho LLM khả thi. Đối với mỗi token, attention tính toán token nào khác trong câu là quan trọng để hiểu token này :

"La banque a refusé mon prêt."
     ↑
Token "banque" nhìn : "refusé", "prêt" → hiểu rằng nó là một tổ chức tài chính

"Je vais me promener sur la banque."
     ↑
Token "banque" nhìn : "promener", "sur" → hiểu rằng nó là một bờ sông

Attention cho phép mô hình nắm bắt ngữ cảnh -- mỗi token được hiểu dựa trên những gì xung quanh nó, không phải riêng lẻ.

4. Dự đoán token tiếp theo

Việc huấn luyện một LLM có vẻ đơn giản một cách lừa dối : chúng ta cho nó xem một văn bản, giấu token cuối cùng, và yêu cầu nó dự đoán. Sau đó lặp lại hàng tỷ lần.

Input: "Je ne comprends"
Ẩn: "pas"
Dự đoán của mô hình : "pas" (xác suất 0.87), "rien" (0.05), "jamais" (0.02)...

Mục tiêu là tối đa hóa xác suất của token đúng tại mỗi vị trí. Đây được gọi là next-token prediction. Trong quá trình huấn luyện, mô hình điều chỉnh hàng tỷ tham số của nó để giảm thiểu lỗi dự đoán trên hàng terabyte văn bản.

Tại thời điểm suy luận (khi chúng ta nói chuyện với nó), mô hình tạo ra từng token một trong vòng lặp :

Token 1: "Je"    (input: "Parle-moi de toi.")
Token 2: "suis"  (input: "Parle-moi de toi. Je")
Token 3: "un"    (input: "Parle-moi de toi. Je suis")
Token 4: "chatbot" (input: "Parle-moi de toi. Je suis un")
...

Mỗi token được lấy mẫu theo xác suất của nó (temperature, top-k, top-p kiểm soát mức độ "sáng tạo"). Và chỉ vậy thôi. Hàng tỷ tham số làm điều này hàng nghìn lần.

Điều gì thay đổi về cơ bản

Khía cạnh Bot tượng trưng (ELIZA, PARRY, ALICE) LLM hiện đại
Biểu diễn Từ và quy tắc rõ ràng Vector số (embedding)
Tạo sinh Chọn từ phản hồi được viết sẵn Dự đoán xác suất từng token
Kiến thức Lưu trong tệp quy tắc Mã hóa trong trọng số mạng
Học tập Thủ công (viết quy tắc) Tự động (huấn luyện trên kho ngữ liệu)
Độ bền vững Không có ngoài mẫu đã định Tổng quát hóa với đầu vào chưa thấy
Khả năng diễn giải Hoàn hảo (có thể đọc quy tắc) Hạn chế (hộp đen)

Chatbot cổ điển minh bạch nhưng mong manh. LLM bền bỉ nhưng tối nghĩa. Cả hai cách tiếp cận vẫn tồn tại đến ngày nay -- không phải là đối thủ cạnh tranh, mà là công cụ cho các nhu cầu khác nhau.

Nếu bạn muốn tìm hiểu sâu hơn về cách LLM hoạt động bên trong, video này là một tài nguyên tuyệt vời:

Nếu bạn muốn tìm hiểu sâu hơn về cách LLM hoạt động bên trong, video này là một tài nguyên tuyệt vời:

How LLMs Work — YouTube

Luna Protocol : Sự tổng hợp hiện đại

Các bài viết về Luna Protocol (liên kết bên dưới) đại diện cho sự tổng hợp tinh tế nhất của mọi thứ chúng ta vừa thấy : một bot Discord hiện đại kết hợp LLM cục bộ với hệ thống hành vi tinh vi, được xây dựng trên những bài học của 60 năm AI hội thoại.

Luna Protocol : tôi đã tạo bot Discord tự động mô phỏng con người

Bài viết này trình bày chi tiết kiến trúc hoàn chỉnh của một bot Discord dựa trên LLM :

  • Hệ thống kích hoạt ưu tiên (mention > DM > tên > từ khóa > follow-up > ngẫu nhiên)
  • Hành vi con người : tập trung thay đổi, lỗi gõ, ngập ngừng (15%), quên (3%), mệt mỏi chủ đề
  • Lịch ngủ : bot ngủ, chậm lại, hoặc phớt lờ tùy theo giờ
  • Pipeline TTS : tổng hợp giọng nói qua Piper + ffmpeg → tin nhắn thoại Discord
  • Streaming thời gian thực : LLM phát ra từng token trên một bus sự kiện có kiểu

Điều kết nối bài viết này với các chatbot lịch sử là cùng một cuộc tìm kiếm : khiến người ta tin rằng họ đang nói chuyện với một con người. ELIZA làm điều đó với gương văn bản. PARRY với mô hình cảm xúc. ALICE với 99k danh mục. Luna Protocol làm điều đó với một LLM fine-tune + hệ thống hành vi mô phỏng các khuyết điểm của con người.

Luna Protocol : tại sao tôi fine-tune mô hình 1.5B

Bài viết thứ hai khám phá fine-tuning và few-shot priming. Khám phá trung tâm : một mô hình nhỏ hơn (1.5B) được huấn luyện trên ít dữ liệu hơn (50k mẫu) vượt trội hơn mô hình lớn hơn (3B) khi được mồi đúng cách với các ví dụ few-shot.

Đó là một bài học cộng hưởng trực tiếp với các chatbot lịch sử :

  • ELIZA cho thấy với vài quy tắc được thiết kế tốt, ta có thể mô phỏng sự hiểu biết
  • ALICE cho thấy với 99k danh mục, ta có thể mô phỏng kiến thức tổng quát
  • Luna Protocol cho thấy với fine-tuning tốt và 5 ví dụ few-shot, một LLM nhỏ có thể mô phỏng con người

Kỹ thuật khác nhau, nhưng nguyên tắc giống nhau : chất lượng dữ liệu và độ chính xác của hệ thống quan trọng hơn kích thước thô.


Kết luận : Ba điều cần nhớ

1. AI hội thoại không bắt đầu với ChatGPT. ELIZA đã 60 tuổi. PARRY đã vượt qua bài kiểm tra Turing vào năm 1972. ALICE đã thắng Loebner ba lần. Jabberwacky đã đặt nền móng cho học tập dựa trên bản ghi, mà Cleverbot đã công nghiệp hóa trên quy mô lớn. Mỗi cách tiếp cận đều mang một mảnh ghép.

2. Nhiều dữ liệu hơn ≠ thông minh hơn. Bản ghi của Jabberwacky không có quy tắc. 99k danh mục của ALICE không học hỏi. Fine-tuning của Luna Protocol trên 50k mẫu vượt trội hơn mô hình 3B. Sự thông thường nói "càng to càng tốt" -- lịch sử chatbot cho thấy kiến trúc và thiết kế quan trọng không kém kích thước.

3. Vấn đề vẫn như cũ suốt 60 năm. Làm thế nào để khiến một con người tin rằng họ đang nói chuyện với một con người khác? ELIZA trả lời bằng gương văn bản. PARRY bằng sự tức giận giả định. ALICE bằng sự thật. Luna Protocol bằng một LLM biết ngủ và gõ sai. Giải pháp thay đổi, nhu cầu vẫn còn.

Repo là mã nguồn mở -- bạn có thể clone, chạy từng bot, và tự thấy 60 năm AI hội thoại nằm gọn trong một repo TypeScript duy nhất.

Tài nguyên Liên kết
GitHub Repo fox3000foxy/chatbots
Luna Protocol -- kiến trúc bot Đọc bài viết
Luna Protocol -- fine-tuning few-shot Đọc bài viết
Script ELIZA gốc anthay/ELIZA
Mã nguồn PARRY gốc lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 gốc PARRY Encounters the DOCTOR
Giải thích tuyệt vời về cách LLM hoạt động https://www.youtube.com/watch?v=YmLp8qe87A0

จาก ELIZA สู่ LLM : 60 ปีของ AI เชิงสนทนา สร้างใหม่ใน TypeScript

ELIZA, PARRY, ALICE, Jabberwacky, Cleverbot -- ห้าสถาปัตยกรรมที่แตกต่างกันโดยสิ้นเชิงสำหรับปัญหาเดียวกัน พอร์ตมาเป็น TypeScript พร้อมข้อมูลต้นฉบับ ตั้งแต่ปี 1966 ถึง LLM สมัยใหม่ ว่าแล้วว่า AI เชิงสนทนาเรียนรู้ที่จะพูดได้อย่างไร และสิ่งที่ repo แชทบอทสอนเราเกี่ยวกับ 60 ปีแห่งการวิจัย

จาก ELIZA สู่ LLM : 60 ปีของ AI เชิงสนทนา สร้างใหม่ใน TypeScript

ปี 1966 Joseph Weizenbaum เขียนโค้ด MAD-SLIP 420 บรรทัดบน IBM 7094 เพื่อสร้างแชทบอทตัวแรกในประวัติศาสตร์ โปรแกรมชื่อ ELIZA มันจำลองนักจิตบำบัดแนวโรเจอเรียนด้วยแพทเทิร์นพื้นฐานและการสลับประโยค หกทศวรรษต่อมา AI เชิงสนทนากลายเป็นหัวข้อหลัก -- ChatGPT, Claude, Gemini อยู่ในการสนทนาทุกครั้ง

แต่ระหว่างสองขั้วนี้ มี PARRY (แชทบอทโรคหวาดระแวง, 1972), ALICE (ราชาแห่ง AIML 99,000 หมวดหมู่, 1995), Jabberwacky (ตัวแรกที่เรียนรู้โดยไม่มีกฎ, 1997), และ Cleverbot (ผู้สืบทอดเชิงอุตสาหกรรม, 2008) ห้าโปรแกรม ห้าสถาปัตยกรรม หนึ่งปัญหาเดียว : ทำให้เครื่องจักรพูดได้

Repo นี้ประกอบด้วยบอททั้งห้าตัว พอร์ตมาเป็น TypeScript พร้อมข้อมูลต้นฉบับ -- สคริปต์ ELIZA, พจนานุกรม PARRY, ไฟล์ AIML ของ ALICE แต่ละพอร์ตทำงานอิสระ พร้อมใช้งาน และมีเอกสารครบถ้วน เป้าหมายไม่ใช่แค่ทำให้มันทำงานได้ : คือการเข้าใจว่ามันทำงานอย่างไร ทำไมมันถึงสร้างประวัติศาสตร์ และสถาปัตยกรรมของมันสอนอะไรเราเกี่ยวกับ AI เมื่อวาน... และวันนี้

bun run eliza    # คุยกับ ELIZA (1966)
bun run parry    # คุยกับ PARRY (1972)
bun run alice    # คุยกับ ALICE (1995)
bun run jabber   # คุยกับ Jabberwacky
bun run cleverbot # คุยกับ Cleverbot
bun run meeting  # ELIZA vs PARRY อัตโนมัติ

เราจะเจาะลึกแต่ละบอท ดูโค้ดของมัน แล้วเชื่อมโยงกับ LLM สมัยใหม่ผ่านบทความเกี่ยวกับ Luna Protocol


ELIZA (1966) : ศิลปะแห่งการทำให้เชื่อว่าคุณเข้าใจ

เริ่มจากตัวที่เก่าที่สุด และอาจจะน่าประทับใจที่สุดในความเรียบง่าย ELIZA ไม่มีปัญญา ในความหมายสมัยใหม่ ไม่มีโครงข่ายประสาทเทียม ไม่มีสถิติ ไม่มีการเรียนรู้ แค่แพทเทิร์นข้อความและการสลับเล็กน้อย

หลักการ

สคริปต์ DOCTOR (เวอร์ชันนักจิตบำบัด) ทำงานด้วยตาราง คีย์เวิร์ด แต่ละตัวเชื่อมโยงกับ แพทเทิร์นการแยกส่วน และ กฎการประกอบใหม่ นี่คือกฎทั่วไป :

(HELLO
    ((0)
        (HOW DO YOU DO.  PLEASE STATE YOUR PROBLEM)))

HELLO คือคีย์เวิร์ด 0 คือแพทเทิร์นการแยกส่วนที่บอกว่า "จับทุกอย่างที่ตามมา" (เหมือน wildcard) HOW DO YOU DO. PLEASE STATE YOUR PROBLEM. คือกฎการประกอบใหม่ เท่านั้นเอง

เมื่อคุณพูด "Hello, I'm sad today" ELIZA :

  1. แปลงข้อความเป็นตัวพิมพ์ใหญ่ : HELLO I'M SAD TODAY
  2. สแกนแต่ละคำเทียบกับตารางคีย์เวิร์ด
  3. เจอ HELLO → ดันลงกองซ้อนคีย์เวิร์ด
  4. เอาคีย์เวิร์ดที่มีลำดับความสำคัญสูงสุด
  5. ลองแต่ละแพทเทิร์นการแยกส่วนตามลำดับ
  6. ถ้าตรงกัน เลือกกฎการประกอบใหม่ถัดไป (round-robin)
  7. แทนที่ (1), (2) ฯลฯ ด้วยส่วนที่ถูกจับ

แต่ส่วนที่ฉลาดจริงๆ คือ กฎ PRE ดูนี่ :

(MY
    ((0)
        (PRE (1 0) (=YOU))))

เมื่อ ELIZA ตรงกับ MY มันแปลงส่วนที่เหลือของประโยค (ที่ถูกจับโดย 0) ผ่านกฎ PRE แล้วส่งผลลัพธ์กลับเข้าไปใหม่ราวกับว่าผู้ใช้เพิ่งพูดคีย์เวิร์ดใหม่ อย่างเป็นรูปธรรม :

คุณพูด : "My mother hates me"
  → PRE แปลง : "YOUR MOTHER HATES YOU"
  → ส่งกลับราวกับคุณเพิ่งพูดมัน
  → อาจตรงกับ "YOU" → คำตอบใหม่

นี่คือสาเหตุที่ ELIZA ดูเหมือนเข้าใจความแตกต่างระหว่าง "ฉัน" และ "คุณ" -- มันไม่ใช่ความเข้าใจ มันคือการแปลงเชิงกลที่ออกแบบมาอย่างสมบูรณ์แบบ

นี่คือโฟลว์ทั้งหมด จากการพิมพ์ของผู้ใช้ถึงคำตอบ :

flowchart TD
    A["User input:<br>'Hello, I'm sad'"] --> B["elizaUppercase()<br>normalise la ponctuation"]
    B --> C["splitUserInput()<br>découpe en mots"]
    C --> D["Build keyword stack<br>ordonné par priorité"]
    D --> E{"Stack non-vide?"}
    E -->|"Oui"| F["Pop highest-priority keyword"]
    E -->|"Non"| G{"Memory recall?"}
    G -->|"Oui"| H["Recall past user statement"]
    G -->|"Non"| I["Fallback: zNONE rule"]
    I --> J["Return response"]
    H --> J
    F --> K["Match decomposition patterns"]
    K --> L{"Match found?"}
    L -->|"Non"| M{"Linked keyword?"}
    M -->|"Oui"| N["Push linked keyword to stack"]
    N --> E
    M -->|"Non"| O["Return NOMATCH"]
    O --> J
    L -->|"Oui"| P["Select next reassembly (round-robin)"]
    P --> Q{"Reassembly type?"}
    Q -->|"PRE"| R["Transform words (I→YOU)<br>push link keyword"]
    R --> N
    Q -->|"NEWKEY"| S["Skip to next keyword"]
    S --> E
    Q -->|"Standard"| T["Expand (1), (2), (0)<br>into final response"]
    T --> J

สิ่งที่ทำให้มันน่าเชื่อถือ

Weizenbaum เลือกอย่างชาญฉลาด : จิตบำบัดแนวโรเจอเรียน วิธีนี้คือการสะท้อนคำพูดของผู้ป่วยโดยไม่ตีความ "ผมเศร้า" → "คุณบอกว่าคุณเศร้า" นี่คือสิ่งที่ ELIZA ทำได้ -- และเนื่องจากเป็นเทคนิคการบำบัดที่ได้รับการยอมรับ ไม่มีใครคิดว่ามันแปลก

ในพอร์ต TypeScript

พอร์ตโหลดสคริปต์ .ela (รูปแบบ S-expression ดั้งเดิม), แยกส่วนทั้งหมด (รวมถึงการเข้ารหัส Hollerith -- รูปแบบสตริงจากยุค 60s), และดำเนินวงจรเดียวกัน : พิมพ์ใหญ่ → แยก → กองซ้อนคีย์เวิร์ด → การแยกส่วน → การประกอบใหม่ → PRE/transforms

➡ ดูซอร์สโค้ด


PARRY (1972) : แชทบอทตัวแรกที่มีอารมณ์

หกปีหลังจาก ELIZA, Kenneth Colby (จิตแพทย์ที่ Stanford) สร้าง PARRY : แชทบอทที่จำลองผู้ป่วยโรค จิตเภทแบบหวาดระแวง ในขณะที่ ELIZA เป็นกระจกเปล่า PARRY มี แบบจำลองอารมณ์ภายใน ที่แท้จริง

แบบจำลองอารมณ์

PARRY มีตัวแปรต่อเนื่องสี่ตัวที่เปลี่ยนแปลงในแต่ละรอบสนทนา :

ตัวแปร เส้นฐาน การลดลง/รอบ คำอธิบาย
ANGER 0 −1.0 ความเป็นศัตรู, ความหงุดหงิด
FEAR 0 −0.2 ความหวาดระแวง (ลดลงช้าหลังจากเริ่มมีอาการหลอน)
MISTRUST 0 −0.05 ความไม่ไว้วางใจ (ลดลงช้ามาก)
HURT 0 −0.5 ความเจ็บปวดทางอารมณ์

ค่าเหล่านี้เพิ่มขึ้นผ่าน การกระโดดทางอารมณ์ (ajump, fjump, hjump) ที่ถูกกระตุ้นโดยกฎการอนุมาน และลดลงตามธรรมชาติกลับสู่เส้นฐานในแต่ละรอบ

เครือข่ายความเชื่อ

PARRY มีความเชื่อ 200+ รายการเก็บในไฟล์ bel :

(BELIEF (FEAR 5) ((PAT PARANOIA)) BELIEF GROUP)

แต่ละความเชื่อมีหมวดหมู่ (HUM = ผู้ป่วย, HUM2 = ผู้อื่น, DOC = หมอ, INT = การสอบสวน, INN = เจตนา) และความแข็งแกร่ง (0-5) กฎการอนุมาน (TH2, EMOTE, IF) เชื่อมโยงความเชื่อถึงกัน :

  • TH2 : ถ้าความเชื่อ A เกินเกณฑ์ มันจะแข็งแกร่งขึ้นและผลลัพธ์ของมันเพิ่มขึ้น
  • EMOTE : ถ้าความเชื่อเกินเกณฑ์ มันจะกระตุ้นการกระโดดทางอารมณ์ (anger/fear/hurt)
  • IF : เงื่อนไข -- ถ้า A เป็นจริง แล้ว B จะเป็นจริงในระดับหนึ่ง

ลำดับชั้นของอาการหลอน (ระบบ Flare)

ส่วนที่น่าทึ่งที่สุดของ PARRY คือระบบ "flares" -- ห่วงโซ่การเพิ่มระดับที่ค่อยๆ นำไปสู่อาการหลอนหลัก :

HORSE → "I USED TO GO TO THE RACES SOMETIMES."
  ↓
RACE → "I KNOW PEOPLE WHO GO TO THE TRACK."
  ↓
MONEY → "MONEY IS TIGHT. I DON'T HAVE MUCH."
  ↓
GAMBLE → "I'VE DONE SOME GAMBLING. IT'S DANGEROUS."
  ↓
BOOKIE → "BOOKIES ARE CROOKED. THEY WORK FOR THE MAFIA."
  ↓
CHEAT → "PEOPLE ARE ALWAYS TRYING TO CHEAT ME."
  ↓
MAFIA → "THE MAFIA IS OUT TO GET ME."

แต่ละคีย์เวิร์ดกระตุ้นคำตอบที่เขียนไว้ล่วงหน้า (ผ่านการจับคู่แพทเทิร์น) และถ้าคู่สนทนาตามหัวข้อ PARRY จะค่อยๆ เลื่อนไปสู่อาการหลอนกลางเรื่องการถูกข่มเหง เมื่อ flare ถูก "กระตุ้น" มันจะกลายเป็นไม่ทำงาน (deadFlares) -- PARRY ไปยังอันถัดไป จำลองคู่สนทนาที่กำลังเจาะลึกหัวข้อ

ทำไม PARRY ถึงเป็นประวัติศาสตร์

ปี 1972 PARRY ผ่านการทดสอบทัวริงรูปแบบหนึ่ง : จิตแพทย์ได้รับบทถอดความสนทนากับ PARRY และกับผู้ป่วยหวาดระแวงจริง และ ไม่สามารถแยกแยะได้อย่างน่าเชื่อถือ มันเป็นโปรแกรมแรกที่ทำได้ 40 ปีก่อน LLM

➡ ดูซอร์สโค้ด


RFC 439 : เมื่อ ELIZA พบ PARRY

วันที่ 18 กันยายน 1972 โปรแกรมสองตัวพูดคุยกันเป็นครั้งแรกในประวัติศาสตร์ -- ตามตัวอักษร : นี่คือ การสนทนาระหว่าง AI ครั้งแรก ที่เคยบันทึกไว้ ELIZA (รันบน BBN ที่บอสตัน) และ PARRY (รันที่สแตนฟอร์ด) สนทนากันผ่าน ARPANET โดยมีผู้ปฏิบัติการมนุษย์คอยพิมพ์เอาต์พุตของตัวหนึ่งเป็นอินพุตของอีกตัว บทสนทนาถูกตีพิมพ์เป็น RFC 439 ("PARRY Encounters the DOCTOR")

นี่คือตัวอย่างว่ามันเป็นอย่างไร :

ELIZA: HOW DO YOU DO. PLEASE STATE YOUR PROBLEM.
PARRY: PEOPLE GET ON MY NERVES SOMETIMES.
ELIZA: I AM NOT SURE I UNDERSTAND YOU FULLY.
PARRY: YOU ARE MAKING ME ANGRY.
ELIZA: SUPPOSE YOU TELL ME ABOUT YOUR PARENTS.
PARRY: THEY ARE ALWAYS AFRAID OF SOMETHING.

มันสอดคล้องกันอย่างน่าประหลาดใจ ELIZA ทำงานนักบำบัดของมัน : ถอดความ, ถาม, สำรวจ PARRY ทำงานผู้ป่วยหวาดระแวงของมัน : บ่น, กล่าวหา, แสดงความไม่ไว้วางใจ โปรแกรมทั้งสองอยู่ในบทบาทของตนอย่างสมบูรณ์ -- ไม่ใช่เพราะพวกมัน "เข้าใจ" สถานการณ์ แต่เพราะกลไกของมัน (แพทเทิร์น ELIZA + แบบจำลองอารมณ์ PARRY) สร้างคำตอบที่บังเอิญเข้ากัน

Repo สามารถจำลองการสนทนานี้ได้ด้วย :

bun run meeting

การจำลองจะรัน 25 รอบอัตโนมัติระหว่างบอททั้งสอง ด้วยหัวข้อเริ่มต้นสุ่ม (ม้า, อาชญากรรมองค์กร, อารมณ์...) เนื่องจากทั้ง ELIZA และ PARRY มีองค์ประกอบที่ไม่ตายตัว (ELIZA round-robin, PARRY การสุ่ม) แต่ละครั้งที่รันจะให้ผลลัพธ์ต่างกัน

สิ่งที่โดดเด่นเกี่ยวกับ ELIZA vs PARRY คือเรามีสองโปรแกรม -- หนึ่งไม่มีสถานะภายใน, อีกหนึ่งมีแบบจำลองอารมณ์สมบูรณ์ -- ที่ร่วมกันสร้างบทสนทนาที่ ดูเหมือน ตั้งใจ สำหรับปี 1972 มันน่าทึ่งมาก


ALICE (1995) : การจับคู่แพทเทิร์นขนาดใหญ่

ALICE (Artificial Linguistic Internet Computer Entity) ถูกสร้างโดย Richard Wallace ในปี 1995 และชนะ รางวัล Loebner สามครั้ง (2000, 2001, 2004) ในขณะที่ ELIZA มีกฎไม่กี่ร้อยข้อและ PARRY ไม่กี่พัน ALICE มี 99,524 กฎ -- กระจายอยู่ใน 66 ไฟล์ AIML

AIML : ภาษาของหมวดหมู่

AIML (Artificial Intelligence Markup Language) คือรูปแบบ XML สำหรับกำหนดคู่คำถาม-คำตอบ :

<category>
  <pattern>WHAT IS YOUR NAME</pattern>
  <template>My name is ALICE.</template>
</category>

แต่พลังของ ALICE มาจาก wildcards และ SRAI (Symbolic Reduction) :

<category>
  <pattern>_ IS YOUR NAME</pattern>
  <template>
    <sr/>  <!-- เท่ากับ <srai><star/></srai> -->
  </template>
</category>

SRAI ทำให้ ALICE สามารถเปลี่ยนเส้นทางอินพุตไปยังหมวดหมู่อื่น สร้างห่วงโซ่การลดรูป :

Input: "WHAT'S UP?"
  → pattern "WHAT IS UP" → srai "HELLO"
    → pattern "HELLO" → template "Hi there!"

นี่คือกลไกที่ทำให้ ALICE มีความยืดหยุ่น : แทนที่จะเขียนคำตอบสำหรับทุกรูปแบบ เขียนคำตอบหลักแล้วเปลี่ยนเส้นทางรูปแบบต่างๆ ไปหามัน ข้อจำกัดความลึกคือ 10 -- เกินกว่านั้น ALICE จะยอมแพ้เพื่อหลีกเลี่ยงลูปอนันต์ (หลีกเลี่ยงอย่างระมัดระวังในการออกแบบหมวดหมู่ แต่ตาข่ายนิรภัยยังจำเป็น)

ALICE จับคู่แพทเทิร์นอย่างไร

แพทเทิร์นถูกเรียงตามความจำเพาะ : พวกที่มี wildcard น้อยที่สุดจะถูกลองก่อน Wildcard * และ _ จับลำดับคำใดก็ได้ เอ็นจินคอมไพล์แต่ละแพทเทิร์นเป็น regex แล้ววนซ้ำหมวดหมู่ที่เรียงแล้วจนกว่าจะเจอที่ตรงกัน

// การใช้งาน TypeScript ของเรา -- เรียบง่ายแต่เที่ยงตรง
function findMatch(input: string, categories: Category[]): Match | null {
  for (const cat of categories) {
    const regex = patternToRegex(cat.pattern);
    const match = input.match(regex);
    if (match) return { category: cat, wildcards: extractWildcards(match) };
  }
  return null;
}

ทำไม ALICE ถึงครอง Loebner

99,524 หมวดหมู่ คือตัวเลขที่เปลี่ยนแปลงทุกอย่าง ELIZA ดูฉลาดเพราะกฎไม่กี่ข้อของมันถูกออกแบบมาอย่างดีสำหรับบริบทเฉพาะ (การบำบัด) ALICE ครอบคลุมหลายหัวข้อจนให้ความรู้สึกว่ามีความรู้ทั่วไปจริง : วิทยาศาสตร์, การเมือง, อารมณ์ขัน, กีฬา, อารมณ์, ทุกอย่าง

➡ ดูซอร์สโค้ด


Jabberwacky (1997) และ Cleverbot (2008) : การแตกหักทางญาณวิทยา

บอทก่อนหน้านี้ทั้งหมดมีสมมติฐานร่วมกัน : ต้องเขียนคำตอบ ELIZA มีกฎ S-expression, PARRY มีแพทเทิร์นแบบเลือกเฉพาะ, ALICE มีหมวดหมู่ AIML Rollo Carpenter กลับด้านโดยสิ้นเชิง : แล้วถ้าเราไม่เขียนอะไรเลยล่ะ?

แนวคิด

Jabberwacky (เปิดตัวประมาณ 1997, กลายเป็น Cleverbot ใน 2008) ไม่ได้เก็บ กฎใดๆ มันเก็บ ประวัติการสนทนาทั้งหมด ใน transcript แบบ flat และเมื่อมีคนคุยกับมัน มันจะค้นหาในประวัติศาสตร์นี้หาช่วงเวลาที่คล้ายที่สุดและใช้สิ่งที่ถูกพูดหลังจากนั้น :

ผู้ใช้ : "hello"
  ↓
ค้นหา : มีใครเคยพูด "hello" มาก่อนไหม?
  ↓
ใช่ ในเซสชัน #3, บรรทัดที่ 14, มีคนพูด "hello" และบอทตอบ "hi there!"
  ↓
ตอบ : "hi there!"

ไม่มีแพทเทิร์น ไม่มีไวยากรณ์ ไม่มี XML แค่คลังข้อมูลขนาดใหญ่ของสิ่งที่คนพูดกัน ถูกนำมาใช้ใหม่ในเวลาที่เหมาะสม นี่คือนิยามของการเกิด emergent

การใช้งาน TypeScript

พอร์ต TypeScript สร้างสถาปัตยกรรมนี้ขึ้นมาใหม่ :

flowchart TD
    A["User input:<br>'hello'"] --> B["TranscriptStore<br>332 lignes seed + historique"]
    B --> C["withReplies()<br>extrait les paires<br>(ligne → reply)"]
    C --> D["findCandidates()"]
    D --> E["relevance = similarity(input, line.text)"]
    E --> F["contextFit = similarity(recentContext,<br>context avant cette ligne)"]
    F --> G["recencyBonus = 1 / (1 + ageDays/30)"]
    G --> H["score = 0.65×relevance<br>+ 0.25×contextFit<br>+ 0.10×recency"]
    H --> I["Top K candidats triés"]
    I --> J{"pickReply()<br>roulette-wheel<br>selection"}
    J -->|"Pick"| K["Reply = reply.text<br>de la paire gagnante"]
    J -->|"Aucun"| L["Fallback: 'I have no idea<br>what to say to that yet.'"]
    K --> M["Append au transcript<br>save() → JSON"]
    L --> M

นี่คือแกนหลักของการให้คะแนน -- ฮิวริสติกของเราเองที่ได้แรงบันดาลใจจากคำอธิบายสาธารณะของ Cleverbot :

const score = 0.65 * relevance + 0.25 * contextFit + 0.10 * recencyBonus;
  • relevance (0.65) : ความคล้ายคลึงระหว่างอินพุตผู้ใช้กับบรรทัดประวัติ
  • contextFit (0.25) : ความคล้ายคลึงระหว่างการสนทนาล่าสุดกับบริบทย้อนหลังจากบรรทัดประวัติ
  • recencyBonus (0.10) : ความทรงจำล่าสุดมีน้ำหนักมากกว่าเล็กน้อย (บุคลิกบอทเปลี่ยนแปลงตามเวลา)

การเลือกเป็นแบบความน่าจะเป็น (roulette-wheel selection) : ผู้สมัครที่ดีที่สุดชนะบ่อยกว่า แต่ไม่เสมอไป -- ซึ่งให้ความหลากหลาย

Cleverbot : สองนวัตกรรมที่มีเอกสาร

Cleverbot เพิ่มกลไกสองอย่างให้แนวคิดพื้นฐานของ Jabberwacky :

  1. การเรียนรู้หลายคน : ผู้ใช้หลายล้านคนมีส่วนร่วมใน transcript ร่วมกัน คำตอบที่ดึงมาจากประวัติอาจมาจากเสียงที่แตกต่างอย่างสิ้นเชิงจากการสนทนาปัจจุบัน -- ซึ่งอธิบายว่าทำไม Cleverbot เปลี่ยนบุคลิกกะทันหัน

  2. การเรียนรู้แบบหน่วงเวลา : สิ่งที่คุณพูดกับ Cleverbot ระหว่างเซสชันจะ ไม่ พร้อมใช้งานสำหรับการจับคู่ระหว่างเซสชันนั้น บรรทัดใหม่ถูกทำเครื่องหมาย pending และจะจับคู่ได้หลังจากการ "รวม consolidated" ระหว่างเซสชันเท่านั้น -- ซึ่งอธิบายว่าทำไมคุณไม่สามารถสอนข้อเท็จจริงให้ Cleverbot และใช้มันซ้ำในการสนทนาเดียวกันได้

// Cleverbot : บรรทัดล่าสุดมองไม่เห็นจนกว่าจะรวม consolidated
const line = store.append("human", text, null, sessionId, false); // pending
// ...consolidate() ถูกเรียกเมื่อเริ่มต้น ไม่ใช่ระหว่างเซสชัน

พอร์ต TypeScript ใช้ทั้งสองพฤติกรรมนี้ : บรรทัดมีแฟล็ก consolidated และแต่ละเซสชัน REPL เริ่มด้วยการรวมบรรทัดที่รออยู่

➡ ดูซอร์สโค้ด


วิเคราะห์พอร์ต TypeScript : การออกแบบสถาปัตยกรรมร่วม

การสร้างบอททั้งห้าตัวด้วยภาษาเดียวกัน คือการเผชิญหน้ากับคำถามที่น่าสนใจ : เราสามารถแยกส่วนโค้ดระหว่างสถาปัตยกรรมที่แตกต่างกันเช่นนี้ได้ไหม?

คำตอบคือ : น้อยมาก แต่ละบอทมีลูปพื้นฐานที่แตกต่างกัน :

บอท ลูปหลัก ข้อมูล การเรียนรู้
ELIZA กองซ้อนคีย์เวิร์ด → การแยกส่วน → การประกอบใหม่ สคริปต์ .ela ในรูปแบบ S-expression ไม่มี
PARRY การทำ token → แพทเทิร์นเลือก / flare / คีย์เวิร์ด / การอนุมาน 58 ไฟล์ PDP-10 (พจนานุกรม, ความเชื่อ, กฎ) ไม่มี
ALICE แพทเทิร์นเรียง → regex → เทมเพลต AIML → SRAI แบบเรียกซ้ำ 66 ไฟล์ AIML XML ไม่มี
Jabberwacky ความคล้ายคลึง → บริบท → ความเร็วล่าสุด → เลือกแบบถ่วงน้ำหนัก JSON transcript (โตตามการใช้งาน) ต่อเนื่อง
Cleverbot เหมือน Jabberwacky + รอ/รวม consolidated + personas JSON transcript + เมล็ดหลาย persona หน่วงเวลา (ระหว่างเซสชัน)

สิ่งที่พวกเขาใช้ร่วมกันคืออินเทอร์เฟซ CLI และโครงสร้างพื้นฐาน TypeScript (biome สำหรับ lint, tsx สำหรับการทำงาน) ที่เหลือเฉพาะแต่ละสถาปัตยกรรม

ตัวเลือกการออกแบบร่วม

1. ความเที่ยงตรงต่อข้อมูลต้นฉบับ สำหรับ ELIZA, PARRY และ ALICE เราใช้ไฟล์ต้นฉบับ -- สคริปต์ ELIZA ที่พบในคลังเอกสาร Weizenbaum ปี 2021, โค้ด PARRY ดั้งเดิมจาก PDP-10 (58 ไฟล์), AIML Free ALICE v1.6 ไม่มีการแปล ไม่มีการเขียนใหม่ บอททำงานเหมือนต้นฉบับเพราะใช้ข้อมูลเดียวกัน

2. Clean-room สำหรับส่วนที่เป็นกรรมสิทธิ์ Jabberwacky และ Cleverbot ต่างกัน : โค้ดต้นฉบับไม่เคยถูกเผยแพร่ (Existor/Rollo Carpenter เก็บไว้เป็นกรรมสิทธิ์) พอร์ตจึงเป็น clean-room reimplementation -- สร้างจากคำอธิบายสาธารณะของพฤติกรรมเท่านั้น ไม่มีการคัดลอกโค้ดหรือข้อมูลที่เป็นกรรมสิทธิ์

3. การพึ่งพาน้อยที่สุด ข้อกำหนดจริงเพียงอย่างเดียวคือ TypeScript ALICE ใช้ dom-js เพื่อแยก XML ของไฟล์ AIML (66 ไฟล์, 99,524 หมวดหมู่, การแยก XML ด้วยตัวเองคงเสียเวลา) ที่เหลือคือ TypeScript ล้วน



จากแชทบอทเชิงสัญลักษณ์สู่ LLM : การก้าวกระโดดทางแนวคิด

บอททั้งห้าตัวที่เราเพิ่งดูมีลักษณะพื้นฐานร่วมกัน : พวกมันเป็น เชิงสัญลักษณ์ "ความรู้" ของมันถูกเก็บเป็นสัญลักษณ์ชัดเจน -- แพทเทิร์นข้อความ, ตารางกฎ, หมวดหมู่ XML, บรรทัด transcript ไม่มี การแสดงตัวเลขของภาษา ในระบบใดๆ เหล่านี้

ซึ่งหมายถึงพวกมันทั้งหมดมีเพดานกระจกเดียวกัน : พวกมันตอบได้เฉพาะสิ่งที่ได้รับการวางแผนหรือบันทึกไว้อย่างชัดเจน ELIZA หลงทางถ้าคุณออกจากกรอบการบำบัด PARRY พูดเรื่องอากาศไม่ได้ ALICE ไม่เรียนรู้อะไรจากการสนทนา Jabberwacky ตอบได้เฉพาะกับคำพูดที่เคยพูดมาก่อน

LLM (Large Language Models) ก้าวผ่านเพดานนี้ด้วยการเปลี่ยนกระบวนทัศน์อย่างสิ้นเชิง : แทนที่จะจัดการสัญลักษณ์ พวกมันแปลงภาษาเป็น ตัวเลข และเรียนรู้ ความสัมพันธ์ทางสถิติ ระหว่างตัวเลขเหล่านี้ พวกมันไม่เก็บคำตอบที่เขียนไว้ล่วงหน้า -- พวกมันสร้างแต่ละ token ทันทีโดยการคำนวณความน่าจะเป็น มาดูกันสั้นๆ ว่ามันทำงานอย่างไร

1. Tokenization

ขั้นแรกคือการตัดข้อความเป็น tokens -- หน่วยที่เล็กกว่าคำแต่ใหญ่กว่าตัวอักษร :

"Je ne comprends pas"
  → ["Je", " ne", " comprend", "s", " pas"]

แต่ละ token มี ID ตัวเลขในคำศัพท์ (ปกติ 32,000 ถึง 128,000 tokens สำหรับโมเดลล่าสุด) การแยกส่วนนี้ช่วยให้โมเดลจัดการคำที่ไม่เคยเห็นโดยการแบ่งเป็นคำย่อยที่รู้จัก

2. Embeddings

แต่ละ token ID ถูกแปลงเป็น เวกเตอร์ -- อาร์เรย์ของตัวเลขทศนิยม (ปกติ 4096 มิติสำหรับโมเดลขนาดกลาง) เวกเตอร์นี้คือ embedding ที่เข้ารหัสความหมายของ token ในพื้นที่ทางคณิตศาสตร์ที่ token ที่มีความหมายใกล้เคียงมีเวกเตอร์ใกล้กัน :

vector("roi") − vector("homme") + vector("femme") ≈ vector("reine")

คุณสมบัตินี้เกิดจากการฝึก -- ไม่มีใครเขียนมันไว้อย่างชัดเจน มันเป็นผลจากการที่คำถูกใช้ในบริบทที่คล้ายคลึงกัน

3. Attention

กลไก attention (นำเสนอโดยบทความ "Attention is All You Need" ในปี 2017) คือสิ่งที่ทำให้ LLM เป็นไปได้ สำหรับแต่ละ token attention จะคำนวณว่า token อื่นใดในประโยคที่สำคัญต่อการเข้าใจมัน :

"La banque a refusé mon prêt."
     ↑
Token "banque" มอง : "refusé", "prêt" → เข้าใจว่ามันคือสถาบันการเงิน

"Je vais me promener sur la banque."
     ↑
Token "banque" มอง : "promener", "sur" → เข้าใจว่ามันคือริมฝั่ง

Attention ช่วยให้โมเดลจับ บริบท -- แต่ละ token ถูกเข้าใจโดยสัมพันธ์กับสิ่งที่ล้อมรอบ ไม่ใช่แยกเดี่ยว

4. การทำนาย token ถัดไป

การฝึก LLM นั้นง่ายอย่างหลอกลวง : เราแสดงข้อความ ซ่อน token สุดท้าย และให้มันทำนาย แล้วทำซ้ำเป็นพันล้านครั้ง

Input: "Je ne comprends"
ซ่อน: "pas"
การทำนายของโมเดล : "pas" (ความน่าจะเป็น 0.87), "rien" (0.05), "jamais" (0.02)...

เป้าหมายคือการเพิ่มความน่าจะเป็นของ token ที่ถูกต้องในแต่ละตำแหน่ง เรียกว่า next-token prediction ระหว่างการฝึก โมเดลปรับพารามิเตอร์หลายพันล้านตัวเพื่อลดข้อผิดพลาดในการทำนายบนข้อความเทราไบต์

ในตอนอนุมาน (เมื่อเราคุยกับมัน) โมเดลสร้างทีละ token ในลูป :

Token 1: "Je"    (input: "Parle-moi de toi.")
Token 2: "suis"  (input: "Parle-moi de toi. Je")
Token 3: "un"    (input: "Parle-moi de toi. Je suis")
Token 4: "chatbot" (input: "Parle-moi de toi. Je suis un")
...

แต่ละ token ถูกสุ่มตามความน่าจะเป็น (temperature, top-k, top-p ควบคุมระดับ "ความคิดสร้างสรรค์") เท่านั้นเอง พารามิเตอร์หลายพันล้านตัวที่ทำแบบนี้หลายพันครั้ง

สิ่งที่เปลี่ยนแปลงโดยพื้นฐาน

แง่มุม บอทเชิงสัญลักษณ์ (ELIZA, PARRY, ALICE) LLM สมัยใหม่
การแสดงผล คำและกฎชัดเจน เวกเตอร์ตัวเลข (embeddings)
การสร้าง เลือกจากคำตอบที่เขียนไว้ล่วงหน้า ทำนายความน่าจะเป็น token ต่อ token
ความรู้ เก็บในไฟล์กฎ เข้ารหัสในน้ำหนักของเครือข่าย
การเรียนรู้ ด้วยมือ (เขียนกฎ) อัตโนมัติ (ฝึกบนคลังข้อมูล)
ความทนทาน ศูนย์นอกแพทเทิร์นที่วางแผนไว้ ทำให้ทั่วไปกับอินพุตที่ไม่เคยเห็น
การตีความ สมบูรณ์ (อ่านกฎได้) จำกัด (กล่องดำ)

แชทบอทคลาสสิก โปร่งใสแต่เปราะบาง LLM ทนทานแต่ทึบแสง ทั้งสองวิธีมีอยู่จนถึงทุกวันนี้ -- ไม่ใช่ในฐานะคู่แข่ง แต่เป็นเครื่องมือสำหรับความต้องการที่ต่างกัน

Si vous voulez approfondir le fonctionnement interne des LLM, cette vidéo est une excellente ressource :

หากคุณต้องการเจาะลึกการทำงานภายในของ LLM วิดีโอนี้เป็นแหล่งข้อมูลที่ยอดเยี่ยม:

How LLMs Work — YouTube

Luna Protocol : การสังเคราะห์สมัยใหม่

บทความเกี่ยวกับ Luna Protocol (ลิงก์ด้านล่าง) แสดงถึงการสังเคราะห์ที่สมบูรณ์ที่สุดของทุกสิ่งที่เราเพิ่งเห็น : บอท Discord สมัยใหม่ที่รวม LLM ในเครื่องกับระบบพฤติกรรมที่ซับซ้อน สร้างจากบทเรียน 60 ปีของ AI เชิงสนทนา

Luna Protocol : ฉันสร้างบอท Discord อัตโนมัติที่จำลองมนุษย์

บทความนี้ลงรายละเอียดสถาปัตยกรรมสมบูรณ์ของบอท Discord ที่ใช้ LLM :

  • ระบบทริกเกอร์แบบลำดับความสำคัญ (mention > DM > ชื่อ > คีย์เวิร์ด > follow-up > สุ่ม)
  • พฤติกรรมมนุษย์ : สมาธิที่แปรผัน, การพิมพ์ผิด, การลังเล (15%), การลืม (3%), ความเหนื่อยล้าตามหัวข้อ
  • ตารางการนอน : บอทนอน, ช้าลง, หรือไม่สนใจตามเวลา
  • ไปป์ไลน์ TTS : การสังเคราะห์เสียงผ่าน Piper + ffmpeg → ข้อความเสียง Discord
  • การสตรีมแบบเรียลไทม์ : LLM ปล่อย token ทีละตัวบนบัสอีเวนต์ที่มี type

สิ่งที่เชื่อมโยงบทความนี้กับแชทบอทในประวัติศาสตร์คือการแสวงหาเดียวกัน : ทำให้เชื่อว่าคุณกำลังคุยกับคนจริง ELIZA ทำด้วยกระจกข้อความ PARRY ด้วยแบบจำลองอารมณ์ ALICE ด้วย 99k หมวดหมู่ Luna Protocol ทำด้วย LLM ที่ fine-tune + ระบบพฤติกรรมที่จำลองความไม่สมบูรณ์แบบของมนุษย์

Luna Protocol : ทำไมฉันถึง fine-tune โมเดล 1.5B

บทความที่สองสำรวจการ fine-tune และการ few-shot priming การค้นพบหลัก : โมเดลที่เล็กกว่า (1.5B) ที่ฝึกบนข้อมูลน้อยกว่า (50k ตัวอย่าง) มีประสิทธิภาพเหนือกว่าโมเดลที่ใหญ่กว่า (3B) เมื่อเตรียมพร้อมอย่างถูกต้องด้วยตัวอย่าง few-shot

นี่คือบทเรียนที่สะท้อนโดยตรงกับแชทบอทในประวัติศาสตร์ :

  • ELIZA แสดงว่าด้วยกฎไม่กี่ข้อที่ออกแบบมาอย่างดี สามารถจำลองความเข้าใจได้
  • ALICE แสดงว่าด้วย 99k หมวดหมู่ สามารถจำลองความรู้ทั่วไปได้
  • Luna Protocol แสดงว่าด้วย fine-tuning ที่ดีและตัวอย่าง few-shot 5 ตัวอย่าง LLM เล็กสามารถจำลองมนุษย์ได้

เทคนิคต่างกัน แต่หลักการเหมือนกัน : คุณภาพของข้อมูลและความแม่นยำของระบบสำคัญกว่าขนาดดิบ


บทสรุป : สามสิ่งที่ควรจำ

1. AI เชิงสนทนาไม่ได้เริ่มที่ ChatGPT ELIZA อายุ 60 ปี PARRY ผ่านการทดสอบทัวริงในปี 1972 ALICE ชนะ Loebner สามครั้ง Jabberwacky วางรากฐานการเรียนรู้แบบ transcript ที่ Cleverbot ทำให้เป็นอุตสาหกรรมขนาดใหญ่ แต่ละวิธีนำชิ้นส่วนของปริศนามา

2. ข้อมูลมาก ≠ ฉลาดกว่า Transcript ของ Jabberwacky ไม่มีกฎ 99k หมวดหมู่ของ ALICE ไม่ได้เรียนรู้ การ fine-tune ของ Luna Protocol บน 50k ตัวอย่างมีประสิทธิภาพเหนือกว่าโมเดล 3B ภูมิปัญญาทั่วไปบอก "ยิ่งใหญ่ยิ่งดี" -- ประวัติศาสตร์แชทบอทแสดงว่าสถาปัตยกรรมและการออกแบบมีความสำคัญเท่าขนาด

3. ปัญหาเหมือนเดิมมา 60 ปี จะทำให้มนุษย์เชื่อว่ากำลังคุยกับมนุษย์อีกคนได้อย่างไร? ELIZA ตอบด้วยกระจกข้อความ PARRY ด้วยความโกรธจำลอง ALICE ด้วยข้อเท็จจริง Luna Protocol ด้วย LLM ที่นอนและพิมพ์ผิด วิธีการเปลี่ยน ความต้องการยังคงเดิม

Repo เป็นโอเพนซอร์ส -- คุณสามารถโคลน รันแต่ละบอท และเห็นด้วยตัวเองว่า 60 ปีของ AI เชิงสนทนาอยู่ใน repo TypeScript เดียวได้อย่างไร

แหล่งข้อมูล ลิงก์
GitHub Repo fox3000foxy/chatbots
Luna Protocol -- สถาปัตยกรรมบอท อ่านบทความ
Luna Protocol -- few-shot fine-tuning อ่านบทความ
สคริปต์ ELIZA ต้นฉบับ anthay/ELIZA
โค้ดต้นฉบับ PARRY lexcore/PARRY
AIML Free ALICE v1.6 drwallace/aiml-en-us-foundation-alice
RFC 439 ต้นฉบับ PARRY Encounters the DOCTOR
คำอธิบายวิธีการทำงานของ LLM ที่ยอดเยี่ยม https://www.youtube.com/watch?v=YmLp8qe87A0

Related Articles