-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Statistikles is a Kautz Type‑1 neurosymbolic system: a neural component and a symbolic component operate side by side, separated by a defined, auditable interface. The neural side (an LLM) handles language; the symbolic side (Julia) handles arithmetic. Nothing crosses that line by accident — and a runtime guardrail proves it did not.
This page describes the split, the request pipeline through src/tools/, and a map of the src/ tree. For the guarantee that polices the boundary, see The-No-Fabrication-Guarantee.
| Neural (LLM) | Symbolic (Julia) | |
|---|---|---|
| Job | Parse the natural‑language question, choose a tool, interpret and phrase the result | Perform every calculation, exactly and deterministically |
| May produce numbers? | No — enforced by the guardrail | Yes — the only source of numbers |
| Lives in |
src/tools/lmstudio.jl, src/tools/chat.jl
|
src/stats/ (~41 modules), dispatched by src/tools/executor.jl
|
| Backend | LM Studio, OpenAI‑compatible chat completions | The Julia runtime |
The contract is stated at the top of src/Statistikles.jl:
MANDATE: No statistical calculation may be performed by the LLM. Every number in the final report is audited by deterministic code.
A single turn of statistical_assistant_chat() (in src/tools/chat.jl) moves through four stages:
-
Route (neural). The user's text plus the tool schemas from
get_tools()are sent to the model bycall_lm_studio(messages, tools)(src/tools/lmstudio.jl). The model responds either with prose or with one or moretool_calls. -
Compute (symbolic).
process_tool_calls(response, messages; tools, max_rounds=5)drives the tool loop. For each requested call it parses the JSON arguments and invokesexecute_tool(fn_name, args)(src/tools/executor.jl), which dispatches to a verified Julia function. Each result is recorded (native JuliaDicts) as fuel for the guardrail. The loop repeats — feeding results back so the model can chain tools — up tomax_roundsrounds. - Explain (neural). With the tool results in the transcript, the model writes a plain‑language interpretation.
-
Audit (symbolic). Before the reply is printed,
enforce_numeric_boundary!(...)(src/tools/chat.jl) runsvalidate_numeric_provenance()(src/tools/guardrail.jl) over the prose. Any numeric literal that does not trace to a recorded tool result (or a user number, or a small structural integer0..12) is an orphan. The model gets one retry; residual orphans are flagged with a visiblePOSSIBLE MOLLOCKwarning. The text is never silently rewritten.
flowchart TD
U["User question<br/>(natural language)"] --> LM["call_lm_studio(messages, tools)<br/>src/tools/lmstudio.jl"]
LM -->|tool_calls requested| PT["process_tool_calls loop<br/>max_rounds = 5"]
PT --> EX["execute_tool(name, args)<br/>src/tools/executor.jl"]
EX --> STATS["Verified Julia functions<br/>src/stats/ (~41 modules)"]
STATS -->|exact numeric result| PT
PT -->|results in transcript| LM
LM -->|interpretive prose| GR["enforce_numeric_boundary!<br/>validate_numeric_provenance()<br/>src/tools/guardrail.jl"]
GR -->|all numbers trace to a tool result| OUT["Reply printed to user"]
GR -->|orphan numbers found| RETRY["Single retry, then<br/>append MOLLOCK warning"]
RETRY --> OUT
subgraph NEURAL["Neural — no numbers produced"]
LM
end
subgraph SYMBOLIC["Symbolic — the only source of numbers"]
EX
STATS
GR
end
| File | Responsibility |
|---|---|
definitions.jl |
get_tools() builds 29 OpenAI‑style function‑calling schemas via the _tool(name, description, properties, required) helper. These are what the model is allowed to call. |
lmstudio.jl |
Transport. call_lm_studio posts chat completions; process_tool_calls runs the bounded tool loop. Config constants BASE_URL, API_KEY, MODEL are overridable via STATISTIKLES_LM_URL, STATISTIKLES_API_KEY, STATISTIKLES_MODEL. |
executor.jl |
execute_tool(tool_name, arguments) — a dispatcher over 55 tool names mapping each to a verified Julia call. Wrapped in try/catch, it returns a clean Dict("error" => …) on failure (full trace only when STATISTIKLES_DEBUG is set). |
guardrail.jl |
validate_numeric_provenance(text, tool_results, user_numbers; rtol=1e-6) and helpers (collect_numbers, extract_numeric_tokens, _token_explained) — the numeric‑provenance audit. |
chat.jl |
The REPL session (statistical_assistant_chat), the system prompt, enforce_numeric_boundary!, run_examples() (offline demo), and main(). |
execute_tool is the point where neural meets symbolic, so it is defensive:
-
Unknown‑subtype guards. Tools with a
typediscriminator (e.g.t_test,nonparametric_test,information_theory) fall through toDict("error" => "Unknown type '…' for …")rather than crashing on an unexpected value. -
Iteration clamps. Resource‑hungry tools cap their work: bootstrap and PERMANOVA reject
n_reps/n_permutationsabove100000; component counts (k) for NMF, ICA, and EM are capped at20. -
Unknown tools. An unrecognised
tool_namereturnsDict("error" => "Unknown tool: …").
src/
├── Statistikles.jl Module root: includes, exports, mandate banner
│
├── stats/ SYMBOLIC KERNEL — ~41 verified statistics modules
│ ├── descriptive.jl mean, median, variance, skewness, kurtosis
│ ├── inferential.jl t-tests, ANOVA, chi-square
│ ├── correlation_regression.jl, nonparametric.jl, effect_sizes.jl
│ ├── power_analysis.jl, bayesian.jl, bayesian_advanced.jl
│ ├── survival.jl, meta_analysis.jl, robust.jl, spatial.jl
│ ├── timeseries.jl, information_theory.jl, machine_learning.jl, nlp.jl
│ ├── causality.jl, sem.jl, multivariate.jl, resampling.jl
│ ├── algebraic.jl, non_classical.jl, dynamic_structured.jl, …
│ └── validation.jl shared ArgumentError input-validation helpers
│
├── pipeline/ DATA STEWARDSHIP — order matters
│ ├── canonicalization.jl epochs, precision, precedence (runs first)
│ ├── dimensional_analysis.jl, detection.jl, validation.jl
│ ├── cleansing.jl, normalization.jl
│
├── tools/ INTERFACE LAYER — the neural / symbolic seam
│ ├── definitions.jl get_tools() — 29 function-calling schemas
│ ├── executor.jl execute_tool() — 55-way dispatch to Julia
│ ├── lmstudio.jl LM Studio transport + tool loop
│ ├── guardrail.jl numeric-provenance enforcement
│ └── chat.jl session, system prompt, main(), run_examples()
│
├── bridge/ CROSS-VERIFICATION & PERSISTENCE
│ ├── aspasia_bridge.jl Aspasia (Octave) cross-verification
│ ├── verisimdb_schema.jl VeriSimDB persistence (port 8096)
│ ├── echidna_adapter.jl ECHIDNA formal-proof dispatch
│ ├── betlang_bridge.jl BetLang probabilistic programming
│ └── typell_levels.jl TypeLL levels 1–12 statistical types
│
├── integrations/ JULIA ECOSYSTEM ADAPTERS
│ ├── axiom_integration.jl property verification
│ ├── smtlib_integration.jl exact-arithmetic checks
│ ├── causals_integration.jl causal DAGs + Bradford Hill
│ ├── bowtie_integration.jl barrier / risk modelling
│ ├── zeroprob_integration.jl zero-inflated models
│ └── quantum_integration.jl Bell / CHSH experiments
│
└── output/ PRESENTATION
├── tables.jl Unicode box-drawing tables
├── graphs.jl ASCII histogram / box / scatter / bar
└── export.jl CSV / JSON export with provenance stamps
The include order in src/Statistikles.jl is deliberate: the statistics kernel loads first, then the data pipeline (canonicalization before any logic), then the bridges and integrations, and finally the interface layer that stitches the LLM to the kernel.
- The-No-Fabrication-Guarantee — why and how the boundary is enforced, including the token‑matching algorithm in detail
-
Statistical-Functions-Reference — the
src/stats/catalogue of statistical methods - Testing-and-Validation — how the symbolic kernel is validated
- Experimental-Surfaces — the Zig FFI and Agda proofs (experimental)
Overview
Using
Development
Project