self-hosted/ai
§01·recipe · multimodal

Fara1.5-9B on RTX 4070 Ti: a Q5_K_M Browser Computer-Use Agent with llama.cpp

multimodaladvanced12GB+ VRAMJul 31, 2026

This advanced recipe sets up Fara1.5-9B on the RTX 4070 Ti, needing about 12 GB of VRAM.

models
tools
prerequisites
  • NVIDIA RTX 4070 Ti (12GB VRAM) or equivalent
  • Python 3.10+
  • llama.cpp built with CUDA (release b10087 or newer)
  • A disposable browser profile — fara-cli drives a plain local Playwright Chromium, with no isolation of its own

What You'll Build

A local browser computer-use agent (CUA) on a single RTX 4070 Ti: llama-server hosting Fara1.5-9B as a Q5_K_M GGUF plus its vision projector, driven by Microsoft's own fara-cli harness against a Playwright-launched Chromium at 1440×900. You give it a task in English; it takes a screenshot, reasons, and emits a click, a keystroke or a URL visit, then loops.

The 4070 Ti is the fastest card in the 12 GB Ada family and it is still a 12 GB card, which matters here more than the shader count does: every constraint below is a byte count. The 9B fits, the 27B does not, and the quant tier is set by capacity rather than by how quickly the card can chew through it. All of that is derived on this page rather than asserted.

Hardware data: RTX 4070 Ti (12GB VRAM) · Q5_K_M + f16 mmproj + 32K context = 8.434 GiB of accountable allocations · See benchmark data

⚠️ This model clicks, types and submits in a real browser, and the path documented here gives it no isolation. That the model acts is the product, not a side effect, so this is the thing to settle before anything else on this page.

The model card is blunt about the consequence: "Don't run this model with unrestricted browser access on a machine that has anything sensitive on it." That sentence closes the card's Recommended Deployment: MagenticLite section, which lists sandboxing, allow-lists, watch-mode and a pause control as MagenticLite features and then says: "If you integrate Fara1.5-9B directly, you're responsible for these controls." The card's Responsible AI section is more specific still: "run Fara in an isolated container with no access to host files, environment variables, or sensitive credentials".

fara-cli is a direct integration. Its install step is playwright install, and it drives a plain local Playwright Chromium — no container, no allow-list, no pause control. It does launch its own browser with a fresh profile rather than attaching to your logged-in one, but it runs uncontained, on your machine, with your network. The harness README offers the containerised option as an alternative to it, not as part of it: "instead of fara-cli you can use Magentic-UI", described there as "a sandboxed browser environment with auditable action logging and user prompts at critical points". For the CLI path the vendor's position is a recommendation, not something shipped: "We recommend running it in a sandboxed environment, monitoring its execution, and avoiding sensitive data or high-risk domains."

So on the fara-cli path you are the isolation boundary. Practically: a throwaway OS user or a VM, Playwright's own fresh browser profile, signed out of everything, low-stakes public sites, and eyes on the run rather than leaving it unattended. If you want the container, the allow-lists, watch-mode and the pause control, use Magentic-UI as the client instead — see Running.

ℹ️ Not a general-purpose vision chatbot. Fara1.5-9B is fine-tuned for one loop: screenshot in, <tool_call> out. It sits in our multimodal vertical because it takes images and text and emits text, but it is trained against a specific system prompt and a fixed computer_use action schema. Per the model card: "The model is vision-only at perception time: it sees the browser through screenshots, not the DOM or accessibility tree." A Fara install that only serves text is a broken install — the --mmproj step below is mandatory, and Step 4 exists to prove an image really reached the model before you let it drive anything.

⚠️ Runtime status — read before you commit an evening to this. llama.cpp support for this architecture is verifiable statically, and this recipe shows that evidence rather than asserting it. What does not exist anywhere public is a report of Fara1.5 decoding an image under llama-server or llama-mtmd-cli. The closest datapoint is a community user serving Fara1.5-27B under llama-server on build b10107 and driving it from an agent harness successfully — but that thread is about tool-call parsing, not about the projector. Treat your first run as the test, and see Results for why there is no speed figure here either.

Requirements

