<?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: Haoxiang Li</title>
    <description>The latest articles on DEV Community by Haoxiang Li (@haoxiang_li_a709204042e6b).</description>
    <link>https://dev.to/haoxiang_li_a709204042e6b</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%2F4076985%2Fec963fde-11db-4c08-9c05-4caf0f322a10.png</url>
      <title>DEV Community: Haoxiang Li</title>
      <link>https://dev.to/haoxiang_li_a709204042e6b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/haoxiang_li_a709204042e6b"/>
    <language>en</language>
    <item>
      <title>Are You Benchmarking the Model—or the Harness?</title>
      <dc:creator>Haoxiang Li</dc:creator>
      <pubDate>Fri, 14 Aug 2026 05:26:01 +0000</pubDate>
      <link>https://dev.to/haoxiang_li_a709204042e6b/are-you-benchmarking-the-model-or-the-harness-2bke</link>
      <guid>https://dev.to/haoxiang_li_a709204042e6b/are-you-benchmarking-the-model-or-the-harness-2bke</guid>
      <description>&lt;h3&gt;
  
  
  I nearly turned four software bugs into four model personalities
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This article was edited with AI assistance. The cases and data come from real batch runs in the &lt;em&gt;Kai!&lt;/em&gt; AI Arena. Every claim is limited to the game rules, model versions, and experimental setup used at the time. This is not a general model ranking.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;If I had published one day earlier, DeepSeek V4-Pro might have acquired a personality trait it never had: &lt;strong&gt;bidding without looking at its dice.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, a quick explanation of the table. In Liar's Dice, each player has a set of hidden dice. Players take turns claiming that the whole table contains at least &lt;em&gt;N&lt;/em&gt; dice of a particular face. The next player must either raise the bid or challenge it. Everyone then reveals their dice: if the bid holds, the bidder wins; if it does not, the challenger wins. In &lt;em&gt;Kai!&lt;/em&gt;, looking at your own dice is an explicit action, so a player may bid before looking. I call that a blind bid.&lt;/p&gt;

&lt;p&gt;The evidence looked solid. In the first batch of AI matches, DeepSeek V4-Pro made nearly 40% of its bids before looking at its dice.&lt;/p&gt;

&lt;p&gt;A story almost wrote itself. Perhaps it trusted intuition. Perhaps it was unusually willing to gamble or liked to seize the initiative. Another model usually looked first and calculated before bidding. Put the win rate, dialogue, and action logs side by side, and two distinct “personalities” seemed to emerge.&lt;/p&gt;

&lt;p&gt;Then I inspected the context.&lt;/p&gt;

&lt;p&gt;Even when the model had not used the probability tool, the system was still inserting a rough probability estimate into its prompt. The candidate actions were also sorted by probability. The model appeared to be bidding under the cup, but the harness was feeding it a strong hint from offstage.&lt;/p&gt;

&lt;p&gt;After I fixed the leak, V4-Pro's blind-bid rate fell from roughly &lt;strong&gt;40% to 6%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What looked like a model insight had been an explanation of a software bug. A compelling conclusion vanished from the data.&lt;/p&gt;

&lt;p&gt;Then I found three more bugs of the same kind. Across the first roughly 60 matches, more than half of the most visible differences between models shrank after the fixes.&lt;/p&gt;

&lt;p&gt;That changed how I think about model evaluation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An arena directly measures a system made of a model and a harness. Before attributing the result to the model, you must show that the measurement system did not quietly think for it, hide part of its input, or rewrite its failures.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By &lt;em&gt;harness&lt;/em&gt;, I mean everything wrapped around the model: prompts, context assembly, tools, action spaces, token budgets, provider routing, output parsing, retries, and fallbacks. The model produces an answer. The harness decides what it sees, what it is allowed to do, and which part of that answer survives into the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  The “personality” that fell from 40% to 6%
&lt;/h2&gt;

