Every surface here is additive. None of them changes how a decision is made, none is required, and each is exactly one install extra, so the choice of how to deploy is independent of everything else in these docs.
Command line
Decisions from the shell, typed flags instead of JSON, and a JSONL batch that pays for one model load.
HTTP server
vegaml-serve: POST /v1/decide returns exactly what decide() returns. Endpoints, errors, and how to scale it.
MCP server
decide() as a tool your agent can gate on, over stdio. Two tools, one client entry, and the instruction to give the agent.
LangChain and LangGraph
Decide inside a chain, route a graph on the winning option, or gate an input and raise instead of rewriting it.
Pydantic models
A pydantic model as the question sheet and the validated answer, with every calibrated field still there beside it.
Docker
The HTTP server as a container, with the checkpoint cached in a volume so a restart does not re-download it.
ONNX and MLX
Export the settling computation and run it without torch: onnxruntime anywhere, MLX on Apple silicon.
Which one to reach for#
If you are not sure, the honest ordering is: the Python API if the thing that needs a decision is already Python; the HTTP server if it is not, or if it is several services; the CLI for a batch you are running once; MCP if an agent is the caller.
The rest are narrower. LangChain and pydantic are conveniences over the Python API for codebases already built on them. Docker is the HTTP server with its dependencies pinned. ONNX and MLX exist for processes that must not import torch at all.
They all return the same thing#
This is worth saying once: every surface returns the decide() dictionary, field for field. The HTTP response is that dictionary as JSON. The MCP tool result is that dictionary. The CLI prints it. So client code reads the same fields no matter how the decision got to it, and moving from one surface to another is a transport change, not a rewrite.
{"answers": {"team": {"choice": "billing",
"probs": {"billing": 0.94, "technical": 0.04, "sales": 0.02},
"confidence": 0.94,
"prediction_set": ["billing"],
"abstain": false,
"unbound": false,
"settle_steps": 7,
"readout": "engine"}},
"latency_ms": 31.4,
"n_tokens": 118,
"views": 1}Note
One process holds one checkpoint.
A decision is a single forward pass measured in tens of milliseconds, so every surface serializes around one loaded model rather than making per-call state concurrent-safe. Scale out with more processes behind a balancer, not with threads inside one.