Watching a Python-to-Rust rewrite was painful enough to build this.
How we built an agent that ports Python ML code to edge hardware for space.
Watching a Python-to-Rust rewrite was painful enough to build this.
How we built an agent that ports Python ML code to edge hardware for space.

A few years ago I was working at a company where almost everything was written in Python. A colleague of mine had been learning Rust on the side, and he kept lobbying to use it on something real. Eventually we got the go-ahead for one service, on the condition that we’d write it twice, once in Python and once in Rust, and compare them in parallel.
The numbers were ridiculous. Rust was around five times faster and used roughly fifteen times less memory. We were both pretty happy with ourselves until I sat down and actually saw the code. Our hundred-line Python script had turned into something like five hundred lines of Rust. Every win came with a different cost: lifetimes, borrow checker fights, manual handling of things Python had been doing for us invisibly.
That was the moment that stuck with me. The performance gain was real and the engineering cost was real, and there was no version of “just go learn Rust properly” that fit into the actual week I had in front of me. This was before AI for coding existed. The only option was to give up the speedup or give up everything else I was supposed to be doing.
I’ve thought about that trade-off a lot since, and it’s basically the reason space-edge exists. It’s an internal tool we built at Loka that takes Python ML code, generates equivalent C and Rust for two different edge devices, compiles and runs both, and gives you back a side-by-side report so you can make the call without having to write either of them yourself. It’s an MVP today, focused on the aerospace industry, and already deployed on AWS for internal testing, but the loop works end to end and that’s the part worth talking about.
What it does
The premise is straightforward. ML engineers write Python. Most of the hardware these models eventually run on (a microcontroller in a drone, a small board in a satellite, a sensor in a factory) doesn’t run Python. Somebody has to bridge that gap, and that somebody usually spends quite some time setting up a toolchain before they write a single line of useful code.
space-edge does that bridging step. You paste a piece of Python, pick which target you care about, and hit send. Behind the scenes a sequence of agents kicks off:
ingestion → hardware → codegen → simulation → benchmark → report → packaging
The output streams back live: a build log, a diff between your Python and the generated C/Rust, a benchmark table comparing the selected hardware devices, a markdown report with a recommendation, and a downloadable zip with all the artifacts.
The available hardware to test right now are is ARM Cortex-M4 microcontroller (tiny, low-power, runs on a coin cell) and an NVIDIA Jetson Nano (small Linux board with a GPU). They sit at opposite ends of the edge spectrum, which is exactly why comparing them is useful: one is small and cheap, the other is fast and hungry, and the right answer depends on what you’re trying to ship.
Here’s a very simple example of a Python code you can paste in:
import numpy as np
def run_inference(x: np.ndarray) -> np.ndarray:
W1 = np.array([[0.5, -0.3], [0.2, 0.8]], dtype=np.float32)
b1 = np.array([0.1, -0.1], dtype=np.float32)
h = np.maximum(0, x @ W1 + b1)
W2 = np.array([[0.4], [-0.6]], dtype=np.float32)
b2 = np.array([0.0], dtype=np.float32)
return h @ W2 + b2
A two-layer neural network. Out the other end: four source files (C and Rust for each target), both compiled, both executed in a CPU emulator, with their outputs checked against the original Python to make sure the conversion didn’t change the answer.

The CPU emulator is QEMU, a piece of software that pretends to be a different CPU: you feed it a binary compiled for an ARM Cortex-M4, and QEMU runs the instructions on an x86 server as if it were that chip. The reason this matters is operational. Keeping every supported board physically available, wired up, and reachable from a backend service gets painful fast, and it gets worse the more boards you add. With QEMU the M4 and the Jetson runs happen inside the same containerised pipeline, on demand, no hardware in the room. The catch is that QEMU is accurate on CPU behaviour but doesn’t model the real timing of memory accesses, interrupts, or the chip’s power profile. So the benchmark numbers are good for relative comparisons (M4 vs Jetson, version A vs version B) and weaker as absolute predictions for a real board.
It also has a second mode, a Q&A assistant over the same knowledge base the codegen agent uses. You can ask things like “what’s the typical memory budget on a Cortex-M4?” and get a cited answer. Useful for the questions that come up while you’re staring at the benchmark report, mainly regarding the standards of the aerospace industry and MISRA compliance.
Why aerospace, and what MISRA has to do with it
This MVP is built specifically for aerospace work, and that choice shapes most of what’s interesting about the tool.
If you’ve never worked in aerospace software, MISRA-C might be a new acronym. It’s a set of coding rules originally written for the automotive industry, now used across most safety-critical fields, including aerospace, medical devices, and defence. The rules ban the parts of C that are most likely to cause subtle bugs: undefined behaviour, implicit type conversions, certain pointer tricks, recursion in places where you can’t afford a stack overflow. If your code is going onto a satellite, somebody will eventually ask whether it passes MISRA, and the answer needs to be yes.
space-edge runs a MISRA check after the codegen step and lists any violations in the report. The codegen agent itself pulls examples from the MISRA rules in the knowledge base while it writes, so most of the obvious violations get caught before the static analysis runs at all.
The knowledge base holds two other sets of documents that are checked by the code generation agent. One is ECSS, the European Cooperation for Space Standardization. ECSS-E-ST-40C, for example, is the software engineering standard most relevant for European space projects, and it’s the kind of document where the question “have you checked your design against it?” has a real answer rather than a vague one.
The other is the hardware developer manuals themselves: the ARM Cortex-M4 technical reference, the Jetson Nano data sheet, and similar specs. When the codegen agent writes Rust for the M4, it’s pulling from the actual ARM manual to get the right instructions, alignment requirements, and intrinsics, rather than guessing from training data.
So the RAG corpus isn’t a generic “embedded knowledge” dump. It’s three deliberate piles: MISRA and edge-computing compliance, aerospace industry standards, and hardware specs. The codegen agent reaches into all three on every run.
Architecture

