Architecture
The repository separates concerns between a fast Rust core and a flexible Python orchestration layer. Python assembles prompts, communicates with local models, cleans and validates outputs, and runs evaluation pipelines. Rust provides the deterministic environment, ideal abstractions, and a simulator that can execute either in the ground MDP or over an abstract state space. This separation keeps experiments both reproducible and quick while making it easy to iterate on prompts and analysis in Python.
At a glance, the Python layer contains the prompt builder, the LLM client and post‑processor, the bisimulation‑style scorer, and the MCTS runners and analysis tools. The Rust core implements the gridworld, homomorphism routines for ideal abstractions, utilities to build transition and reward matrices, and the stateless simulator exposed to Python via pyo3. The short overview below names the most relevant modules on each side; the component diagram in Figure 1 illustrates how they connect.
Overview
- Rust core (src/core). The game module defines Game and a fast stateless simulator; abstraction exposes get_all_states and get_abstraction to support matrix construction; runner hosts MCTS search that Python calls through PyRunner.
- Python layer (llm_abstraction/). llm.prompts.generate_prompts assembles prompts; llm.ollama.query_llm queries local models; llm.clean normalises responses; llm.scoring.bisimulation_similarity compares clusterings; evaluation modules run MCTS via the Rust extension.
Key entry points
- CLI: main.py defines subcommands.
- Prompt builder: llm_abstraction.llm.prompts.generate_prompts.
- LLM client: llm_abstraction.llm.ollama.query_llm (wraps ollama.chat).
- Cleaner: llm_abstraction.llm.clean.clean_with_regex_and_validate.
- Scorer: llm_abstraction.llm.scoring.bisimulation_similarity.
- Runners: llm_abstraction.evaluation.mcts.run_mcts, run_mcts_llm.
- Rust bindings: core_rust module in src/lib.rs (e.g., PyRunner, generate_mdp).
flowchart LR
subgraph Python Layer
CLI["CLI (main.py)"]
PB["llm_abstraction.llm.prompts.generate_prompts"]
LLM["llm_abstraction.llm.ollama.query_llm (Ollama)"]
CLEAN["llm_abstraction.llm.clean.clean_with_regex_and_validate"]
SCORE["llm_abstraction.llm.scoring.bisimulation_similarity"]
RUN["llm_abstraction.evaluation.mcts.run_mcts / run_mcts_llm"]
end
subgraph Rust Core
WORLD["core::game::game_logic::Game"]
HOMO["core::abstraction::homomorphism::get_abstraction"]
MATR["core::utils::matrices::build_matrices"]
SIM["PyRunner (core::runner::Runner)"]
end
CLI --> PB --> LLM --> CLEAN --> SCORE
SCORE --> RUN
RUN --> SIM
SIM --> WORLD
RUN --> MATR
HOMO --> MATR
WORLD --> HOMO
Figure 1: Component diagram of the Python and Rust layers. Arrows indicate the main control and data flow between modules.
The Python layer also uses core_rust.generate_mdp for T and R and for the ideal abstraction, and it calls visualize_world_map and visualize_abstraction to generate artifacts under outputs/.