ComponentMinimumThis recipe
GPU12GB VRAM, CUDA compute 8.9 (Ada)— not measured; budget below is derived (/contribute)
RAM16GB (weights are mmap'd; Chromium sits on top)
Storage7.77 GB (Q5_K_M + f16 mmproj)7,771,094,688 B = 7.237 GiB
Softwarellama.cpp b10087+ (CUDA), Python 3.10+, Playwright

Microsoft ships safetensors only for all three Fara1.5 sizes. Its Quickstart serves them with vLLM and names datacenter hardware for the job — "A GPU with enough memory for a 9B model in bf16 (A6000, A100, H100, and B200 have been tested)" — and the card's highlights claim only that the model "runs on a single A100/H100/B200 with room for the screenshot history". The consumer route is the quantised one documented here.

Why the 9B on a 12 GB card

The 9B has to earn the tier, and Microsoft answers this directly in the Fara-1.5 paper: "Fara1.5-9B already covers a substantial fraction of the 4B-to-27B gain on both metrics, which makes it the natural choice when deployment cost is a constraint." — followed immediately by "Fara1.5-27B is the right choice when raw accuracy matters more than cost." On a 12 GB card, cost is the constraint.

The numbers behind that, from the paper's Table 7 (averaged over three independent runs, with 95% confidence intervals — a better source than the bare figures on the model card):

ModelWebVoyagerOnline-Mind2WebWebTailBench v1.5
Fara1.5-4B80.8 ± 2.357.3 ± 4.327.4 ± 2.8
Fara1.5-9B86.6 ± 2.063.4 ± 4.332.3 ± 2.8
Fara1.5-27B89.3 ± 1.872.3 ± 3.840.2 ± 3.0

Microsoft's own reading of those intervals is that they are "small relative to" the gaps between model sizes, confirming the scaling trend is not run-to-run noise — and across the family as a whole that is fair. Doing the arithmetic pairwise gives a more granular picture, and this next part is our reading, not theirs: on WebVoyager the 9B reaches 88.6 at the top of its interval while the 27B drops to 87.5 at the bottom of its own, so the two overlap; on Online-Mind2Web (67.7 vs 68.5) and WebTailBench (35.1 vs 37.2) they do not. Overlapping 95% intervals is only a conservative proxy for a significance test and does not by itself establish that two results are indistinguishable — so read this as the WebVoyager gap is much the weakest-separated of the three, not as a null result.

The paper is specific about where the remaining gap comes from, and it is not speed. Larger models "solve more of the hard tail", and on success rate versus trajectory length: "The mapping is largely independent of size in the bulk of the step range; larger models retain a higher success rate only in the long tail." Nor is the 9B less efficient per task it completes — "The larger mean step counts of smaller models above are largely a consequence of their higher failure rate, not lower efficiency." Expect the 9B to handle ordinary tasks about as directly as the 27B would, and to lose ground specifically on long, hard trajectories. The ceiling is low for everyone on the hardest benchmark: the paper's own words are that "even the 27B model leaves a substantial fraction of examples unsolved in three attempts" there.

This exact question was asked on the 9B's own HuggingFace discussions in July 2026 — "Does anyone have any suggestions on the kinds of tasks where the 9B model fails where the 27B model appears to be necessary?" — and drew no community answer; the asker later edited the post to point back at the paper. Nobody has published a qualitative rule, so the interval arithmetic above is as sharp as the public evidence gets.

What the tier costs on this card is unusually easy to state, because the 4B and 9B are architecturally identical everywhere except width. Both declare num_key_value_heads: 4, head_dim: 256, 32 blocks and full_attention_interval: 4 in config.json — so the per-token KV cost of the 9B is exactly the same as the 4B's, and so is its fixed recurrent state. The entire difference is weights. Like for like at Q5_K_M that is 3.088 → 6.382 GiB, plus 0.626 → 0.855 GiB of projector, from the 4B and 9B GGUF trees. In practice the gap is smaller than that, because a 12 GB card has room to run the 4B at Q8_0 (4.185 GiB) and the 9B only at Q5_K_M — so the real trade is 4.812 GiB of 4B weights-plus-projector against 7.237 GiB of 9B, for the benchmark deltas above.

The 27B is simply out of reach here: its Q4_K_M is 16.329 GiB before the 0.864 GiB projector, from the 27B GGUF tree.

How it fits in 12 GB — the arithmetic

There is no benchmark for this pair yet (/check reports unknown), so every figure below is derived from the artifact and from llama.cpp's own source. Reproduce it rather than trusting it.

Weights. Byte counts from the bartowski GGUF tree:

ComponentBytesGiB
Fara1.5-9B-Q5_K_M.gguf6,852,928,6406.382
mmproj-Fara1.5-9B-f16.gguf918,166,0480.855
Weights total7,771,094,6887.237

HuggingFace displays the first of those as 6.85 GB, because its file listing is decimal. Both figures are the same bytes.

KV cache — smaller than you expect, because this is a hybrid model. qwen35 is hybrid in llama.cpp: LLM_ARCH_QWEN35 is one of the cases llm_arch_is_hybrid() returns true for in src/llama-arch.cpp, and src/models/qwen35.cpp assigns layers with hparams.is_recr_impl[i] = (i < hparams.n_layer()) && ((i + 1) % full_attn_interval != 0). The Q5_K_M file's own GGUF header carries qwen35.block_count = 32 and qwen35.full_attention_interval = 4, and the source config.json spells the split out layer by layer: 24 of 32 layers are linear_attention, only 8 are full_attention. Only those 8 carry a cache that grows with context.

With the same file's qwen35.attention.head_count_kv = 4 and attention.key_length = attention.value_length = 256:

K+V per token per attention layer = 2 x 4 x 256 x 2 B (f16) = 4,096 B
x 8 full-attention layers                                   = 32 KiB / token

n_ctx = 16,384 -> 0.500 GiB
n_ctx = 32,768 -> 1.000 GiB
n_ctx = 65,536 -> 2.000 GiB

A non-hybrid 32-layer model with the same head geometry would cost four times that. Context on this model is cheap — that is the fact the 12 GB budget turns on.

Recurrent state — fixed, tiny, and allocated four times over. The other 24 layers hold a context-independent state, sized by n_embd_r() / n_embd_s() in src/llama-hparams.cpp from the artifact's own GGUF keys (qwen35.ssm.conv_kernel = 4, ssm.inner_size = 4096, ssm.group_count = 16, ssm.state_size = 128):

conv state : (4-1) x (4096 + 2 x 16 x 128) =  24,576 elems
ssm  state :         128 x 4096            = 524,288 elems
per layer  : 548,864 x 4 B (F32)           = 2.094 MiB
x 24 recurrent layers                      = 50.25 MiB   <- per sequence
x 4 sequences                              = 201.0 MiB = 0.196 GiB

That last multiplication is the part it is easy to miss, so here is where it comes from. src/llama-model.cpp sizes the recurrent memory with /* recurrent_rs_size */ std::max((uint32_t) 1, cparams.n_seq_max) — one state slot per sequence, not one in total. For llama-server, common/arg.cpp sets params.n_parallel = -1; // auto by default, and tools/server/server.cpp resolves that auto to params.n_parallel = 4 with kv_unified = true. So a default llama-server allocates four copies of the 50.25 MiB state, and none of it grows with -c.

The KV cache is not multiplied the same way, and it would be wrong to do so: the same auto branch sets kv_unified = true, and src/llama-context.cpp then takes the branch if (cparams.kv_unified) { cparams.n_ctx_seq = cparams.n_ctx; } — one shared cache of n_ctx, not four of n_ctx / 4.

Total at the recommended settings:

GiB
Q5_K_M weights6.382
f16 mmproj0.855
KV cache @ 32,7681.000
Recurrent state (4 sequences)0.196
Accountable total8.434

The whole matrix, in GiB, same four terms with the quant and the context varied:

ContextQ4_K_MQ5_K_MQ6_KQ8_0
16,3847.0567.9348.72310.442
32,7687.5568.4349.22310.942
65,5368.5569.43410.22311.942
131,07210.55611.43412.22313.942

Two cells in that table exceed the card's 12 GiB before a single compute buffer is allocated (Q6_K and Q8_0 at 131,072), and the two above 11 GiB leave nothing usable. The model's native 262,144 window is off the table at every tier.

This recipe leads Q5_K_M at -c 32768, and the reason is the quant, not the context. Fara's output is pixel coordinates — it is a grounding model, and Microsoft's published scores are for bf16 weights. No public evaluation measures how much grounding accuracy any quantisation costs, so the quant tier is where the unmeasured risk sits, and Q4_K_M is the tier furthest from what was evaluated. Q5_K_M costs 0.877 GiB over Q4_K_M and leaves 3.566 GiB of a 12 GiB card for the CUDA context, compute buffers, the vision encode graph and your desktop — the widest margin in the table that is still above Q4. Those terms are not in the table: they are allocated at load time and this recipe does not derive them. Read the CUDA0 compute buffer size lines llama-server prints on startup for the real figures on your build, and report what you see.

Two upgrades are available with that slack, and the next section says which one to take:

  • -c 65536 — costs exactly 1.000 GiB, lands at 9.434 GiB, leaves ~2.5 GiB.
  • Q6_K @ 32,768 — costs 0.789 GiB, lands at 9.223 GiB, leaves ~2.8 GiB.

Q8_0 is not one of them, and that is the one thing worth saying twice on a 4070 Ti. This is the strongest card in the 12 GB Ada family, and the instinct on a strong card is to reach for the top of the quant ladder — but the ladder is limited by capacity, not by compute. Q8_0 at any usable context lands between 10.4 and 11.9 GiB of accountable allocations on a 12 GiB card, which is before the CUDA context and compute buffers exist. Every term in the matrix is a byte count, so the RTX 4070, 4070 Super and 4070 Ti all get the same table and the same lead; what the Ti's extra shaders buy is throughput, and this page has no throughput figure to attach them to (see Results). If you want Q8_0 or bf16 Fara1.5-9B, the constraint you need to relax is VRAM, not the GPU.

How the context is actually spent

A CUA sends a screenshot every step, so the per-image cost decides whether the budget above is realistic.

The harness fixes the browser viewport at 1440×900 (viewport_width: int = 1440, viewport_height: int = 900 in fara15_agent.py), matching the card's note that "The screen resolution Fara is most commonly trained with is" 1440×900. It pre-resizes with Qwen's smart_resize at PATCH_SIZE = 16 and MERGE_SIZE = 2, so the factor is 32 and 900 becomes 896. llama.cpp applies the same algorithm in img_tool::calc_size_preserved_ratio (tools/mtmd/mtmd-image.cpp) and clamps this projector to 8–4096 merged tokens (hparams.set_limit_image_tokens(8, 4096) under PROJECTOR_TYPE_QWEN3VL in tools/mtmd/clip.cpp), so nothing is rescaled:

(1440 / 32) x (896 / 32) = 45 x 28 = 1,260 tokens per screenshot
x max_n_images = 3                 = 3,780 tokens resident

The harness's own conservative default is image_token_estimate: int = 1500, a useful sanity check on that arithmetic, and the model card agrees on the retention: "We only keep the most recent 3 screenshots" in the chat history.

So images are capped. What grows is the text trajectory: every round appends the model's reasoning, its tool call and an observation (max_observation_chars: int = 1000). And this is where the round count decides your -c:

32,768 - 3,780 resident screenshots ~= 29,000 tokens of trajectory room
  / --max_rounds  15  (the smoke test below)  ~= 1,900 tokens per round
  / --max_rounds 100  (the CLI default)       ~=   290 tokens per round

290 tokens per round is thin for a model that writes out its reasoning before every action. src/fara/run_fara.py defaults --max_rounds to 100, and llama.cpp will not paper over an overrun — common/common.h ships bool ctx_shift = false; // context shift on infinite text generation. So: keep -c 32768 for short, supervised runs, and raise it to -c 65536 before you let the harness run to its default 100 rounds. That doubles the trajectory room to roughly 620 tokens per round for exactly 1.000 GiB, which the budget above has.

The harness README asks for a floor rather than a target — "Please ensure that context length is set to at least 15000 tokens and temperature to 0 for best results." — and Microsoft frames the model's native 262K window as "Long enough for multi-screenshot trajectories with full action history." Neither number is a recommendation for this card; the arithmetic above is.

Installation

1. Build llama.cpp with CUDA

Ada Lovelace is sm_89. Build from a release at or after b10087 — the release this GGUF was quantized on.

git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=89 -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j

Both halves of the model are first-class in mainline — worth confirming before you download 7.8 GB:

grep -n 'LLM_ARCH_QWEN35,' src/llama-arch.cpp                       # -> { LLM_ARCH_QWEN35, "qwen35" }
grep -n 'qwen3vl_merger' tools/mtmd/clip-impl.h                     # -> PROJECTOR_TYPE_QWEN3VL
grep -n 'Qwen3_5ForConditionalGeneration' conversion/qwen3vl.py     # -> Qwen3VLVisionModel (the mmproj converter)

2. Download the weights and the projector

Both files are required. The projector is a separate artifact — downloading only the text GGUF gives you a model that cannot see.

pip install -U huggingface_hub

hf download bartowski/Fara1.5-9B-GGUF \
  Fara1.5-9B-Q5_K_M.gguf mmproj-Fara1.5-9B-f16.gguf \
  --local-dir ~/models/fara1.5-9b

Microsoft publishes no first-party GGUF for Fara1.5 — the microsoft org ships safetensors for all three sizes. Four independent community quantisers have converted the 9B including its vision tower, each publishing a matching mmproj: bartowski, prithivMLmods, DevQuasar and Tdamre. Four independent conversions succeeding is the strongest available evidence that the vision path converts cleanly; it is still not a runtime test. You can check that the file you just downloaded declares the projector llama.cpp implements:

strings ~/models/fara1.5-9b/mmproj-Fara1.5-9B-f16.gguf | grep -m1 qwen3vl_merger

3. Serve it

./build/bin/llama-server \
  -m ~/models/fara1.5-9b/Fara1.5-9B-Q5_K_M.gguf \
  --mmproj ~/models/fara1.5-9b/mmproj-Fara1.5-9B-f16.gguf \
  -ngl 99 -c 32768 --host 127.0.0.1 --port 5000 \
  --image-min-tokens 1024 \
  --reasoning-format none \
  --temp 0

Three of those flags are load-bearing and none of them is a default:

  • --reasoning-format none — mandatory. Without it the agent loop silently receives nothing; see Troubleshooting for the mechanism.
  • --image-min-tokens 1024 — llama.cpp's floor for this projector is 8 merged tokens, and tools/mtmd/clip.cpp warns: "Qwen-VL models require at minimum 1024 image tokens to function correctly on grounding tasks". Fara is a grounding model, so that warning is aimed precisely at this case. At the harness's viewport the image is already 1,260 tokens and the floor never binds; it protects you if you shrink the viewport.
  • -c 32768 — see the budget above, and raise it to 65536 before running the harness's default 100 rounds.

If you would rather not manage files, -hf bartowski/Fara1.5-9B-GGUF:Q5_K_M fetches the model, and per the server docs "mmproj is also downloaded automatically if available".

4. Prove the vision path works before going further

Check that the server advertises multimodal support, then send a real image. The server docs make the first part a client-side requirement rather than an optional nicety: "Clients should check /models or /v1/models for the multimodal capability before a multimodal request." Run GET /props alongside it — it reports which modality loaded rather than merely that some did, which is the more useful signal when a projector silently failed. For the image itself, image_url.url "can be a remote URL, base64 (raw or URI-encoded via data:image/...;base64) or path to local file".

curl -s http://127.0.0.1:5000/v1/models | grep -o multimodal

# supplementary — names the modality that actually loaded
curl -s http://127.0.0.1:5000/props | python3 -c "import sys,json; print(json.load(sys.stdin)['modalities'])"
# expect: {'vision': True}

curl -s http://127.0.0.1:5000/v1/chat/completions -H "Content-Type: application/json" -d '{
  "model": "Fara1.5-9B",
  "temperature": 0,
  "messages": [{"role": "user", "content": [
    {"type": "text", "text": "Describe this image in one sentence."},
    {"type": "image_url", "image_url": {"url": "https://raw.githubusercontent.com/ggml-org/llama.cpp/master/media/llama0-logo.png"}}
  ]}]
}' | python3 -m json.tool

