<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: zhijie</title>
    <description>The latest articles on DEV Community by zhijie (@zjshen14).</description>
    <link>https://dev.to/zjshen14</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4155601%2Fb5ffae32-4c7b-42de-8389-d16dcf9f4891.png</url>
      <title>DEV Community: zhijie</title>
      <link>https://dev.to/zjshen14</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zjshen14"/>
    <language>en</language>
    <item>
      <title>Post-Training a Jev-Inspired Decision Model on an RTX 3090</title>
      <dc:creator>zhijie</dc:creator>
      <pubDate>Sun, 18 Oct 2026 20:06:09 +0000</pubDate>
      <link>https://dev.to/zjshen14/fine-tuning-qwen-on-an-rtx-3090-why-accuracy-wasnt-enough-3318</link>
      <guid>https://dev.to/zjshen14/fine-tuning-qwen-on-an-rtx-3090-why-accuracy-wasnt-enough-3318</guid>
      <description>&lt;p&gt;What would it take to post-train a small decision model inspired by Jev and the System One (System 1) idea? On one RTX 3090, I used Qwen3.5 Base as a frozen pretrained backbone and trained LoRA parameters plus a pointer decision head to output probabilities over candidate answers. This is a local learning experiment, without a claim to reproduce Jev's proprietary architecture or RLCD training method.&lt;/p&gt;

&lt;p&gt;In one tiny development probe, fine-tuning raised correct answers from &lt;strong&gt;7/28 to 13/28&lt;/strong&gt;, while mean negative log likelihood worsened from &lt;strong&gt;2.215 to 3.916&lt;/strong&gt;. The training path worked, but accuracy alone left an important part of the result unseen.&lt;/p&gt;

&lt;p&gt;This hands-on account walks through three things you can examine in your own decision-model experiments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How a frozen Qwen backbone, LoRA and a pointer head produce probabilities over candidate descriptions.&lt;/li&gt;
&lt;li&gt;How task wording and labels can disagree even when the data format is valid.&lt;/li&gt;
&lt;li&gt;How to separate development results, public benchmark diagnostics and future confirmation when deciding what to improve next.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The experiment post-trains decision models using Qwen3.5-0.8B-Base and Qwen3.5-4B-Base as pretrained backbones on a single RTX 3090. Adapters, trained decision heads and data-reconstruction materials are linked below. This is a learning retrospective with configurations and evidence, rather than a complete command-by-command tutorial.&lt;/p&gt;

&lt;p&gt;Originally published on &lt;a href="https://zjshen14.github.io/en/blog/training-small-decision-model-3090/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=qwen_decision_training&amp;amp;utm_content=full_article" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. The blog is the maintained source of truth; this is its English cross-post.&lt;/p&gt;

&lt;p&gt;Some calls in an agent workflow only need a judgment: is the user adding context or changing an earlier decision? Does a passage support a claim? Which team should handle a ticket? The answer may be a single option, yet it determines what the program does next.&lt;/p&gt;

&lt;p&gt;Reading about &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;TypeSafe's Jev and System One models&lt;/a&gt; made me curious about one capability: reading context, a question and candidate answers, then returning probabilities over those candidates. It sounded complex. I wanted to understand whether the underlying structure and training process could be broken into steps an individual developer could understand and try firsthand.&lt;/p&gt;

&lt;p&gt;I used the RTX 3090 from my earlier &lt;a href="https://zjshen14.github.io/en/blog/local-qwen-opencode-3090/" rel="noopener noreferrer"&gt;local coding agent experiments&lt;/a&gt;, using Qwen3.5 Base as the pretrained backbone for post-training a decision model: first the 0.8B backbone, then 4B. The backbone weights stayed frozen; supervised post-training updated newly initialized LoRA parameters and a pointer decision head. The resulting model reads context, a question and candidate answers, and returns a probability for each candidate. Those sizes also reflected local memory and compute constraints. This was primarily a personal learning experiment: implement the basic candidate-scoring path with the resources available, understand how the model learns these judgments, and learn how to check the results. Matching the full results of enterprise-scale cluster training was outside the goal of this experiment.&lt;/p&gt;

&lt;p&gt;Initially, I only wanted to establish that the training path worked. The model quickly learned a tiny set of training questions. On another set, something more interesting happened: &lt;strong&gt;the number of correct answers increased while mean NLL got worse.&lt;/strong&gt; Later, inspecting the training data revealed labels derived from review stars, even though the question asked about the attitude expressed in the text.&lt;/p&gt;

&lt;p&gt;This account follows the first two model versions: turning a language model into a candidate scorer, understanding why accuracy alone cannot establish training success, and making supervision and evaluation answer the same question. It also introduces the weaknesses revealed by public JevBench questions that motivated further quality optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading a question and scoring its candidates
&lt;/h2&gt;

&lt;p&gt;Consider a review judgment. The text and probabilities below are fictional, illustrating the interface:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Context: The food was excellent and the service attentive. I'll be back.&lt;br&gt;&lt;br&gt;
Question: Based only on this text, is the reviewer clearly positive overall?&lt;br&gt;&lt;br&gt;
Candidates: Yes—clearly favorable overall; No—clearly unfavorable, or substantively mixed without a clear overall favorable judgment.&lt;br&gt;&lt;br&gt;
Output shape: &lt;code&gt;{"yes": 0.8, "no": 0.2}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The candidate answers and their descriptions are part of the input. A ticket-routing request could instead supply descriptions for billing, shipping and returns. The output would still be a probability for each candidate.&lt;/p&gt;

&lt;p&gt;The structure used here has three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Qwen3.5 Base backbone reads the input.&lt;/strong&gt; Context, question and candidate descriptions are encoded as tokens, and the pretrained model computes contextual representations at their positions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A pointer decision head scores the candidates.&lt;/strong&gt; This small readout takes the representations at the decision and candidate positions, projects and matches them to obtain candidate scores, then applies softmax to produce a probability distribution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoRA adapts the model to the task.&lt;/strong&gt; The original backbone weights stay frozen. Training updates low-rank parameters inserted into it and the newly initialized readout, reducing the parameters and optimizer state that need to be optimized.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fzjshen14.github.io%2Fdiagrams%2Fdecision-model-training-en.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fzjshen14.github.io%2Fdiagrams%2Fdecision-model-training-en.svg" alt="Context, question and candidate descriptions enter Qwen3.5. A frozen backbone with trainable LoRA produces representations; a pointer head converts candidate scores into probabilities." width="600" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For questions with a determinate label, the correct candidate supplies the supervision. Cross-entropy encourages the model to increase that candidate's probability, and gradients update LoRA and the readout. This connects reading the text, scoring the candidates and learning from labels.&lt;/p&gt;

&lt;p&gt;Why choose this structure? A fixed-class classification head usually reserves an output position for each class. Scoring candidate descriptions supplied in each request better matches the interface I wanted to explore. Reading scores directly also removes the step of generating a natural-language answer and parsing its label. Prompting an existing language model to generate a label is another valid approach; this experiment explores direct scoring and does not provide a controlled speed, cost or reliability comparison between the two.&lt;/p&gt;

&lt;p&gt;The starting point was &lt;a href="https://github.com/jaredpalmer/kev/tree/2ecafc49fe1e8945cd600443e6c04c1cf0b3a78f" rel="noopener noreferrer"&gt;Jared Palmer's KEV training process and implementation&lt;/a&gt;: a pretrained Base model with fresh LoRA parameters and a fresh readout. The architecture comes from that reference. Our changes at this stage focused on supervision, evaluation separation and training recovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why more correct answers were not enough
&lt;/h2&gt;

&lt;p&gt;The training mixture covered classification, question answering and rule composition; Yelp review judgments were only one task. The first step was a tiny training check. I selected the two shortest records from each data source, giving &lt;strong&gt;24 records and 30 questions&lt;/strong&gt;; one record can contain several questions. The 0.8B model repeatedly trained on them for 80 optimizer updates.&lt;/p&gt;

&lt;p&gt;Before and after training, I checked both those training questions and a separate exploratory development set. The latter came from the same original datasets but used different data groups and was not directly used as training examples in this run.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Questions and metric&lt;/th&gt;
&lt;th&gt;Before training&lt;/th&gt;
&lt;th&gt;After training&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Correct on 30 training questions&lt;/td&gt;
&lt;td&gt;7/30&lt;/td&gt;
&lt;td&gt;29/30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean NLL on 30 training questions&lt;/td&gt;
&lt;td&gt;2.1845&lt;/td&gt;
&lt;td&gt;0.1030&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct on 28 development questions&lt;/td&gt;
&lt;td&gt;7/28&lt;/td&gt;
&lt;td&gt;13/28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean NLL on 28 development questions&lt;/td&gt;
&lt;td&gt;2.2153&lt;/td&gt;
&lt;td&gt;3.9161&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Accuracy checks whether the highest-probability option is correct. Negative log likelihood, or NLL, also checks how much probability goes to the correct answer. For a question with a determinate answer, it is &lt;code&gt;−ln(probability of the correct answer)&lt;/code&gt;. Lower is better.&lt;/p&gt;

&lt;p&gt;As a hypothetical illustration, suppose a binary classifier gives the correct answer a probability of 0.4 or 0.001. Both predictions count as wrong. The latter almost rules out the correct answer, so its NLL penalty is much larger. More questions can therefore be answered correctly while mean NLL deteriorates. The tiny experiment showed these metrics moving apart; the aggregate table alone does not identify which questions caused the change.&lt;/p&gt;

&lt;p&gt;Moving from 7/30 to 29/30 on the repeated training questions showed that the model could learn that set. I also checked that backbone weights stayed unchanged, LoRA and readout weights changed, and saving and reloading preserved outputs. Together, these checks validated the training and saving path. The 28 development questions were too few and too limited in origin to establish broad generalization.&lt;/p&gt;

&lt;p&gt;A program using probabilities to decide whether to act also needs &lt;strong&gt;calibration&lt;/strong&gt; checks: among judgments made with roughly 80% confidence, are roughly 80% actually correct? Softmax only makes probabilities sum to one, and NLL does not replace a calibration assessment. This experiment led me to examine correct-answer counts alongside probability metrics, rather than watching training loss alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did the labels answer the question being asked
&lt;/h2&gt;

&lt;p&gt;Before scaling up, I inspected the Yelp review task in the mixed training data. Its binary labels came from a star-rating threshold, while the question asked: "Based only on this review text, is the reviewer clearly positive overall?"&lt;/p&gt;

&lt;p&gt;Those labels need not express the same judgment. Imagine a fictional review: "The food arrived quickly, but it was disappointing. Overall, I wouldn't recommend it." Suppose it carries four stars and the old rule maps that rating to Yes. That label follows the rating rule, but the text-based question calls for No. The model receives conflicting instructions and supervision.&lt;/p&gt;

&lt;p&gt;We specified the criterion explicitly: use only the supplied text. Clearly favorable overall means Yes. Clearly unfavorable, or substantively mixed without a clear favorable overall judgment, means No. Insufficient information, truncation or unresolved material ambiguity means holding the question aside. "Still waiting for the food; I'll update this later," for example, cannot support an overall judgment.&lt;/p&gt;

&lt;p&gt;Annotation packets hid the original stars and old labels. We used model-assisted annotation, with crosschecks of changed labels, uncertain cases and a subset of unchanged cases. &lt;strong&gt;Uncertain was an annotation holding status, not a third output class for this binary task.&lt;/strong&gt; Ultimately, 38 relevant questions were withheld from supervision while their records and other questions could remain.&lt;/p&gt;

&lt;p&gt;The useful lesson was that well-formed data can still teach a different task. Valid JSON, acceptable input length and in-range labels do not reveal this mismatch. The question, candidate descriptions and label have to be read together. Additional epochs cannot resolve a contradiction in the task definition for us.&lt;/p&gt;

&lt;p&gt;Here, "repair" means aligning supervision with an explicit text criterion. Model-assisted labels can still share errors, and the subsequent comparison does not isolate the causal benefit of this change. This audit finding also cannot directly explain the earlier tiny experiment's NLL deterioration; that causal link was not tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aligning training and development criteria
&lt;/h2&gt;

&lt;p&gt;Beyond the Yelp supervision issue, structural checks, overlap screening and sampled semantic review quarantined 56 whole records involving near-copy risks, unresolved ambiguity and label conflicts. After incorporating the Yelp repair, training used &lt;strong&gt;12,520 records and 15,476 questions&lt;/strong&gt;. Versions were saved separately with checksums so an intermediate dataset would not be mistaken for the data a final model actually saw.&lt;/p&gt;

&lt;p&gt;Evaluation also needed to follow the revised task definition. Historical star-derived labels could not evaluate the new text question, so we built a &lt;strong&gt;development set&lt;/strong&gt; using the same text criterion. Starting with 200 Yelp candidates, we screened overlap with historical material and conducted two model-assisted review passes. We admitted 194 and held six because of ambiguity or disagreement.&lt;/p&gt;

&lt;p&gt;Development data supports examining models and comparing approaches. Calibration data fits probability adjustments. A final test supports confirmation after the approach is fixed. These 194 records served only the first purpose. The training entry point used an explicit allowlist of sources to reject them as training data.&lt;/p&gt;

&lt;p&gt;This separation makes comparison more meaningful, but screening cannot rule out all semantic overlap or pretraining exposure. Agreement between two model-assisted passes is not independent human ground truth. Holding difficult cases aside also leaves some difficult situations outside the reported scores. Those limits matter when interpreting the Yelp results below.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the two trained versions tell us
&lt;/h2&gt;

&lt;p&gt;After the repair, I trained fresh from Base at 0.8B and 4B, naming them &lt;strong&gt;B1 and B2&lt;/strong&gt;. Both used fresh LoRA parameters and readouts, the same repaired training data and two epochs. Their learning rates were 0.0001 and 0.00005 respectively. This compares two training recipes; it cannot isolate the effect of parameter count.&lt;/p&gt;

&lt;p&gt;Before training 4B, I measured forward, backward and optimizer updates on representative and stress inputs on the 3090. LoRA reduces trainable parameters, but backbone weights, intermediate activations and input length still consume memory. Loading the model is only part of a training-capacity check.&lt;/p&gt;

