← Back to projects

Given an L5X export and live tag values, walk the ladder back from a failed output, narrate the trace in plain English, and surface the matching SOPs from a plant document corpus.

Pure-function walker on the L5X AST does the reasoning; the LLM only narrates. Read-only PLC access, on-prem, degrades gracefully without LLM, PLC, or Redis. Built on the constraints a real plant would impose.

L5X File Rockwell export AST Parser XML -> tree Condition Walker backward trace Tag Resolver live PLC values LLM Narrator qwen2.5:3b OUT deterministic core (pure functions) narration only stale.json cache L5X AST Walker Pipeline Failed output rung -> backward condition trace -> plain-English narration
01

Deterministic core, not LLM reasoning

The reasoning core is a pure-function walker on the L5X AST. Given a failed output rung, it walks backward through every condition, resolving live tag values against expected states. The LLM's only job is to narrate what the walker already found — it never reasons about ladder semantics on its own.

02

Read-only PLC access

pycomm3 connects to the Allen-Bradley controller in read-only mode. No writes, no state mutations, no risk to the running process. A real plant would never sign off on anything else.

03

Graceful degradation

If the LLM is down, you still get the raw trace. If the PLC is unreachable, you still get analysis from cached tag values. If Redis is gone, the stale cache serves from disk. Every dependency is optional after the core walker.

04

On-prem, zero cloud dependencies

Ollama runs locally. FAISS indexes live on disk. The entire stack runs inside the plant network with no outbound traffic. In OT environments with air-gapped segments, anything that phones home is a non-starter.

// llm as narrator, not reasoner

LLM as narrator, not reasoner

Domain logic lives in deterministic code that's testable and auditable. The LLM narrates results; it doesn't derive them. When you swap models, the trace stays the same.

// mock-swappable driver pattern

Mock-swappable driver pattern

pycomm3 is wrapped in a trait-like interface. CI runs against a mock driver that returns scripted tag values. Integration tests run against a Studio 5000 emulator. Production runs against real hardware. Same code path, three backends.

// distance to production

Distance to production

Add role-based access control. Connect to the plant historian for trend data. Wire the anomaly detector to the maintenance ticketing system. The interfaces are already shaped for these additions.

LangGraphFAISSsentence-transformersFastAPIpycomm3Allen-Bradley L5XOllama
// the code

Read the source, run it locally, open issues.