Chapter 6: rules, or how to decide without deciding

Somewhere in every program like this there is a function full of ifs. If Jev cannot answer it, refuse. If it is a yes-or-no question, show Jev's answer. If it needs options, call the LLM, unless... and so on, until nobody can say what happens to a question without running it in their head. This package is what that function became instead: a list of rules, and an engine that runs them.

A fact is something known about the question: can Jev judge it, what kind of question is it, is it several questions. A rule is a few tests on facts and what to do when they all pass. The engine looks at what is known and says what to do next. Here is every rule the site runs, drawn by the site itself:

The rules as a Rete network: facts on the left, the tests on them, the joins, and the rules on the right

That picture is /rules.svg, drawn live from this package, so it is always the network that runs. Read it left to right: the facts, one test on a fact per box, the joins where a rule's tests meet, and the rules. Click it to see it bigger.

Try it. https://lmjtfy.fun/rules is the same drawing with every fact clickable. Set answerable to yes, kind to pick, and watch "draft options" light up: that is the moment the LLM would be called.

The rules, in words

Rule order is priority: of the rules that hold, the first with something left to do decides.

RuleWhenThen
refuseJev cannot judge itSay "No. That's not a question I can answer", and stop.
answer yes or noone question, read as yes-or-no, Jev's yes is knownShow Jev's yes: Yes., No., or Yes? / No? when the likelier answer is under 70%.
grade itone question, read as how-much, a general scale will do, Jev's degree is knownShow Jev's degree, from "Not at all" to "Extremely".
give the oddsone question, read as how-likely, Jev's chance is knownShow the chance as a percentage, 28%., and say it is a Noul.
draft a scaleread as how-much, but it needs its own scale ("how hot is the sun")Ask the LLM to write the levels.
draft optionsread as a pickAsk the LLM to write the options.
split it upseveral questionsAsk the LLM to write each one.
judge the draftsthe LLM wrote somethingAsk Jev to judge it.
show the answersJev judged itShow them.
list itJev can judge it, and it is fit to show strangersPut it on the public feed.

Two readings can hold at once. Jev answers "what kind of question is this?" with a probability for each kind, and the runner-up counts too when it gets at least 30%. Then the rules of both kinds hold and both are answered: "is C++ better than C#?" comes back as a yes-or-no and as a pick.

Rete, briefly

A rules engine with many rules could test every rule against the facts each time a fact changes. A Rete network (Forgy, 1979) does less: rules that start with the same tests share them, so each distinct test is one node (an alpha), and each rule is a chain of joins over those nodes, shared for as long as two rules begin alike. When a fact arrives, only the nodes that test it change, and only the joins below them are looked at again.

flowchart LR
  F1["fact: answerable"] --> A1["answerable = yes"]
  F1 --> A0["answerable = no"]
  F2["fact: reads Noul"] --> A2["reads Noul = yes"]
  A0 --> R0["rule: refuse"]
  A1 --> J1(("join"))
  A2 --> J1
  J1 --> R1["rule: answer yes or no"]

Aside: the one trick. A textbook engine fetches a fact when a rule first needs it, one at a time. That would be slow and expensive here, because each fact from Jev is a network round trip. So this engine does something sneakier: it collects every Jev fact that any rule still alive is waiting on, and asks for them all in one request (Next::Ask). Jev answers several questions for barely more than one, so the first request usually settles the whole question.

How the page draws it

Under every answer, the page draws this network marked with the question just asked, and it fills in as the answers arrive:

  • The Jev facts sit in one frame, because they came in one request. Each shows the number Jev gave, not the yes or no it was read as.
  • Filled is what a rule that fired stood on. Outlined was learned and turned out not to be needed. Dashed is a test that failed.
  • A rule that holds is pink, and the ones that decided a step are numbered in the order they did.
  • A line runs from a rule back round to a fact when the rule's effect is the call that teaches it. That loop is what the engine runs.

For the people who maintain it

A fact is something known about the query (Fact): whether Jev can answer it, which kinds of question it reads as (four facts, one per Kind: Noul, Choice, Score, Chance), whether it is several, whether it needs a scale written for it, Jev's own answer read as yes-or-no, as how-much and as a chance, whether it is fit to show, whether the LLM drafted anything, whether Jev judged any of it. A rule (RULES) is a list of tests on facts and what follows when they all hold: the query ends outright, something is done that costs a call and teaches another fact, or something is noted that decides no step (an answer to show, or that the question may go on the feed).

Network::compile merges the rules: one alpha node per distinct test, and for each rule a chain of joins, shared by rules that begin with the same tests. Network::next says what to do given what is known:

NextWhen
EndA rule that ends the query outright holds (Jev cannot answer it).
DoA rule holds whose effect has not been done yet.
AskNo rule decides yet. Every Jev fact that a rule not yet failed is waiting on, to be asked for in one request.
DoneNo rule has anything left to do and nothing more can be learned. What to show is in the notes (Network::shows).

Network::decide is next with the rule that said so, which is how the page numbers the rules in the order they decided. Network::alpha and Network::join give each node's State (holds, fails, waits), and Network::used says which of the nodes that hold a fired rule stands on. apps/lmjtfy/src/diagram.rs draws all of it.

In this folder

PathWhat
src/The engine and the rules.
Cargo.tomlThe crate. It depends on nothing but serde.

← Previous: Chapter 5, packages/ · Up: packages · Next: Chapter 6½, rules/src/ →