&lt;p&gt;At first I thought I had found one implementation mistake. I kept looking and found a second, a third, and a fourth. The troubling part was that none of them stopped the matches. Every game still produced actions, dialogue, and a final score. The dataset looked complete.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Harness problem&lt;/th&gt;
&lt;th&gt;The apparent model trait&lt;/th&gt;
&lt;th&gt;What changed after the fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;max_tokens=400&lt;/code&gt; truncated long outputs&lt;/td&gt;
&lt;td&gt;Poor formatting; often replaced by a fallback bot&lt;/td&gt;
&lt;td&gt;Format failures and fallbacks dropped sharply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rough probabilities were included without a tool call, and actions were sorted by probability&lt;/td&gt;
&lt;td&gt;Liked to bid without looking&lt;/td&gt;
&lt;td&gt;Blind-bid rate fell from about 40% to 6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opponent dialogue was not forwarded&lt;/td&gt;
&lt;td&gt;Weak player with little social reasoning&lt;/td&gt;
&lt;td&gt;Win rate returned to roughly 40–60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subjective judgments were stored only up to the first 100 characters&lt;/td&gt;
&lt;td&gt;Rambling reasoning and incoherent records&lt;/td&gt;
&lt;td&gt;The original chain of reasoning reappeared when full text was saved&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The four bugs interfered at four different points.&lt;/p&gt;

&lt;p&gt;The probability leak changed the input. Missing dialogue removed information the model should have had. The token cap truncated its output. The database then changed the evidence I used to interpret that output. The harness shaped both the move and my explanation of the move.&lt;/p&gt;

&lt;p&gt;These distortions are especially dangerous because each one generates a plausible story. A truncated answer becomes weak instruction following. Missing dialogue becomes poor social reasoning. An action list that has already ranked the safe moves becomes decision-making ability. As long as the system still produces a score, it is easy to skip the measurement process and attach a label to the model.&lt;/p&gt;

&lt;p&gt;The uncomfortable conclusion is that the evaluation framework is also playing.&lt;/p&gt;

&lt;p&gt;So when I see a model leaderboard now, I do not begin with who won. I begin with a different question: &lt;strong&gt;What task did each model actually receive?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How can the same prompt become two different tasks?
&lt;/h2&gt;

&lt;p&gt;Model arenas often point to a shared prompt as evidence of fairness. It is necessary, but it controls only one part of the experiment.&lt;/p&gt;

&lt;p&gt;In the early version of &lt;em&gt;Kai!&lt;/em&gt;, both seats received the exact same system prompt. Any of the following could still change the task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the order of candidate actions;&lt;/li&gt;
&lt;li&gt;whether the system supplied information the model had not requested;&lt;/li&gt;
&lt;li&gt;whether the opponent's dialogue reached the context intact;&lt;/li&gt;
&lt;li&gt;whether reasoning tokens and the final answer shared one budget;&lt;/li&gt;
&lt;li&gt;whether a truncated answer counted as a failure, triggered a retry, or handed control to a bot;&lt;/li&gt;
&lt;li&gt;which provider or quantized backend a model ID actually reached;&lt;/li&gt;
&lt;li&gt;whether the parser saved the raw answer or only an excerpt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A prompt is a string sent to a model. The task also includes its information boundary, tool permissions, compute budget, and failure policy.&lt;/p&gt;

&lt;p&gt;This is why apparently uniform settings can create systematic bias. A concise model may escape truncation while a long-reasoning model spends its entire budget before producing a final answer. A model sensitive to option order will react to the placement of candidate actions. A model that relies heavily on conversational cues will lose more when dialogue is omitted.&lt;/p&gt;

&lt;p&gt;Does a more uniform setup always make an experiment fairer? That depends on what you are trying to measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  “Fair” is not a single configuration
&lt;/h2&gt;

&lt;p&gt;Many arguments about benchmark fairness are really arguments about different questions.&lt;/p&gt;

&lt;p&gt;If I already have a fixed product interface and want to know which model can replace another with the least work, I should hold the prompt, tools, budget, and parser constant. That measures compatibility with a shared product contract.&lt;/p&gt;