The agent layer uses Strands Agents on top of Bedrock (Claude Sonnet for codegen and report writing). Qdrant runs as a managed vector store, holding the three collections described above. The compilation and emulation happen inside dedicated containers (one per target hardware), and the backend talks to them through the Docker SDK.
Artifacts go to S3, keyed by job ID, with presigned URLs in the download link.
The whole thing is deliberately simple. A small agent graph, a fixed pipeline, a streaming UI on top.
Why the pipeline isn’t agentic
There’s a temptation when you’re building anything with “agents” in the name to let the LLM decide the next step. We chose not to.
The pipeline steps always run in the same order. Ingestion is always followed by hardware profile loading. Code generation always comes after that. The static analysis step always needs the generated code. An LLM deciding “what’s next?” would only add cost, latency, and a new class of failure modes (imagine the agent deciding to skip the MISRA check because the code “looks fine”). We use the LLM where it earns its tokens: generating code, writing the report, routing the Q&A questions. Everywhere else, plain Python. You can see the split in the architecture diagram above: the boxes marked “LLM” or “RAG + LLM” use a model, the ones marked “No AI” don’t.
The benchmark step, for example, is just arithmetic. It compares binary sizes and execution times and prints a table. Doing that with an LLM would be slower, more expensive, and less reliable, and the output would be mostly the same.
I think this is the most honest thing we can say about agents right now: they’re great in certain spots and overkill in most. A pipeline with known transitions isn’t a place where you need autonomy. It’s a place where you need a clean function call.
What surprised us
Three things, in increasing order of usefulness.
Strands’ event stream maps almost one-to-one onto a good streaming UX. We didn’t have to build a separate “agent thinking” channel; we intercepted the tool-use events Strands was already emitting, pushed them as thinking SSE frames, and let them disappear when a real progress event arrived.
RAG over aerospace technical standards works embarrassingly well. These documents are dense, structured, and full of the exact terminology a developer would use in a question. A plain top-3 cosine similarity search has been good enough that we haven’t needed a reranker or hybrid search yet.
The benchmark table changes how people talk about hardware choices. Before, “Jetson vs Cortex-M4” was a vibes conversation. Now it’s a row that says “Jetson Nano is 18× faster, Cortex-M4 has 1/200th the binary footprint, here’s the code that produced both.” That’s not a new insight to anyone who’s done this work, but having it auto-generated next to the source it came from is a different conversation to have with a customer.
Where it goes next
Real hardware in the loop is the obvious next milestone. Running in an emulator tells us the code compiles and behaves correctly; it doesn’t tell us how it actually performs on a board with real interrupts and real power draw.
After that: more complexity input in the code generator (entire Python projects), proper sandboxing for the user-submitted code, and broadening beyond aerospace into the other safety-critical domains that share most of the same compliance vocabulary.
The thing I keep coming back to is the version of me from a few years ago, stuck between a Python script and a Rust rewrite, with no time to do either properly. The whole point of this tool is to give that person a third option: keep working in the language you know, and let the agent do the translation that used to require a week and a colleague who’d been studying Rust on the side.
메타데이터
- post_id
- fd3801ad02ba
- slug
- watching-a-python-to-rust-rewrite-was-painful-enough-to-build-this-fd3801ad02ba
- url
- https://medium.com/loka-engineering/watching-a-python-to-rust-rewrite-was-painful-enough-to-build-this-fd3801ad02ba
- canonical_url
- https://medium.com/loka-engineering/watching-a-python-to-rust-rewrite-was-painful-enough-to-build-this-fd3801ad02ba
- author_url
- https://medium.com/@joaoafonsoppereira
- status
- ok
- fetched_at
- 2026-06-28 10:39:35