self-hosted/ai
§01·recipe · multimodal

Fara1.5-27B on RTX 3090 Ti: Q4_K_M Browser Computer-Use Agent on Ampere sm_86

multimodaladvanced24GB+ VRAMJul 31, 2026

This advanced recipe sets up Fara1.5-27B on the RTX 3090 Ti, needing about 24 GB of VRAM.

models
tools
prerequisites
  • NVIDIA RTX 3090 Ti (24GB VRAM, Ampere GA102, CUDA compute 8.6 / sm_86)
  • 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): Microsoft's Fara1.5-27B served by llama-server on an RTX 3090 Ti, with the vision projector loaded so the model actually sees browser screenshots, driven by Microsoft's own fara-cli harness against a plain local Playwright Chromium.

This is the largest Fara1.5 the catalogue documents on one consumer card, and 24 GB is exactly where it stops being comfortable. The 4B and 9B recipes for this same board run the vendor's own unquantised BF16. At 27B that option is 50.113 GiB of weights — more than twice the card — so this page is about spending a fixed 24 GiB well, not about avoiding a compromise.

Hardware data: RTX 3090 Ti (24GB VRAM) · Q4_K_M weights + f16 mmproj = 17.193 GiB · 19.778 GiB accountable at a 32,768-token context (derived below) · See benchmark data

⚠️ This model clicks, types and submits in a real browser, and the path documented here gives it no isolation of any kind. That is the operating requirement, not boilerplate, and it 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 four controls as MagenticLite features — the first of them sandboxing, described there as "the browser runs in a Docker container with no access to host files or environment variables" — and then says: "If you integrate Fara1.5-27B directly, you're responsible for these controls." The card's Responsible AI section asks you to "run Fara in an isolated container with no access to host files, environment variables, or sensitive credentials". Fara also reads whatever is on the page, so a hostile page is a prompt-injection surface — the card lists "Prompt injection" among its known model risks by name.

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. The harness README offers the containerised option as an alternative to it rather than as part of it: "instead of fara-cli you can use Magentic-UI", described in the same sentence 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 boundary. Practically: a throwaway OS user or a VM, Playwright's own fresh browser profile (never your daily one), signed out of everything, low-stakes public sites, and eyes on the run rather than leaving it unattended. The command in Running passes --headful for exactly that last reason. If you want the container, the allow-lists, watch-mode and the pause control, use Magentic-UI as the client instead.

ℹ️ Vision is the product, not a bonus. 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 that an image really reached the model before you let it drive anything.

⚠️ This install is off the vendor's supported path, deliberately. The card's Requirements section asks for "A set of GPUs with enough memory for a 27B model in bf16 (A6000, A100, H100, and B200 have been tested). We recommend sharding the model over at least 2 GPUs." Everything below runs a community 4-bit quantisation Microsoft never published or blessed, and no public evaluation measures what that costs a model whose output is pixel coordinates. Results says so plainly; read that section before you trust a click coordinate.

⚠️ 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. There is one public report of this model served by llama-server and consumed by an agent harness (see Troubleshooting), which is a stronger starting point than the 4B and 9B pages have. What still does not exist anywhere public is a log showing an image decode reaching Fara1.5 under llama-server, or a throughput measurement of this model on any consumer GPU — see Results for why there is no speed figure here. Treat your first run as the test, and use Step 4, which reads llama.cpp's own load-time self-report rather than asking you to trust this page.

Requirements

ComponentMinimumThis recipe
GPU24GB VRAM, CUDA compute 8.0+— not measured; budget below is derived (/contribute)
RAM16GB (weights are mmap'd on their way to VRAM; Playwright's Chromium sits on top)
Storage18.46 GB (Q4_K_M + f16 mmproj)18,461,159,904 B = 17.193 GiB, plus the harness and Chromium
Softwarellama.cpp b10087+ (CUDA), Python 3.10+, Playwright