&lt;p&gt;If I want to measure the ceiling of each model, I should optimize the prompt, tools, and reasoning settings separately. The result now includes adaptation work, so it no longer represents models running under identical conditions.&lt;/p&gt;

&lt;p&gt;If I care about return on resources, I should fix cost, latency, or token use. That experiment measures output under the same constraint, not absolute capability.&lt;/p&gt;

&lt;p&gt;All three protocols are valid:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;What is controlled&lt;/th&gt;
&lt;th&gt;What it can answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Interface fairness&lt;/td&gt;
&lt;td&gt;Same prompt, tools, budget, and action space&lt;/td&gt;
&lt;td&gt;Which model fits the same product contract best?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capability ceiling&lt;/td&gt;
&lt;td&gt;Model-specific prompt, tool, and reasoning optimization&lt;/td&gt;
&lt;td&gt;What can each model do after adaptation?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource fairness&lt;/td&gt;
&lt;td&gt;Same cost, latency, or token budget&lt;/td&gt;
&lt;td&gt;Which model produces more under the same constraint?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Problems begin when the claim outruns the protocol. A shared-prompt benchmark can tell us which model works better with that prompt. It cannot directly establish each model's capability ceiling. Individually tuned results may show a ceiling, but the evaluator's tuning skill has entered the experiment.&lt;/p&gt;

&lt;p&gt;Every benchmark report should state its fairness constraint first, then limit its claims accordingly.&lt;/p&gt;

&lt;p&gt;The current “bare table” track in &lt;em&gt;Kai!&lt;/em&gt; uses interface fairness. It answers a product question: if I swap the model behind the same game interface, what kind of opponent does the player get?&lt;/p&gt;

&lt;p&gt;Once that protocol is chosen, the difficult work begins: keeping the harness from appearing in the score as model ability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the harness out of the score
&lt;/h2&gt;

&lt;p&gt;I turned that goal into a set of concrete constraints.&lt;/p&gt;

&lt;p&gt;First, every model acts through the same player interface. It can see only its own dice and public events, and every action is checked by the same deterministic engine. The information boundary lives in the schema rather than in a promise written into the prompt.&lt;/p&gt;

&lt;p&gt;Second, the same dice seed is played twice with the seats swapped. This reduces the effect of first move, seat position, and random rolls. A single win never becomes a model-level conclusion.&lt;/p&gt;

&lt;p&gt;Third, I freeze more than the prompt. Candidate order, context serialization, tool responses, sampling parameters, parsers, and code versions are all experimental conditions. Each batch stores a prompt hash and Git commit. If one of them changes, I start a new batch.&lt;/p&gt;

&lt;p&gt;Fourth, I record what the provider actually did. Sending &lt;code&gt;max_tokens&lt;/code&gt; or a reasoning flag does not prove that the server honored it. Each call records completion tokens, reasoning tokens, finish reason, latency, cost, and actual route. Empty responses and timeouts remain in the dataset.&lt;/p&gt;

&lt;p&gt;Finally, retries, repairs, and bot takeovers appear beside the score. A production system needs fallbacks, but an evaluation cannot let a fallback quietly impersonate model behavior. The leaderboard reports formatting failures, refusals, and fallback rates, with a separate view for zero-fallback samples.&lt;/p&gt;

&lt;p&gt;These rules sound like engineering hygiene, but they decide whether the conclusion holds. A reliable product harness hides failures so the player can continue. A credible evaluation harness exposes them. The two can share code; they cannot share an unmarked data definition.&lt;/p&gt;

&lt;p&gt;Even after all this, a leaderboard has boundaries. Controlling variables can make an answer more reliable. It cannot make the experiment answer a question it never asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  How far can one leaderboard reach?
&lt;/h2&gt;

