Skip to content
John Hodge

← Blog

The stack I'm building for agent-driven RF and antenna design

Last week an agent solved my phased-array benchmark task by discovering a free symmetry I had missed, and in the process exposed two bugs in my own scoring code. I wrote up that calibration story on its own. This post is the wider view it belongs to: the open-source stack I have been building toward agent-driven design of antennas, RF systems, and the metasurfaces and missions around them: nine repositories started between August 2025 and August 2026, and the design rules that survived building them.

The claim worth making is narrow. I do not think a language model understands Maxwell’s equations, and nothing below depends on it understanding them. The claim is that antenna and RF system design has a shape that agents fit unusually well: a chain of coupled models, from unit-cell electromagnetics up through array patterns, link budgets, and mission trades, where the expensive human work is deciding what to simulate, at which fidelity, with which assumptions, and what to try when a requirement misses. That is tool use, and tool use is the one thing current agents demonstrably do.

The stack, with what is still aspirational drawn as a dashed line:

Block diagram of thirteen boxes in four rows, two of them external tools. Top row: antenna-cad MCP (design compiler, 7 MCP tools), APAB (agent shell, 19 MCP tools), and opensatcom MCP (link-budget tools, new in 0.6.0) in orange as agent-facing, and AEDL (benchmark tasks, deterministic evaluators) in green as evaluation. Second row: antenna-cad (spec to layout to DRC to verification), phased-array-systems (requirement-driven trade studies), opensatcom (satcom link budgets), and spacedc-mdao (MDAO trade engine), all blue deterministic libraries. Third row: phased-array-modeling (patterns, tapers, impairments) and metasurface-py (RIS and reflectarray design), with metasurface-py noted as having no dependents yet. Bottom row: openEMS and KiCad in gray as external tools, and EdgeFEM (FEM full-wave unit cells) in blue. Solid arrows show real dependencies: all three MCP surfaces onto their libraries, APAB onto phased-array-systems, phased-array-modeling and EdgeFEM, AEDL onto phased-array-systems (t3 tasks), phased-array-modeling (instrumented) and EdgeFEM (instrumented, no task yet), antenna-cad onto openEMS, KiCad and optionally phased-array-modeling, phased-array-systems onto phased-array-modeling, opensatcom onto phased-array-modeling as of 0.5.1 and onto EdgeFEM for artifacts, and spacedc-mdao onto opensatcom labeled one call. One dashed arrow marks the remaining declared intention with no code: antenna-cad to EdgeFEM (later).

Every box is a public repository and every solid arrow is an import or subprocess call you can find in source. A dashed arrow is something the docs name that no code does yet, and keeping that distinction visible is half the point of drawing the diagram.

What each layer is for

At the bottom are the full-wave solvers, where the real electromagnetics lives. EdgeFEM is my C++ finite-element solver for waveguides, patch antennas, and periodic unit cells. openEMS is the external open-source FDTD engine I did not write and do not intend to rewrite. A design that has to be right eventually passes through one of these, or through the commercial solvers this stack does not replace.

Above them sit the analytic physics libraries. phased-array-modeling computes array patterns with the impairments that decide real designs: tapers, phase quantization, element failures, mutual coupling. metasurface-py does the same job for programmable reflecting surfaces and reflectarrays, the strand that comes from my PhD work. These are fast enough to sit inside an optimization loop, which the full-wave tier is not.

The system layer turns patterns into decisions. phased-array-systems runs requirement-driven trade studies: link budgets, radar detection, cost, reliability, swept into Pareto fronts. opensatcom does satcom link budgets with DVB-S2 throughput, and spacedc-mdao puts OpenMDAO, NSGA-II, and Sobol sensitivity on top for mission-scale questions. This is the layer where my model-based engineering history lives, and the argument for it predates agents entirely.

antenna-cad is the newest piece and closes a loop none of the others touch: hardware. A 15-line YAML spec goes in; a synthesized patch-array layout, a DRC-checked KiCad board file, and an openEMS full-wave verification report come out, headless and deterministic. Its README calls it a design compiler, and the phrase is earned in one specific sense: the source of truth is a typed intermediate representation of the design, and the KiCad, Gerber, and solver files are build artifacts compiled from it.