Two notes on that table. min_vram_gb is 24, and it is a filter floor rather than a measured peak: the smallest real card capacity on which the install documented below runs. 19.778 GiB is accounted before a single compute buffer, so no 16 GB card holds it at any context setting, and 24 is the next capacity real hardware ships in. The sibling pages for the 4B and 9B on this same board set different floors for the same reason — they document different installs, not different cards. And the GPU cell is not a measurement: /check reports unknown with zero benchmark rows, and nothing on this page was run on hardware by us.

What 24 GB actually buys

There is no benchmark for this pair, so every number below is arithmetic from the artifacts' own metadata and from llama.cpp's source, not a measurement. Reproduce it rather than trusting it. All totals are GiB (2^30 bytes), the unit a "24GB" NVIDIA card is really in, so they compare directly against 24.

⚠ The 27B is not a scaled-up 9B, and three constants change. If you have read the 4B or 9B pages, do not carry their arithmetic over. The source config.json declares 64 layers, a layer_types array of 48 linear_attention + 16 full_attention, num_attention_heads: 24, num_key_value_heads: 4, head_dim: 256 and max_position_embeddings: 262144. Against the smaller sizes' 8 attention layers and 16 heads, that doubles the per-token KV cost, triples the recurrent state, and raises the flash-attention-off scores bound by half. The GGUF agrees with the config key for key: reading the file's own header gives qwen35.block_count 64, attention.head_count 24, attention.head_count_kv 4, key_length 256, value_length 256, full_attention_interval 4, ssm.inner_size 6144, ssm.group_count 16, ssm.state_size 128, ssm.conv_kernel 4.

Weights. Byte counts from the bartowski GGUF tree, quantised on release b10087 with an imatrix:

FileBytesGiB
Fara1.5-27B-IQ4_XS.gguf15,328,839,48814.276
Fara1.5-27B-Q4_K_S.gguf16,474,163,00815.343
Fara1.5-27B-Q4_K_M.gguf17,533,552,44816.329
Fara1.5-27B-Q5_K_M.gguf20,513,802,04819.105
Fara1.5-27B-Q6_K.gguf23,224,145,72821.629
Fara1.5-27B-Q8_0.gguf28,665,067,32826.696
Fara1.5-27B-bf16 (2 shards)53,808,281,34450.113
mmproj-Fara1.5-27B-f16.gguf927,607,4560.864
mmproj-Fara1.5-27B-bf16.gguf931,146,4000.867

(HuggingFace displays these as decimal GB — 17.53 GB for the Q4_K_M file — which is the same bytes, not a different number.)

The bf16 row is in that table so nobody has to go looking. It exists, the quantiser's card labels it "Full BF16 weights." and marks it split across two files, and at 50.113 GiB it is 26.113 GiB past this card. The 4B and 9B recipes for this same board both lead bf16; at 27B that door is closed by arithmetic, not by availability.

KV cache — 64 KiB per token, twice the smaller sizes. qwen35 is a hybrid architecture: llm_arch_is_hybrid() in src/llama-arch.cpp returns true for LLM_ARCH_QWEN35, and src/models/qwen35.cpp marks layers recurrent off full_attention_interval. Only the 16 full-attention layers carry a cache that grows with context:

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

-c  32,768 ->  2.000 GiB      -c 131,072 ->  8.000 GiB
-c  65,536 ->  4.000 GiB      -c 262,144 -> 16.000 GiB

Context is still cheaper here than on a dense 64-layer model with the same head geometry — that would cost four times as much — but it is not the 1.000 GiB-per-doubling the 4B and 9B pages describe. Every doubling from 32,768 costs 2.000 GiB.

Recurrent state — fixed, and allocated four times over. The other 48 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 sets no n_embd_head_kda, so both fall through to the Mamba forms:

conv state : (4-1) x (6144 + 2 x 16 x 128) =  30,720 elems
ssm  state :         128 x 6144            = 786,432 elems
per layer  : 817,152 x 4 B (F32)           = 3.117 MiB
x 48 linear-attention layers               = 149.625 MiB   per sequence
x 4 sequences                              = 598.5 MiB = 0.584 GiB