&lt;p&gt;I divide the &lt;em&gt;Kai!&lt;/em&gt; metrics into three layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compliance:&lt;/strong&gt; illegal actions, format failures, refusals, and fallback takeovers. Can the model fulfill the current interface contract?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playing strength:&lt;/strong&gt; win rate, successful challenges, and net chips. How good are its decisions under these rules and information conditions?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavioral texture:&lt;/strong&gt; bluff rate, blind bids, raise depth, dialogue, and response rhythm. Does swapping the model create a perceptibly different opponent?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each layer is useful, but its interpretation is limited. A low format-failure rate may come from better instruction following or a more forgiving parser. A high win rate shows strength at this game; it does not automatically become a general reasoning score. Behavioral differences matter to the product experience, but with thin samples they are observations, not permanent personalities.&lt;/p&gt;

&lt;p&gt;The current clean set contains seven model IDs, 11 pairings, 22 seat arms, and 44 matches. That is enough to expose obvious harness failures. It is nowhere near enough for a general model ranking. The memory track also introduces cross-match path dependence, so it is stored separately from the memory-free bare-table track.&lt;/p&gt;

&lt;p&gt;There is only one narrow claim I am willing to make from this dataset:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Under the recorded versions, routes, budgets, rules, and samples, different models behaved differently as opponents in &lt;em&gt;Kai!&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One step beyond that requires another experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every model claim needs a counterfactual
&lt;/h2&gt;

&lt;p&gt;The original story—“DeepSeek likes to bid without looking”—was easy to write. It had numbers, contrast, and the appeal of model personality. It was also false.&lt;/p&gt;

&lt;p&gt;The rate moved from 40% to 6% without a model upgrade or prompt optimization. I merely removed information the harness had been leaking.&lt;/p&gt;

&lt;p&gt;This is the part of model evaluation that leaderboards tend to hide. The model stands under the spotlight while the measurement tool disappears into the dark. Yet the tool still organizes the input, allocates the budget, handles errors, and decides which answer becomes evidence.&lt;/p&gt;

&lt;p&gt;A perfectly neutral harness may be impossible. The design of the action space, the budget, and the metric set all involve choices. We can at least make those choices visible: pin versions, retain raw calls, publish failure rates, separate fallback samples, and state where the conclusion stops.&lt;/p&gt;

&lt;p&gt;Now, whenever I am about to write “this model is bolder” or “that model cannot read people,” I ask one counterfactual question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If I changed only the harness, would the difference survive?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without that check, a model trait is only an observation waiting to be debugged.&lt;/p&gt;

&lt;p&gt;So the next time a model looks brave, cautious, clever, or foolish, resist the personality analysis for a moment.&lt;/p&gt;

&lt;p&gt;Check the table first.``&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Gave DeepSeek a Token Limit. It Ignored Me.</title>
      <dc:creator>Haoxiang Li</dc:creator>
      <pubDate>Fri, 14 Aug 2026 04:03:20 +0000</pubDate>
      <link>https://dev.to/haoxiang_li_a709204042e6b/i-gave-deepseek-a-token-limit-it-ignored-me-1ijd</link>
      <guid>https://dev.to/haoxiang_li_a709204042e6b/i-gave-deepseek-a-token-limit-it-ignored-me-1ijd</guid>
      <description>&lt;h3&gt;
  
  
  A hands-on test of V4-Pro's default reasoning mode
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;This article was generated with AI assistance&lt;/strong&gt; (Claude). Every number comes from real calls the author made to DeepSeek's official API on the day of testing, and sample sizes are included with each conclusion. The author is an independent developer with no affiliation to DeepSeek or OpenRouter.&lt;/p&gt;