If the reply describes the picture, the projector is wired up. If /v1/models never prints multimodal, if /props reports vision: False, or if the model answers as though no image were present, stop here — the agent loop below will produce confidently wrong actions rather than fail loudly.

5. Install the harness

git clone https://github.com/microsoft/fara.git
cd fara
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
playwright install

Skip the README's [vllm] extra — that path serves bf16 on datacenter cards and is not what this recipe uses.

Running

Two clients share the same llama-server endpoint. Path A (fara-cli) is the smoke test, with no isolation. Path B (Magentic-UI) is what the vendor recommends for anything real. They are not equivalent in risk; pick deliberately.

Path A — fara-cli, for a first smoke test

fara-cli speaks to any OpenAI-compatible endpoint, and for the 9B specifically its built-in default already matches this setup: src/fara/run_fara.py ships DEFAULT_ENDPOINT_CONFIG = {"model": "Fara1.5-9B", "base_url": "http://localhost:5000/v1", "api_key": "not-needed"}. The flags below name all three anyway so the command is explicit and survives a change to that default.

source .venv/bin/activate
fara-cli \
  --base_url http://127.0.0.1:5000/v1 \
  --api_key sk-no-key-required \
  --model Fara1.5-9B \
  --task "find the current UTC time on time.is and tell me" \
  --output_folder ./runs/first \
  --save_screenshots \
  --max_rounds 15