That last line is the one most budgets for this model get wrong, and it is worth following because it also explains why the KV cache is not multiplied the same way. common/arg.cpp sets params.n_parallel = -1 for the server example specifically; tools/server/server.cpp resolves that sentinel with params.n_parallel = 4; params.kv_unified = true;; common/common.cpp then assigns cparams.n_seq_max = params.n_parallel. src/llama-model.cpp sizes the recurrent memory with /* recurrent_rs_size */ std::max((uint32_t) 1, cparams.n_seq_max) on the branch that names LLM_ARCH_QWEN35 explicitly, and src/llama-memory-recurrent.cpp allocates n_rows = mem_size * (1 + n_rs_seq), where n_rs_seq defaults to 0. So the recurrent state is allocated once per slot — four times. The same default line sets kv_unified = true, which gives all four slots one shared attention cache, so the 64 KiB/token figure above is not multiplied. One term scales with the slot count and the other does not; a budget that treats them alike is wrong in one direction or the other.

The whole trade, in one table. Accountable allocations (text weights + f16 projector + KV + the 0.584 GiB recurrent state) in GiB, against a 24.000 GiB card. Each cell sums the exact byte-derived inputs and is rounded once at the end — rounding the inputs first moves the third decimal.

ContextIQ4_XSQ4_K_SQ4_K_MQ5_K_MQ6_K
32,76817.72418.79119.77822.55325.078
65,53619.72420.79121.77824.55327.078
131,07223.72424.79125.77828.55331.078
262,144 (native)31.72432.79133.77836.55339.078

Two hard limits fall straight out of it.

The model's native 262,144-token window is unreachable on this card at every quant, and no flag rescues it. The KV cache alone is 16.000 GiB at that setting, and the cheapest total in the table is IQ4_XS at 31.724 GiB — 7.724 GiB past the card. -c must be capped; the vendor's own vLLM Quickstart passes --max-model-len 262144 and that line does not transfer to a 24 GB board.

Q5_K_M and Q6_K are out too. Q5_K_M at the shortest useful window is 22.553 GiB, leaving 1.447 GiB for the CUDA context, llama.cpp's compute buffers and the vision encoder's working set — none of which this page derives. Q6_K does not fit at all.

The decision: Q4_K_M at 32,768, and the two runners-up

Q4_K_M @ 32,768 = 19.778 GiB, leaving 4.222 GiB. That is the lead. Two alternatives are close enough to deserve an explicit answer rather than silence.

Q4_K_M @ 65,536 (21.778 GiB, 2.222 GiB left) — declined. It fits on paper. What it does not leave is room for the terms this page cannot derive, and the CUDA context plus compute buffers plus a 5,040-patch vision encode is not a rounding error. For calibration: the 9B recipe for the RX 7900 XTX declines its own upgrade rung at 2.259 GiB of remaining slack, on the same reasoning. If you have measured your own buffer sizes on a headless box, this is the first upgrade to make — see the per-round budget below for what it buys.

IQ4_XS @ 65,536 (19.724 GiB) — declined, and this is the interesting one. It is not merely close to the lead, it is 0.053 GiB cheaper than it. One quant tier buys exactly one context rung here, at slightly negative VRAM cost. The tiebreak is which side of the trade is measured:

  • Quantisation cost is unmeasured. Fara's output is pixel coordinates — the card describes its grounding data as "Curated datasets for predicting actions and pixel coordinates from screenshots, with images, text, and bounding boxes." Nothing public says what any tier costs that faculty, on this model or any other member of the family. The quantiser's card carries only generic per-quant descriptors (Q4_K_M is "Good quality, default size for most use cases", IQ4_XS is "Decent quality, smaller than Q4_K_S with similar performance"), which is a size ranking, not a grounding evaluation.
  • Context need is bounded and stated. The harness README asks that "context length is set to at least 15000 tokens and temperature to 0 for best results", and the image half is capped outright — the card says "We only keep the most recent 3 screenshots", which at the derived 1,260 tokens each pins images at 3,780 tokens for a whole run.