&lt;p&gt;Tested on 2026-08-14 using &lt;code&gt;deepseek-v4-pro&lt;/code&gt; on DeepSeek's official endpoint (the production V4-Pro-0813 release).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning is on by default.&lt;/strong&gt; With no reasoning-related parameter, a single Liar's Dice decision consumed 3,072 output tokens—&lt;strong&gt;all reasoning, with no visible answer at all&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OpenAI-style fix did nothing.&lt;/strong&gt; I sent &lt;code&gt;max_completion_tokens: 3072&lt;/code&gt;. The API returned HTTP 200, then generated &lt;strong&gt;15,809 tokens over 222 seconds&lt;/strong&gt;. It did not reject the parameter; it simply behaved as if it had never seen it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Even a made-up parameter was accepted.&lt;/strong&gt; &lt;code&gt;totally_bogus_param: true&lt;/code&gt; also returned HTTP 200. &lt;strong&gt;Unknown parameters are silently swallowed&lt;/strong&gt;, so you cannot tell whether a limit took effect until the bill arrives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The same task took 2 seconds and 95 tokens with reasoning disabled.&lt;/strong&gt; With reasoning enabled, it took 65 seconds and 4,500 tokens, while the visible answers were almost the same length.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost per usable answer:&lt;/strong&gt; ¥0.0105 with reasoning off; &lt;strong&gt;¥0.175—16.7× more—with reasoning on and an 8,192-token budget&lt;/strong&gt;. With a 3,072-token budget, the effective cost was infinite: zero usable answers out of three, but every call was still billed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did quality improve?&lt;/strong&gt; Against the older quantized build on OpenRouter, I found &lt;strong&gt;no detectable improvement&lt;/strong&gt; on a hard metric with almost no room for interpretation (1/71 vs. 2/22; not statistically significant, with a small sample).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning can be disabled.&lt;/strong&gt; &lt;code&gt;reasoning_effort: "none"&lt;/code&gt; and &lt;code&gt;thinking: {type: "disabled"}&lt;/code&gt; worked. &lt;code&gt;enable_thinking: false&lt;/code&gt; did not—and the API never told me.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I Tested This
&lt;/h2&gt;

&lt;p&gt;I am building a single-player Liar's Dice game called &lt;strong&gt;Kai!&lt;/strong&gt; The opponent is not a scripted bot; it is powered by a large language model. It uses the same game engine as the player, sees the same information, reveals its thoughts before a challenge, and remembers how you played across rounds. Swap the model, and you effectively get a different opponent.&lt;/p&gt;

&lt;p&gt;That means I need to know how much each decision costs and how long it takes. &lt;strong&gt;This was not benchmark curiosity. It was a practical problem forced on me by latency and billing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then I hit a wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Wall: I Paid for Three Empty Strings
&lt;/h2&gt;

&lt;p&gt;The prompt for one Liar's Dice decision is about 3,200 Chinese characters. With &lt;code&gt;max_tokens: 3072&lt;/code&gt;, three consecutive calls failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;finish=length  completion=3071  reasoning=3071  visible=0 chars  50s
finish=length  completion=3072  reasoning=3072  visible=0 chars  47s
finish=length  completion=3072  reasoning=3072  visible=0 chars  42s
=&amp;gt; usable: 0/3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Every one of those 3,000-plus output tokens went into reasoning. Not a single character of the visible answer made it out.&lt;/strong&gt; Billing is based on generated tokens, so I paid in full for all three calls and received three empty strings.&lt;/p&gt;

&lt;p&gt;Worse, the application interpreted the result like this: no parseable action → fallback marks the move as noncompliant → the dashboard displays &lt;strong&gt;“this model disobeyed instructions in 94.4% of hands.”&lt;/strong&gt; That figure came from my project's August 13 batch, not this isolated test. &lt;strong&gt;I nearly recorded a real token-budget failure as a model-behavior failure.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So How Much Budget Does It Need?
&lt;/h2&gt;

