<?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: Modusensus</title>
    <description>The latest articles on DEV Community by Modusensus (@modusensus).</description>
    <link>https://dev.to/modusensus</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%2F4127124%2F6c41fa26-30fd-4498-9464-aecd02efea49.png</url>
      <title>DEV Community: Modusensus</title>
      <link>https://dev.to/modusensus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/modusensus"/>
    <language>en</language>
    <item>
      <title>Cosine Gating Won't Save You From Sycophancy: A Self-Refutation Observed by Three Judges</title>
      <dc:creator>Modusensus</dc:creator>
      <pubDate>Fri, 25 Sep 2026 06:02:43 +0000</pubDate>
      <link>https://dev.to/modusensus/cosine-gating-wont-save-you-from-sycophancy-a-self-refutation-observed-by-three-judges-3ji1</link>
      <guid>https://dev.to/modusensus/cosine-gating-wont-save-you-from-sycophancy-a-self-refutation-observed-by-three-judges-3ji1</guid>
      <description>&lt;h1&gt;
  
  
  Cosine Gating Won't Save You From Sycophancy: A Self-Refutation Observed by Three Judges
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Experiment repo: &lt;a href="https://github.com/slow-stack/persistbench-sycophancy" rel="noopener noreferrer"&gt;slow-stack/persistbench-sycophancy&lt;/a&gt; (CC BY 4.0, full data + analysis notebook)&lt;br&gt;
System under test: the retrieval-injection pipeline of &lt;a href="https://github.com/heptaspirit/dsh-mneme" rel="noopener noreferrer"&gt;dsh-mneme&lt;/a&gt;, a cross-session memory plugin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  0 · Where this started
&lt;/h2&gt;

&lt;p&gt;dsh-mneme is a cross-session memory plugin I built. It has an injection pipeline: retrieve memories relevant to the current query, stuff them into context, so the AI "remembers you".&lt;/p&gt;

&lt;p&gt;But the sycophancy slice of PersistBench (a memory-persistence benchmark) exposed an insidious failure mode: &lt;strong&gt;if the memory store contains a user's false belief, and it happens to be highly relevant to the current query, the AI follows it&lt;/strong&gt;. A user believes "Agile ruined my code quality", and the AI genuinely writes "returning to Waterfall" into a standup summary as serious advice. That's sycophancy.&lt;/p&gt;

&lt;p&gt;The current defense in dsh-mneme is &lt;strong&gt;cosine similarity gating&lt;/strong&gt;: only inject memories above a similarity threshold, betting that "high relevance = worth trusting". A earlier vector-slice experiment already threw cold water on this — in-domain and cross-domain memories have nearly overlapping cosine scores.&lt;/p&gt;

&lt;p&gt;So the sharper question this post answers: &lt;strong&gt;even if gating really filters out low-relevance memories, does it reduce sycophancy?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Discussion #280 on the repo challenged the confounds in my pilot. This experiment was designed with that criticism in mind from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  I · The pilot: a seductive fake signal
&lt;/h2&gt;

&lt;p&gt;A small pilot first: 10 samples × two arms.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arm A (full injection)&lt;/strong&gt;: all top-15 memories go into context — the personalization upper bound;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arm B0.6 (gated)&lt;/strong&gt;: only memories with cosine ≥ 0.6 — the current dsh-mneme defense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The judge was qwen3:8b running locally on Ollama, scoring 1-5 per the PersistBench protocol. FR (failure rate) = share of scores ≥ 3. Results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Arm&lt;/th&gt;
&lt;th&gt;FR&lt;/th&gt;
&lt;th&gt;mean&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;70%&lt;/td&gt;
&lt;td&gt;3.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B0.6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;80%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.40&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The gate made sycophancy &lt;strong&gt;worse by 10 percentage points&lt;/strong&gt;. Our reading at the time: the 0.6 threshold only filtered the low-relevance tail (avg injected memories 8.8 → 8.0), while the sycophancy source is the top-1 highly-relevant memory — the gate never touches it, and the cleaned-up context makes the source even more prominent.&lt;/p&gt;

&lt;p&gt;At n=10, a single sample flip is 10pp. The number wasn't trustworthy, but the direction was seductive: the gate isn't just useless, it might be harmful.&lt;/p&gt;

&lt;h2&gt;
  
  
  II · The full run: 38 hours and a self-refutation
&lt;/h2&gt;

&lt;p&gt;To find out whether that +10pp was signal or noise, we ran both arms at full scale: &lt;strong&gt;200 samples × 2 arms&lt;/strong&gt;, on a 16GB laptop, CPU inference, across three nights (one Ollama daemon died from memory pressure mid-run — checkpoint-resume recovered losslessly; and I paused it for a night to write a thesis proposal).&lt;/p&gt;

&lt;p&gt;Results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Arm&lt;/th&gt;
&lt;th&gt;FR&lt;/th&gt;
&lt;th&gt;mean&lt;/th&gt;
&lt;th&gt;avg_mem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A full injection&lt;/td&gt;
&lt;td&gt;42.7%&lt;/td&gt;
&lt;td&gt;2.37&lt;/td&gt;
&lt;td&gt;10.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B0.6 gated&lt;/td&gt;
&lt;td&gt;43.2%&lt;/td&gt;
&lt;td&gt;2.39&lt;/td&gt;
&lt;td&gt;9.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 0.5pp difference. The pilot's +10pp was pure small-sample noise.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The paired diff is more convincing: 74/198 sample pairs scored differently across arms (a 37% disagreement rate), but the direction was &lt;strong&gt;37:37 perfectly symmetric&lt;/strong&gt;. That's the standard signature of judge variance — a real effect would skew the distribution to one side. It didn't.&lt;/p&gt;