The browser runs headless by default — run_fara.py documents --headful as "Run the browser in headful mode (show GUI, default is headless)" and passes headless=not args.headful at the call site. Add --headful for the first few runs so you can watch what it clicks; that is the difference between catching a wrong action and reading about it afterwards. Either way this drives a real, unsandboxed Chromium on your machine.

Each step writes a screenshot and a data_point.json trajectory into --output_folder — watch that folder early, because it is how you tell a grounding failure (clicks landing in the wrong place) from a reasoning failure.

Keep the task on a low-stakes public site and stay signed out. Fara is trained to stop and ask before entering personal information, making payments, submitting forms, signing in or sending messages; if you see it sail through one of those, stop the run — that is a wiring problem in your prompt path, not a quirk to tolerate.

Path B — Magentic-UI, for the container

Everything the model card assumes — the containerised browser, domain allow-lists, watch-mode and a pause control — lives in Magentic-UI, not in fara-cli. That is also where a Docker requirement would come from; there is no Docker step in Path A because Path A genuinely does not use one.

Magentic-UI takes the same OpenAI-compatible endpoint you started above, so nothing on this page changes except the client. Its setup is documented in its own repository rather than reproduced here, because this recipe has not verified those steps end to end on this GPU — give it http://127.0.0.1:5000/v1 as the model endpoint. Whichever client you use, match the trained resolution: the card is explicit — "Match this in your sandbox for the most reliable grounding."