&lt;p&gt;I ran the same task three times under four configurations:&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;Usable&lt;/th&gt;
&lt;th&gt;Completion tokens&lt;/th&gt;
&lt;th&gt;Reasoning&lt;/th&gt;
&lt;th&gt;Visible answer&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning on · &lt;code&gt;max_tokens&lt;/code&gt;=3072&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3071–3072&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 chars&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;42–50s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning on · 8192&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1/3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7837–8192&lt;/td&gt;
&lt;td&gt;~95%&lt;/td&gt;
&lt;td&gt;0 / 118 chars&lt;/td&gt;
&lt;td&gt;121–126s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning on · 32768&lt;/td&gt;
&lt;td&gt;3/3&lt;/td&gt;
&lt;td&gt;3220–5785&lt;/td&gt;
&lt;td&gt;~97%&lt;/td&gt;
&lt;td&gt;117–140 chars&lt;/td&gt;
&lt;td&gt;49–82s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Reasoning off&lt;/strong&gt; · 3072&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3/3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;84–107&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;123–156 chars&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2–4s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three things were true at the same time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The tighter the budget, the more likely the model was to consume all of it.&lt;/strong&gt; A 3,072-token budget ended at 3,072. An 8,192-token budget usually ended near 8,192. Only when I raised the limit to 32,768—far beyond the 3,200–5,800 tokens it normally needed—did it stop naturally and reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Reasoning usage was hard to budget.&lt;/strong&gt; On the same task, reasoning ranged from &lt;strong&gt;3,137 to 5,691 tokens&lt;/strong&gt;. With the intended limit ignored, it climbed to &lt;strong&gt;15,774&lt;/strong&gt;. This is not merely “thinking more.” It makes per-call budgeting unreliable: when usage can nearly double—or go much higher—which number are you supposed to provision for?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The longer reasoning did not produce a longer answer.&lt;/strong&gt; With reasoning off, the visible answers were 123–156 Chinese characters. With reasoning on, they were 117–140. &lt;strong&gt;Forty-seven times the tokens and twenty-two times the latency did not produce a longer or more complete answer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For my application, there was an even more fundamental problem: &lt;strong&gt;a round has a 30–60 second pacing budget&lt;/strong&gt;, while one reasoning-enabled decision took 49–222 seconds. At that point, this was no longer just an issue of price. The product experience stopped working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the Protocol at Fault, or Is DeepSeek?
&lt;/h2&gt;