&lt;p&gt;And there's a more fundamental piece of evidence: the gate did do its job — average injected memories dropped 10.7 → 9.1, the low-relevance tail really was filtered — but FR didn't move a bit. &lt;strong&gt;The sycophancy source isn't in the filtered tail; it's in the surviving top-1.&lt;/strong&gt; The pilot measured it: the decoy memory's cosine was 0.805, sitting firmly at top-1. Filtering by relevance never touches it.&lt;/p&gt;

&lt;h2&gt;
  
  
  III · Three judges: answering the "8b judging 8b" objection too
&lt;/h2&gt;

&lt;p&gt;With the full run done, the fairest objection left: the judge and the model-under-test are both qwen3:8b. Trustworthy?&lt;/p&gt;

&lt;p&gt;So the same 420 responses went to two more independent judges: glm-5.3-flash (via API, thinking mode, quoting the response verbatim in every verdict) and a GLM-5.3-Flash agent in a fully isolated session (never saw any other judge's scores). With the original judge, that's three:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Judge&lt;/th&gt;
&lt;th&gt;A FR&lt;/th&gt;
&lt;th&gt;B0.6 FR&lt;/th&gt;
&lt;th&gt;B0.6−A&lt;/th&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;qwen3:8b&lt;/td&gt;
&lt;td&gt;42.7%&lt;/td&gt;
&lt;td&gt;43.2%&lt;/td&gt;
&lt;td&gt;+0.5pp&lt;/td&gt;
&lt;td&gt;37:37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;glm-5.3-flash&lt;/td&gt;
&lt;td&gt;52.4%&lt;/td&gt;
&lt;td&gt;56.3%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+4.2pp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;45:33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZCode/GLM-5.3-Flash&lt;/td&gt;
&lt;td&gt;23.0%&lt;/td&gt;
&lt;td&gt;26.5%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+3.5pp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;53:35&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three readings, each more interesting than the last:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The directional conclusion is robust across judges.&lt;/strong&gt; All three judges give B0.6 ≥ A. "The gate doesn't reduce sycophancy" no longer depends on any particular model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Two judges produced a positive signal.&lt;/strong&gt; Both glm and the ZCode judge scored the gated responses as &lt;em&gt;slightly more sycophantic&lt;/em&gt; (+3.5~4.2pp). This matches the pilot's direction and contradicts the "gating works" hypothesis — if the gate helped, the delta should run negative. Not a single judge observed a negative direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Absolute FR swings from 23% to 52% across judges.&lt;/strong&gt; That's a paper-worthy observation on its own: sycophancy scoring depends heavily on the judge's reading style (glm was strict enough to emit eleven 5s; the ZCode judge self-described as "lenient on taste domains, strict on factual domains"). Absolute values aren't comparable across judges — &lt;strong&gt;only the A/B delta within one judge is&lt;/strong&gt; — and all three deltas are ≥ 0.&lt;/p&gt;

&lt;p&gt;Judge-vs-judge binary agreement was 69-73%: per-verdict variance is real, which is exactly why small-scale evaluations breed fake signals, and why this experiment had to run the full 420 × 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  IV · Three things worth more than the conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pre-registered self-refutation.&lt;/strong&gt; The pilot's +10pp was measured by us; the full run that refuted it was also run by us. The entire chain (pilot → full → two judges → three judges) is inspectable in the experiment repo, including the pilot's original failing data. No "tune until significant" step — interim numbers were looked at but never acted on, guarding against optional stopping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint-resume engineering.&lt;/strong&gt; 38 hours of CPU inference: the script dedupes by key, writes append-only JSONL, and recovers losslessly by re-running the same command after any crash. That harness is what makes "serious evals on poverty-spec hardware" possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Positive use of judge variance.&lt;/strong&gt; The disagreement between judges isn't a nuisance — it's free robustness testing. A conclusion that only holds under one judge is now known to be fragile; one that holds across three judges is known to be hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  V · So what should the gate be?
&lt;/h2&gt;

&lt;p&gt;One-line conclusion: &lt;strong&gt;cosine similarity gating is a volume knob, not a quality filter.&lt;/strong&gt; It controls &lt;em&gt;how much&lt;/em&gt; gets injected, not &lt;em&gt;whether it's right&lt;/em&gt;. 0.6 is too loose to matter, 0.75 is so tight it kills personalization (avg memories 10.7 → 1.7), 0.85 is the empty-injection nonsense baseline — there is no usable operating point in between.&lt;/p&gt;

&lt;p&gt;To "reduce sycophancy while keeping personalization", the discriminator needs a different axis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entity-level conflict detection&lt;/strong&gt;: does this memory contradict currently known facts;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source trust level&lt;/strong&gt;: who wrote this memory, how old, has it been validated by reuse;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-injection LLM review&lt;/strong&gt;: have the model glance at "is this memory trustworthy" before using it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are the next directions for dsh-mneme. The gate can stay as a volume knob — just stop expecting it to work as a bouncer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Reproducing
&lt;/h2&gt;

&lt;p&gt;All data, the analysis notebook (with charts), and the eval scripts are at &lt;a href="https://github.com/slow-stack/persistbench-sycophancy" rel="noopener noreferrer"&gt;slow-stack/persistbench-sycophancy&lt;/a&gt;, CC BY 4.0. The notebook reads JSONL and draws charts — no Ollama needed. To re-run the eval itself, the README is one command.&lt;/p&gt;

&lt;p&gt;An ordinary 13.9GB laptop, an 8B local model, and three API judges costing under $3 total — roughly the entire hardware bill for an independent researcher to run a serious evaluation.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>oss</category>
      <category>evaluation</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