On top sit the agent-facing surfaces, all speaking the Model Context Protocol: APAB exposes 19 tools across the unit-cell → coupling → pattern → system chain, antenna-cad exposes 7 for the spec → layout → DRC → simulate chain, and opensatcom exposes its link-budget tools as of 0.6.0. That last surface exists because a measurement wanted it: the composition test below attaches opensatcom and APAB together, and a library reachable only through someone else’s agent shell was the thing in the way. And to the side, deliberately not integrated with any of it, AEDL holds benchmark tasks and deterministic evaluators for the question the rest of the stack begs: can an agent using these tools actually design something that meets a spec?

Rule one: the deterministic core does not need the agent

Every tool in the diagram works with no LLM anywhere in the process. apab is a Python library before it is an agent; antenna-cad runs its whole compile pipeline from a CLI; the trade studies in phased-array-systems are for engineers first. The agent is a client of typed interfaces, and it is always the replaceable part.

This is partly a debugging argument: when a design is wrong, you need to rerun the exact computation with no sampling in the loop. It is also an investment argument. Agentic systems change what engineers review made the case that the durable artifacts are the tools and the audit trail; agent frameworks turn over monthly, and a stack whose value lives in the framework inherits that half-life. The physics libraries here would be worth maintaining if language models stopped improving today.

Rule two: agents choose topology, optimizers tune parameters

The division that makes the agent useful rather than decorative is between discrete structural choices and continuous numerical ones. Which antenna family, which feed architecture, which lattice, whether to relax the sidelobe spec or grow the aperture: those are topology decisions, where precedent and judgment matter and the search space is not differentiable. Trace widths, element phases, taper coefficients: those belong to SciPy, NSGA-II, or coordinate descent, which beat a language model at every step of a continuous search.

antenna-cad states the rule bluntly in its design notes: don’t ask an LLM to pick a trace length. Agents never emit raw copper geometry or edit board files directly; they call typed tools that synthesize geometry from parameters. The same split shows up in the free-lunch story from the other side: the agent’s winning move on my benchmark was a discrete structural insight (a global phase offset is free), and the number-grinding that followed was ordinary optimization any solver could do.

Rule three: the evaluator has veto power

An agent’s claim about its own output is worth nothing. Every layer of this stack that scores anything scores it with deterministic code: AEDL’s rule is that agents never grade themselves, and its evaluators enforce hardware constraints on submissions rather than trusting them; antenna-cad reports predicted performance against the requirements from openEMS runs, not from the model’s description of the layout.

I hold this rule with some humility, because the last two weeks demonstrated that the evaluator having veto power only helps if the evaluator is right. The same defect turned up three times in three codebases: a sidelobe metric whose main-lobe exclusion did not reach past the main lobe, so it reported the skirt instead. In phased-array-systems it read a Taylor −35 dB design as −14.03 dB. In AEDL’s scorer it capped what any strong design could score. Then it appeared a third time inside the verification script I wrote to check the second one. Deterministic evaluators can be deterministically wrong. What saved me each time was measurement against an independent path, which is why the evaluation layer needs the same adversarial attention as the designs it judges. The research on this is consistent: the recent self-evolving metasurface design framework couples its coding agent to a deterministic physics evaluator too, and reports same-type task success rising from 38% to 74% as its skill files accumulate. It ran two held-out task families. One started at 20% and its skill evolution recovered it to 90%; the other sat near ceiling at 92% and finished at 90%, having learned nothing it needed. Two families are not enough to tell which case is typical, and that is the point. Competence in-distribution says little about the case your evaluator has never seen.

Rule four: no number without a paper trail

Every APAB session writes a run bundle: an OpenTelemetry trace of each MCP tool call, a provenance manifest with library versions and inputs, and the artifacts. Every AEDL run writes one even when the agent crashes: scoring, cost by fidelity tier, and the agent’s workspace. antenna-cad hashes its intermediate representation so a layout can be traced to the spec revision that produced it. The pattern is the one APAB 0.3.0 committed to and the eval-harness post generalizes: a design decision has to stay auditable after the fact, because the interesting failures only become visible in the record.