So the rung being declined is the one whose value is known and modest; the tier being kept is the one whose cost nobody has priced. Q4_K_S @ 65,536 (20.791 GiB, 3.209 left) sits between the two if you want the window and half a tier of insurance.

How much context you actually get, per round

fara-cli defaults to --max_rounds 100argparse sets default=100 in run_fara.py, and both agent entry points carry the same default in their signatures. Images are capped at three; the text trajectory is not capped at all, and it accumulates for the whole run. So the window divides by rounds:

 32,768 - 3,780 image tokens =  28,988 for text / 100 rounds ~   290 tokens per round
 65,536 - 3,780             =  61,756 for text / 100 rounds ~   618 tokens per round

290 tokens per round is a real budget for a model that emits a chain-of-thought block before every action, but it is a budget, not a guarantee — and context shift is off by default (ctx_shift = false in common/common.h), so an overrun is a hard error rather than a silent truncation of the oldest turns. The practical rule on this card: -c 32768 for interactive work you are watching; if you intend a long unattended run, or you watch a run and see long <think> traces, raise to -c 65536 before you start it and accept the 2.222 GiB margin. Do not raise --max_rounds past 100 without raising -c with it.

The per-round figure is also why the harness's own smoke-test example passes a small --max_rounds: at 15 rounds the same window gives roughly 1,900 tokens a round, which is why a short first run tells you nothing about whether a hundred-round run will fit.

One coupling worth knowing about

This recipe does not pass --flash-attn, which leaves it at its auto default. common/arg.cpp documents the flag as [on|off|auto], and src/llama-context.cpp sets cparams.flash_attn = params.flash_attn_type != LLAMA_FLASH_ATTN_TYPE_DISABLED — so auto starts enabled, and is then confirmed by a runtime probe that logs either an enabled line or "not supported, set to disabled".

If that probe ever came back unsupported on your build, llama.cpp would take the explicit branch in src/llama-graph.cpp, which builds kq = ggml_mul_mat(ctx0, k, q) and pins it with ggml_mul_mat_set_prec(kq, GGML_PREC_F32) — the source's own comment reads "this op tends to require high floating point range". That materialises n_kv × n_ubatch × n_head × 4 B of attention scores, and on this model n_head is 24, not the 16 the smaller sizes carry:

32,768 x 512 x 24 x 4 B = 1.500 GiB      (and 3.000 GiB at -c 65536)

At the lead configuration that would take the footprint to 21.278 GiB, still inside 24 — which is why min_vram_gb: 24 holds whichever way the probe resolves. At -c 65536 it would take it to 24.778 GiB and not fit, which is a second, independent reason the shorter window is the default here. Check the flash_attn line in the startup log if a load fails unexpectedly.

Nothing in the tables above is free space you can plan against to the last byte. Read the CUDA0 compute buffer size lines llama-server prints on startup for the real figures on your build, watch nvidia-smi with the agent actually running, and report what you see.

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. It is not a fixed number baked into the projector: llama.cpp treats this one as dynamic-resolution, which is what --image-min-tokens exists to floor.