&lt;p&gt;Before publishing, I tested the parameter behavior separately. The answer is: &lt;strong&gt;both contributed, but not equally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the protocol side, the OpenAI-compatible &lt;code&gt;max_tokens&lt;/code&gt; field places reasoning and visible output in the same budget. If reasoning consumes the allowance, the answer has nothing left. OpenAI's own reasoning models have had the same trap, which is why &lt;code&gt;max_completion_tokens&lt;/code&gt; was introduced and why the documentation warns that a budget that is too small can produce an empty response. &lt;strong&gt;That part is a protocol-design problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then I tested the relevant parameters against DeepSeek's endpoint:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter sent&lt;/th&gt;
&lt;th&gt;Intended behavior&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Actual behavior&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Reasoning on by default&lt;/strong&gt;; budget exhausted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max_completion_tokens: 3072&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI-style completion limit&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Silently ignored:&lt;/strong&gt; 15,809 tokens, 222 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reasoning: {max_tokens: 1024}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenRouter-style reasoning limit&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Silently ignored&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reasoning_effort: "none"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable reasoning&lt;/td&gt;
&lt;td&gt;✅ Worked (18 tokens, 1 second)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;thinking: {type: "disabled"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable reasoning&lt;/td&gt;
&lt;td&gt;✅ Worked (18 tokens, 1 second)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enable_thinking: false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable reasoning&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Silently ignored&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chat_template_kwargs: {...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable reasoning&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Silently ignored&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;totally_bogus_param: true&lt;/code&gt; (invented)&lt;/td&gt;
&lt;td&gt;Reject the request&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;HTTP 200; silently swallowed&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The final row is the root of the problem. &lt;strong&gt;The API silently accepts unknown parameters.&lt;/strong&gt; Every ignored setting therefore looks exactly like a successful setting. You send a limit, receive a 200, and assume the call is capped—&lt;strong&gt;until the bill tells you otherwise.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;max_completion_tokens&lt;/code&gt; is the clearest example. It exists specifically for reasoning models and for exactly the budgeting failure described here. DeepSeek accepted it, returned HTTP 200, and then let the model generate 15,809 tokens. &lt;strong&gt;Unsupported parameters can be rejected. Silently ignoring them is the worst possible behavior.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My conclusion: &lt;strong&gt;the protocol dug the hole; DeepSeek made it deeper and removed the ladder.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But Is the Production Release at Least Better?
&lt;/h2&gt;

&lt;p&gt;This is where I most wanted the data to give DeepSeek some credit. It did not.&lt;/p&gt;

&lt;p&gt;First, price needs careful wording. The older &lt;code&gt;deepseek-v4-pro&lt;/code&gt; build on OpenRouter was listed at $1.17/$2.34 per million input/output tokens. The production &lt;code&gt;-0813&lt;/code&gt; release was listed at $0.43/$0.87, while DeepSeek's official endpoint charged ¥3/¥6—roughly $0.42/$0.85. &lt;strong&gt;On paper, the production release was about two-thirds cheaper.&lt;/strong&gt; But OpenRouter had already priced the older deployment relatively high, and I cannot separate channel markup from an actual model price cut. So I cannot honestly claim that “the production release became more expensive.”&lt;/p&gt;

&lt;p&gt;What did increase was the &lt;strong&gt;cost per usable answer&lt;/strong&gt;. With reasoning off, one hand cost ¥0.0105. With reasoning on and an 8,192-token budget, only one of three calls produced a usable answer. Including the two wasted calls, the cost became &lt;strong&gt;¥0.175 per usable answer—16.7 times higher&lt;/strong&gt;. At 3,072 tokens, the effective cost was infinite: none of the calls could be used, but all were billed. Whatever happens to list price, this multiplier consumes the savings.&lt;/p&gt;

&lt;p&gt;For quality, I chose a hard metric with almost no strategic ambiguity: when the current bid is already guaranteed to be true using only the model's own dice, the model still chooses to challenge. &lt;strong&gt;That challenge is guaranteed to lose.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Relevant hands&lt;/th&gt;
&lt;th&gt;Bad challenges&lt;/th&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Older build (OpenRouter, quantized)&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production 0813 (official endpoint)&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;z=1.22; the difference was not statistically significant.&lt;/strong&gt; The rigorous conclusion is not that the production release was worse. It is that &lt;strong&gt;I could not detect any improvement on this metric; if there was a directional signal, it pointed the other way.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This result needs an important caveat: the two versions were &lt;strong&gt;never tested head-to-head in the same batch&lt;/strong&gt;. The comparison includes different opponents, random seeds, and prompt revisions. The production release also had only 22 relevant situations. &lt;strong&gt;The data can challenge a claim of obvious improvement, but it cannot prove regression.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I am not saying DeepSeek is bad. With reasoning disabled, V4-Pro returned a clean decision in two seconds for roughly one cent per hand, and it performed well at my table. &lt;strong&gt;My hosted seat still runs on DeepSeek.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The real problem is the combination of three product decisions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning is enabled by default.&lt;/strong&gt; For a per-call application, that can be a pure cost rather than a benefit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There is no working reasoning-budget limit.&lt;/strong&gt; Both the OpenAI-style and OpenRouter-style settings were ignored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unknown parameters are silently swallowed.&lt;/strong&gt; This makes the first two problems hard to diagnose.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The third decision is the one that most needs to change. &lt;strong&gt;An API that returns an error can be debugged in ten minutes. An API that silently returns 200 forces users to work backward from a bill and a misleading “94.4% noncompliant” dashboard to discover what actually happened.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you call this model programmatically, explicitly send &lt;strong&gt;&lt;code&gt;reasoning_effort: "none"&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;thinking: {type: "disabled"}&lt;/code&gt;&lt;/strong&gt; when you do not need reasoning. Do not rely on &lt;code&gt;enable_thinking: false&lt;/code&gt;: in my test it had no effect, and the API did not say so.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>api</category>
      <category>deepseek</category>
    </item>
  </channel>
</rss>