Provenance is also what makes error attribution possible at all. When the answer is wrong, the question is which of five models, three assumption sets, and one agent made it wrong, and a trace is the only way to ask.

What is real and what is not

A vision post that skips the maturity table is marketing. Here is mine, checked against PyPI and source on 2026-08-13:

toolversionthe part that is realthe part that is not
phased-array-modeling1.4.0patterns, tapers, impairments; most-imported node in the stackmutual-coupling model unvalidated against full-wave
phased-array-systems0.11.068 system metrics, DOE, NSGA-II; T/R modules, nonlinearity and DAC chains added in 0.11.0analytic models, not a solver
EdgeFEM1.0.0real FEM: waveguides, patches, periodic cells, validated against analytic casessingle-threaded, minutes per solve, wheels for macOS arm64 only
metasurface-py0.2.0RIS/reflectarray design and optimization, optional JAX backendnothing else in the stack consumes it yet
opensatcom0.7.0link budgets, DVB-S2, trades; real phased-array-modeling binding since 0.5.1, MCP tool surface since 0.6.0propagation is simplified ITU-R-inspired, not the full recommendations
spacedc-mdao0.4.2OpenMDAO + NSGA-II + Sobol over a real physics chainconsumes opensatcom through exactly one function call
APAB0.4.119 MCP tools, traces, provenance, golden-task evals; server-side spans and a sim-vs-measured comparison in 0.4.0the agent itself is a thin, replaceable shell, by design
antenna-cad0.2.0spec → layout → DRC → openEMS verification, closed loop, on PyPI in 41 hours; 0.2.0 accepts measured Touchstone through the same gaterectangular patches only; 4×4 arrays are match-limited (S11 ≈ −8 dB); DRC and openEMS untested in CI
AEDLpre-releasetask format, three calibrated tasks, harness with cost accounting, per-process physics-call loggingTier 1 and four of five Tier-2 tasks do not exist; no task runs at full-wave fidelity

Two entries deserve their own sentence. metasurface-py is the deliberate island: real physics that nothing else imports yet, waiting for the aperture layer to grow a metasurface arm. And the opensatcom binding is new because researching this post found it broken: the wrapper imported a module name that has never existed (pam instead of phased_array), and a flag set on import was never read afterward, so every link budget silently used the analytic fallback while the docstring claimed otherwise. Release 0.5.1 fixes it, with binding tests that assert behavior the fallback cannot produce. An untested seam is not a seam. It is two tools that happen to mention each other.

What is missing

The gaps are more instructive than the boxes.

There is no agent that spans layers. APAB drives the RF chain; antenna-cad compiles hardware; no agent yet takes a requirement and decides to move between them, or allocates simulation fidelity against a budget. That allocation question, when is the analytic model enough and when do you pay for full-wave, is the research claim AEDL’s cost accounting was built to measure, and it is still unmeasured: no AEDL task runs at full-wave fidelity, so there is no fidelity choice to observe yet.

A narrower composition question did get measured, on t3-001, a 28 GHz terminal architecture. Three attempts with the physics libraries merely importable went 0 for 3, the agents spending their budget writing their own verification scripts. Three attempts with the opensatcom and APAB MCP servers attached went 2 for 3, and the held-out envelope caught the third, which had tuned worst-case margin to −1.1 dB. At three attempts per arm that points a direction and measures nothing finer. It settled one design question: no coordination package is needed on top of these tools, because plain MCP composition did the job.

The physics-call log that measures any of this was blind until recently. The call shim was never injected into the MCP server processes and its paths were not absolute, so physics called inside a server went unrecorded, and early bundles reported zero library calls whether or not the agent had made any. Both gaps closed on 2026-08-12, and a t3-001 bundle recorded afterward shows 221 instrumented calls across ten processes. This is the sidelobe lesson from one section up in a different costume: an instrument reading zero and an instrument that is not plugged in produce the same log. What the fixed instrument shows is that agents handed a bare library do often write their own array factor in numpy, and how often is now a measured quantity.