The harness fixes the browser viewport at 1440x900 (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" 1440x900. 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 then counts merged patches per axis in tools/mtmd/clip.cpp, where the PROJECTOR_TYPE_QWEN3VL branch returns (img->nx() / params.patch_size) / 2 across and the same down:

across : (1440 / 16) / 2 = 90 / 2 = 45
down   : ( 896 / 16) / 2 = 56 / 2 = 28
tokens : 45 x 28                 = 1,260 per screenshot
x max_n_images = 3               = 3,780 tokens resident

The source config.json's vision_config agrees on the geometry (patch_size: 16, spatial_merge_size: 2, depth: 27, out_hidden_size: 5120), and so does the projector artifact's own header (clip.vision.patch_size 16, spatial_merge_size 2, block_count 27). The harness's conservative default is image_token_estimate: int = 1500, a useful sanity check on the arithmetic.

The other consequence of that number: every agent step submits a prompt-processing batch well over a thousand tokens. This workload never sits in the "small request" regime.

Installation

1. Build llama.cpp with CUDA for sm_86

Ampere GA102 is compute capability 8.6. There is no special toolkit requirement here: 86-real sits in llama.cpp's default architecture list in ggml/src/ggml-cuda/CMakeLists.txt with no version gate around it. Naming the architecture explicitly still costs nothing and makes a mismatch a build-time failure rather than an idle GPU:

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

Build from a release at or after b10087 — the release the GGUF used here was quantised on.

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

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

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-27B-GGUF \
  Fara1.5-27B-Q4_K_M.gguf mmproj-Fara1.5-27B-f16.gguf \
  --local-dir ~/models/fara1.5-27b

That is 18.46 GB over the wire, so start it before you read the rest of the page.

Microsoft publishes no first-party GGUF for Fara1.5 — the microsoft org ships Fara1.5-4B, Fara1.5-9B and Fara1.5-27B as safetensors only, plus an ONNX build of the previous-generation Fara-7B, which is a different model. Four independent community quantisers have converted the 27B including its vision tower — bartowski, prithivMLmods, Abiray and DevQuasar each publish a matching mmproj. Four independent conversions succeeding is the strongest available evidence that the vision path converts cleanly; it is still not a runtime test. Their quant recipes differ, so the budget above applies only to the file it names.

On the two projector files. bartowski publishes the projector twice, -f16 and -bf16, and one of the two preserves the source bytes — but config.json is the wrong place to work out which. Reading the safetensors headers directly, every shipped tensor is BF16, vision tower included; the 27B's config.json in fact declares "dtype": "float32" under both text_config and vision_config while its top-level dtype says bfloat16, and the weights side with the top level. So mmproj-Fara1.5-27B-bf16.gguf is the byte-faithful conversion here and mmproj-…-f16.gguf is a re-encode — one that keeps every bit of bfloat16's 7-bit mantissa (binary16 has 10) and gives up bfloat16's wider exponent range. This recipe still takes the f16 build so the page stays comparable with every other Fara1.5 recipe in the catalogue; the bf16 build is a one-word substitution costing 3,538,944 B — 3.375 MiB, moving the accountable total from 19.778 to 19.781. Nobody has measured which grounds better.

You can confirm the artifact you just downloaded declares the projector llama.cpp implements:

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

3. Serve it

./build/bin/llama-server \
  -m ~/models/fara1.5-27b/Fara1.5-27B-Q4_K_M.gguf \
  --mmproj ~/models/fara1.5-27b/mmproj-Fara1.5-27B-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. arg.cpp documents this value as "none: leaves thoughts unparsed in message.content".
  • --image-min-tokens 1024 — this projector ships no minimum-pixel key, so llama.cpp falls back to the floor set by hparams.set_limit_image_tokens(8, 4096) in the PROJECTOR_TYPE_QWEN3VL branch of tools/mtmd/clip.cpp, and warns a few lines later in the same branch: "Qwen-VL models require at minimum 1024 image tokens to function correctly on grounding tasks", pointing at issue #16842 on bounding-box correctness. 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 table. Do not leave it at the model's native 262,144: at Q4_K_M that is 33.778 GiB and will not load.

Do not pass --parallel. Leaving it alone keeps llama-server's unified KV cache — the same n_parallel = -1 sentinel the recurrent-state derivation above hangs on. Setting it explicitly takes n_parallel out of the auto path, leaves kv_unified at its false struct default, and src/llama-context.cpp then computes n_ctx_seq = n_ctx / n_seq_max, dividing your 32,768-token window between slots. --parallel 4 would leave each slot 8,192 tokens, below the harness's documented 15,000-token floor.

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 modalities loaded rather than merely that some did, the more useful signal when a projector silently failed to load.

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": "fara",
  "temperature": 0,
  "messages": [{"role": "user", "content": [
    {"type": "image_url", "image_url": {"url": "https://raw.githubusercontent.com/ggml-org/llama.cpp/master/media/llama0-logo.png"}},
    {"type": "text", "text": "Describe this image in one sentence."}
  ]}]
}' | python3 -m json.tool