&lt;p&gt;Evaluation used two development sets: the 194 text-based review judgments described above, and historical multitask development data covering classification, question answering and rule composition. The second row below counts &lt;strong&gt;1,184 original questions&lt;/strong&gt;; another 204 constructed variants, including reordered options, are excluded from this table.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Development data&lt;/th&gt;
&lt;th&gt;Correct, 0.8B (B1)&lt;/th&gt;
&lt;th&gt;Correct, 4B (B2)&lt;/th&gt;
&lt;th&gt;B1 mean NLL&lt;/th&gt;
&lt;th&gt;B2 mean NLL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Yelp text-based attitude judgments&lt;/td&gt;
&lt;td&gt;184/194&lt;/td&gt;
&lt;td&gt;192/194&lt;/td&gt;
&lt;td&gt;0.09620&lt;/td&gt;
&lt;td&gt;0.04024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Original historical multitask questions&lt;/td&gt;
&lt;td&gt;951/1,184&lt;/td&gt;
&lt;td&gt;1,033/1,184&lt;/td&gt;
&lt;td&gt;0.60133&lt;/td&gt;
&lt;td&gt;0.46749&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are outputs without temperature calibration, at temperature 1. B2 did better on the correct-answer counts and mean NLL shown here. The table compares two trained versions. Without an untrained control on the same sets, it cannot quantify the gain from fine-tuning itself or establish that general capabilities were retained.&lt;/p&gt;

&lt;p&gt;Another slice is closer to the risk of automated action. On the Yelp set, B1 made 181 predictions with confidence at least 0.9, including one error. B2 made 188, including two errors. The samples are small and cover different subsets, so this is not a significance claim. It does illustrate why better averages do not automatically mean fewer high-confidence errors, and why 0.9 is not a ready-made production threshold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Longer runs also need real recovery
&lt;/h2&gt;

&lt;p&gt;Moving from tiny experiments to longer runs required another engineering change: training recovery. Loading LoRA and readout weights allows prediction to continue. Continuing the same training run also requires the optimizer, learning-rate progress, random state and data position.&lt;/p&gt;

&lt;p&gt;We compared uninterrupted training against a run terminated mid-process and resumed. After adjusting determinism settings on the actual CUDA path, both completed eight optimizer updates with bit-identical trainable parameters and training state. Reloaded probabilities on 30 check questions were identical too.&lt;/p&gt;

&lt;p&gt;This validated process-interruption recovery in that single-GPU environment. It provided a recoverable starting point for further iteration; the test did not cover retraining on different hardware or physical power loss. Saving and comparison details are in the notes below.&lt;/p&gt;

&lt;h2&gt;
  
  
  JevBench exposed the next problems to solve
&lt;/h2&gt;

&lt;p&gt;Training, saving and loading worked, and results on the existing development sets had improved. The next question was how well the model would handle a different set of practical decisions. We evaluated the unchanged 4B version locally on a &lt;a href="https://github.com/fstandhartinger/jevbench/tree/609a664e81f46e03731600bd7bd9f1b7a61257c5" rel="noopener noreferrer"&gt;fixed public JevBench revision&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That checkout contained &lt;strong&gt;231 downloadable public questions&lt;/strong&gt;. Every request returned a valid probability vector without input truncation. Breaking the results down by the benchmark's groups revealed a clear gap:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Public JevBench group&lt;/th&gt;
&lt;th&gt;Correct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;48/48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Original&lt;/td&gt;
&lt;td&gt;69/72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;td&gt;53/111&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The overall result was 170/231, but &lt;strong&gt;58 of the 61 errors came from Hard&lt;/strong&gt;. A further breakdown by task family showed 0/15 on temporal and numeric questions and 4/19 on long-policy judgments. Handling simple decisions did not establish reliability on date calculations, combined conditions, exception clauses or evidence distributed across a long document. The overall score hid these weaknesses.&lt;/p&gt;

&lt;p&gt;That gave the subsequent training a more specific direction. First, inspect task conversions, options and labels item by item to understand the failures. Then add independently checkable date calculations, rule combinations and exception cases, progressively increasing the context the model needs to process. Keep original-task training data and evaluations alongside them to check whether improving difficult questions costs performance on decisions the model could already handle.&lt;/p&gt;

&lt;p&gt;Length was one factor to investigate: the original 4B training configuration limited context to 384 tokens, while these long-policy contexts contained roughly 2,203–3,666 tokens. However, the longer inputs also all belonged to Hard. Length and difficulty were entangled, so the results could not establish that length alone caused the failures. Labels and conversions in these small task-family groups also still needed item-level review.&lt;/p&gt;

&lt;p&gt;These are local public-question diagnostics, not an official sealed evaluation or leaderboard rank. Differences between task groups also do not establish a regression caused by training. The inspected public questions can track known weaknesses, but independent reserved data is needed to confirm whether improvements generalize. Quality optimization is still underway. Future posts will examine which training changes help these weaknesses, which do not, and whether performance on other tasks is preserved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model downloads and data reconstruction
&lt;/h2&gt;