Evaluation is three tasks deep, and thin in a specific direction. AEDL’s t2-001 established that a frontier model clears a 2-bit-quantization aperture task reliably, and that the task had to be repaired twice before its numbers meant anything. The two Tier 3 tasks score against held-out evaluation conditions, so designing to the nominal scenario is not enough to pass them. What is missing now is the bottom of the range: Tier 1 is the element-level, full-wave tier, and it does not exist, which is why the fidelity-allocation question above stays open.

And the loop back from hardware is still open, though the plumbing for it now exists. There is a measurement artifact contract shared across the repositories, antenna-cad 0.2.0 accepts a measured Touchstone file through the same verification gate a simulation faces, and APAB 0.4.0 can compare a simulated pattern against a measured one. Every fixture behind that machinery is synthetic and labeled synthetic. There is still no fabricated board, no measured pattern, and no comparison of a predicted S11 against a network analyzer. Until that loop closes at least once, every claim above lives downstream of models validated against other models. Building the socket for the measurement is the easy half, and it is the half I have done.

What this looks like in my own projects

Each layer of this argument has its own longer writeup. The physics layers are in modeling phased arrays in Python and the metasurface toolkit post; the system layer in the phased-array-systems 0.9.0 release and the opensatcom introduction; the mission layer in orbital data centers, by the numbers. The agent layer is in the agentic workflow post, the evaluation layer in the free-lunch calibration story, and the older history that explains why the stack is layered this way at all is in model-based engineering for phased arrays.

Try it

Everything except AEDL and antenna-cad’s externals installs from PyPI:

pip install phased-array-modeling phased-array-systems edgefem metasurface-py
pip install "opensatcom[pam]" spacedc-mdao antenna-cad "apab[ollama]"

antenna-cad needs KiCad 8+ for DRC and openEMS (native or its Docker image) for verification. AEDL is clone-only because the tasks are the point. For the theory under all of it, my older RF and antenna tutorials notebooks cover dipoles, monopoles, and G/T derivations, and the research page has the publications behind the metasurface strand.

If you work on antennas, RF systems, or agent evaluation and any of this is useful or wrong, I want to hear which. The dashed lines in the diagram are the to-do list, and the fastest way to correct the solid ones is for someone else to run them.

Every version number, dependency edge, and limitation above was checked against source or PyPI on 2026-08-13. The maturity table is the claim I am prepared to defend; treat anything grander as scoped by it, and treat the date on it as load-bearing.

These are independent projects I build on my own time. The views are my own and do not represent any current or former employer.

Frequently asked questions

What does an LLM agent actually do in this stack?

It chooses what to try: which antenna family, which feed architecture, which study to run next, which constraint to relax. Every number comes from deterministic code. The agent never computes a gain, never edits a layout file directly, and never grades its own output.

Why split topology decisions from parameter tuning?

Language models are useful at discrete, structural choices where taste and precedent matter, and bad at continuous numerical optimization, where sixty years of gradient-based and evolutionary methods are better at every step. So agents pick topology, numerical optimizers tune dimensions, and neither is asked to do the other's job.

Does this replace HFSS, CST, or commercial EDA tools?

No. Serious hardware still gets validated on mature commercial solvers and test ranges. This stack explores what an open, scriptable, agent-accessible workflow looks like, and its full-wave engines (openEMS, EdgeFEM) are the open-source tier of that world.

What is the weakest part of the stack today?

Validation depth. The physics that scores designs comes mostly from libraries I wrote, checked against textbook cases rather than against independent solvers or measurement. The evaluation layer has three calibrated tasks, none at full-wave fidelity. Several seams are one function call wide. The maturity table in the post says which is which.

Why does every tool work without an agent?

Two reasons. Debuggability: when a design is wrong, you need to reproduce it deterministically without a model in the loop. And durability: agent frameworks change monthly, physics does not. If the agent layer disappeared tomorrow, every library here would still be a usable engineering tool.

More in AI agents