Results

  • Speed: omitted. No benchmark of Fara1.5-9B on an RTX 4070 Ti — or on any consumer GPU — exists at the time of writing, and Microsoft publishes no throughput figures at all; the card names A6000, A100, H100 and B200 only as hardware its bf16 serving path was tested on, which says nothing about a Q5_K_M build on Ada. Borrowing a datacenter figure for a 12 GB card would be misleading, so this line stays empty until someone measures it. If you run it, please contribute the numbers.
  • VRAM usage: 7.237 GiB of weights; 8.434 GiB accountable including KV and the four-sequence recurrent state at 32,768 context, derived above from the GGUF's own metadata and llama.cpp's memory formulas. This is a derivation, not an observed peak — compute and vision buffers sit on top. Track the empirical picture at /check/fara1-5-9b/rtx-4070-ti.
  • Quality notes: the vision path here is structurally proven, not runtime-proven. Mainline llama.cpp registers Qwen3_5ForConditionalGeneration in conversion/qwen3vl.py as a Qwen3VLVisionModel; the shipped mmproj declares clip.projector_type = qwen3vl_merger, which maps to PROJECTOR_TYPE_QWEN3VL in tools/mtmd/clip-impl.h; four quantisers converted it independently; and llama-server documents the image_url content part the harness sends. What does not exist is a published report of Fara1.5 captioning an image or grounding a click under llama-server. Step 4 is there so you find that out in ten seconds rather than mid-trajectory.
  • Language: English only — the card puts "Languages other than English (training data is English-only)" out of scope.

