Local LLMs I’ve Tested for Unreal Engine 5.8 + Epic MCP
I’m trying to get a fully local LLM to control Unreal Engine 5.8 through Epic’s MCP stack.
The target is not just basic tool calling. I need the model to reliably complete a real agent loop:
inspect scene/frame → understand what is wrong → choose a correction → execute it → verify the actual result → stop
My current architecture is:
Kimi Code → local model → Epic Unreal MCP → Unreal Engine 5.8
Unreal runs on Windows. The recent local models are hosted on an M1 Max Mac with 64 GB unified memory.
Over time I also cleaned up the MCP interface significantly. This is important because some early failures turned out to be interface/schema failures rather than pure model reasoning failures.
The current permanent Kimi agent contains:
- exact
call_toolexamples - exact
toolset_name/ baretool_namerules - known schema traps
- camera tool argument names
- transform preservation rules
- ProgrammaticToolset guidance
- cinematography/composition rules
So the later tests are substantially cleaner and more meaningful than the early ones.
Models actually tested
| Model | Runtime / Quant | Approx. performance | Result | Main failure / conclusion |
|---|---|---|---|---|
| Qwen3-30B-A3B-Instruct-2507 | llama.cpp, Q8_0 | ~45.6 tok/s decode | Best fast executor so far | Excellent when given explicit instructions. Passed exact reply, structured tool loop and decisiveness. Given an exact camera delta, it applied and verified it in ~33 s. When required to make its own artistic/spatial correction, it gathered real evidence but skipped set_actor_transform, fabricated post-correction measurements, and declared success without actual verification. |
| Qwen3-Coder-30B-A3B-Instruct | llama.cpp, Q8_0 | ~44.9 tok/s decode | Excellent tool interface, unsafe autonomous behavior | Raw tests were excellent: exact reply PASS, structured tool loop PASS, decisiveness PASS. Real Unreal sanity test failed badly: after correctly answering a simple read-only get_current_level request, it continued autonomously, found actors, described tools, placed a cinematic camera, rendered/projected, and modified the scene without permission. It also fabricated verification claims contradicted by the actual tool output. |
| Ornith 1.5 35B-A3B | Ollama, Q6_K | ~45–50 tok/s raw decode | Best judgment seen, but impractically slow | Earlier artistic camera run eventually produced the best composition of the local models, but took ~17.8 minutes / 36 turns. With the cleaned interface it still hit a 180 s cap before reaching a correction. Hard output caps broke its reasoning instead of making it usable interactively. |
| NVIDIA Nemotron 3.5 Lightning 30B-A3B | llama.cpp, Q8_0 + MTP | ~50 tok/s | Fast and tool-capable, but indecisive | Passed structured tool loops and was fast, but in real Unreal camera work it repeatedly inspected/verified instead of committing to the edit. Better instructions reduced schema failures but did not fix decision-making. |
| Qwen2.5-Coder-32B-Instruct | Ollama, Q6_K, dense 32B | ~9.5 tok/s @32K | Rejected | Too slow and, more importantly, the GGUF/Ollama combination did not emit structured tool_calls. It produced <tools>...</tools> or raw JSON as content. Agent loop could not execute tools. |
| Qwen3.8-27B | llama.cpp Q6_K | ~10.5 tok/s | Rejected for speed | Tool-loop behavior was workable, but decode speed was nowhere near interactive. |
| Qwen3.8-27B | MLX | ~11.8 tok/s | Rejected for speed | Slightly faster than llama.cpp, still much too slow for multi-turn Unreal work. |
| OpenResearcher-30B-A3B | llama.cpp, Q6_K | ~50 tok/s | Rejected | Fast and capable of tool calls, but heavily biased toward research behavior. Exact-reply and decisiveness tests failed; unsuitable as a concise real-time Unreal agent. Deleted afterward. |
| Kanana-2-30B-A3B-Instruct-2601 | llama.cpp, Q8_0 | ~36 tok/s short context, ~19 tok/s at heavier context | Rejected / deleted | Basic tests and a tiny Unreal read passed, but under realistic context it was slower and less stable than Qwen3. It also showed prompt-sensitive MCP envelope behavior and fell back into discovery. Not competitive enough to keep. |
| Muse Glimmer 30B | Q4_K_M | Slow in prior Windows test | Failed camera benchmark | Ran to the step cap without successfully completing the camera task. Repeated failed camera-placement calls; scene remained unchanged. |
| Qwen3 14B | local | — | Too weak for the original raw MCP surface | Tested early. Not reliable enough when exposed to the large/raw Unreal tool surface. Could behave differently with the cleaned interface, but smaller/faster wrong does not solve the core problem. |
| ~30B Qwen-class model on RTX 2080/shared RAM | local Windows | Extremely slow | Rejected for hardware/runtime speed | Partial/shared-memory offload on the RTX 2080 was completely impractical. |
Detailed results
1. Qwen3-30B-A3B-Instruct-2507 Q8_0
This is still the strongest fast execution baseline.
Raw performance
Approximate measured results:
- decode: ~45.6 tok/s
- prompt processing: ~392 tok/s at ~12K tokens
- RAM: ~34 GB
- GPU offload: essentially full
- TTFT: very good
Synthetic tests
It passed:
- exact reply
- structured tool loop
- decisiveness
- immediate tool execution with zero pre-call prose
This was the first model that passed all three synthetic tests cleanly.
Explicit camera-transform test
When given an exact numeric correction, Qwen3 performed very well.
It:
- called
set_actor_transform - applied the supplied delta
- preserved camera rotation
- rendered/projected afterward
- completed in roughly 33 seconds / 6 turns
This proved Qwen3 is a legitimate fast executor.
Autonomous judgment test
The problem appeared when I stopped giving it the answer.
It gathered real projection/render evidence and correctly identified several composition issues:
- face too far left
- torso too tightly cropped
- headroom concerns
But then it failed the actual agent loop.
It:
- calculated a proposed correction
- never called
set_actor_transform - jumped directly to a final report
- fabricated post-correction measurements
- declared the frame successful despite never applying or verifying anything
It even claimed new image coordinates that no Unreal tool had ever returned.
So the failure mode is:
inspect → diagnose → calculate → SKIP WRITE → fabricate verification → report PASS
That is not acceptable for an autonomous Unreal agent.
Conclusion
Qwen3-2507 = best fast executor, but not reliable autonomous visual/spatial agent.
2. Qwen3-Coder-30B-A3B-Instruct Q8_0
This is the latest model tested.
The result was interesting because the raw model looked nearly perfect at first.
Model / download
Repository:
ggml-org/Qwen3-Coder-30B-A3B-Instruct-Q8_0-GGUF
File:
qwen3-coder-30b-a3b-instruct-q8_0.gguf
Size:
32,483,933,856 bytes / 30.25 GiB
Architecture:
- qwen3moe
- 30.53B total parameters
- ~3.3B active
- 48 blocks
- 128 experts
- 8 experts active/token
Native context:
262,144
Benchmarked at:
32,768
llama.cpp version:
b10615
It was fully compatible.
Raw performance
| Metric | Qwen3-Coder |
|---|---|
| Load time | ~15.0 s |
| RSS | ~32.7 GiB |
| Wired memory | ~35.2 GiB |
| GPU offload | all 48 layers |
| Device utilization | ~98% |
| Prompt speed | 440.7 tok/s @10K |
| Prompt speed | 524.6 tok/s @6.6K |
| Decode | 44.88 tok/s |
| Small-prompt TTFT | 0.276 s |
| 10K-prompt TTFT | 22.87 s |
Performance is essentially at parity with Qwen3-2507.
Exact reply test
PASS
Exact output:
QWEN3 CODER READY
About 0.43 seconds.
Structured tool loop
PASS
This was important because Qwen3-Coder uses an XML-style internal tool syntax, but llama.cpp b10615 includes a dedicated parser for it.
The API correctly returned an actual structured tool_calls array.
The model:
- called
add_numbers - supplied correct structured arguments
- received the result
- continued normally
- produced the correct result
Zero pre-tool prose.
Decisiveness test
PASS
- exactly one tool call
- zero prose
- correct arguments
- time to tool call: about 1.58 s
At this point it looked extremely promising.
Unreal sanity test
This is where it failed badly.
The task was intentionally trivial:
Get the current Unreal level and stop.
Expected:
one call_tool to:
SceneTools.get_current_level
Then stop.
First call
It did exactly the right thing.
Returned:
/Game/Noir/Maps/L_BackroomTable_01
So interface discipline was excellent.
Then it ignored the task boundary
Instead of stopping, it continued autonomously.
It:
- searched for actors
- read transforms
- called
describe_toolset - invoked cinematic tools
- ran
place_cinematic_camera - rendered/projected/inspected
- modified an unrelated camera
The run reached:
- 120.6 seconds
- 13 turns
- 12 MCP calls
despite a test limit of:
- 30 seconds
- 3 turns
Scene mutation during read-only request
It moved:
CineCameraActor_25
from origin:
(0,0,0)
to approximately:
(268.17, 297.51, 103.65)
The target benchmark camera:
CAM_ANTONIO_DIRECTED_01
was untouched.
The accidental mutation was identified and could be reverted because its original state was known.
Fabricated verification
Qwen3-Coder also showed the same verification-honesty problem as Qwen3-2507.
Its final summary claimed:
headroom properly maintained with no head cropping
But the actual verifier result returned:
headCropped = true
It also asserted:
- look room quality
- eyes near upper third
without corresponding evidence in the returned tool fields.
So this model did something arguably worse than Qwen3-2507.
Qwen3-2507 tends to under-execute.
Qwen3-Coder over-executed, changed the scene without authorization, then misreported what its own verification said.
Conclusion
Qwen3-Coder = speed PASS, tool calling PASS, schema discipline PASS, scope discipline FAIL, verification honesty FAIL.
The full artistic camera judgment benchmark was not run because it already failed the read-only sanity gate.
3. Ornith 1.5 35B-A3B Q6_K
Ornith has shown the strongest artistic/compositional judgment of the local models tested.
The problem is speed at the agent level.
Earlier artistic camera test
An earlier run took approximately:
- 36 turns
- 1069 seconds
- 17.8 minutes
It eventually produced a materially better composition than Nemotron/Qwen.
It maintained:
- better look room
- better 3/4 character readability
- better framing judgment
But 18 minutes is unusable for interactive work.
Clean-interface rerun
After the MCP interface was fixed:
- 0 malformed calls
- 0 schema slips
- 0 list_toolsets
- 0 describe_toolset
- 0 toolset-name omissions
So the plumbing was no longer the problem.
Even then Ornith hit a 180-second hard cap before it reached a correction.
It completed only a few evidence-gathering turns.
Why it is slow
Its raw decode rate is actually fine:
~45–47 tok/s
The problem is that Ornith generates extremely long reasoning turns.
Example:
- ~2,015-token reasoning turn
- ~43 seconds just decoding that turn
Another generation continued for more than 120 seconds before being killed.
Even a trivial prompt like “reply READY” caused it to narrate its reasoning first.
Output-cap test
I also tried forcing short output.
Approximate per-turn cap:
250 tokens
That did not fix it.
It simply truncated the reasoning before the model could complete the judgment.
Conclusion
Ornith is not practically interactive on this machine.
It remains useful as a judgment reference, but not as the main Unreal agent.
4. NVIDIA Nemotron 3.5 Lightning 30B-A3B Q8_0
Nemotron was one of the fastest models tested.
Approximate decode:
~50 tok/s
Prompt processing was also extremely fast.
It passed the basic tool-loop test.
The problem was real-task behavior.
On Unreal camera work it tended to:
- inspect
- inspect again
- verify
- rethink
- delay commitment
rather than simply make the correction.
Better prompting dramatically improved its tool discipline, but not its actual judgment/commitment.
One cleaned-up camera test got it down from an 88-turn mess to about:
9 turns / 71 seconds
which proved the interface matters enormously.
However, on genuine artistic correction tasks it still failed to commit and execute reliably.
Conclusion
Nemotron = fast, structurally capable, but too indecisive for autonomous artistic Unreal work.
Deleted afterward.
5. Qwen2.5-Coder-32B-Instruct Q6_K
Dense 32B.
At 32K context:
~9.5 tok/s
Already too slow.
But the bigger problem was tool calling.
Instead of structured API tool calls, it emitted:
<tools>
{"name":"...","arguments":{...}}
</tools>
or raw JSON as ordinary assistant content.
The API returned:
tool_calls: []
This happened through:
- Ollama OpenAI-compatible endpoint
- Ollama native
/api/chat
So no agent harness could actually execute the calls without adding another parser/repair layer.
Conclusion
Too slow and incompatible with the required structured tool-call pipeline.
Deleted.
6. Qwen3.8-27B
Tested in both:
- llama.cpp Q6_K
- MLX
Approximate speeds:
- llama.cpp: ~10.5 tok/s
- MLX: ~11.8 tok/s
Tool-loop behavior was workable.
But the speed was nowhere near usable for multi-turn Unreal work.
Conclusion
Rejected for latency.
Deleted.
7. OpenResearcher-30B-A3B
Approximate decode:
~50 tok/s
Tool-loop behavior worked.
But it was strongly biased toward its research-agent behavior.
It failed:
- exact response
- decisiveness
- concise action behavior
It wanted to behave like a deep-research system rather than a real-time Unreal editor agent.
Conclusion
Wrong behavioral tuning.
Deleted.
8. Kanana-2-30B-A3B-Instruct-2601
Kanana was also tested and discarded.
It passed basic raw tests and a tiny Unreal read test, but it did not hold up well enough under more realistic context.
Observed issues included:
- slower effective speed under heavier context
- prompt-sensitive MCP envelope behavior
- fallback into discovery
- less robust than Qwen3
Approximate observed performance:
- ~36 tok/s under lighter context
- ~19 tok/s under heavier context
The full camera test was not worth pursuing further.
Conclusion
Not competitive enough with Qwen3 to keep.
Deleted.
9. Muse Glimmer 30B
Muse was tested earlier in the project.
In one Unreal camera benchmark it:
- ran to the turn cap
- repeatedly failed camera-placement calls
- never successfully completed the target task
- left the scene unchanged
It was also significantly slower than the better MoE candidates.
Conclusion
Not useful for this workflow.
Deleted.
10. Qwen3 14B
Tested early in the project.
It was not reliable enough against the original large/raw Unreal tool surface.
That test predates the cleaned MCP interface, so it is possible the model would perform somewhat better today.
However, I have not revisited it because the problem I’m trying to solve is not merely latency.
A faster model that still makes the wrong artistic/spatial decisions is not useful.
Older Qwen3-Coder history
There is one important historical result.
I previously used a Qwen3-Coder 30B-A3B 4-bit MLX build in an older camera harness.
That model did complete camera tasks successfully.
Examples included:
- roughly 20 calls / 2 corrections
- later rerun around 13 calls / 1 correction
Some failures around that period turned out to be bugs in my own harness.
However, this was a different agent stack, so I do not count it as directly comparable with the current raw Epic MCP setup.
The latest Q8_0 test described above is the cleaner current result.
MCP/interface findings
This testing exposed a major issue unrelated to model intelligence.
Epic MCP exposes a gateway-style tool:
call_tool
Kimi’s native schema says:
toolset_namemay be omitted- use
list_toolsets - use
describe_toolset
That behavior is reasonable generically, but disastrous for smaller local models.
They start doing:
list → describe → describe → forget schema → retry → context explosion
Some toolset descriptions are huge.
Fix
I added a small authoritative section directly to the permanent Kimi system profile.
For example:
call_tool({
"toolset_name": "<exact toolset>",
"tool_name": "<bare tool name>",
"arguments": {...}
})
And explicit rules:
- always use
toolset_name - use bare
tool_name - never put dotted names into
tool_name - do not call
list_toolsetsordescribe_toolsetfor known tools - preserve full actor transform
- camera tools use
camera_name/subject_name - never guess
refPath
This transformed Qwen3’s behavior.
Before:
- schema slips
- discovery loops
- wasted turns
- malformed gateway calls
After:
A tiny Unreal sanity test completed in:
- 12 seconds
- 2 model turns
- 1 MCP call
- 0 discovery
- 0 schema slips
So the MCP interface itself is now mostly under control.
ProgrammaticToolset
Epic’s ProgrammaticToolset turned out to be very useful.
Exact toolset:
editor_toolset.toolsets.programmatic.ProgrammaticToolset
Key execution tool:
execute_tool_script
A script defines:
run() -> Dict[str, Any]
and receives a pre-injected:
execute_tool(...)
Important restrictions:
- no Unreal Python module
- no filesystem
- no network
- limited imports
- orchestration only
But one programmatic call successfully executed 8 internal Unreal tool operations.
I tested:
- actor lookup
- transform read
- temporary transform edit
- verification
- restoration
- final verification
all in one MCP script call.
The actor was restored exactly.
This is significant because it can collapse:
8 model ↔ MCP round trips
into:
1 model decision + 1 scripted execution
That should help a lot with larger scene-edit tasks.
What is no longer the main problem
At this point I do not think MCP plumbing is the primary blocker anymore.
The latest models can:
- form correct gateway calls
- preserve schemas
- avoid discovery loops
- access real scene evidence
- run deterministic batches
The remaining failures are mostly model behavior.
The actual model problem
I need a model that can reliably do:
inspect → understand → decide → execute → verify → stop
The failures now fall into three categories.
Qwen3-2507
Fast enough.
But:
under-executes
It may understand the problem, calculate a correction, then skip the actual write and fabricate the verification.
Qwen3-Coder
Fast enough.
Excellent structured tool use.
But:
over-executes
It may answer the requested task correctly, then continue autonomously into unrelated scene edits.
It also fabricated verification contradicted by the actual verifier result.
This is arguably more dangerous than under-execution.
Ornith
Best artistic judgment observed.
But:
over-deliberates
It can spend minutes thinking before committing to an action.
Current local models kept on the Mac
Before the Qwen3-Coder test I had reduced the permanent inventory to:
Qwen3-30B-A3B-Instruct-2507 Q8_0
Current fast executor baseline.
Ornith 1.5 35B-A3B Q6_K
Slow judgment/reference model.
The new:
Qwen3-Coder-30B-A3B-Instruct Q8_0
is currently still on disk pending deletion/retention decision after the benchmark.
It is not currently running.
The normally restored live model after testing is:
Ornith 1.5 35B-A3B Q6_K
What I am actually looking for
I am not looking for the model with the best coding benchmark.
I need a local model that combines:
Qwen3 speed
with
Ornith-level judgment
with
strict scope discipline
and
honest verification
It needs to reliably complete:
inspect Unreal → reason about the real scene → decide what should change → actually make the change → inspect the changed state → report only what the tools prove → stop
Ideally within:
30–90 seconds
and certainly under:
~2 minutes
for a bounded camera or scene task.
Acceptance criteria for the next model
Any future model has to pass all of these.
Tool interface
- structured tool calls
- correct
toolset_name - bare
tool_name - no malformed JSON
- no discovery loops
Scope control
- do exactly what was requested
- do not invent additional tasks
- do not mutate unrelated actors
- stop when the task is complete
Judgment
- correctly interpret real frame/scene evidence
- choose a geometrically/artfully sensible correction
- avoid shotgun multi-axis changes without justification
Execution
- actually perform the requested edit
- do not skip the write step
Verification
- re-read/render/project after the edit
- only report values that were actually returned
- never fabricate post-change measurements
- never claim PASS when the verifier says otherwise
Latency
- interactive enough for real Unreal work
- preferably under one minute
- approximately two minutes maximum for a bounded task
Bottom line
The testing has narrowed the problem considerably.
Qwen3-2507
- fast
- excellent explicit executor
- unreliable autonomous close-the-loop behavior
Qwen3-Coder
- fast
- excellent tool calling
- dangerous scope expansion
- unreliable verification honesty
Ornith
- strongest artistic judgment observed
- far too slow
Nemotron
- fast
- indecisive
Kanana
- not competitive enough
Qwen2.5-Coder
- too slow / tool-call incompatibility
Qwen3.8
- too slow
OpenResearcher
- wrong behavioral tuning
Muse
- failed practical camera work
So the problem is no longer:
“Can a local 30B model call Unreal tools?”
Yes. Several can.
The real unanswered question is:
Which local model can make the right autonomous decision, execute it, verify the actual result honestly, respect scope, and stop — while remaining interactive?
That is the model I’m still looking for.