In the startup log, also find the clip_ctx: CLIP using <backend> backend line — clip.cpp emits it as "CLIP using %s backend" with the backend's own name, and CPU there means the projector is not on the GPU at all. 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 — you are hosting with llama-server, and it pulls a large dependency tree you will not use.

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, but its built-in defaults are for a different size: DEFAULT_ENDPOINT_CONFIG in run_fara.py is {"model": "Fara1.5-9B", "base_url": "http://localhost:5000/v1", "api_key": "not-needed"}. The port lines up; the model name does not, so pass it explicitly.

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

--headful is in that command on purpose. The harness runs the browser headless by defaultrun_fara.py declares the flag with the help text "Run the browser in headful mode (show GUI, default is headless)" and both launch sites read headless=not args.headful. Either way this drives a real, unisolated Chromium on your machine; the danger is identical with or without the flag. What the flag changes is whether you can see it, and the supervision advice above assumes you can — so it is what makes that advice true rather than aspirational. Drop it once you trust the loop and are reading the trajectory files instead.

Each step writes a screenshot and a data_point.json into --output_folder, which is how you tell a grounding failure (clicks landing in the wrong place) from a reasoning failure. Note the low --max_rounds 15 above: the budget on this page is sized for the default 100, but a first run should be short.

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 published measurement of Fara1.5-27B exists for this card or for any consumer GPU, and the only hardware Microsoft names is datacenter class. Borrowing a figure from an A100 for a 24 GB Ampere card would be misleading, and the vendor publishes no throughput numbers to borrow in the first place. A generation-speed number is also the wrong headline for a CUA: what you feel per step is prompt-processing a fresh 1,260-token screenshot, decoding a chain-of-thought block, and a Playwright action. If you run this, please contribute your numbers so this line can be replaced with a measurement.
  • VRAM usage: 17.193 GiB of weights and projector; 19.778 GiB accountable including the f16 KV cache and the four-slot recurrent state at a 32,768-token context, derived above from byte counts and llama.cpp's memory formulas, leaving 4.222 GiB of a 24 GiB card. This is a derivation, not an observed peak — the CUDA context, compute buffers, the vision graph and the browser's own GPU memory sit on top. Track the empirical picture at /check/fara1-5-27b/rtx-3090-ti.
  • Quality notes — read this one. Fara's whole job is emitting precise pixel coordinates, and 4-bit quantisation of a grounding model is a real risk, not a formality. No evaluation of how Q4_K_M affects Fara1.5's grounding accuracy exists — not from Microsoft, not from the community. The vendor's published scores for this size (89.3 WebVoyager, 72.3 Online-Mind2Web, 40.2 WebTailBench, from the model card's evaluation table) are for the bf16 model on datacenter hardware; nothing licenses carrying them to a 4-bit build. Treat a misclick here as possibly the quantisation rather than the model, and if a task depends on precision, verify against a bf16-hosted endpoint before concluding anything. This is the one thing the 4B and 9B recipes for this same card do not have to say, because on those sizes 24 GB reaches bf16.
  • On the headline comparison. The fara README states that "Fara1.5-27B outperforms much larger proprietary systems such as OpenAI Operator and Gemini 2.5 Computer Use on Online-Mind2Web." Its table shows 72.3 against 58.3 and 57.3 — but the same table's footnote says "† denotes numbers sourced from the model's official release or leaderboard rather than re-run by us.", and both competitors carry that dagger. So it is a vendor-run agentic benchmark comparing Microsoft's own averaged runs against self-reported figures, not a controlled head-to-head.
  • 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-27b/rtx-3090-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. The raw output then begins with a stray unclosed <think> tag, which is cosmetic.