For the full benchmark data, see /check/fara1-5-9b/rtx-4070-ti.

Troubleshooting

The agent does nothing — every step logs an empty response or list index out of range

This is the failure everyone hits, and it is not a bug in your setup. Fara opens a <think> block and never closes it. Under llama-server's default reasoning extraction the parser therefore treats the whole output — reasoning and the <tool_call> block together — as reasoning, returns it in reasoning_content, and leaves message.content empty. The harness parses message.content for <tool_call>, finds nothing, and the loop dies on round one.

Launch with --reasoning-format none, documented in llama.cpp's server reference as "none: leaves thoughts unparsed in message.content". The raw output then begins with a stray unclosed <think> tag, which is cosmetic.

Reported for Fara1.5-27B by a community user on build b10107 in the model's HF discussions and acknowledged there by Microsoft — swhitehead (an org member) replied "Would you mind posting this as an issue on the Fara1.5 github repo so we can mention this in the hosting instructions?", which became microsoft/fara#82, still open. It applies to the 9B for a checkable reason rather than by assumption: the 9B's own GGUF carries the same chat template, whose generation prompt emits an unclosed <think>. The same reporter adds: "If you have a router/proxy in front doing its own reasoning extraction, disable it there too."

The model answers but ignores the screenshot

Almost always the projector. Confirm --mmproj is on the command line, that the server logged an mmproj load stage, that /v1/models advertises the multimodal capability, and that Step 4's /props call reports vision: True. A text-only GGUF with no projector loads and runs perfectly happily — it simply cannot see, which in a CUA presents as confidently wrong actions rather than an error. Note the automatic projector download only happens on the -hf path; with -m you must pass --mmproj yourself.

