The topology is now a search problem. The metric is still yours.
The graph you drew for the packet is a hypothesis. DSPy 3.3.0 will search a different one if you can score the result.
Last night I asked whether you could name the field each specialist writes, the credential it cannot hold, and the check that stops the next hop. That is still the filter. Tonight is the other half. If that leftover work has an interface, you can stop hand-tuning how the hops talk to each other and let a compiler search the topology.
I covered DSPy in May as the Stanford project that treats prompts as a compiler problem. Signatures. Modules. MIPROv2. The model-swap recompile. That piece still holds. Version 3.3.0, published August 3, is the first release that treats the program shape the same way it already treated the wording. The current package on PyPI is 3.3.0. GitHub agrees on the tag. The repo sits at 37,441 stars, MIT license, last push yesterday. CrewAI is still larger. AutoGen is still larger. The commit traffic is not a museum, and the cadence is still academic: 3.2.1 in May, a 3.3 beta at the end of May, stable in August. You do not get a patch every other day. You get a release that moves the abstraction.
The module is called Flex. It is marked experimental. Pin the version if you depend on the serialization format. You construct it from a signature the way you construct Predict. Without tools it starts as one Predict over the whole signature. With tools it starts as an RLM so the baseline can call them. Then you hand the program to GEPA with a metric and a trainset. GEPA rewrites the entire module source: how many predictors, which primitives, what runs in Python instead of a model call. The artifact is module_src. Print it, save it, load it later, and you get the same implementation back.
program = dspy.Flex("question -> answer")
optimized = dspy.GEPA(metric=metric, reflection_lm=reflection_lm).compile(
program,
trainset=trainset,
valset=valset,
)
print(optimized.module_src)That is the product. Not a better system prompt. A searched program.
GEPA is not new in 3.3. What is new is that a Flex submodule is a code component, not an instruction component. Ordinary predictors still get their prompts rewritten. A Flex gets its source rewritten. Predictors that live inside the Flex are not tuned in parallel, because the next candidate may not contain them. That is the difference between compiling a prompt and compiling a coordination pattern. MIPROv2 still earns its keep when the shape is right and the wording is wrong. Flex is the tool when you do not trust the shape.
The generated code never runs in your process. Flex sandboxes it through a CodeInterpreter factory that defaults to PythonInterpreter, which means Deno and a Pyodide WASM box. Predictor construction and LM calls bridge back to the host. Broken candidates score as failures instead of crashing the search. max_predictor_calls defaults to 100 so a rewritten loop cannot burn the budget in one forward. The sandbox is the right instinct for a compiler that authors code. Deno is a new operational dependency if the box does not already have it.
The other 3.3 piece that matters for agents is ReActV2, also experimental. Native tool calling. History as structured messages instead of one ever-growing trajectory string. Parallel tool calls with IDs preserved. The maintainers report up to a 50 percent cost drop on some tasks from prompt caching on those stable prefixes. Treat that number as a lab result until you measure it on your own tool set. The architectural move is the one I want either way: stop stuffing the whole transcript into a single user message.
Here is the trap. Flex will invent a crew if your metric looks like a demo. Give it a score that rewards “the letter sounds like a determination” and GEPA will happily discover three predictors that argue. Tuesday I said most multi-agent systems are one unfinished agent wearing extra nameplates. A compiler does not save you from that. It industrializes it. The metric has to refuse the second hop the same way a schema refuses a missing ICD code.
The other trap is treating DSPy as a runtime. August’s question is whether the thing you defined still runs after the process dies. DSPy compiles a program. It does not own the loop. Flex will not resume a prior-authorization packet from step 37. Restate will. A LangGraph Postgres checkpointer will, for short hops. Hermes will, if this is a personal agent that compounds. The compiled module_src is an artifact you then have to place. Put it inside a listener. Put it behind a Restate handler. Do not confuse a better program for a surviving one.
Compile cost is real. GEPA with a frontier reflection model and max_metric_calls in the dozens is an afternoon of tokens, sometimes more. Debugging a rewritten module is harder than debugging a handwritten prompt, because you are now reading Python a model authored against your failures. The learning curve is steep. The community is smaller than the chat-first frameworks. None of that is a reason to skip it. All of it is a reason not to start here on a packet that already has a legal path.
Use Flex when the decomposition is unknown and you have the two things May already demanded: a metric that correlates with production quality, and a trainset that looks like production, including the ugly tickets. A trace-aware metric can take program_trace and penalize LM calls. That is how you tell the compiler that arithmetic belongs in Python and that a second specialist has to earn the hop. The invoice example in the docs is the toy version: extract line items with the model, sum them in code. The production version is extract the eligibility fields, then refuse to call clinical if the schema is incomplete.
Do not use Flex to search a path you already owe an auditor. Eligibility before clinical before coding before the letter is not a search problem. That is CrewAI Flows or LangGraph with a router that returns a label. Searching it will find a cheaper graph that skips a gate. Cheaper is not legal.
Skip DSPy entirely when you do not have a metric yet. The compiler cannot invent the score. If the work is a one-shot JSON extraction against a known schema, Predict is enough and Flex is a different kind of theater.
If you already live in LangGraph or CrewAI Flows, the honest integration is a compiled Flex as a node, not a replacement for the graph. Compile the specialist that keeps drifting. Pin module_src. Leave the topology in code a human can read in an incident.
Tomorrow is the Friday RIFF that closes this arc. The question I would take into the weekend is narrower than whether you should go multi-agent. Can you write a metric that would fire a candidate for adding a second predictor? If you cannot, do not give GEPA the keys. Finish the single module until the leftover work has an interface. Then let the compiler search only that remainder.
If this was useful, forward it to one engineer who needs less noise in their feed.