&lt;p&gt;The models and accompanying materials are available on Hugging Face:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/zjshen/qwen3.5-0.8b-decision" rel="noopener noreferrer"&gt;Qwen3.5 0.8B decision model (experiment B1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/zjshen/qwen3.5-4b-decision" rel="noopener noreferrer"&gt;Qwen3.5 4B decision model (experiment B2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/datasets/zjshen/qwen3.5-decision-data" rel="noopener noreferrer"&gt;Training-data indexes, label repairs and reproduction materials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each model repository includes the LoRA adapter, decision readout, native loader and training configuration. Loading retrieves the separately pinned Qwen Base. These are inference checkpoints, excluding optimizer, random-state and other training-resume snapshots. Follow the repository's loading instructions so the decision readout is included.&lt;/p&gt;

&lt;p&gt;The data repository provides source indexes, label repairs, reconstruction scripts and a training reproduction kit, without redistributing original review or question-answer text. Reconstruction retrieves pinned upstream data and verifies the final file's SHA-256. Training and evaluation roles are recorded separately, alongside source attribution and component-specific terms.&lt;/p&gt;

&lt;p&gt;Data versions and evaluation scope&lt;/p&gt;

&lt;p&gt;One record can contain several questions. The data versions were:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data stage&lt;/th&gt;
&lt;th&gt;Records&lt;/th&gt;
&lt;th&gt;Questions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial training data passing input checks&lt;/td&gt;
&lt;td&gt;12,576&lt;/td&gt;
&lt;td&gt;15,576&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interim data after record quarantine&lt;/td&gt;
&lt;td&gt;12,520&lt;/td&gt;
&lt;td&gt;15,514&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B1/B2 training data incorporating Yelp repair&lt;/td&gt;
&lt;td&gt;12,520&lt;/td&gt;
&lt;td&gt;15,476&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Of the 56 quarantined records, six involved public cross-split near-copy risks, 39 unresolved ambiguity and 11 reviewed label conflicts. The move from 15,514 to 15,476 questions withheld 38 Yelp judgments from supervision without changing the retained record count. These checks reduced identified risks; they do not estimate the whole dataset's label-noise rate.&lt;/p&gt;

&lt;p&gt;Training files had their own checksums and manifests. A copied manifest still describing old data was replaced before launch. Development data had an evaluation-only declaration, while training also checked an explicit source allowlist. A legacy entry point without that allowlist could not provide the required separation.&lt;/p&gt;

&lt;p&gt;The historical multitask data was called the retention panel in the records. It contained 1,388 questions. The table uses 1,184 original questions, marked clean in the scoring records. The other 204 comprise 84 option-permutation variants, 60 none-option controls with the answer present among the candidates, and 60 with the answer absent. Here, none means "none of the above." These variants are excluded from the table's correct-answer counts and mean NLL. Historical labels and overlap remain limitations, so these results do not establish independent external-test performance.&lt;/p&gt;

&lt;p&gt;Training configuration and recovery&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;0.8B (B1)&lt;/th&gt;
&lt;th&gt;4B (B2)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pretrained model&lt;/td&gt;
&lt;td&gt;Qwen3.5-0.8B-Base&lt;/td&gt;
&lt;td&gt;Qwen3.5-4B-Base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initialization&lt;/td&gt;
&lt;td&gt;Fresh LoRA and readout&lt;/td&gt;
&lt;td&gt;Fresh LoRA and readout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Training records&lt;/td&gt;
&lt;td&gt;12,520&lt;/td&gt;
&lt;td&gt;Identical file bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Epochs and optimizer updates&lt;/td&gt;
&lt;td&gt;2; 3,130&lt;/td&gt;
&lt;td&gt;2; 3,130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Physical batch and accumulation&lt;/td&gt;
&lt;td&gt;1; 8&lt;/td&gt;
&lt;td&gt;1; 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA rank&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning rate&lt;/td&gt;
&lt;td&gt;0.0001&lt;/td&gt;
&lt;td&gt;0.00005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backbone storage and autocast&lt;/td&gt;
&lt;td&gt;FP32; BF16&lt;/td&gt;
&lt;td&gt;FP32; BF16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token budget per pass&lt;/td&gt;
&lt;td&gt;4,096&lt;/td&gt;
&lt;td&gt;8,192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random seed&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The original comparison verified that all admitted 4B inputs already fitted the 4,096 budget; using 8,192 did not change actual pass packing. Each recipe had this one run, so it cannot estimate training-seed variation. The earlier tiny run used physical batch 1, accumulation 4 and 80 updates.&lt;/p&gt;

&lt;p&gt;During training, a record can produce augmented variants with distractor candidates or none-of-the-above controls. The training objective averages question losses within each variant, then sums according to variant weights. The result tables average NLL over the selected original questions, so the aggregation units are not interchangeable. An earlier 0.8B full-mixture model was named B0. B1 was trained fresh from Base to avoid carrying forward B0's old supervision history.&lt;/p&gt;

&lt;p&gt;Recovery saves trainable parameters, optimizer state, scheduler, random state, counters and the next data position. Saves occur after a complete optimizer update, excluding unfinished gradient accumulation. Several checksummed save generations allow fallback. The first CUDA recovery comparison showed small numerical drift. With determinism enabled on the actual path, uninterrupted and resumed runs matched in parameters, optimizer, scheduler, random state and data position.&lt;/p&gt;

&lt;p&gt;Implementation and measurement sources&lt;/p&gt;

&lt;p&gt;Local training started from &lt;a href="https://github.com/jaredpalmer/kev" rel="noopener noreferrer"&gt;Jared Palmer's KEV&lt;/a&gt;, pinned to &lt;a href="https://github.com/jaredpalmer/kev/tree/2ecafc49fe1e8945cd600443e6c04c1cf0b3a78f" rel="noopener noreferrer"&gt;commit &lt;code&gt;2ecafc49fe1e8945cd600443e6c04c1cf0b3a78f&lt;/code&gt;&lt;/a&gt;, adding local training-recovery support without loading a trained KEV adapter. The pretrained models were &lt;a href="https://huggingface.co/Qwen/Qwen3.5-0.8B-Base" rel="noopener noreferrer"&gt;Qwen3.5-0.8B-Base&lt;/a&gt; and &lt;a href="https://huggingface.co/Qwen/Qwen3.5-4B-Base" rel="noopener noreferrer"&gt;Qwen3.5-4B-Base&lt;/a&gt;. See &lt;a href="https://arxiv.org/abs/2106.09685" rel="noopener noreferrer"&gt;Hu et al.&lt;/a&gt; for LoRA and &lt;a href="https://arxiv.org/abs/1706.04599" rel="noopener noreferrer"&gt;Guo et al.&lt;/a&gt; for background on calibration and temperature scaling.&lt;/p&gt;

&lt;p&gt;This implementation runs each question with its context as an independent causal sequence on the Qwen3.5 hybrid backbone, excluding other questions from its context. Batching does not reproduce Jev's architecture or latency. The tiny experiment used the reference PyTorch compute path actually verified in that run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zjshen14.github.io/experiments/decision-training-first-baselines/summary.json" rel="noopener noreferrer"&gt;Selected aggregate measurements&lt;/a&gt; support checking the counts and configurations here. That file contains summary values only; model downloads, data reconstruction and training reproduction materials are in the Hugging Face repositories above.&lt;/p&gt;

&lt;p&gt;This article was prepared with AI assistance from local experiment records. It covers the initial B1/B2 versions, their corresponding development results, and the 4B public JevBench diagnostic that motivated further optimization.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>llm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Making Qwen Faster on an RTX 3090</title>
      <dc:creator>zhijie</dc:creator>
      <pubDate>Sat, 10 Oct 2026 18:56:30 +0000</pubDate>
      <link>https://dev.to/zjshen14/making-qwen-faster-on-an-rtx-3090-16f9</link>
      <guid>https://dev.to/zjshen14/making-qwen-faster-on-an-rtx-3090-16f9</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://zjshen14.github.io/en/blog/qwen-3090-quantization-context-followup/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. &lt;a href="https://zjshen14.github.io/zh/blog/qwen-3090-quantization-context-followup/" rel="noopener noreferrer"&gt;中文版&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Over the past few days, I continued tuning Qwen3.8-27B on a single 24GB RTX 3090. Generation speed during coding tasks increased from roughly &lt;strong&gt;33 tokens/s&lt;/strong&gt; with the original configuration to around &lt;strong&gt;60 tokens/s&lt;/strong&gt;. A subsequent runtime and draft-sampling update reached &lt;strong&gt;68.4 tokens/s&lt;/strong&gt; across eight coding attempts; the matched speed screen below isolates its additional gain.&lt;/p&gt;

&lt;p&gt;Two other findings deserve attention: a repeatedly failing task turned out to have a defective test, and one configuration generated tokens faster but took three extra minutes to deliver its code.&lt;/p&gt;

&lt;p&gt;My current speed candidate is &lt;strong&gt;IQ3_S, Q8 main KV, and MTP4 on llama.cpp b11429 with probabilistic draft sampling&lt;/strong&gt;. Its latest eight-task screen used a 220K total window and a 50K output cap, delivering 7/8. Long input has also run, though a full 170K-input-plus-50K-output sequence remains untested.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Writing disclosure: this article was drafted with AI assistance from recorded local experiments and reviewed by the experimenter. The results and limits below refer to those saved runs.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually tested
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://zjshen14.github.io/en/blog/qwen-agentic-tuning-3090/" rel="noopener noreferrer"&gt;The previous post&lt;/a&gt; focused on output budgets and reasoning settings, eventually reaching 23/24 delivered passes. &lt;strong&gt;Those were eight tasks repeated three times, not 24 distinct problems.&lt;/strong&gt; Later eight-attempt parameter screens ran each task once.&lt;/p&gt;

&lt;p&gt;The suite has six Python tasks and two TypeScript tasks covering caching, database transactions, incremental streams, paths, ledger processing, build dependencies, asynchronous concurrency, and pagination. Seven are authored repair tasks; one has an injected defect in a pinned version of boltons. The agent must inspect code, edit files, run checks, and hand over a result. Saying “tests pass” is insufficient.&lt;/p&gt;

&lt;p&gt;I record a functional pass when the code passes independent acceptance, regression, public tests, and applicable syntax or type checks. A delivered pass additionally requires normal completion, a self-check, and a final handoff. These small repair tasks help choose a configuration; they do not represent every kind of production repository work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tokens/s below measures generation:&lt;/strong&gt; total generated tokens divided by total decode time. It includes reasoning and tool-call syntax but excludes prompt processing. Whole-task time also includes file access, tests, and tool waits. Generation speed and delivery speed are different measurements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update (October 10): a runtime change adds another speed gain
&lt;/h2&gt;

&lt;p&gt;After the initial publication, I upgraded llama.cpp from b11146 to &lt;strong&gt;b11429&lt;/strong&gt; and tested &lt;code&gt;--spec-draft-sampling probabilistic&lt;/code&gt; for MTP. This changes how draft tokens are sampled; the target model's temperature and other sampling settings stayed fixed. The weights and maximum draft depth also stayed the same.&lt;/p&gt;

&lt;p&gt;The speed screen used identical cold inputs of approximately 8K and 32K tokens, 1,024 output tokens, and three shared seeds per setting. All settings used IQ3_S, Q8 main KV, F16 draft KV, MTP4, and a 220K total window. The old runtime ran before and after the candidates, with less than 1% drift; its table entry averages those two controls.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime and draft sampling&lt;/th&gt;
&lt;th&gt;8K input generation rate&lt;/th&gt;
&lt;th&gt;32K input generation rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;b11146, original behavior&lt;/td&gt;
&lt;td&gt;49.7 tokens/s&lt;/td&gt;
&lt;td&gt;44.2 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b11429, greedy&lt;/td&gt;
&lt;td&gt;52.7 tokens/s&lt;/td&gt;
&lt;td&gt;46.1 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b11429, probabilistic&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;61.6 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;57.5 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The runtime upgrade with greedy drafting gained approximately &lt;strong&gt;6% / 4%&lt;/strong&gt; over the old controls. With probabilistic drafting, the combined gain was &lt;strong&gt;24% / 30%&lt;/strong&gt;. Including input processing, the latter's output throughput gains were approximately &lt;strong&gt;16% / 10%&lt;/strong&gt;. These percentages describe this fixed-length screen, not every coding task.&lt;/p&gt;

&lt;p&gt;The candidate then completed &lt;strong&gt;one attempt on each of the same eight coding tasks&lt;/strong&gt;, using a 220K total window and a 50K per-response output cap. Its pooled generation rate was &lt;strong&gt;68.4 tokens/s&lt;/strong&gt;, with &lt;strong&gt;7/8 strict functional and delivered passes&lt;/strong&gt;. All eight passed independent acceptance and regression checks, but Buildplan's submitted tests assumed an agent-specific temporary directory existed. Fifteen public tests errored during setup in the clean grading environment, so that attempt remains a failure. I did not repair its answer or rerun it to improve the score.&lt;/p&gt;

&lt;p&gt;This is encouraging evidence for the speed candidate, but eight familiar development tasks run once cannot establish general coding-quality equivalence. Higher TPS also does not guarantee shorter tasks: in the already completed wallet pair, the candidate generated faster but took 549 seconds versus 543 seconds on the old runtime. No additional old-runtime coding runs were made after switching to the single-pass candidate screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F16 draft KV was already the previous default&lt;/strong&gt;, and it was held fixed here. It is not a new source of the gain. Switching the &lt;em&gt;main&lt;/em&gt; KV from Q8 to F16 failed GPU allocation at the unchanged 220K window, so that arm was excluded rather than measured at a smaller context.&lt;/p&gt;

&lt;p&gt;The remaining sections retain the earlier experiments and their original settings. The current recommendation at the end includes this update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I am keeping IQ3 for now
&lt;/h2&gt;

&lt;p&gt;The original Q4_K_M file was about 15.66 GiB. The &lt;a href="https://huggingface.co/ISTA-DASLab/Qwen3.8-27B-GSQ-RCO-GGUF" rel="noopener noreferrer"&gt;ISTA-DASLab GSQ-RCO IQ3_S&lt;/a&gt; file I selected includes an MTP prediction head and is about 11.29 GiB. Smaller weights leave more GPU memory for caches and runtime buffers.&lt;/p&gt;

&lt;p&gt;Two compression settings are easy to confuse: &lt;strong&gt;IQ3 compresses weights; Q8 KV uses an eight-bit format for the attention cache stored during inference.&lt;/strong&gt; They can be used together. The IQ3 filename also does not mean every weight has exactly three bits; this model assigns different precision to different tensors.&lt;/p&gt;

&lt;p&gt;I tested &lt;a href="https://huggingface.co/bartowski/Qwen3.8-27B-GGUF" rel="noopener noreferrer"&gt;Bartowski's IQ4_XS&lt;/a&gt; too, adding a Q4_0 MTP head locally. The comparison below used eight tasks repeated three times per configuration, a 128K total window, a 32K per-response output cap, and xhigh reasoning.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Generation rate&lt;/th&gt;
&lt;th&gt;Delivered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Original Q4_K_M, MTP off&lt;/td&gt;
&lt;td&gt;33.29 tokens/s&lt;/td&gt;
&lt;td&gt;23/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GSQ-RCO IQ3_S, MTP3, Q8 KV&lt;/td&gt;
&lt;td&gt;59.87 tokens/s&lt;/td&gt;
&lt;td&gt;21/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bartowski IQ4_XS, local MTP2, Q8 KV&lt;/td&gt;
&lt;td&gt;62.36 tokens/s&lt;/td&gt;
&lt;td&gt;22/24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The IQ3 bundle generated approximately &lt;strong&gt;80% faster&lt;/strong&gt; than the original Q4 bundle. Both weights and speculative decoding changed, so the improvement cannot be attributed entirely to IQ3. Q4 and IQ3 controls also came from earlier runs rather than a contemporaneous randomized comparison.&lt;/p&gt;

&lt;p&gt;The table preserves older test scores, including the Ledger fixture defect discussed below. These historical cohorts have not all been regraded, so they cannot establish either a final quality ranking or complete quality preservation.&lt;/p&gt;

&lt;p&gt;In confirmation on four different tasks repeated three times, IQ4 and Q4 both achieved 11/12 functional and delivered passes. However, the final IQ4-versus-IQ3 comparison using a new random seed gave me a reason to retain IQ3: IQ4 generated &lt;strong&gt;7.8% faster&lt;/strong&gt;, but delivered 6/8 versus IQ3's 7/8, and total task time was approximately &lt;strong&gt;2.1% longer&lt;/strong&gt;. IQ4's wallet answer had an actual overflow-handling defect.&lt;/p&gt;

&lt;p&gt;That does not establish lower general IQ4 quality. It also does not give me a compelling reason to replace IQ3 immediately.&lt;/p&gt;

&lt;p&gt;Other tuning produced similar tradeoffs. Fewer CPU threads, different CUDA wait behavior, and added ngram speculation gave no consistent speed benefit. F16 caches improved some short-input screens at the cost of memory. Smaller IQ3_XXS weights also had short-screen gains, but the partial coding follow-up results did not support immediate replacement. Short screens help select candidates; the agent still needs to solve tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  MTP4 was fastest on these coding tasks
&lt;/h2&gt;

&lt;p&gt;MTP proposes several upcoming tokens for the main model to verify. Accepting more drafts can reduce the main model's token-by-token generation work.&lt;/p&gt;

&lt;p&gt;Drafting has its own cost. This local model has one MTP head, invoked repeatedly at greater depths; rejected later positions waste that work. &lt;strong&gt;MTP4 means at most four proposed draft tokens, not four guaranteed acceptances per round.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I fixed IQ3_S, Q8 KV, a 128K total window, and a 64K output cap, then ran each of the eight coding tasks at depths 2 through 6. One shared seed per task produced 40 completed attempts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Maximum draft tokens&lt;/th&gt;
&lt;th&gt;Generation rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;57.98 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;60.72 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;61.31 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;58.08 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;56.37 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MTP4 was approximately &lt;strong&gt;5.7% faster&lt;/strong&gt; than MTP2; greater depth added no benefit. The short-input screen had favored MTP2, while actual coding favored MTP4. I prioritize the latter because it is closer to the work I want to run.&lt;/p&gt;

&lt;p&gt;This was one attempt on each of eight familiar tasks, with imperfectly matched ordering and thermal conditions. It supports a choice on this machine, not a universal optimal depth for all models and GPUs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Ledger made me inspect the test
&lt;/h2&gt;

&lt;p&gt;Ledger processes CSV records and money values. Its repeated failures made insufficient context seem plausible. Diagnosis found two different issues.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;a response really was truncated by its output cap&lt;/strong&gt;. I changed only the cap from 32K to 64K, retaining the 128K total window and other settings. The new attempt's longest response reached 40,612 tokens and passed functional and delivery checks. Its longest actual sequence was approximately 73K, comfortably inside 128K. The task also grew from approximately 628 to 1,140 seconds.&lt;/p&gt;

&lt;p&gt;Later, all five Ledger answers in the MTP depth comparison failed without exhausting output or context. The test's file and output mocks lacked normal interfaces. Valid answers were rejected because of those interface defects, including some that never reached the intended read-error behavior.&lt;/p&gt;

&lt;p&gt;After correcting those interfaces, &lt;strong&gt;all five unchanged answers passed 13/13 acceptance checks instead of 12/13&lt;/strong&gt;. Code deliberately omitting read-error handling still failed, so the behavioral requirement had not been relaxed.&lt;/p&gt;

&lt;p&gt;I created a new suite version and regraded MTP4's eight existing answers. Functional and delivered results changed from 7/8 to &lt;strong&gt;8/8&lt;/strong&gt;, without generating new answers. This corrected a false rejection; it did not improve the model. Other historical cohorts do not automatically receive revised scores.&lt;/p&gt;

&lt;p&gt;It changed how I approach failures: inspect truncation and code defects, and check whether the test actually exercises the behavior it claims to measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  220K fits long input; full-window generation is still untested
&lt;/h2&gt;

&lt;p&gt;Input and output share the context window; K here means 1,024 tokens. A 170K input budget plus 50K output requires 220K total. A 192K input budget plus 64K output requires 256K. Raising the output cap does not remove that total-capacity constraint.&lt;/p&gt;

&lt;p&gt;IQ3_S + Q8 KV + MTP4 processed &lt;strong&gt;179,532 input tokens followed by short output&lt;/strong&gt; with a &lt;strong&gt;220K total window&lt;/strong&gt;, peaking around 22.80 GiB of whole-GPU memory. At 256K, MTP4 failed allocation during startup. Disabling MTP and reducing batch sizes allowed the 256K configuration to complete the migration experiment below.&lt;/p&gt;

&lt;p&gt;To test usefulness, I built a schema migration task for 12 services whose new specifications were scattered through long material containing stale specifications. All three arms disabled MTP and differed in how input was supplied.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input strategy&lt;/th&gt;
&lt;th&gt;Actual input tokens&lt;/th&gt;
&lt;th&gt;Independent checks passed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;256K window, full material&lt;/td&gt;
&lt;td&gt;179,532&lt;/td&gt;
&lt;td&gt;198/198&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;128K window, relevant files selected in advance&lt;/td&gt;
&lt;td&gt;6,383&lt;/td&gt;
&lt;td&gt;198/198&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;128K window, simple head/tail crop&lt;/td&gt;
&lt;td&gt;54,755&lt;/td&gt;
&lt;td&gt;138/198&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full material retained required information; cropping omitted specifications for nine services. Selecting relevant files in advance also passed, taking approximately 712 seconds for prompt processing and generation versus 1,583 seconds with the full material.&lt;/p&gt;

&lt;p&gt;A larger window helped avoid information loss. Accurate selection let a smaller window solve the task faster. This was &lt;strong&gt;one task, one seed, and 198 checks&lt;/strong&gt;, not 198 coding problems. It did not test whether the agent could retrieve every necessary file itself.&lt;/p&gt;

&lt;p&gt;The earlier 128K-versus-220K coding comparison stopped after 2/8 tasks. Cache and wallet passed; during the third task, CPU temperature reached a sampled 93.25°C and triggered the configured 92°C guard. Six tasks in that historical comparison have no valid score. The newer b11429 screen above completed all eight at 220K, with 7/8 deliveries; it does not complete that older paired comparison or test a full 170K input followed by 50K output.&lt;/p&gt;

&lt;p&gt;Wallet makes the tradeoff concrete. Generation at 220K was approximately &lt;strong&gt;6.4% faster&lt;/strong&gt; than at 128K, but output increased from about 35,000 to 46,000 tokens. Task time increased from &lt;strong&gt;654 to 835 seconds&lt;/strong&gt;. Generating more content slightly faster can still delay delivery by three minutes. One observation cannot establish that larger context caused verbosity, but completion time and correctness need to be part of the optimization target.&lt;/p&gt;

&lt;h2&gt;
  
  
  DFlash2 has no generation-speed advantage here yet
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://z-lab.ai/projects/dflash/" rel="noopener noreferrer"&gt;DFlash&lt;/a&gt; also asks the main model to verify drafts, but a separate small model proposes an entire block in parallel. Local MTP drafts sequentially. &lt;a href="https://inco.ai/blog/dflash2/" rel="noopener noreferrer"&gt;DFlash2&lt;/a&gt; improves drafting further. The mechanism deserves testing; its benefit still depends on hardware and workload.&lt;/p&gt;

&lt;p&gt;Before the runtime update, I compared MTP4 and DFlash2 separately on b11146 with the same IQ3_S target, Q8 main cache, and 220K total window. MTP was disabled when DFlash2 was active. Identical cold inputs of approximately 8K, 32K, and 179K tokens ran once per configuration, generating 512, 512, and 128 tokens respectively.&lt;/p&gt;

&lt;p&gt;DFlash2's &lt;strong&gt;generation rate was approximately 3–6% lower&lt;/strong&gt;. Its memory peak was about 23.33 GiB, approximately &lt;strong&gt;540 MiB&lt;/strong&gt; above MTP4. For the longest input, its complete request was slightly faster, around 303 versus 307 seconds, because request time also includes prompt processing.&lt;/p&gt;

&lt;p&gt;I will keep MTP4 for now. This short test did not cover long output or complete coding tasks and does not answer the coding-quality question. It shows no generation-speed advantage for this local configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would run now
&lt;/h2&gt;

&lt;p&gt;My current experimental speed candidate is &lt;strong&gt;GSQ-RCO IQ3_S + Q8 main KV + F16 draft KV + MTP4&lt;/strong&gt;, with &lt;strong&gt;probabilistic draft sampling on llama.cpp b11429&lt;/strong&gt;. The completed eight-task screen used a &lt;strong&gt;220K total window and a 50K per-response output cap&lt;/strong&gt;. Input and output share that window. Temperature remains 1.0, with thinking enabled and xhigh reasoning effort.&lt;/p&gt;

&lt;p&gt;The updated stack uses CUDA 12.8 and OpenCode 2.0.20, with one concurrent slot, Flash Attention, and all model layers on GPU. Earlier results used b11146 unless stated otherwise. The newer long-input capacity check processed 179,532 input tokens plus 128 output tokens without truncation, with sampled whole-GPU memory around 22.31 GiB. That is a separate b11429 check, not a replacement for the earlier 22.80 GiB measurement. These experiments did not rewrite the daily-service defaults.&lt;/p&gt;

&lt;p&gt;Power draw and cooling also matter for sustained use. Two nearly complete early coding segments averaged approximately &lt;strong&gt;329W&lt;/strong&gt; of GPU board power. That excludes the CPU, other components, and PSU losses; whole-PC power needs measurement at the wall.&lt;/p&gt;

&lt;p&gt;The update gives me a faster runtime and draft-sampling candidate without changing the weights. The measured gain is useful; the 7/8 strict delivery result and its remaining failure stay visible. More distinct tasks and repeated comparisons are still needed to establish how well coding quality holds across broader work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zjshen14.github.io/experiments/qwen-3090-quantization-context-followup/technical-notes.en.md" rel="noopener noreferrer"&gt;Detailed methods and tables&lt;/a&gt; · &lt;a href="https://zjshen14.github.io/experiments/qwen-3090-quantization-context-followup/benchmark-data.json" rel="noopener noreferrer"&gt;Benchmark aggregates&lt;/a&gt; · &lt;a href="https://zjshen14.github.io/zh/blog/qwen-3090-quantization-context-followup/" rel="noopener noreferrer"&gt;中文版本&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>osdc</category>
      <category>testing</category>
    </item>
    <item>
      <title>Tuning a Local Qwen Coding Agent: What Changed, What Still Fails</title>
      <dc:creator>zhijie</dc:creator>
      <pubDate>Mon, 05 Oct 2026 05:50:15 +0000</pubDate>
      <link>https://dev.to/zjshen14/tuning-a-local-qwen-coding-agent-what-changed-what-still-fails-4554</link>
      <guid>https://dev.to/zjshen14/tuning-a-local-qwen-coding-agent-what-changed-what-still-fails-4554</guid>
      <description>&lt;p&gt;Read the &lt;a href="https://zjshen14.github.io/en/blog/qwen-agentic-tuning-3090/" rel="noopener noreferrer"&gt;original English article&lt;/a&gt; or the &lt;a href="https://zjshen14.github.io/zh/blog/qwen-agentic-tuning-3090/" rel="noopener noreferrer"&gt;完整中文版&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Giving this local Qwen coding agent more room to respond coincided with &lt;strong&gt;5 → 14 → 20 successful deliveries out of 24&lt;/strong&gt; as the output cap rose from 8K to 16K to 32K. Changing the reasoning setting from &lt;strong&gt;medium to &lt;code&gt;xhigh&lt;/code&gt; at the same 32K cap&lt;/strong&gt; brought the observed result to &lt;strong&gt;23/24&lt;/strong&gt;. The cost was longer waits: median attempt time rose from &lt;strong&gt;245.5 to 775.8 seconds&lt;/strong&gt; across the sequence.&lt;/p&gt;

&lt;p&gt;Those counts come from &lt;strong&gt;eight repair tasks attempted three times per configuration—24 attempts, not 24 different tasks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Those are promising &lt;strong&gt;development results&lt;/strong&gt;, not proof of a generally better coding agent. The rounds reused earlier controls and the same eight cases. A later temperature test lost one delivery overall, and &lt;strong&gt;zero held-out confirmation attempts ran&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This follow-up to the &lt;a href="https://zjshen14.github.io/en/blog/local-qwen-opencode-3090/" rel="noopener noreferrer"&gt;deployment guide&lt;/a&gt; and &lt;a href="https://zjshen14.github.io/en/blog/qwen-mtp-speed-quality/" rel="noopener noreferrer"&gt;MTP comparison&lt;/a&gt; explains the two tuning decisions separately: first make room for a response to finish, then compare reasoning settings within that room. The earlier studies used different protocols; their scores are not pooled here.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Writing disclosure: this article was drafted with AI assistance from recorded experiment reports and the reviewed development export. The public checker verifies aggregation, not patch semantics or generalization.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What do the 24 attempts actually test?
&lt;/h2&gt;

&lt;p&gt;This tuning study uses &lt;strong&gt;agentic-v2.0.1&lt;/strong&gt;, a newly assembled development suite of &lt;strong&gt;eight small code-repair tasks: six in Python and two in TypeScript&lt;/strong&gt;. The agent works on code, runs tools and tests, and returns a final handoff. The suite tests whether it can finish a bounded repair, rather than answer a coding question in text. It is separate from the four-task deployment and MTP studies above.&lt;/p&gt;

&lt;p&gt;Seven tasks are authored test projects. The eighth, nested paths, starts from a pinned version of the upstream &lt;strong&gt;boltons&lt;/strong&gt; library with a deliberately inserted fault. These are development exercises, not official SWE-bench results or eight independent production projects. The &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/protocol.json" rel="noopener noreferrer"&gt;public protocol&lt;/a&gt; describes their scope:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;What the repair must handle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cache&lt;/td&gt;
&lt;td&gt;Expiration and least-recently-used eviction, while preserving the existing API.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wallet&lt;/td&gt;
&lt;td&gt;Atomic, concurrent transfers and overflow checks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event stream&lt;/td&gt;
&lt;td&gt;Incremental UTF-8 parsing when incoming chunks split at arbitrary boundaries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nested paths&lt;/td&gt;
&lt;td&gt;A nested-lookup regression in an upstream library.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ledger&lt;/td&gt;
&lt;td&gt;Exact monetary arithmetic, CSV input and failure-safe command-line behavior.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build planner&lt;/td&gt;
&lt;td&gt;Deterministic dependency-graph planning and compatibility.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async pool&lt;/td&gt;
&lt;td&gt;A limit on concurrent asynchronous work and recovery from failures.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;Pagination across multiple files, duplicate removal and cancellation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last two tasks are TypeScript; the other six are Python. &lt;strong&gt;Each configuration gets the same eight tasks, with three separate attempts per task: 24 attempts in total.&lt;/strong&gt; Each attempt contributes either one delivered success or none, regardless of how many assertions its tests contain. So &lt;strong&gt;5/24 means five successful deliveries among 24 attempts&lt;/strong&gt;, not five distinct tasks or five passing unit tests. The later 14/24, 20/24 and 23/24 use the same denominator.&lt;/p&gt;

&lt;p&gt;A success requires more than the agent saying it is done. &lt;strong&gt;Functional success&lt;/strong&gt; means the candidate passes both grading stages, including predefined acceptance and regression checks kept outside the agent workspace, while preserving protected files, starter files and configuration. &lt;strong&gt;Delivered success&lt;/strong&gt; also requires the agent to finish the required turn normally, pass its recognized self-checks, and produce final text after using tools. TypeScript additionally requires type checks. The detailed scoring rules are below. Passing the fixed checks does not prove that every defect is absent.&lt;/p&gt;

&lt;p&gt;For a concrete example, ledger's &lt;strong&gt;0/3 → 2/3&lt;/strong&gt; from medium/32K to xhigh/32K means the same ledger repair failed all three medium attempts, then succeeded in two of the three xhigh attempts. It does not mean two new ledger tasks were added. The final &lt;strong&gt;23/24&lt;/strong&gt; consists of three successes on each of the other seven tasks and two on ledger. These repeats show variability on familiar development cases; they do not establish success rates on fresh tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four settings that sound similar but do different jobs
&lt;/h2&gt;

&lt;p&gt;OpenCode reads files, runs tools, edits code, and tests the changes. The local llama.cpp server generates reasoning, tool requests, and text. A single repair can involve many model responses before the agent produces its final handoff.&lt;/p&gt;

&lt;p&gt;That makes four limits worth separating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output cap:&lt;/strong&gt; how many tokens one model response may generate. Reasoning, tool requests, and final text share this allowance. An 8,192-token cap is not 8,192 tokens reserved for the answer after thinking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning setting:&lt;/strong&gt; the configured effort label, &lt;code&gt;medium&lt;/code&gt; or &lt;code&gt;xhigh&lt;/code&gt;, passed through the model's chat-template options. Both modes keep thinking enabled. The label is not a measured quantity of internal computation or a guaranteed number of thinking tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context capacity:&lt;/strong&gt; the server can accommodate up to &lt;strong&gt;131,072 tokens&lt;/strong&gt; of context, shared by input and output. It does not grant every response that much output or demonstrate coding quality at full capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whole-attempt budget:&lt;/strong&gt; each repair has &lt;strong&gt;1,800 seconds, 65,536 generated tokens, and 120 tool calls&lt;/strong&gt;. These limits cover the attempt across responses; increasing one response's cap does not increase them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stage 1: raise the output cap, keep medium reasoning
&lt;/h2&gt;

&lt;p&gt;The reason to try a larger cap was concrete. In the earlier deployment and MTP studies, some attempts used the entire 8,192-token response allowance on reasoning and ended before editing production code. More context could not solve that particular limit. The question for this development suite was whether a larger response allowance would let more repairs reach a working patch and final delivery.&lt;/p&gt;

&lt;p&gt;This stage kept thinking enabled and reasoning at &lt;strong&gt;&lt;code&gt;medium&lt;/code&gt;&lt;/strong&gt;, while increasing the configured output cap from &lt;strong&gt;8,192 → 16,384 → 32,768 tokens&lt;/strong&gt;. Context stayed at 131,072, temperature at 1.0, and the whole-attempt budget stayed fixed.&lt;/p&gt;

&lt;p&gt;Each row below uses the same eight development cases, repeated three times. The fourth row belongs to the next stage, where the cap stops changing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage and profile&lt;/th&gt;
&lt;th&gt;Functional&lt;/th&gt;
&lt;th&gt;Delivered&lt;/th&gt;
&lt;th&gt;Median attempt time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 · Medium, 8,192 output tokens&lt;/td&gt;
&lt;td&gt;5/24&lt;/td&gt;
&lt;td&gt;5/24&lt;/td&gt;
&lt;td&gt;245.5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 · Medium, 16,384 output tokens&lt;/td&gt;
&lt;td&gt;14/24&lt;/td&gt;
&lt;td&gt;14/24&lt;/td&gt;
&lt;td&gt;527.5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 · Medium, 32,768 output tokens&lt;/td&gt;
&lt;td&gt;20/24&lt;/td&gt;
&lt;td&gt;20/24&lt;/td&gt;
&lt;td&gt;586.6 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 · Xhigh, 32,768 output tokens&lt;/td&gt;
&lt;td&gt;23/24&lt;/td&gt;
&lt;td&gt;23/24&lt;/td&gt;
&lt;td&gt;775.8 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/aggregates.json" rel="noopener noreferrer"&gt;cohort aggregates&lt;/a&gt;. Medians include failures and exclude server startup, rests, and independent grading. These are observed waiting times, not speedups for identical successful work.&lt;/p&gt;

&lt;p&gt;The gains appeared across several kinds of repair. The &lt;strong&gt;async pool&lt;/strong&gt;, which tests bounded concurrent scheduling and failure recovery, went from &lt;strong&gt;0/3 at 8K to 3/3 at 16K&lt;/strong&gt;. The &lt;strong&gt;build planner&lt;/strong&gt;, which tests deterministic dependency planning and compatibility, went from &lt;strong&gt;0/3 to 1/3 to 3/3&lt;/strong&gt; as the cap increased. These examples show why counting completed repairs is more informative than counting generated tokens.&lt;/p&gt;

&lt;p&gt;More room did not solve everything. &lt;strong&gt;Pagination&lt;/strong&gt; reached &lt;strong&gt;2/3 at 32K&lt;/strong&gt;, while the &lt;strong&gt;CSV ledger&lt;/strong&gt;, testing exact money and failure-safe handling, remained &lt;strong&gt;0/3 at every medium cap&lt;/strong&gt;. That remaining gap motivated the next comparison: would a different reasoning setting help while keeping response space fixed?&lt;/p&gt;

&lt;p&gt;The waiting cost matters. Median time more than doubled between 8K and 16K, then increased again at 32K. A short unsuccessful attempt is not a fast successful repair; a larger allowance can keep an unproductive attempt running longer too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 2: compare medium with xhigh at a fixed 32K cap
&lt;/h2&gt;

&lt;p&gt;This stage changed the configured &lt;strong&gt;&lt;code&gt;reasoning_effort&lt;/code&gt; from &lt;code&gt;medium&lt;/code&gt; to &lt;code&gt;xhigh&lt;/code&gt;&lt;/strong&gt;. The conversational phrase “extreme high” refers here to that recorded &lt;code&gt;xhigh&lt;/code&gt; value. &lt;strong&gt;The output cap remained 32,768, and thinking stayed enabled in both modes.&lt;/strong&gt; This was not a thinking-on versus thinking-off comparison.&lt;/p&gt;

&lt;p&gt;In the recorded OpenCode profiles, the cap is &lt;code&gt;limit.output&lt;/code&gt;; the effort label is &lt;code&gt;reasoning_effort&lt;/code&gt; inside &lt;code&gt;body.chat_template_kwargs&lt;/code&gt;, alongside &lt;code&gt;enable_thinking: true&lt;/code&gt;. These are different controls. Setting &lt;code&gt;xhigh&lt;/code&gt; requests a different reasoning mode through the template; it does not establish that every response used more computation, or that the server assigned a known extra reasoning budget. The records support the configured comparison, not a measurement of internal thinking depth.&lt;/p&gt;

&lt;p&gt;The observed result rose from &lt;strong&gt;20/24 to 23/24&lt;/strong&gt; for both success metrics. All three matched improvements came from two cases: &lt;strong&gt;ledger went from 0/3 to 2/3&lt;/strong&gt;, and &lt;strong&gt;pagination from 2/3 to 3/3&lt;/strong&gt;. The other six cases remained at 3/3. The ledger still failed one repeat, so even this development result was not uniformly successful.&lt;/p&gt;

&lt;p&gt;Median attempt time rose from &lt;strong&gt;586.6 to 775.8 seconds&lt;/strong&gt;. That is the tradeoff to assess for a coding workflow: more of these repairs finished, but the typical recorded attempt took longer. It is not evidence that &lt;code&gt;xhigh&lt;/code&gt; will improve every task or every local model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the improvement is still provisional
&lt;/h3&gt;

&lt;p&gt;The case and repeat labels match across cohorts, but the controls were collected earlier. The output rounds and reasoning round were sequential extensions selected after previous feedback, rather than fresh randomized, interleaved comparisons. Rest policies and recovery history also changed. Matching labels therefore describes observed transitions; it cannot isolate an &lt;code&gt;xhigh&lt;/code&gt;-only causal effect.&lt;/p&gt;

&lt;p&gt;The main cohorts contain &lt;strong&gt;eight distinct small cases&lt;/strong&gt;, not 24 independent production repositories. Repeats share tasks and checks, and later tool trajectories can diverge. The selected &lt;strong&gt;xhigh / 32K / temperature 1.0&lt;/strong&gt; profile is worth investigating as a complete configuration; these results do not establish a universal optimum or a new adoption decision.&lt;/p&gt;

&lt;p&gt;All eight cases and matched transitions&lt;/p&gt;

&lt;p&gt;Each cell is successes out of three attempts. Functional and delivered counts agree in this table.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Case&lt;/th&gt;
&lt;th&gt;Medium 8K&lt;/th&gt;
&lt;th&gt;Medium 16K&lt;/th&gt;
&lt;th&gt;Medium 32K&lt;/th&gt;
&lt;th&gt;Xhigh 32K&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cache&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wallet&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event stream&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nested paths&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async pool&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ledger&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build planner&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cases cover expiration/LRU, atomic transfers, incremental UTF-8 parsing, nested lookup, bounded async scheduling, exact-money CSV handling, pagination, and graph planning. Seven are authored fixtures; nested paths uses a pinned upstream seeded fault. They belong to &lt;strong&gt;agentic-v2.0.1&lt;/strong&gt;, separate from the deployment and MTP studies.&lt;/p&gt;

&lt;p&gt;Matched transitions show nine improvements and zero regressions from 8K to 16K, six and zero from 16K to 32K, and three and zero from medium to xhigh at 32K, for both metrics. These are descriptive counts from &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/aggregates.json" rel="noopener noreferrer"&gt;aggregates.json&lt;/a&gt;, not randomized effect estimates.&lt;/p&gt;

&lt;p&gt;The later 16K continuation and medium/32K round used 300-second rests; the reasoning round used 180-second rests. Earlier recovery and pauses further limit attribution. The &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/README.md" rel="noopener noreferrer"&gt;data notes&lt;/a&gt; retain that history and the cohort inclusion rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lower temperature exposed the gap between a patch and a delivery
&lt;/h2&gt;

&lt;p&gt;After the reasoning comparison, temperature &lt;strong&gt;0.8&lt;/strong&gt; was tested on ledger and pagination, three repeats each, at xhigh/32K. These cases were selected after earlier failures. Their temperature-1.0 controls are six reused attempts from the existing xhigh cohort, not six new concurrent controls.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Targeted profile&lt;/th&gt;
&lt;th&gt;Functional&lt;/th&gt;
&lt;th&gt;Delivered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Temperature 1.0, reused controls&lt;/td&gt;
&lt;td&gt;5/6&lt;/td&gt;
&lt;td&gt;5/6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temperature 0.8, new attempts&lt;/td&gt;
&lt;td&gt;5/6&lt;/td&gt;
&lt;td&gt;4/6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One ledger attempt, &lt;strong&gt;&lt;code&gt;a0100&lt;/code&gt;&lt;/strong&gt;, passed both independent grades but hit the wall-time limit at &lt;strong&gt;1,800.017 seconds&lt;/strong&gt;, exiting with code -15 and no final text. It counted as a functional patch, but not a completed delivery. Another, &lt;strong&gt;&lt;code&gt;a0101&lt;/code&gt;&lt;/strong&gt;, exited normally with passing agent tests and final text, yet failed independent acceptance checks. Neither metric counted it as successful.&lt;/p&gt;

&lt;p&gt;Together these explain why green agent tests, a correct patch, and a finished handoff must be tracked separately. At matched case/repeat level, temperature 0.8 produced one functional improvement and one regression; delivery had one improvement and two regressions. This targeted result does not support making 0.8 the better default.&lt;/p&gt;

&lt;p&gt;Exact scoring and sampling limits&lt;/p&gt;

&lt;p&gt;Functional success requires solved phase-one and final grades plus unchanged agent configuration. A solved grade requires no protected-file changes, no missing starter files, and every required check group passing. Python requires acceptance, regression, public-test, and syntax groups. TypeScript requires acceptance, regression, public-test, project-typecheck, and consumer-typecheck groups, with no separate syntax group. Predefined acceptance and regression checks stay outside the agent workspace.&lt;/p&gt;

&lt;p&gt;Delivered success also requires the expected turn count, completed termination with exit code zero, nonempty final text after tools, and the last recognized agent test validation passing. TypeScript also requires the last recognized typecheck to pass. Every development case requires one turn. The medium/32K ledger timeout &lt;strong&gt;&lt;code&gt;a0060&lt;/code&gt;&lt;/strong&gt; remains a scored failure, not an infrastructure exclusion.&lt;/p&gt;

&lt;p&gt;Final-text presence does not prove that the handoff accurately describes the patch. Passing frozen checks does not prove every defect is absent. The &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/attempts.json" rel="noopener noreferrer"&gt;attempt records&lt;/a&gt; expose outcome fields, not patches or raw conversations.&lt;/p&gt;

&lt;p&gt;Historical sampling observations covered all six 0.8 attempts and matched the requested temperature within floating-point tolerance, but did not capture every request. Profile metadata and configured request bodies are evidence of configuration; they are not independent capture of every effective field throughout every run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirmation never launched
&lt;/h2&gt;

&lt;p&gt;A frozen held-out protocol planned &lt;strong&gt;four fresh cases, three paired repeats, and two profiles&lt;/strong&gt;: medium/8K versus xhigh/32K, both at temperature 1.0 and 131,072 context capacity. That would be 24 attempts and 12 pairs.&lt;/p&gt;

&lt;p&gt;Preflight stopped launch because hardware readiness remained unresolved after historical CPU machine-check reports. Their cause is unexplained: they do not diagnose a defective component or establish a confirmation-induced fault. An absence of new matching events in retained observations would not, by itself, clear the hardware. A separate &lt;strong&gt;OpenCode resource-ownership prerequisite&lt;/strong&gt; also remained unresolved. Neither guard was bypassed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero confirmation attempts ran, zero pairs completed, and there are no confirmation quality measurements.&lt;/strong&gt; Missing observations are not 0/12 success rates or model failures. There was no confirmation inference, runtime-identity measurement, or fresh weight hash. Further testing depends on independently resolved readiness and software ownership; the development result cannot answer whether the gains transfer to fresh cases.&lt;/p&gt;

&lt;p&gt;Confirmation registration&lt;/p&gt;

&lt;p&gt;The frozen protocol's registration SHA-256 is:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6760f39c13e67f0814edbdaf314b3572d985c30c8ef94752631e551482e423cd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Even a completed four-case, three-repeat test would contain four distinct cases with correlated repeats. No such test completed here, and this article provides no hardware clearance or promise of a completed held-out comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying the comparison in your own workflow
&lt;/h2&gt;

&lt;p&gt;The useful order is to identify the failure first. If one response ends at its output cap before useful edits, compare a larger allowance while keeping the reasoning setting fixed. Once that comparison is understood, test reasoning settings at a fixed cap. Keep independent acceptance checks, failed attempts, and elapsed time in the same record; inspect the actual diff and final handoff too.&lt;/p&gt;

&lt;p&gt;Before changing a working deployment, save its configuration and record the actual request allowance, thinking options, sampling fields, and model digest. Restore the saved configuration when reverting. The &lt;a href="https://zjshen14.github.io/en/blog/local-qwen-opencode-3090/" rel="noopener noreferrer"&gt;deployment guide&lt;/a&gt; covers integration; this article supplies historical profile values, not a full launch command or ready-to-run benchmark harness.&lt;/p&gt;

&lt;p&gt;Selected development configuration and model identity&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Recorded value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning / thinking&lt;/td&gt;
&lt;td&gt;xhigh / enabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output per response&lt;/td&gt;
&lt;td&gt;32,768 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context capacity&lt;/td&gt;
&lt;td&gt;131,072 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temperature&lt;/td&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sampling&lt;/td&gt;
&lt;td&gt;top-p 0.95; top-k 20; min-p 0; repetition penalty 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speculation&lt;/td&gt;
&lt;td&gt;Off&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Within the model entry of the recorded OpenCode configuration, the two tuning fields look like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;131072&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32768&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"chat_template_kwargs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enable_thinking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reasoning_effort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xhigh"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This is a field excerpt, not a complete provider configuration. Stage 1 changes &lt;code&gt;limit.output&lt;/code&gt; with effort at &lt;code&gt;medium&lt;/code&gt;; stage 2 changes the effort label with output fixed at 32768.&lt;/p&gt;

&lt;p&gt;The recorded model is &lt;strong&gt;Qwen3.8-27B Q4_K_M&lt;/strong&gt;, a 16,810,714,464-byte weight file with SHA-256:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f5f1dd8920d417aac2718b0bda3403da274301efdd6760b4f0f4b864ff2ad57d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/protocol.json" rel="noopener noreferrer"&gt;protocol export&lt;/a&gt; pins llama.cpp &lt;strong&gt;b11146-7fe450e19&lt;/strong&gt;, CUDA &lt;strong&gt;12.8&lt;/strong&gt;, OpenCode &lt;strong&gt;2.0.20&lt;/strong&gt;, tool versions, and serving settings. Completed records agree on recorded model/runtime identity. This is historical identity, not fresh attestation of loaded GPU tensors or independent upstream-branding verification. A client alias alone cannot identify the weights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download and recompute the results
&lt;/h2&gt;

&lt;p&gt;The public export lets you recompute these aggregates without a model. Download all eight files into one directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/README.md" rel="noopener noreferrer"&gt;README.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/protocol.json" rel="noopener noreferrer"&gt;protocol.json&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/attempts.json" rel="noopener noreferrer"&gt;attempts.json&lt;/a&gt; and &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/attempts.csv" rel="noopener noreferrer"&gt;attempts.csv&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/aggregates.json" rel="noopener noreferrer"&gt;aggregates.json&lt;/a&gt; and &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/aggregates.csv" rel="noopener noreferrer"&gt;aggregates.csv&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/check.py" rel="noopener noreferrer"&gt;check.py&lt;/a&gt; and &lt;a href="https://zjshen14.github.io/experiments/qwen-agentic-tuning-3090/development/SHA256SUMS" rel="noopener noreferrer"&gt;SHA256SUMS&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Python 3.10 or later, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 check.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It needs no network or model and writes nothing. Missing, changed, or unexpected files fail validation. This is &lt;strong&gt;aggregation reproduction only&lt;/strong&gt;: it does not rerun inference or grading, validate patches, or prove the source records true.&lt;/p&gt;

&lt;p&gt;Inventory, exclusions, and reproduction boundaries&lt;/p&gt;

&lt;p&gt;There are &lt;strong&gt;102 unique primary attempts&lt;/strong&gt;: four 24-attempt cohorts plus six new temperature-0.8 attempts. Reused temperature-1.0 controls add none. The broader inventory has &lt;strong&gt;159 records&lt;/strong&gt;, including 119 completed results and 40 unscored records or planned slots.&lt;/p&gt;

&lt;p&gt;Extra 8K trials, pilots, the cancelled thinking-off direction, infrastructure predecessors, and configuration preflights remain separate. A registered replacement completes the 16K cohort at 24 attempts. Cancelled or unstarted slots have null outcomes; they are not fabricated failures.&lt;/p&gt;

&lt;p&gt;The checker verifies checksums, JSON/CSV parity, cohort membership, inventory, grade and outcome formulas, totals, medians, and matched transitions. Fixtures, prompts, candidate patches, raw sessions, private tests, numeric seeds, and the inference/grading harness are omitted. Public repeat labels preserve matching, but cannot substitute for inference seeds. Checksums cannot establish authenticity if both data and checksums are changed together.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
      <category>osdc</category>
    </item>
    <item>
      <title>MTP on an RTX 3090: Faster Tokens, but What About Coding Quality?</title>
      <dc:creator>zhijie</dc:creator>
      <pubDate>Fri, 02 Oct 2026 21:22:39 +0000</pubDate>
      <link>https://dev.to/zjshen14/mtp-on-an-rtx-3090-faster-tokens-but-what-about-coding-quality-28j</link>
      <guid>https://dev.to/zjshen14/mtp-on-an-rtx-3090-faster-tokens-but-what-about-coding-quality-28j</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://zjshen14.github.io/en/blog/qwen-mtp-speed-quality/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=qwen_mtp&amp;amp;utm_content=article_intro" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Enabling MTP on this RTX 3090 raised generation throughput from &lt;strong&gt;36.0 to 56.2 tokens/s, about 56% faster&lt;/strong&gt;. The two cache tasks that succeeded in both modes also finished about 20% and 40% sooner.&lt;/p&gt;

&lt;p&gt;One transfer-task pair produced a patch-quality difference, however. I am continuing to test MTP while keeping it off by default.&lt;/p&gt;

&lt;p&gt;This experiment grew out of a reader's suggestion on the &lt;a href="https://zjshen14.github.io/en/blog/local-qwen-opencode-3090/" rel="noopener noreferrer"&gt;previous local-deployment post&lt;/a&gt;: try MTP or a quantization that includes it. The RTX 3090 left over from Ethereum mining was already running Qwen3.8-27B through OpenCode. I wanted to see whether faster generation would deliver correct patches sooner.&lt;/p&gt;

&lt;p&gt;We ran &lt;strong&gt;eight paired comparisons, sixteen attempts&lt;/strong&gt;, using the same model file. Each pair shared its task, seed, and initial environment; only the MTP setting changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  MTP drafts tokens for the target model to verify
&lt;/h2&gt;

&lt;p&gt;Ordinary generation typically produces one token per step. MTP (Multi-Token Prediction) uses an embedded prediction head to propose upcoming tokens, then asks the target model to verify them. Accepting several in sequence can reduce serial decoding steps. This is a &lt;a href="https://github.com/ggml-org/llama.cpp/blob/b11146/docs/speculative.md" rel="noopener noreferrer"&gt;speculative-decoding path in llama.cpp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The target model still verifies the candidates. The &lt;a href="https://arxiv.org/abs/2211.17192" rel="noopener noreferrer"&gt;speculative-decoding paper&lt;/a&gt; shows that a correct algorithm can preserve its output distribution. Speed need not inherently cost model quality.&lt;/p&gt;

&lt;p&gt;Our existing &lt;strong&gt;Qwen3.8-27B Q4_K_M GGUF contained an MTP prediction layer&lt;/strong&gt;, so we used it directly without switching to another Unsloth quantization. The server commands differed only in &lt;code&gt;--spec-type none&lt;/code&gt; versus &lt;code&gt;--spec-type draft-mtp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generation was 56% faster; successful tasks saved 20–40%
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1fkealsgyz9296u9ftt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1fkealsgyz9296u9ftt.png" alt="Generation throughput increased from 36.0 to 56.2 tokens per second with MTP; completed and passing tasks remained 2 out of 8 in both modes. Small sample with a shared 8K response budget." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generation throughput counts tokens produced per second, including the model's reasoning output. Task time also includes reading source, executing tools, and running tests. It is closer to how long you wait for a patch.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;MTP off&lt;/th&gt;
&lt;th&gt;MTP on&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Aggregate coding-generation throughput&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;36.0 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;56.2 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predefined independent checks passed&lt;/td&gt;
&lt;td&gt;35/80&lt;/td&gt;
&lt;td&gt;26/80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completed within deadline and passed predefined checks&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2/8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2/8&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two cache pairs that succeeded in both modes provide the clearest evidence of time saved:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Paired run&lt;/th&gt;
&lt;th&gt;MTP off&lt;/th&gt;
&lt;th&gt;MTP on&lt;/th&gt;
&lt;th&gt;Task time saved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pair one&lt;/td&gt;
&lt;td&gt;192.0 s&lt;/td&gt;
&lt;td&gt;153.5 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20.1%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pair two&lt;/td&gt;
&lt;td&gt;431.2 s&lt;/td&gt;
&lt;td&gt;259.0 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;39.9%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both delivered a patch passing the predefined checks sooner. A failed run ending earlier does not count as speeding up a successful task. Later prompts also diverge with the agent's actions, so aggregate throughput is not an identical-text speed comparison.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short replies and generation-timing definitions
&lt;/h3&gt;

&lt;p&gt;Short replies to matched first requests reached &lt;strong&gt;38.9–39.8 tokens/s&lt;/strong&gt; with MTP off and &lt;strong&gt;74.0–84.2 tokens/s&lt;/strong&gt; with it on. These were brief tool-selection responses; they do not show that whole coding tasks finish in half the time.&lt;/p&gt;

&lt;p&gt;Aggregate throughput is total accepted target-output tokens divided by total corresponding server generation time across completed generations. It includes reasoning and tool calls, excludes prompt processing and rejected drafts, and omits interrupted generations without final timing records.&lt;/p&gt;

&lt;p&gt;Task time includes prompt processing, tools, and the agent's own tests. Server startup and independent grading afterward are excluded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nine differing checks came from one transfer repair
&lt;/h2&gt;

&lt;p&gt;The entire 35/80 versus 26/80 gap came from &lt;strong&gt;one transfer task at one seed&lt;/strong&gt;. Cache, ledger, and build-planner scores matched within every pair.&lt;/p&gt;

&lt;p&gt;With MTP off, the agent produced a production-code patch that subsequently passed all ten independent checks. It reached the deadline at &lt;strong&gt;480.1 seconds&lt;/strong&gt; without a final handoff.&lt;/p&gt;

&lt;p&gt;With MTP on, the agent ended after &lt;strong&gt;186.1 seconds&lt;/strong&gt; without editing production code. It passed only the one check the initial implementation already passed.&lt;/p&gt;

&lt;p&gt;The off candidate performed better on those checks, and neither attempt met the full delivery standard. We therefore tracked &lt;strong&gt;patch correctness&lt;/strong&gt; and &lt;strong&gt;timely delivery&lt;/strong&gt; separately: completed tasks remained 2/8 in both modes. Nine related checks are not nine independent task regressions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task scores and an early difference that did not recur
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;MTP off, two scores&lt;/th&gt;
&lt;th&gt;MTP on, two scores&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Expiring cache&lt;/td&gt;
&lt;td&gt;10/10, 10/10&lt;/td&gt;
&lt;td&gt;10/10, 10/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSV ledger&lt;/td&gt;
&lt;td&gt;1/10, 1/10&lt;/td&gt;
&lt;td&gt;1/10, 1/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incremental build planner&lt;/td&gt;
&lt;td&gt;1/10, 1/10&lt;/td&gt;
&lt;td&gt;1/10, 1/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLite transfers&lt;/td&gt;
&lt;td&gt;1/10, &lt;strong&gt;10/10&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;1/10, &lt;strong&gt;1/10&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Completed and passing requires no timeout, successful process exit, unchanged protected contracts/configuration, passing public and added tests, and all ten independent checks passing. It is not proof that every possible defect is covered.&lt;/p&gt;

&lt;p&gt;An early exploratory comparison showed a build-planner score difference. It did not recur under these tighter controls; those scores are not pooled into this study.&lt;/p&gt;

&lt;p&gt;The cache task also gave me a reason to inspect code after green tests. An MTP candidate's added TTL validation overflowed on a very large integer, while the paired off candidate passed the extra probe. This extreme API boundary is worth recording; it establishes neither everyday failure frequency nor MTP as the cause.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extra review: cache overflow and shared omissions
&lt;/h3&gt;

&lt;p&gt;All four cache attempts passed the ten predefined checks. One MTP candidate added &lt;code&gt;math.isfinite(ttl)&lt;/code&gt;. The contract permits finite positive integer TTLs. With &lt;code&gt;10**400&lt;/code&gt; and an injected integer clock increasing on every call, validation raised &lt;code&gt;OverflowError&lt;/code&gt; while converting the integer to a float. The paired off candidate passed.&lt;/p&gt;

&lt;p&gt;Ledger probes for large exact amounts, header names, and read-time errors failed in both modes at both seeds. A wallet destination-integer-overflow probe passed off and failed on in the second pair; it failed both modes in the first pair.&lt;/p&gt;

&lt;p&gt;Extra review ran after grading, never reached the agent, and was not added retrospectively to the eighty checks. The &lt;a href="https://zjshen14.github.io/experiments/qwen-mtp-speed-quality/controlled-summary.json" rel="noopener noreferrer"&gt;paired records&lt;/a&gt; retain details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faster output still exhausts the same allowance
&lt;/h2&gt;

&lt;p&gt;Most unsuccessful attempts shared a problem: they spent the &lt;strong&gt;8,192-token response allowance&lt;/strong&gt; on reasoning, ended with &lt;code&gt;length&lt;/code&gt;, and never edited production code. Many 1/10 scores belong to unchanged starting code; they do not mean a tenth of the repair was completed.&lt;/p&gt;

&lt;p&gt;Increasing context capacity would not resolve that limit. Context determines how much input and output can fit; the response allowance caps what one generation can produce. MTP can produce tokens faster without adding room to that allowance.&lt;/p&gt;

&lt;p&gt;This also limits the quality comparison. Both modes frequently produced no patch, leaving few completed repairs to compare. There were only two seeds per task and four small Python projects. The eighty checks are correlated, not eighty independent samples.&lt;/p&gt;

&lt;p&gt;The transfer difference deserves investigation, but its cause remains unresolved. Numerical behavior in batched verification, state handling, and sampling are possible avenues to investigate. An agent's different action can also change later inputs and patches. These are possible explanations, not findings confirmed by this experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controls and the limits of this comparison
&lt;/h2&gt;

&lt;p&gt;Each task ran at two seeds, with MTP off and on; the second seed reversed the order. Every attempt rebuilt the same initial code and fixed the working directory, prompts, tools, permissions, and generation settings. Ten independent test methods per task stayed outside the agent's workspace, with no results fed back to it.&lt;/p&gt;

&lt;p&gt;A proxy checked the complete first request actually sent to the model: &lt;strong&gt;all eight pairs matched&lt;/strong&gt;, as did initial file hashes. Each attempt restarted the server and disabled prefix-cache reuse in both modes. These results therefore cannot predict gains in ordinary cached conversations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full configuration, request audit, and scope
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Fixed configuration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model and GPU&lt;/td&gt;
&lt;td&gt;Same Qwen3.8-27B Q4_K_M GGUF; RTX 3090 24GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Software&lt;/td&gt;
&lt;td&gt;llama.cpp b11146 / CUDA 12.8; OpenCode 2.0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;131,072-token capacity; one slot; all model layers on GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache and compute&lt;/td&gt;
&lt;td&gt;q8_0 K/V; flash attention; batch 512 / microbatch 256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;Medium thinking; temperature 1.0; top_p 0.95; top_k 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Budgets&lt;/td&gt;
&lt;td&gt;8,192 output tokens per generation; 480 seconds per task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paired seeds&lt;/td&gt;
&lt;td&gt;4242 and 8675309&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speculation&lt;/td&gt;
&lt;td&gt;Off: &lt;code&gt;none&lt;/code&gt;; on: &lt;code&gt;draft-mtp&lt;/code&gt;; maximum draft length 3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pairs one and two in the elapsed-time table correspond to seeds 4242 and 8675309.&lt;/p&gt;

&lt;p&gt;Target and draft backend sampling were disabled in both modes; synthetic acceptance was not used. Each pair shared the initial commit, agent title, project configuration, and grader. Reported cached-prefix token counts were zero. The first seed ran off→on; the second ran on→off.&lt;/p&gt;

&lt;p&gt;These were fresh-server attempts without prefix-cache reuse. Task timing excludes server startup but includes prompt processing, tools, and the agent's own tests. Independent grading afterward is excluded. Desktop and other background GPU activity were not fully controlled.&lt;/p&gt;

&lt;p&gt;The largest actual input was &lt;strong&gt;20,057 tokens&lt;/strong&gt;. The 128K setting is capacity; this experiment did not measure full-128K coding performance or acceleration in ordinary cached conversations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the current default; test a larger output budget next
&lt;/h2&gt;

&lt;p&gt;For this setup, I am keeping MTP off by default. Saving 20–40% on the successful cache tasks is useful; before expanding its use, I want to investigate the transfer-patch difference.&lt;/p&gt;

&lt;p&gt;The next comparison should raise the output allowance equally in both modes, add paired seeds, and use tasks closer to everyday work. Cached conversations need a separate test. That should give a better basis for deciding whether faster generation consistently delivers correct patches sooner.&lt;/p&gt;

&lt;p&gt;If you try MTP, record generation speed, task time, independent tests, and final delivery together, then decide using the tasks you actually do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data and references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/experiments/qwen-mtp-speed-quality/controlled-summary.json" rel="noopener noreferrer"&gt;Paired experiment JSON&lt;/a&gt;: scores, elapsed times, generation timings, request checks, and extra probes for sixteen attempts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/experiments/qwen-mtp-speed-quality/README.txt" rel="noopener noreferrer"&gt;Method and data notes&lt;/a&gt;: attachments omit experiment dates, locations, personal paths, session identifiers, and raw conversations. They are result records, not a complete reproduction kit.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/en/blog/local-qwen-opencode-3090/" rel="noopener noreferrer"&gt;Previous post: local Qwen3.8-27B + OpenCode deployment&lt;/a&gt;: installation and integration. Its first-attempt study is a separate batch; scores should not be pooled with these.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ggml-org/llama.cpp/blob/b11146/docs/speculative.md" rel="noopener noreferrer"&gt;llama.cpp b11146 speculative-decoding documentation&lt;/a&gt; and the &lt;a href="https://arxiv.org/abs/2211.17192" rel="noopener noreferrer"&gt;speculative-decoding paper&lt;/a&gt;: mechanism background. Measurements on this card come from the paired records.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OpenCode Field Notes
&lt;/h2&gt;

&lt;p&gt;Follow the setup through to measured results:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/run-opencode-from-your-phone-or-laptop-remote-hosting-authentication-and-reverse-proxy-16bd"&gt;Remote OpenCode: hosting, authentication and reverse proxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/qwen38-27b-on-rtx-3090-with-opencode-and-measured-coding-results-4m4h"&gt;Local Qwen3.8-27B on an RTX 3090: deployment and coding results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/mtp-on-an-rtx-3090-faster-tokens-but-what-about-coding-quality-28j"&gt;MTP: generation speed versus coding quality&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Run OpenCode from your phone or laptop: remote hosting, authentication, and reverse proxy</title>
      <dc:creator>zhijie</dc:creator>
      <pubDate>Thu, 01 Oct 2026 20:09:27 +0000</pubDate>
      <link>https://dev.to/zjshen14/run-opencode-from-your-phone-or-laptop-remote-hosting-authentication-and-reverse-proxy-16bd</link>
      <guid>https://dev.to/zjshen14/run-opencode-from-your-phone-or-laptop-remote-hosting-authentication-and-reverse-proxy-16bd</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://zjshen14.github.io/en/blog/setup-opencode-remote-web-ide/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=remote_opencode" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. This is an adapted version of the deployment guide.&lt;/p&gt;

&lt;p&gt;A coding agent is easier to use across devices when the workspace lives on one host. The phone or laptop opens the browser UI; the host keeps the repository, runs tools, and connects to the model provider.&lt;/p&gt;

&lt;p&gt;That host can be a desktop, a homelab machine, or a VPS. You do not need a public domain to begin. The choices that matter are how clients reach the service, how access is authenticated, and what happens when the launching terminal closes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the workspace and authentication
&lt;/h2&gt;

&lt;p&gt;Enter the project directory before starting OpenCode. For a service behind a reverse proxy on the same host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your/workspace
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENCODE_SERVER_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'replace-with-a-strong-password'&lt;/span&gt;
opencode web &lt;span class="nt"&gt;--hostname&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--port&lt;/span&gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default authentication username is &lt;code&gt;opencode&lt;/code&gt;. &lt;code&gt;opencode web&lt;/code&gt; includes the browser interface; &lt;code&gt;opencode serve&lt;/code&gt; runs the headless API service. See the &lt;a href="https://opencode.ai/docs/web/" rel="noopener noreferrer"&gt;official Web documentation&lt;/a&gt; for the current flags.&lt;/p&gt;

&lt;p&gt;Treat access to this UI as access to the host's development tools. Use a dedicated workspace and account with only the permissions the agent needs. Keep the password out of committed configuration and screenshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose a connection path
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Private access:&lt;/strong&gt; Keep the service on a private network. A LAN connection works for devices on that network; Tailscale is another way to connect authorized devices without exposing the IDE directly to the public internet. If you bind to &lt;code&gt;0.0.0.0&lt;/code&gt;, remember that it listens on every interface: restrict reachability with the host firewall and keep authentication enabled. Plain HTTP on a LAN is not encrypted by itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A VPS with a domain:&lt;/strong&gt; Keep OpenCode on loopback and put an HTTPS reverse proxy in front. For example, a Caddy site can be as small as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ide.example.com {
    reverse_proxy 127.0.0.1:8080
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The domain must point to the server and the necessary ports must be reachable. Caddy can manage HTTPS certificates and proxy WebSocket connections; see its &lt;a href="https://caddyserver.com/docs/caddyfile/directives/reverse_proxy" rel="noopener noreferrer"&gt;reverse-proxy documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A reverse proxy does not automatically provide application authentication. Confirm that an unauthenticated visitor cannot enter the OpenCode workspace. With Nginx, check WebSocket upgrades and long-running streamed responses rather than testing only whether the initial page loads. The full article includes an Nginx example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the host process running
&lt;/h2&gt;

&lt;p&gt;A remote host helps only while its service stays alive. On Linux, systemd can restart OpenCode after a process failure and start it after a reboot. My guide includes an example service with a dedicated working directory and a protected environment file for the password.&lt;/p&gt;

&lt;p&gt;Use the actual installed binary path and service user. Check service logs and test a restart before relying on it for longer tasks. Closing a client browser and shutting down the host are different events; the host must remain awake and connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Select the model separately
&lt;/h2&gt;

&lt;p&gt;Hosting OpenCode's workspace does not mean the model runs locally. The original guide uses &lt;strong&gt;Muse Spark 1.3 Contributor Free&lt;/strong&gt; through OpenCode Zen as one example. Zen currently lists this as a limited-time free model, and its Contributor terms permit Meta to use prompts and completions for future model training. Review the &lt;a href="https://opencode.ai/docs/zen/" rel="noopener noreferrer"&gt;current Zen pricing and privacy terms&lt;/a&gt; before using it, and keep confidential code and secrets out of that endpoint.&lt;/p&gt;

&lt;p&gt;The deployment pattern also works with a provider appropriate to your data requirements. In my &lt;a href="https://zjshen14.github.io/en/blog/local-qwen-opencode-3090/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=remote_opencode" rel="noopener noreferrer"&gt;follow-up experiment&lt;/a&gt;, inference runs on an RTX 3090 with Qwen3.8-27B and llama.cpp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the whole path
&lt;/h2&gt;

&lt;p&gt;Before handing it a long task, verify these steps from the intended client device:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Authentication blocks unauthenticated access.&lt;/li&gt;
&lt;li&gt;The expected project and model are selected.&lt;/li&gt;
&lt;li&gt;A harmless file-read request succeeds.&lt;/li&gt;
&lt;li&gt;Tool calls and live output work through the chosen network path.&lt;/li&gt;
&lt;li&gt;The host service survives the terminal disconnects and restarts you expect.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The benefit is one workspace that I can reach from different screens. The phone becomes a way to check progress or send a follow-up; execution remains on the host.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zjshen14.github.io/en/blog/setup-opencode-remote-web-ide/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=remote_opencode" rel="noopener noreferrer"&gt;Full guide with service and proxy configuration&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenCode Field Notes
&lt;/h2&gt;

&lt;p&gt;Follow the setup through to measured results:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/run-opencode-from-your-phone-or-laptop-remote-hosting-authentication-and-reverse-proxy-16bd"&gt;Remote OpenCode: hosting, authentication and reverse proxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/qwen38-27b-on-rtx-3090-with-opencode-and-measured-coding-results-4m4h"&gt;Local Qwen3.8-27B on an RTX 3090: deployment and coding results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/mtp-on-an-rtx-3090-faster-tokens-but-what-about-coding-quality-28j"&gt;MTP: generation speed versus coding quality&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Qwen3.8-27B on RTX 3090 with OpenCode and measured coding results</title>
      <dc:creator>zhijie</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:05:13 +0000</pubDate>
      <link>https://dev.to/zjshen14/qwen38-27b-on-rtx-3090-with-opencode-and-measured-coding-results-4m4h</link>
      <guid>https://dev.to/zjshen14/qwen38-27b-on-rtx-3090-with-opencode-and-measured-coding-results-4m4h</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://zjshen14.github.io/en/blog/local-qwen-opencode-3090/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=local_qwen_3090" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. This cross-post includes the setup and measured results; the linked records and configuration kit let you inspect the experiment.&lt;/p&gt;

&lt;p&gt;My &lt;strong&gt;RTX 3090&lt;/strong&gt; is a leftover from my Ethereum mining days. Running a local model gives it a second life: the same card now powers a coding agent.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://zjshen14.github.io/en/blog/setup-opencode-remote-web-ide/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt; covered accessing one OpenCode workspace from a laptop or phone. This time, I wanted to move inference onto that host too and find out how far the hardware already on hand could take a local AI coding workflow.&lt;/p&gt;

&lt;p&gt;We loaded &lt;strong&gt;Qwen3.8-27B Q4_K_M&lt;/strong&gt; into the 3090's &lt;strong&gt;24GB of VRAM&lt;/strong&gt;, served it through llama.cpp, and connected OpenCode. After checking chat and tool calls, we gave it four small coding tasks.&lt;/p&gt;

&lt;p&gt;The results make me want to keep using it: &lt;strong&gt;bounded fixes and small features with clear acceptance criteria can produce useful patches.&lt;/strong&gt; Exhausted output budgets, a timeout, and defects missed by tests also showed why independent validation still matters.&lt;/p&gt;

&lt;p&gt;I'll explain the model choice and its connection to OpenCode, then share the measured results and lessons from using it. Full setup instructions follow later; to get straight to them, jump to deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Qwen3.8-27B
&lt;/h2&gt;

&lt;p&gt;Qwen3.8-27B is a prominent local coding candidate, with results that make it worth trying: the &lt;a href="https://huggingface.co/Qwen/Qwen3.8-27B#text-performance" rel="noopener noreferrer"&gt;official model card&lt;/a&gt; reports &lt;strong&gt;61.7 on SWE-bench Pro&lt;/strong&gt; and &lt;strong&gt;73.0 on Terminal Bench 2.1&lt;/strong&gt;. Software fixes and terminal-agent tasks are close to what I want OpenCode to do. These reported results gave me a reason to test it first.&lt;/p&gt;

&lt;p&gt;The hardware fit mattered just as much. &lt;strong&gt;The 27B Q4_K_M weights occupy about 16.8GB&lt;/strong&gt;, fitting entirely into the 3090's 24GB VRAM in our configuration with room for KV cache and runtime buffers. For this old card, the balance of capability, memory use, and speed matters more than chasing a larger parameter count.&lt;/p&gt;

&lt;p&gt;Its agent focus also matches the workflow: reading code, calling tools, and revising changes after test feedback. The &lt;a href="https://github.com/QwenLM/Qwen3.8#introduction" rel="noopener noreferrer"&gt;official introduction&lt;/a&gt; highlights coding, multi-step agent execution, and adjustable thinking, making this workflow a useful place to test those capabilities.&lt;/p&gt;

&lt;p&gt;Those three considerations made it the starting point for this experiment. &lt;strong&gt;What its Q4 version can accomplish on this card still needs validation with our own tasks.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the old card does now
&lt;/h2&gt;

&lt;p&gt;With the model chosen, the next step is connecting it to a workflow that can operate on a repository. OpenCode owns the workspace and tools: reading files, editing code, running shell commands, and executing tests. llama-server handles inference, connected through a local API.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A laptop or phone connects to OpenCode through a LAN or private network.&lt;/li&gt;
&lt;li&gt;OpenCode sends messages and tool definitions to llama-server at &lt;code&gt;127.0.0.1:8080/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;llama-server runs Qwen3.8-27B on the RTX 3090 and returns text or tool calls.&lt;/li&gt;
&lt;li&gt;OpenCode executes the tools in its workspace and sends their results back to the model.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;OpenCode executes the model's &lt;code&gt;tool_calls&lt;/code&gt; and returns tool results for the next turn. The model API itself does not execute terminal commands.&lt;/p&gt;

&lt;p&gt;The host has an &lt;strong&gt;RTX 3090 24GB, Ryzen 7 5800X, and 64GB RAM&lt;/strong&gt;. We used &lt;strong&gt;llama.cpp b11146 / CUDA 12.8&lt;/strong&gt; and &lt;strong&gt;OpenCode 2.0.20&lt;/strong&gt;, placing all model layers on the GPU with one generation slot.&lt;/p&gt;

&lt;p&gt;Weights came from &lt;a href="https://ollama.com/library/qwen3.8:27b-q4_K_M" rel="noopener noreferrer"&gt;Ollama's &lt;code&gt;qwen3.8:27b-q4_K_M&lt;/code&gt; tag&lt;/a&gt; and were loaded directly by llama-server, without installing an Ollama service. Only the text model was loaded; vision input was not tested.&lt;/p&gt;

&lt;p&gt;Two settings matter throughout this post: &lt;strong&gt;131,072 tokens of context capacity and an 8,192-token per-response output allowance&lt;/strong&gt;. Input and output share context, and reasoning consumes the response allowance too. That distinction later explained one of the coding failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Large prompts fit, but fresh processing takes time
&lt;/h2&gt;

&lt;p&gt;With short input, generation reached about &lt;strong&gt;36.4 tokens/s&lt;/strong&gt;. At roughly 120K input tokens, it fell to &lt;strong&gt;20.9 tokens/s&lt;/strong&gt;, and a fresh request took about &lt;strong&gt;three minutes&lt;/strong&gt; to produce its first token.&lt;/p&gt;

&lt;p&gt;The model processes the input before it starts generating. As input grows, that initial wait becomes a substantial part of the experience:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Actual input&lt;/th&gt;
&lt;th&gt;Fresh prompt processing&lt;/th&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;First token, fresh&lt;/th&gt;
&lt;th&gt;First token, cached&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2,073 tokens&lt;/td&gt;
&lt;td&gt;999 tokens/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;36.4 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.75 s&lt;/td&gt;
&lt;td&gt;0.46 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16,378 tokens&lt;/td&gt;
&lt;td&gt;995 tokens/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;33.5 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;17.00 s&lt;/td&gt;
&lt;td&gt;0.47 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;65,537 tokens&lt;/td&gt;
&lt;td&gt;795 tokens/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;25.9 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;83.41 s&lt;/td&gt;
&lt;td&gt;0.51 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120,011 tokens&lt;/td&gt;
&lt;td&gt;656 tokens/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20.9 tokens/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;183.01 s&lt;/td&gt;
&lt;td&gt;0.63 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last column is particularly relevant to coding sessions. Cached continuations reused almost the entire prefix and processed only &lt;strong&gt;27–28 new input tokens&lt;/strong&gt;. Even with roughly 120K context retained, first-token latency fell to &lt;strong&gt;0.63 seconds&lt;/strong&gt;. Sustained generation still had to attend to the long context, so it did not regain short-input speed.&lt;/p&gt;

&lt;p&gt;This suggests a practical approach: keep stable session prefixes and supply source as needed. Repeatedly opening fresh requests with large inputs incurs the processing cost again.&lt;/p&gt;

&lt;p&gt;A separate capacity check processed &lt;strong&gt;119,968 input tokens&lt;/strong&gt;, retrieved three widely separated values, and answered a follow-up. It verified capacity for that synthetic retrieval task. Coding quality at the same input length needs its own evaluation.&lt;/p&gt;

&lt;p&gt;VRAM was also close to the card's limits. Samples during throughput testing showed peak total use of &lt;strong&gt;22,162 MiB&lt;/strong&gt;, minimum free memory of &lt;strong&gt;1,943 MiB&lt;/strong&gt;, and a maximum temperature of &lt;strong&gt;85°C&lt;/strong&gt;, including desktop GPU use. The configuration fit, with limited headroom for another model or GPU-heavy workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timing methodology, sample counts, and source data
&lt;/h3&gt;

&lt;p&gt;Every row used the same &lt;strong&gt;131,072-token server capacity&lt;/strong&gt;, varying only actual input length. Main runs disabled thinking and used synthetic source listings, temperature 0, and seed 1234. Fresh requests generated 512 tokens; continuations generated 256. Generated text was never executed.&lt;/p&gt;

&lt;p&gt;Generation uses server decode timing, excluding initial prompt processing. First-token latency uses the streaming HTTP client, including tokenization and processing overhead. The 2K row is the median of three fresh requests; larger inputs had one fresh request and one continuation each. These measurements do not establish reliability intervals. GPU samples were collected every two seconds.&lt;/p&gt;

&lt;p&gt;A separate short request with medium thinking generated about &lt;strong&gt;35.7 tokens/s&lt;/strong&gt;, including reasoning. Throughput does not directly measure usable code produced per second. The script measures single-request performance, excluding model loading and multi-user throughput.&lt;/p&gt;

&lt;p&gt;Download the &lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/throughput-results.json" rel="noopener noreferrer"&gt;measurement records&lt;/a&gt; and &lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/throughput-summary.json" rel="noopener noreferrer"&gt;summary&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real test: four small coding projects
&lt;/h2&gt;

&lt;p&gt;After measuring speed and capacity, we checked whether the setup could complete code changes. We prepared four small Python repositories: an expiring cache, a CSV ledger, an incremental build planner, and SQLite transfers. Each had a written acceptance contract, a fresh session, and an eight-minute deadline.&lt;/p&gt;

&lt;p&gt;To check more than the agent's own tests, we prepared ten independent test methods per task, kept them outside its workspace, and never fed their results back to the model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Independent checks, before → after&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Agent-added tests&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Expiring LRU cache&lt;/td&gt;
&lt;td&gt;0/10 → &lt;strong&gt;10/10&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;≈3m 07s&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;Completed; strongest result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSV ledger and refunds&lt;/td&gt;
&lt;td&gt;1/10 → &lt;strong&gt;10/10&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;≈5m 44s&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;Completed; review found boundary and error-handling gaps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incremental build planner&lt;/td&gt;
&lt;td&gt;1/10 → &lt;strong&gt;1/10&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;3m 58s&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;No patch; exhausted one response's output allowance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atomic SQLite transfers&lt;/td&gt;
&lt;td&gt;1/10 → &lt;strong&gt;10/10&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;8m deadline&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Candidate passed checks; final test rerun and handoff unfinished&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Three candidates passed all predefined independent checks; two completed editing, testing, and final handoff within the deadline.&lt;/strong&gt; The wallet candidate passed checks, but the agent timed out before its final test rerun and handoff. Both outcomes matter.&lt;/p&gt;

&lt;p&gt;First-attempt candidates passed &lt;strong&gt;31/40 independent test methods&lt;/strong&gt;. The build planner made no edits and already passed one method at baseline. Methods also differ in difficulty, so 31/40 should not be interpreted as a general coding success rate.&lt;/p&gt;

&lt;p&gt;Two findings were more useful than the aggregate score.&lt;/p&gt;

&lt;h3&gt;
  
  
  One failure came from the response budget
&lt;/h3&gt;

&lt;p&gt;The build-planner agent read the repository and never produced a patch. Its final response generated &lt;strong&gt;8,192 tokens&lt;/strong&gt;, all reasoning, and ended with &lt;code&gt;length&lt;/code&gt;. OpenCode exited with status zero without a final answer.&lt;/p&gt;

&lt;p&gt;That request had roughly &lt;strong&gt;4,985 input tokens&lt;/strong&gt;, far below the 128K limit. Thinking and the response allowance needed attention; increasing context capacity would not resolve an exhausted output budget. The &lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/buildplan-termination.json" rel="noopener noreferrer"&gt;termination record&lt;/a&gt; preserves the evidence.&lt;/p&gt;

&lt;p&gt;A separate diagnostic restarted from the original repository with thinking disabled, keeping the prompt, allowance, and deadline unchanged. It completed in &lt;strong&gt;5m 40s&lt;/strong&gt; and passed &lt;strong&gt;9/10 independent checks&lt;/strong&gt;. The remaining failure was &lt;code&gt;load_manifest()&lt;/code&gt; returning tuples where the original API returned lists; the agent's own test accepted that change too.&lt;/p&gt;

&lt;p&gt;The diagnostic is reported separately and does not replace the first attempt. One additional sample at temperature 1 cannot establish that disabling thinking is generally better, but it identifies a setting worth testing further.&lt;/p&gt;

&lt;h3&gt;
  
  
  Many new tests still missed defects
&lt;/h3&gt;

&lt;p&gt;The ledger added &lt;strong&gt;40 tests&lt;/strong&gt;, passed the predefined independent checks, and had a reasonably clear structure. Further review still found that Python's default 28-digit Decimal precision rounded a valid large amount when adding &lt;code&gt;0.01&lt;/code&gt;, violating the no-rounding contract. An I/O error during file iteration could also escape as a traceback.&lt;/p&gt;

&lt;p&gt;The wallet exposed a different problem: its own concurrency tests submitted one future and immediately waited before submitting the next. Execution was sequential. Our separate eight-caller concurrent test passed, but the agent's tests had not demonstrated overlapping requests.&lt;/p&gt;

&lt;p&gt;Review also found that crediting two cents to a destination near SQLite's signed-integer limit converted the balance to &lt;code&gt;REAL&lt;/code&gt; while recording success. That extreme numerical boundary should be rejected with a rollback.&lt;/p&gt;

&lt;p&gt;These probes ran after grading and were not added retroactively to the forty checks. They made me focus on what tests actually cover rather than how many were added. Review found no additional acceptance-contract defect in the cache, though expiration cleanup scans the whole cache and our assessment remains limited to the small task tested.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coding protocol and additional test results
&lt;/h3&gt;

&lt;p&gt;Each repository had three fixed public tests. Independent checks were prepared before the corresponding attempt; the evaluator did not repair candidate production code. The agent left protected acceptance contracts, public tests, and configurations unchanged.&lt;/p&gt;

&lt;p&gt;First attempts ran serially with medium thinking and an 8,192-token per-response output allowance. Repository file tools and a narrow allowlist of local test commands were available; network tools, subagents, and cloud fallback were disabled. Infrastructure-only aborted launches were excluded from scores. Cache and ledger durations were recovered from event records and are approximate.&lt;/p&gt;

&lt;p&gt;The evaluator subsequently ran public and agent-added tests: cache &lt;strong&gt;26/26&lt;/strong&gt;, ledger &lt;strong&gt;43/43&lt;/strong&gt;, and wallet &lt;strong&gt;14/14&lt;/strong&gt; passed. Wallet's green rerun occurred after the agent timed out. The original build planner still failed its three public tests. The thinking-disabled diagnostic passed &lt;strong&gt;47/47&lt;/strong&gt; public and agent-added tests, while retaining the independent-check failure described above.&lt;/p&gt;

&lt;p&gt;These were four deliberately bounded Python repositories. We did not measure reliability across seeds, large production repositories, Q4 versus higher-precision weights, or coding quality at 64K / 128K. No controlled model comparison was performed. The &lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/coding-summary.json" rel="noopener noreferrer"&gt;coding summary&lt;/a&gt; retains scores and review probes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I would keep using it
&lt;/h2&gt;

&lt;p&gt;I would give it bounded fixes and small features, state the acceptance criteria first, and inspect the actual diff, independent tests, and final handoff. It demonstrated the ability to edit across files, add regression coverage, and correct some mistakes found during its own checks.&lt;/p&gt;

&lt;p&gt;For longer tasks, I would watch whether reasoning crowds out output and whether the workflow times out. Numerical boundaries, concurrency, and error handling still deserve review. The next experiment should retain the same tasks and checks, add repeated runs, and compare thinking settings and response allowances.&lt;/p&gt;

&lt;p&gt;From Ethereum mining to local coding, this 3090 has found another useful job. If similar hardware is already on hand, a small task with clear acceptance criteria is a practical way to see what it can contribute to your workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the local coding setup
&lt;/h2&gt;

&lt;p&gt;Here is the deployment path we used. The &lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/reproduction-kit.zip" rel="noopener noreferrer"&gt;download kit&lt;/a&gt; contains the full configuration. Essential launch and verification steps are below, with additional settings under the headings below.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the model service
&lt;/h3&gt;

&lt;p&gt;On Ubuntu, prepare an NVIDIA driver, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;unzip&lt;/code&gt;, Python 3, and a working user session, then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/local-qwen &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/local-qwen
curl &lt;span class="nt"&gt;-fL&lt;/span&gt; https://zjshen14.github.io/experiments/qwen3.8-27b-3090/reproduction-kit.zip &lt;span class="nt"&gt;-o&lt;/span&gt; kit.zip
unzip kit.zip
bash setup.sh
./run-server.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;setup.sh&lt;/code&gt; downloads and verifies the GGUF and &lt;a href="https://github.com/ggml-org/llama.cpp/releases/tag/b11146" rel="noopener noreferrer"&gt;llama.cpp b11146 CUDA 12.8 release archives&lt;/a&gt;. Downloads total roughly &lt;strong&gt;17.6GB&lt;/strong&gt;, plus extracted runtime space; weights are not inside the ZIP. Prebuilt binaries avoid compiling a CUDA development toolchain. The script installs no system service and makes no global OpenCode configuration changes.&lt;/p&gt;

&lt;p&gt;Once the service is ready, verify the API from another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-f&lt;/span&gt; http://127.0.0.1:8080/health
curl &lt;span class="nt"&gt;-f&lt;/span&gt; http://127.0.0.1:8080/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"qwen3.8-27b","messages":[{"role":"user","content":"Reply with OK."}],"max_tokens":64,"chat_template_kwargs":{"enable_thinking":false}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server listens on &lt;code&gt;127.0.0.1:8080&lt;/code&gt;, and local requests need no real API key. A client requiring a value can use a nonsecret placeholder such as &lt;code&gt;local&lt;/code&gt;, but that is not access control. Keep this unauthenticated endpoint on loopback.&lt;/p&gt;

&lt;h4&gt;
  
  
  Full experiment settings, launch options, and background service
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Tested configuration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;Ubuntu, Linux x86-64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;NVIDIA RTX 3090, 24GB VRAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU / RAM&lt;/td&gt;
&lt;td&gt;Ryzen 7 5800X / 64GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weights&lt;/td&gt;
&lt;td&gt;Qwen3.8-27B, Q4_K_M, 16,810,714,464 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serving engine&lt;/td&gt;
&lt;td&gt;llama.cpp &lt;strong&gt;b11146&lt;/strong&gt;, prebuilt CUDA 12.8 runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;OpenCode &lt;strong&gt;2.0.20&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context / per-response output limit&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;131,072 / 8,192 tokens&lt;/strong&gt;; input and output share context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KV cache&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;q8_0&lt;/strong&gt; for both K and V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU / concurrency&lt;/td&gt;
&lt;td&gt;All model layers on GPU; &lt;strong&gt;one generation slot&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other settings&lt;/td&gt;
&lt;td&gt;Flash attention; batch 512 / microbatch 256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thinking for coding tasks&lt;/td&gt;
&lt;td&gt;Enabled, &lt;strong&gt;medium&lt;/strong&gt; effort&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Q4_K_M describes weight quantization; q8_0 describes KV-cache quantization. Beyond the roughly 16.8GB weight file, VRAM is needed for caches, compute buffers, and desktop applications. The kit's &lt;code&gt;run-server.sh&lt;/code&gt; supplies the full command; these are its core options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llama-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--model&lt;/span&gt; models/Qwen3.8-27B-Q4_K_M.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--alias&lt;/span&gt; qwen3.8-27b &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--host&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--port&lt;/span&gt; 8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ctx-size&lt;/span&gt; 131072 &lt;span class="nt"&gt;--parallel&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--n-gpu-layers&lt;/span&gt; all &lt;span class="nt"&gt;--fit&lt;/span&gt; off &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash-attn&lt;/span&gt; on &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cache-type-k&lt;/span&gt; q8_0 &lt;span class="nt"&gt;--cache-type-v&lt;/span&gt; q8_0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--batch-size&lt;/span&gt; 512 &lt;span class="nt"&gt;--ubatch-size&lt;/span&gt; 256 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--jinja&lt;/span&gt; &lt;span class="nt"&gt;--reasoning-format&lt;/span&gt; deepseek &lt;span class="nt"&gt;--reasoning-preserve&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-context-shift&lt;/span&gt; &lt;span class="nt"&gt;--no-mmproj&lt;/span&gt; &lt;span class="nt"&gt;--metrics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packaged script sets library paths and invokes the actual binary. If other applications need VRAM, stop the server and use &lt;code&gt;MODEL_CONTEXT=65536 ./run-server.sh&lt;/code&gt; for 64K capacity. All measurements in this post used 128K capacity.&lt;/p&gt;

&lt;p&gt;To survive closing the launching terminal, stop the foreground server first and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; logs
systemd-run &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--collect&lt;/span&gt; &lt;span class="nt"&gt;--unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qwen-local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"WorkingDirectory=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"StandardOutput=append:&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/logs/server.log"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"StandardError=append:&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/logs/server.log"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/run-server.sh"&lt;/span&gt;
&lt;span class="c"&gt;# Stop: systemctl --user stop qwen-local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a transient user service that must be started again after reboot. Survival after logging out of the entire user session depends on user-manager configuration and was not tested here.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configure OpenCode
&lt;/h3&gt;

&lt;p&gt;Place the kit's &lt;code&gt;opencode.json&lt;/code&gt; in your project root, or merge its provider into &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;. This configuration was verified with &lt;strong&gt;OpenCode 2.0.20&lt;/strong&gt;: model ID &lt;code&gt;local-qwen/qwen3.8-27b&lt;/code&gt;, API base URL &lt;code&gt;http://127.0.0.1:8080/v1&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Full OpenCode provider configuration
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://opencode.ai/config.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local-qwen/qwen3.8-27b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"providers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"local-qwen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Local Qwen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@opencode/ai/providers/openai-compatible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"baseURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8080/v1"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"models"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"qwen3.8-27b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Qwen3.8 27B — 128K"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;131072&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"compatibility"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reasoningField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reasoning_content"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"chat_template_kwargs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"enable_thinking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"reasoning_effort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tested configuration uses &lt;code&gt;providers&lt;/code&gt; / &lt;code&gt;package&lt;/code&gt; / &lt;code&gt;settings&lt;/code&gt;; the &lt;a href="https://opencode.ai/docs/providers/#custom-provider" rel="noopener noreferrer"&gt;online provider guide&lt;/a&gt; has shown &lt;code&gt;provider&lt;/code&gt; / &lt;code&gt;npm&lt;/code&gt; / &lt;code&gt;options&lt;/code&gt;. If your installed version differs, follow its actual schema and avoid combining structures.&lt;/p&gt;

&lt;p&gt;Start the model service, then restart OpenCode. Our installation also reloaded the configuration with &lt;code&gt;opencode reload&lt;/code&gt;. Existing sessions may retain their previous model; choose &lt;code&gt;local-qwen/qwen3.8-27b&lt;/code&gt; through &lt;code&gt;/models&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the previous post's Web workflow, launch from your project and choose a different port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENCODE_SERVER_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'replace-with-a-strong-password'&lt;/span&gt;
opencode web &lt;span class="nt"&gt;--hostname&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--port&lt;/span&gt; 4096
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;8080 serves the model; 4096 serves OpenCode.&lt;/strong&gt; On the same machine, the provider uses loopback while remote browsers connect to OpenCode. If OpenCode runs on another host or inside a container, &lt;code&gt;127.0.0.1&lt;/code&gt; points to that environment and needs replacing with a private connection to the GPU host. See the &lt;a href="https://zjshen14.github.io/en/blog/setup-opencode-remote-web-ide/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt; for network binding and authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Verify tools, then try a task
&lt;/h3&gt;

&lt;p&gt;Check an ordinary reply first, then create a harmless fixture and ask the agent to &lt;strong&gt;use its &lt;code&gt;read&lt;/code&gt; tool&lt;/strong&gt; and return the contents. Both checks passed here, along with synthetic tool-call round trips and streamed tool calls at the server.&lt;/p&gt;

&lt;p&gt;To repeat throughput measurements, keep the server idle and choose a new output directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 benchmark_throughput.py &lt;span class="nt"&gt;--report-dir&lt;/span&gt; reports/throughput-new-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then try a bounded task of your own and validate it with independent tests and review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration, data, and reproduction materials
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/reproduction-kit.zip" rel="noopener noreferrer"&gt;Configuration and measurement ZIP&lt;/a&gt;: verified downloads, full launcher, OpenCode configuration, throughput script, and data. It includes neither weights nor the complete coding-task fixtures.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/README.txt" rel="noopener noreferrer"&gt;Experiment notes&lt;/a&gt;, &lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/throughput-results.json" rel="noopener noreferrer"&gt;throughput measurements&lt;/a&gt;, and &lt;a href="https://zjshen14.github.io/experiments/qwen3.8-27b-3090/coding-summary.json" rel="noopener noreferrer"&gt;coding summary&lt;/a&gt;. Experiment dates, absolute timestamps, timezones, personal paths, and session identifiers were removed from attachments; measurements, scores, and review probes are retained.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://huggingface.co/Qwen/Qwen3.8-27B" rel="noopener noreferrer"&gt;Official Qwen model card&lt;/a&gt;, &lt;a href="https://github.com/ggml-org/llama.cpp/blob/b11146/tools/server/README.md" rel="noopener noreferrer"&gt;pinned llama-server documentation&lt;/a&gt;, and &lt;a href="https://opencode.ai/docs/providers/" rel="noopener noreferrer"&gt;OpenCode provider documentation&lt;/a&gt;. Upstream sources provide selection context, reported benchmarks, and deployment guidance; this host's performance and four-task coding results come from the attached experiment records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Measurements and review findings are supported by the linked experiment records.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenCode Field Notes
&lt;/h2&gt;

&lt;p&gt;Follow the setup through to measured results:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/run-opencode-from-your-phone-or-laptop-remote-hosting-authentication-and-reverse-proxy-16bd"&gt;Remote OpenCode: hosting, authentication and reverse proxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/qwen38-27b-on-rtx-3090-with-opencode-and-measured-coding-results-4m4h"&gt;Local Qwen3.8-27B on an RTX 3090: deployment and coding results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zjshen14/mtp-on-an-rtx-3090-faster-tokens-but-what-about-coding-quality-28j"&gt;MTP: generation speed versus coding quality&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