This was reported against this exact model — a community user on llama.cpp b10107 with the 27B, in the model's HF discussions, who documents the repro as "default reasoning format → content: \"\", thoughts + <tool_call> block in the reasoning field, finish_reason: stop" against "--reasoning-format none → full raw output in content, parses fine", and adds "If you have a router/proxy in front doing its own reasoning extraction, disable it there too." Microsoft acknowledged it there — swhitehead, whose HF account carries the org-member flag on that repo, 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 at the time of writing. PR #83 proposes reading reasoning_content as a harness-side fallback; until it lands, the server flag is the fix.

That thread is also, incidentally, the best public evidence that this configuration runs at all: it is a report of this GGUF-served model producing a well-formed <tool_call> block under llama-server.

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 and a CLIP using … line, 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.

Clicks land in the wrong place

Check the viewport first: the harness defaults to 1440x900 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 — and unlike the 4B and 9B pages for this card, here that suspicion is live rather than closed off, because this configuration is quantised and nobody has measured what that costs. Climbing to Q5_K_M would need a card this page's budget table says you do not have; the honest test is a bf16-hosted endpoint elsewhere.

CUDA out of memory

19.778 GiB accountable against 24 GiB is a real but not generous margin on a card that also drives your display, and the Chromium this agent opens competes for the same 24 GB. In order of what to try, with what each actually frees:

ChangeFreesCost
-c 32768163841.000 GiB~126 tokens per round at --max_rounds 100; still clears the 15,000-token floor
--cache-type-k q8_00.469 GiBK-half only; block_q8_0 is 34 B per 32 values against f16's 64, taking the cache from 64 to 49 KiB/token
Q4_K_M → Q4_K_S0.987 GiBhalf a quant tier, on an unmeasured grounding ladder
Q4_K_M → IQ4_XS2.053 GiBa full tier down
--no-mmproj-offload0.864 GiBprojector stays in system RAM; slower image encoding, once per agent turn

Take the context first — it is the cheapest and the best-understood. Do not add --cache-type-v q8_0 to the K-cache line unless flash attention is definitely enabled: src/llama-context.cpp rejects a quantised V cache with flash attention disabled outright, logging "quantized V cache requires flash_attn to be enabled" and returning a null context rather than starting. K-only is legal either way.

Don't reach for speculative decoding to fill the headroom

The model family carries a multi-token-prediction layer — the source config.json declares mtp_num_hidden_layers: 1 inside text_config. That layer is not part of the GGUF conversion path used here: llama.cpp names the corresponding header key nextn_predict_layers (LLM_KV_NEXTN_PREDICT_LAYERS in src/llama-arch.cpp), and the file this recipe downloads carries no blk.*.nextn.* tensors for --spec-type to attach to. Nothing on this page enables it; if you go looking, that is why it does nothing.

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

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

The agent runs, but it is not the model you think

fara-cli ships two generations. Its --fara-7b flag, its DEFAULT_FARA_7B_ENDPOINT_CONFIG, and the GGUF / LM Studio / Ollama pointers in the repo's hosting section all refer to Fara-7B, the previous-generation Qwen2.5-VL model — not Fara1.5. Older issues in that repo describing small quants and 16 GB cards are Fara-7B threads. And there is no Ollama library entry for Fara1.5 in any form: ollama.com/library/fara, /fara1.5 and /fara1-5 all 404. Any GGUF runtime tracking llama.cpp b10087+ that can load a separate mmproj should work in principle, but llama-server is the path this recipe documents.

Generation is slower than you expected

One lead worth knowing about rather than a known property: llama.cpp issue #25162 is an open, community-reported regression against exactly the SSM/linear-attention kernels this architecture leans on — 48 of this model's 64 layers — titled "Performance regression on Turing GPUs after 9e58d4d69 — Qwen35 SSM kernels affected". Read its scope carefully: the title says Turing, the reporter measured on an RTX 2080 Ti, its two comments are both community accounts with no maintainer response, and nobody has posted numbers for an Ampere card. It is a thing to check with llama-bench across a couple of builds, not a property of this one.

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

About 24 GB — the minimum this recipe targets.

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

RTX 3090 Ti (24 GB).

How hard is this setup?

Advanced — follow the steps above.