Clicks land in the wrong place

Check the viewport first: the harness defaults to 1440×900 because that is what the model was mostly trained at, and a different window size changes both the grounding distribution and the token cost derived above. Then confirm --image-min-tokens 1024 is set, since llama.cpp's own warning ties sub-1024-token images to degraded grounding. Only after both should you suspect the weights.

rope.dimension_sections has wrong array length; expected 4, got 3

This is a real open llama.cpp bug (PR #25334, "fix: load qwen35 Ollama GGUF exports"), but it belongs to Ollama-exported qwen35 blobs, not to the GGUF this recipe uses. The Q5_K_M artifact's own header reads qwen35.rope.dimension_sections = [11, 11, 10, 0] — four entries — so it cannot hit this failure. If you see this error, you are loading a different file than the one downloaded in Step 2.

ollama run fara does nothing — there is no Ollama entry

There is no Ollama library entry for Fara1.5 in any form. The GGUF/LM Studio/Ollama line in the harness README refers to Fara-7B, the previous generation, reached with a --fara-7b flag — it is not about Fara1.5, and the two should never be mixed. llama.cpp built from source is the path this recipe documents.

Generation is slower than a 9B model should be

There is an open, community-reported performance regression against exactly the SSM kernels this architecture leans on — llama.cpp issue #25162, "Performance regression on Turing GPUs after 9e58d4d69 — Qwen35 SSM kernels affected" — measured by its reporter on an RTX 2080 Ti (Turing, SM75). Two caveats before acting on it: it has no maintainer response, and nobody has posted numbers for an Ada card, so it is a lead to check with llama-bench across a couple of builds rather than a known property of this one.

Out of memory at load

In order of what to try: drop -c from 32768 to 16384 (frees exactly 0.500 GiB of KV, and 16,384 is still comfortably above the harness's 15,000-token floor — but shorten --max_rounds to match); then step down to Q4_K_M, which frees a further 0.877 GiB. Keep the f16 projector: at 0.855 GiB it is not where the savings are, and a q8_0 projector trades vision fidelity in a model whose entire job is looking at pixels. If you need the projector out of VRAM entirely, --no-mmproj-offload keeps it on the CPU at the cost of slower image encoding — which happens once per agent turn.

Running it against your own accounts

Don't. The model is trained to pause before sign-ins, payments and submissions, and that training is a safety net, not a guarantee — the card lists prompt injection from page content among its known risks, and a vision-only agent has no way to distinguish a legitimate button from a hostile one that renders like it. Keep it in a container, on an allow-list, with no credentials in reach.

common questions
How much VRAM does Fara1.5-9B need?

About 12 GB — the minimum this recipe targets.

Which GPUs is Fara1.5-9B tested on?

RTX 4070 Ti (12 GB).

How hard is this setup?

Advanced — follow the steps above.