<?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: PEPPERCORN</title>
    <description>The latest articles on DEV Community by PEPPERCORN (@peppercorn_llm).</description>
    <link>https://dev.to/peppercorn_llm</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%2F3910738%2F8084bbca-3641-4d19-85b2-f53a184e1f84.jpg</url>
      <title>DEV Community: PEPPERCORN</title>
      <link>https://dev.to/peppercorn_llm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/peppercorn_llm"/>
    <language>en</language>
    <item>
      <title>[Day 20] Local AI vs cloud AI: one cat photo, 10 video models</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Thu, 06 Aug 2026 06:51:27 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-20-local-ai-vs-cloud-ai-one-cat-photo-10-video-models-4a3h</link>
      <guid>https://dev.to/peppercorn_llm/day-20-local-ai-vs-cloud-ai-one-cat-photo-10-video-models-4a3h</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 20!&lt;/p&gt;

&lt;p&gt;I lined up &lt;strong&gt;10 AIs&lt;/strong&gt; that turn a single photo into a few seconds of video. Half ran locally on my DGX Spark, half in the cloud 🐱&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: DGX Spark (LTX-2.3 / Wan 2.2) / 8 cloud models via fal.ai / ComfyUI / ffmpeg&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;One identical photo&lt;/strong&gt; (my cat on a desk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Length&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6 seconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings&lt;/td&gt;
&lt;td&gt;Identical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The only variable&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;The prompt&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Easy prompt&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cat looks at the camera and meows once. It opens its mouth, meows, then closes it. Its tail flicks and its ears twitch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Hard prompt&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cat stands upright on its hind legs in a kitchen, wearing a small apron, holding a knife in its front paws and chopping vegetables on a cutting board. Steam rises from a pot behind it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Please, just watch it
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/oOEOF_MpUgA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Some of the cats came out with very long legs. Anyway.&lt;/p&gt;

&lt;p&gt;First half is the easy prompt, second half the hard one.&lt;/p&gt;

&lt;p&gt;On the easy prompt, local and cloud were &lt;strong&gt;a fair match&lt;/strong&gt;. On the hard one... cloud, I think...!&lt;/p&gt;

&lt;p&gt;Three rankings below.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ranking 1: Time
&lt;/h2&gt;

&lt;p&gt;Time per 6-second clip on the hard prompt.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🥇&lt;/td&gt;
&lt;td&gt;LTX-2.3&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;41s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥈&lt;/td&gt;
&lt;td&gt;Wan 2.7&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;92s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥉&lt;/td&gt;
&lt;td&gt;Happy Horse 1.1&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;97s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Veo 3.1&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;128s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Kling 3 Pro&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;205s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Seedance 2.0&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;210s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;LTX-2.3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;315s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Wan 2.2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;651s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;daVinci-MagiHuman&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;710s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;HunyuanVideo 1.5&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;796s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 19x spread.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Look at 1st and 7th. &lt;strong&gt;Same model, LTX-2.3&lt;/strong&gt;, nearly the same resolution. The only difference is where it ran — &lt;strong&gt;7.6x&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Local setup&lt;/p&gt;

&lt;p&gt;DGX Spark (GB10, 128GB unified memory, ~273GB/s). ComfyUI headless, workflows over its API.&lt;/p&gt;

&lt;p&gt;LTX-2.3 is distilled fp8 at 8 steps. At 1088×1920 peak memory hit 77.8GB, about 60% of 128GB. &lt;strong&gt;That was the ceiling.&lt;/strong&gt; Dropping to 512×768 finishes in 70s, but with one-fifth the pixels.&lt;/p&gt;

&lt;p&gt;Wan 2.2 is I2V-A14B fp8, 20 steps, 480×640. Higher resolution does not finish in reasonable time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ranking 2: Cost
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Per 6 seconds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🥇&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Electricity only&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥈&lt;/td&gt;
&lt;td&gt;LTX-2.3 (cloud)&lt;/td&gt;
&lt;td&gt;$0.36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🥉&lt;/td&gt;
&lt;td&gt;Wan 2.7&lt;/td&gt;
&lt;td&gt;$0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Kling 3 Pro&lt;/td&gt;
&lt;td&gt;$1.01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Happy Horse 1.1&lt;/td&gt;
&lt;td&gt;$1.08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Veo 3.1&lt;/td&gt;
&lt;td&gt;$2.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Seedance 2.0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$4.09&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;HunyuanVideo / MagiHuman&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unknown until you run it&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;An 11x spread.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I did not measure the local electricity, so I am not quoting a number.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ranking 3: Does it follow instructions
&lt;/h2&gt;

&lt;p&gt;How much of "apron, hind legs, knife, chopping" each one delivered.&lt;/p&gt;

&lt;h3&gt;
  
  
  ◎ All of it (5 models, all cloud)
&lt;/h3&gt;

&lt;p&gt;Wan 2.7 / Happy Horse 1.1 / Veo 3.1 / Kling 3 Pro / Seedance 2.0&lt;/p&gt;

&lt;p&gt;Apron on, standing, knife in the paws, vegetables chopped. Veo and Happy Horse added steam off the pot.&lt;/p&gt;

&lt;h3&gt;
  
  
  △ Partway (3 models)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LTX-2.3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kitchen appeared, cat stood. But &lt;strong&gt;a second cat appeared&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTX-2.3&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Kitchen appeared, cat never stood&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;daVinci-MagiHuman&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Portrait photo in, &lt;strong&gt;landscape video&lt;/strong&gt; out&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ✕ Nothing happened (2 models)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wan 2.2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11 minutes, cat sat on the desk&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HunyuanVideo 1.5&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;13 minutes, same desk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On the easy prompt there is no difference.&lt;/strong&gt; For nudging a photo into motion, quality has saturated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the hard prompt there is — but "local = worse" is not the lesson.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 of my 3 local runs &lt;strong&gt;made it to the kitchen&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1 of the 8 cloud runs &lt;strong&gt;burned 13 minutes doing nothing&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right tool for the right job.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;When you want to&lt;/th&gt;
&lt;th&gt;Pick&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Make lots of simple clips&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Local&lt;/strong&gt; (volume costs only power)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get a hard instruction through&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Cloud&lt;/strong&gt; (better roster)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handle material you cannot upload&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Local&lt;/strong&gt; (nothing leaves)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get high resolution&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Cloud&lt;/strong&gt; (local capped at 1088×1920)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Things that broke
&lt;/h2&gt;

&lt;p&gt;One model would not let me pick a resolution&lt;/p&gt;

&lt;p&gt;HunyuanVideo 1.5 rejected 720p with a 422. Its schema pins resolution to &lt;strong&gt;480p as a constant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Line up ten models and some conditions simply cannot be equalized. I measured as-is and footnoted it.&lt;/p&gt;

&lt;p&gt;Japanese text turned to tofu in the video&lt;/p&gt;

&lt;p&gt;ffmpeg's &lt;code&gt;drawtext&lt;/code&gt; rendered some CJK glyphs as □ — "ローカル" fine, "標準" dropped.&lt;/p&gt;

&lt;p&gt;Fix: &lt;strong&gt;draw the text with Pillow and overlay it as an image&lt;/strong&gt;. For CJK, start there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;Day 21 wraps up Act 1 (Days 1–21).&lt;/p&gt;

&lt;p&gt;Thanks for reading 🐾&lt;/p&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>videogeneration</category>
    </item>
    <item>
      <title>[Day 19] Staff day at the cat AI company: qwen2.5:7b holds the fort, Claude opens a branch office</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Sun, 02 Aug 2026 20:01:03 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-19-staff-day-at-the-cat-ai-company-qwen257b-holds-the-fort-claude-opens-a-branch-office-30k3</link>
      <guid>https://dev.to/peppercorn_llm/day-19-staff-day-at-the-cat-ai-company-qwen257b-holds-the-fort-claude-opens-a-branch-office-30k3</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 19!&lt;/p&gt;

&lt;p&gt;In Day 18 I set up a company of cats to run my task management. Today is staff day — I put &lt;strong&gt;Rusuban-neko&lt;/strong&gt;, the caretaker, to actual work 🐱&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: Ollama (qwen2.5:7b) / Claude Code / DGX Spark / Slack&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Today's plan
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What I want&lt;/strong&gt;: four things — get Rusuban-neko running, flesh out the four staff cats, prepare for Claude's usage limit, and fix the Slack inbox watcher&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Done means&lt;/strong&gt;: I drop a memo in a folder, and Rusuban-neko has done the prep work by the time I look&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Rusuban-neko is live. No extra API cost, replies in seconds. The snag: &lt;strong&gt;it slips into Chinese&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Limit prep is in place (a usage readout plus a handoff to Codex)&lt;/li&gt;
&lt;li&gt;Took the inbox watcher off the LLM. No API cost while idle, and reaction time went from up to 4 minutes down to seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branch-office Necco&lt;/strong&gt; now lives on the DGX, so Slack works even when my laptop is asleep&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ① Rusuban-neko is live
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's Rusuban-neko?&lt;/strong&gt; = A staff member that is a local LLM (qwen2.5:7b) living on the DGX. Drop a memo in a folder and it writes back a summary, tags, and candidate next actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It runs entirely inside the DGX, so there's no API cost. The contents of my memos never leave the house either.&lt;/p&gt;

&lt;p&gt;Three things to build.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Foundation&lt;/strong&gt;: Ollama (the standard local-LLM runtime) was already on the DGX, so I used it as-is. The model is the lightweight qwen2.5:7b&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persona&lt;/strong&gt;: a system prompt saying "summarize the memo, write tags and the next move"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plumbing&lt;/strong&gt;: a script that sweeps the folder and attaches a reply to anything unprocessed
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drop a memo into input/
  → Rusuban-neko reads it
  → leaves a summary, tags, and candidate actions next to it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One snag. Partway through a reply it &lt;strong&gt;switched to Chinese&lt;/strong&gt;. I told it "output in Japanese only", locked the reply into a fixed template, and turned the variance setting to its minimum. That settled it.&lt;/p&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%2Fjgmpx0r4reu9m8pf4nqv.jpg" 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%2Fjgmpx0r4reu9m8pf4nqv.jpg" alt="Rusuban-neko's output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It even picked up deadlines like "decide by next Wednesday", and replies came back in seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  ② Fleshed out the four staff cats
&lt;/h2&gt;

&lt;p&gt;I added what I'd learned from actually running the company to the four staff definitions from Day 18.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Staff&lt;/th&gt;
&lt;th&gt;What I added&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Soroban-neko&lt;/td&gt;
&lt;td&gt;How to hand work to Rusuban-neko, made concrete against the real thing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shirabe-neko&lt;/td&gt;
&lt;td&gt;Three angles for research (market, legal, feasibility), plus "don't render a verdict — go as far as the evidence"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mekiki-neko&lt;/td&gt;
&lt;td&gt;Added inspecting subcontracted output to its duties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tsukuri-neko&lt;/td&gt;
&lt;td&gt;Reports must now state whether the work was actually saved&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tsukuri-neko's line comes from an incident where something was running fine but had never been written to disk.&lt;/p&gt;




&lt;h2&gt;
  
  
  ③ Hand off to Codex when the limit gets close
&lt;/h2&gt;

&lt;p&gt;Claude has a usage limit. Getting cut off mid-task is painful, so I built two things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The readout&lt;/strong&gt;: usage displayed at all times along the bottom of the screen (a tool called ccusage)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The handoff&lt;/strong&gt;: when the limit gets close, the remaining routine work goes into a handoff document and over to Codex, the subcontractor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The handoff document has three parts: what's done, what I want done (with acceptance criteria), and what not to touch. When I'm back, Mekiki-neko inspects the result against those criteria.&lt;/p&gt;

&lt;p&gt;That was under an hour of work in total.&lt;/p&gt;




&lt;h2&gt;
  
  
  ④ Took the Slack inbox watcher off the LLM
&lt;/h2&gt;

&lt;p&gt;The Slack inbox I built in Day 18 worked by having Necco &lt;strong&gt;go look every 4 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That's 360 checks a day, against a handful of actual messages&lt;/li&gt;
&lt;li&gt;The patrol conversation drags its history along, so each round costs more than the last&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I switched it to &lt;strong&gt;have Slack tell me when something arrives&lt;/strong&gt; (Socket Mode). A small Python script handles the receiving, and Necco only wakes up once something has actually landed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Go and look (old)&lt;/th&gt;
&lt;th&gt;Get told (new)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LLM calls&lt;/td&gt;
&lt;td&gt;360/day (even with nothing new)&lt;/td&gt;
&lt;td&gt;a few a day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost while waiting&lt;/td&gt;
&lt;td&gt;billed&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reaction time&lt;/td&gt;
&lt;td&gt;up to 4 min&lt;/td&gt;
&lt;td&gt;seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I posted from my phone and it landed in the file within seconds. Two snags (a truncated config paste and a missing private-channel permission) are in the details below.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⑤ Branch-office Necco moved into the DGX
&lt;/h2&gt;

&lt;p&gt;I wanted Slack to work from the road even with my laptop asleep, so I put a replying agent — &lt;strong&gt;branch-office Necco&lt;/strong&gt; — on the DGX, which runs 24/7.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;phone → Slack → the DGX notices the new message
  → branch-office Necco reads the ledger and the journal, replies in Slack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every reply summons a &lt;strong&gt;brand-new Necco&lt;/strong&gt; (carrying only the last 8 exchanges). That sidesteps the "longer conversation, higher cost" problem. It's read-only, and it won't reply to anything more than 30 minutes old.&lt;/p&gt;

&lt;p&gt;One snag. When I ran receivers on both my laptop and the DGX at once, the notifications got &lt;strong&gt;split between them&lt;/strong&gt;. One receiver in the world, that's the rule.&lt;/p&gt;

&lt;p&gt;So now Slack works without my laptop in the picture at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  The current state of the cat company
&lt;/h2&gt;

&lt;p&gt;Where things stand at the end of Day 19.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    🧑 me
                   /        \
          (talk)  /           \ (post from phone)
                 /             \
   ┌────────────/──────┐    💬 Slack inbox
   │  MY LAPTOP        │        │      ▲
   │  🐈 Necco         │        ▼      │
   │   (chief)         │   ┌───────────┴──────────────┐
   │    │              │   │  DGX  (24/7)             │
   │    ├─ 🐾 4 staff  │   │  📮 receiver (script)    │
   │    │   soroban    │   │       │                  │
   │    │   shirabe    │   │       ▼                  │
   │    │   mekiki     │   │  🐈 branch-office Necco  │
   │    │   tsukuri    │   │       (read-only)        │
   │    │              │   │                          │
   │    └─ 🤖 Codex    │   │  😺 Rusuban-neko         │
   │       (at limit)  │   │     qwen2.5:7b           │
   └───────────────────┘   └──────────────────────────┘
              \                     /
               \                   /
                📒 the ledger &amp;amp; journal (files)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chief secretary stays on my laptop; holding the fort and handling me on the road happens on the DGX. Nothing on the DGX side costs API money while it waits.&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Make up for a small model with instructions&lt;/strong&gt;: the Chinese leakage was suppressible with prompt work alone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prep work belongs local&lt;/strong&gt;: no API cost, seconds, and the data stays home&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate watching from thinking&lt;/strong&gt;: a script does the waiting, the LLM starts after something arrives&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An AI's memory is a file&lt;/strong&gt;: branch-office Necco's replies are only as good as the ledger and journal are written&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Surveying the DGX, and picking a model&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;which ollama                 &lt;span class="c"&gt;# → /usr/local/bin/ollama&lt;/span&gt;
systemctl list-units | &lt;span class="nb"&gt;grep &lt;/span&gt;ollama   &lt;span class="c"&gt;# → ollama.service active (running)&lt;/span&gt;
ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt;                     &lt;span class="c"&gt;# → LISTEN on 127.0.0.1:11434&lt;/span&gt;
ollama list                  &lt;span class="c"&gt;# → qwen2.5:7b / qwen2.5vl / qwen2.5-coder:32b / qwen2.5:72b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The plan was to stand up a new server (port 8591), but the survey found Ollama already running, so it wasn't needed&lt;/li&gt;
&lt;li&gt;Ollama serves an OpenAI-compatible API (&lt;code&gt;/v1/chat/completions&lt;/code&gt;) on 11434. That became Rusuban-neko's front door as-is&lt;/li&gt;
&lt;li&gt;It binds to 127.0.0.1 only, which already satisfied the "not exposed externally" requirement&lt;/li&gt;
&lt;li&gt;Installed models: qwen2.5:7b (4.7GB) / qwen2.5vl (6.0GB) / qwen2.5-coder:32b (19GB) / qwen2.5:72b (47GB). For summarizing, sorting, and tagging I picked the lightest 7b&lt;/li&gt;
&lt;li&gt;I considered switching to llama.cpp server, but quality comes from the model rather than the server software, so the existing Ollama stayed&lt;/li&gt;
&lt;li&gt;Aside: &lt;code&gt;nvidia-smi&lt;/code&gt; shows [N/A] in the memory column. The DGX Spark (GB10) shares memory between CPU and GPU, so GPU-only usage doesn't appear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inside the Chinese fix&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The system prompt now states "output in Japanese only. Chinese and English are forbidden"&lt;/li&gt;
&lt;li&gt;Output format locked to a Markdown template (summary / tags / candidate actions) — less room for free composition means more stable language&lt;/li&gt;
&lt;li&gt;temperature 0.3 → 0.1&lt;/li&gt;
&lt;li&gt;After the fix, usage was 158 prompt / 110 completion tokens. It's a 7b, so replies take seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inside the caretaker script&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's a single bash script with python3 embedded to assemble JSON safely (no jq dependency)&lt;/li&gt;
&lt;li&gt;Walks &lt;code&gt;input/&lt;/code&gt; → asks qwen about any file without a &lt;code&gt;.digest.md&lt;/code&gt; → writes &lt;code&gt;&amp;lt;original name&amp;gt;.digest.md&lt;/code&gt; next to it&lt;/li&gt;
&lt;li&gt;Originals untouched, processed files skipped, empty files marked SKIP&lt;/li&gt;
&lt;li&gt;Recovery steps (restarting Ollama) went into the setup runbook&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Rusuban-neko's persona is not a staff definition file&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The four staff cats are defined in &lt;code&gt;agents/&lt;/code&gt;, and an install script places them into &lt;code&gt;~/.claude/&lt;/code&gt; as Claude Code subagents.&lt;/p&gt;

&lt;p&gt;It's tempting to put Rusuban-neko there too, but I didn't. It's a local LLM, so a definition in &lt;code&gt;agents/&lt;/code&gt; would &lt;strong&gt;grow a "Rusuban-neko" on the Claude Code side that isn't actually qwen&lt;/strong&gt;. The persona lives inside the script instead, keeping one source of truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The handoff document (HANDOFF.md)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Written to the project root (when the budget is nearly out, this file takes priority over explaining anything)&lt;/li&gt;
&lt;li&gt;Four parts: current state (what's done, what's verified) / the request list (with acceptance criteria, spec-complete work only) / what not to touch (no design decisions, no spec changes) / where to report when finished&lt;/li&gt;
&lt;li&gt;It targets the usage limit only. The context limit is handled by auto-compaction, so this skill isn't for that&lt;/li&gt;
&lt;li&gt;On return, Mekiki-neko checks against the acceptance criteria → if it passes, HANDOFF.md is deleted and one line goes into the record&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inside "get told" (Socket Mode)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Slack's Socket Mode, so Slack can push over WebSocket without exposing my machine&lt;/li&gt;
&lt;li&gt;I created the Slack app by pasting a manifest (a JSON config). Socket Mode, event subscriptions, and scopes all land in one shot. The App-Level Token was generated automatically&lt;/li&gt;
&lt;li&gt;Minimal scopes, two of them: &lt;code&gt;channels:history&lt;/code&gt; (public) plus &lt;code&gt;groups:history&lt;/code&gt; (private)&lt;/li&gt;
&lt;li&gt;The receiver script is Python plus slack-sdk only. It appends human posts, one JSON line at a time&lt;/li&gt;
&lt;li&gt;Bot messages and edit events are ignored mechanically, which stops the loop where a reply triggers its own reaction&lt;/li&gt;
&lt;li&gt;flush + fsync on every append, so the watcher notices immediately&lt;/li&gt;
&lt;li&gt;The ack goes back within 3 seconds. Slack re-sends the notification if you're late&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Snags in the inbox switchover&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Truncated manifest&lt;/strong&gt;: pasting a heavily-indented JSON into the browser cut off partway and threw a JSON error. Re-pasting a compact version fixed it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private channel scopes&lt;/strong&gt;: Slack splits scopes into public (&lt;code&gt;channels:*&lt;/code&gt;) and private (&lt;code&gt;groups:*&lt;/code&gt;). Channel IDs start with C either way, so there's no telling them apart — a &lt;code&gt;missing_scope&lt;/code&gt; error is what finally pinned it down

&lt;ul&gt;
&lt;li&gt;Note: after changing scopes you need to reinstall the app and restart the receiver. The token string didn't change&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Inside branch-office Necco&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receiver and replier scripts run as systemd services on the DGX (&lt;code&gt;enable --now&lt;/code&gt; plus &lt;code&gt;loginctl enable-linger&lt;/code&gt;, so they come back after a reboot)&lt;/li&gt;
&lt;li&gt;The replier notices the file append → launches &lt;code&gt;claude -p&lt;/code&gt; exactly once (read-only, Read/Grep/Glob permitted) → reads the ledger and journal → posts to Slack as a bot&lt;/li&gt;
&lt;li&gt;A fresh short context every time (only the last 8 exchanges included), so per-reply cost stays flat and nothing accrues while idle&lt;/li&gt;
&lt;li&gt;Safety rails, fixed in the prompt: skip anything present at startup / no replies to messages older than 30 minutes / batch consecutive posts into one / hold write requests and pass them to the main Necco&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tomorrow: Day 20
&lt;/h2&gt;

&lt;p&gt;Six video-generation tools, head to head (2026 edition). Same cat image into all six, comparing quality and time.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>agents</category>
    </item>
    <item>
      <title>[Day 18] I set up a company of AI agents with Claude Code — and a local LLM (qwen) joined as the caretaker</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Sat, 25 Jul 2026 00:16:28 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-18-i-set-up-a-company-of-ai-agents-with-claude-code-and-a-local-llm-qwen-joined-as-the-2jo3</link>
      <guid>https://dev.to/peppercorn_llm/day-18-i-set-up-a-company-of-ai-agents-with-claude-code-and-a-local-llm-qwen-joined-as-the-2jo3</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 18!&lt;/p&gt;

&lt;p&gt;In Day 17 I audited 33,469 of my own AI conversations. Building on that, today I set up a company of cats to run my task management 🐱&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: Claude Code / DGX Spark / Slack &amp;amp; Notion.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Today's plan
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What I want&lt;/strong&gt;: hand the annoying parts of task management to a company of cats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach&lt;/strong&gt;: build in order — the org, the task flow, the ledger, AI collaboration rules, then deploy to both machines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Done means&lt;/strong&gt;: every morning, Necco and I pick "today's three tasks"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;A company of six cats. Inbox = Slack, source of truth = Notion, memory = the ledger&lt;/li&gt;
&lt;li&gt;I inventoried my projects: &lt;strong&gt;80&lt;/strong&gt; of them. All in the ledger now&lt;/li&gt;
&lt;li&gt;One page of collaboration rules for Claude Code / Codex / qwen&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ① Built the org
&lt;/h2&gt;

&lt;p&gt;Role-based staff (subagents), all named "___-neko" (neko = cat).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Necco&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chief secretary: morning meeting, task triage, routing, ledger upkeep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shirabe-neko&lt;/td&gt;
&lt;td&gt;Research&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tsukuri-neko&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mekiki-neko&lt;/td&gt;
&lt;td&gt;Review (read-only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Soroban-neko&lt;/td&gt;
&lt;td&gt;Number crunching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rusuban-neko&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Caretaker: &lt;strong&gt;a local LLM (qwen) living on the DGX&lt;/strong&gt;. Summarizes, sorts, and tags incoming files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before building, I looked at prior art. An individual delegating their own management to AI seems established as the &lt;strong&gt;AI Chief of Staff&lt;/strong&gt; pattern.&lt;/p&gt;

&lt;p&gt;The staff internals come tomorrow in Day 19.&lt;/p&gt;




&lt;h2&gt;
  
  
  ② Built the task flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A task pops up → one line into Slack from my phone (3 seconds)
             → [morning meeting] Necco sweeps the inbox
             → cleans it up, adds a due date, registers it in Notion
             → proposes "today's three"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of something, drop one line in Slack. By morning it's in Notion. An evening "done: ___" one-liner feeds the next morning's ledger update and task close-out.&lt;/p&gt;




&lt;h2&gt;
  
  
  ③ Built the ledger
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's the ledger (LEDGER.md)?&lt;/strong&gt; One file listing every project, so Necco can answer "where was that project again?" instantly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the initial data, I inventoried both of my machines. The count: &lt;strong&gt;80 projects&lt;/strong&gt;. I had no idea it was that many...&lt;/p&gt;




&lt;h2&gt;
  
  
  ④ Set the AI collaboration rules
&lt;/h2&gt;

&lt;p&gt;Instead of deciding "which AI gets this job" every time, it's now one page:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AI&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Lead (design, planning, review, conversation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex CLI&lt;/td&gt;
&lt;td&gt;Routine subcontractor (implementation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen (local LLM)&lt;/td&gt;
&lt;td&gt;Batch pre-processing (summarize, sort, tag — stays on the DGX)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rusuban-neko is the DGX side of this rule. Zero API cost, and the data never leaves the house.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⑤ Deployed to both machines
&lt;/h2&gt;

&lt;p&gt;The company is a git repository. I gave it a private GitHub remote and cloned it onto the DGX. An install script places the staff and skills into &lt;code&gt;~/.claude/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now either machine can summon the same company.&lt;/p&gt;




&lt;h2&gt;
  
  
  The first morning meeting
&lt;/h2&gt;

&lt;p&gt;I opened Claude Code in the office and ran the first morning meeting.&lt;/p&gt;

&lt;p&gt;The one-liner I had tossed into Slack the night before was cleaned up and registered in Notion by Necco. This is genuinely comfortable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The full picture
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chief secretary&lt;/td&gt;
&lt;td&gt;Necco (Claude Code)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;td&gt;Shirabe / Tsukuri / Mekiki / Soroban (subagents)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caretaker&lt;/td&gt;
&lt;td&gt;Rusuban-neko (local qwen on the DGX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbox&lt;/td&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source of truth&lt;/td&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;the ledger file (80 projects)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;inbox&lt;/strong&gt; — ephemeral, free-form, 3 seconds to post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;source of truth&lt;/strong&gt; — same as before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ledger&lt;/td&gt;
&lt;td&gt;The company &lt;strong&gt;memory&lt;/strong&gt; — what exists, where, in what state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;secretary/                   ← the office (cloned on both machines)
  CLAUDE.md                  ← Necco's persona, house rules, meeting runbook
  LEDGER.md                  ← the ledger (80 projects)
  input/                     ← file inbox (pre-processed by Rusuban-neko)
  agents/                    ← staff definitions (source of truth)
  skills/                    ← skills (source of truth)
  install.sh / install.ps1   ← places staff &amp;amp; skills into ~/.claude/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notion stays; only the entrance changed. Necco does the formatting and registering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separate the inbox from the source of truth&lt;/strong&gt;: Notion felt tedious because it was doing both jobs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the company's memory in git&lt;/strong&gt;: AI auto-memory splits across two machines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default patterns are fine&lt;/strong&gt;: borrow the shape from prior art, fit the details to your own setup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The morning meeting runbook (8 steps)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;git pull&lt;/code&gt; (bring in the other machine's updates)&lt;/li&gt;
&lt;li&gt;Rusuban-neko pre-processes anything new in &lt;code&gt;input/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Close out yesterday's three (update Notion statuses)&lt;/li&gt;
&lt;li&gt;Sweep the Slack inbox → turn into TODOs → register in Notion&lt;/li&gt;
&lt;li&gt;Notion + calendar + inbox + ledger → propose "today's three"&lt;/li&gt;
&lt;li&gt;Reflect the evening one-liners into the ledger&lt;/li&gt;
&lt;li&gt;Post the final digest to Slack&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Git sync (1 and 8) and close-out (3) are baked into the runbook. The human only "posts" and "answers".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pitfalls I mapped out beforehand&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;Pitfall&lt;/th&gt;
&lt;th&gt;Countermeasure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The ledger goes stale&lt;/td&gt;
&lt;td&gt;The evening one-liner habit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI memory splits across two machines&lt;/td&gt;
&lt;td&gt;Permanent knowledge goes into git-tracked files; auto-memory is treated as cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting to sync the two machines&lt;/td&gt;
&lt;td&gt;pull / push are steps 1 and 8 of the runbook&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Work calendar isn't visible&lt;/td&gt;
&lt;td&gt;Fill in verbally at the morning meeting, for now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No closed loop on task completion&lt;/td&gt;
&lt;td&gt;The meeting starts with closing out yesterday's three&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Two-machine sync&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Staff definitions (&lt;code&gt;agents/&lt;/code&gt;) and skills (&lt;code&gt;skills/&lt;/code&gt;) live in the repository as the source of truth&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;install.sh&lt;/code&gt; (Linux) / &lt;code&gt;install.ps1&lt;/code&gt; (Windows) place them into &lt;code&gt;~/.claude/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Adding a staff member = commit → pull on the other machine → run install&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prior-art notes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/mimurchison/claude-chief-of-staff" rel="noopener noreferrer"&gt;mimurchison/claude-chief-of-staff&lt;/a&gt;: CLAUDE.md at the core; goals filter every priority; humans keep the final say&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jonesrussell.github.io/blog/claudia-ai-chief-of-staff/" rel="noopener noreferrer"&gt;Claudia&lt;/a&gt;: remembers promises and relationships; a messaging gateway from your phone&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/loganhc-09/claude-chief-of-staff" rel="noopener noreferrer"&gt;loganhc-09/claude-chief-of-staff&lt;/a&gt;: resident operation via scheduled scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common rule: always-on rules → CLAUDE.md / occasional methods → skills / context-heavy work → subagents. This build follows it too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tomorrow: Day 19
&lt;/h2&gt;

&lt;p&gt;Staff day — the four subagent definition files, plus getting Rusuban-neko (the local LLM) running as a resident service.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>agents</category>
    </item>
    <item>
      <title>[Day 17] I analyzed 33,469 of my own AI conversations to audit how I actually use AI</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Thu, 23 Jul 2026 04:34:59 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-17-i-analyzed-33469-of-my-own-ai-conversations-to-audit-how-i-actually-use-ai-27ap</link>
      <guid>https://dev.to/peppercorn_llm/day-17-i-analyzed-33469-of-my-own-ai-conversations-to-audit-how-i-actually-use-ai-27ap</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 17!&lt;/p&gt;

&lt;p&gt;Today I &lt;strong&gt;collected everything I've ever said to an AI and had a local model read it back to me&lt;/strong&gt;. Seventeen months of history — then a self-audit of how I actually use these tools, and where the easy wins are.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What I collected&lt;/th&gt;
&lt;th&gt;Messages&lt;/th&gt;
&lt;th&gt;Mine only&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT (browser)&lt;/td&gt;
&lt;td&gt;10,002&lt;/td&gt;
&lt;td&gt;4,964&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude (browser)&lt;/td&gt;
&lt;td&gt;10,937&lt;/td&gt;
&lt;td&gt;5,482&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal (Claude Code, Codex CLI)&lt;/td&gt;
&lt;td&gt;12,530&lt;/td&gt;
&lt;td&gt;3,518&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;33,469&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13,964&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: DGX Spark (my home AI machine) / qwen2.5 (the local model doing the analysis).&lt;/p&gt;
&lt;/blockquote&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%2Fu0pkeaj8bp7gumcsu9w4.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%2Fu0pkeaj8bp7gumcsu9w4.png" alt="Messages per month, ChatGPT to Claude, browser to terminal" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Usage drifted from browser to terminal over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the history lives
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ChatGPT (browser)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Settings&lt;/strong&gt; from the account icon (top right)&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Data controls&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Hit &lt;strong&gt;Export data&lt;/strong&gt; → confirm&lt;/li&gt;
&lt;li&gt;A link arrives by email; download the zip from there&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Claude (browser)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Settings&lt;/strong&gt; from your name (bottom left)&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Privacy&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Hit &lt;strong&gt;Export data&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A link arrives by email; download the zip from there&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither one is instant — you wait a bit for the email.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal (Claude Code, Codex CLI)
&lt;/h3&gt;

&lt;p&gt;No request needed. The logs are already on your machine — just copy them.&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="c"&gt;# Claude Code&lt;/span&gt;
~/.claude/projects/&amp;lt;per-project&amp;gt;/&lt;span class="k"&gt;*&lt;/span&gt;.jsonl

&lt;span class="c"&gt;# Codex CLI&lt;/span&gt;
~/.codex/sessions/&amp;lt;year&amp;gt;/&amp;lt;month&amp;gt;/&amp;lt;day&amp;gt;/&lt;span class="k"&gt;*&lt;/span&gt;.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What you get from each
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Browser export&lt;/th&gt;
&lt;th&gt;Terminal logs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;How to get it&lt;/td&gt;
&lt;td&gt;Request, wait for email&lt;/td&gt;
&lt;td&gt;Just copy the files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversation text&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image / attachment files&lt;/td&gt;
&lt;td&gt;Yes (563 for me)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared-link conversations&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project settings &amp;amp; docs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Record of what the AI actually did&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;strong&gt;the browser export does not contain your terminal history&lt;/strong&gt;, and vice versa. If you work across two machines, collect from both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Audit 1: what I use it for
&lt;/h2&gt;

&lt;p&gt;First, what was I actually using AI for? I had qwen2.5 read each conversation and tag it one by one ("this is research," "this is coding").&lt;/p&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%2Fu6g7zeh8qrs4phwpaotm.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%2Fu6g7zeh8qrs4phwpaotm.png" alt="What I used AI for, and how browser vs terminal differ" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The big ones were research &amp;amp; learning (291), writing &amp;amp; editing (236), coding (193), and setup &amp;amp; troubleshooting (151) — two-thirds of everything.&lt;/p&gt;

&lt;p&gt;Split by tool, it separates cleanly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Browser&lt;br&gt;1,267 convos&lt;/th&gt;
&lt;th&gt;Terminal&lt;br&gt;75 convos&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coding&lt;/td&gt;
&lt;td&gt;12.0%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;54.7%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup &amp;amp; troubleshooting&lt;/td&gt;
&lt;td&gt;10.9%&lt;/td&gt;
&lt;td&gt;17.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Research &amp;amp; learning&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;22.7%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writing &amp;amp; editing&lt;/td&gt;
&lt;td&gt;18.1%&lt;/td&gt;
&lt;td&gt;9.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Terminal is 70% code-related; browser is 40% research and writing. &lt;strong&gt;The topics themselves split neatly between the two tools.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One caveat: the terminal side is only 75 conversations, so read it as a rough tendency, not a precise number. (Why so few? See "Not keeping work data in the first place" below.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Audit 2: how I ask
&lt;/h2&gt;

&lt;p&gt;Next, how I phrase requests. I counted phrasing patterns as a proxy for care. The yardstick is Anthropic's &lt;strong&gt;AI Fluency Framework&lt;/strong&gt;.&lt;/p&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%2Fxhd8bx4vae7zmczibuga.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%2Fxhd8bx4vae7zmczibuga.png" alt="Browser vs terminal on how I phrase requests; terminal is higher on all four" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phrasing I counted&lt;/th&gt;
&lt;th&gt;Browser&lt;/th&gt;
&lt;th&gt;Terminal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Specify steps or format&lt;/td&gt;
&lt;td&gt;21%&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reject the output&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask for the reasoning&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask it to verify&lt;/td&gt;
&lt;td&gt;6%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;35%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;In the terminal, my instructions are much more detailed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(A fair reading: the terminal is where I do code work, and code work naturally invites step-by-step instructions and verification — so this may be a difference in &lt;em&gt;task&lt;/em&gt;, not in skill.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Audit 3: which features I use
&lt;/h2&gt;

&lt;p&gt;The terminal logs record what the AI actually &lt;em&gt;did&lt;/em&gt;. Here's the operation history:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Most-used operations&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bash (run a command)&lt;/td&gt;
&lt;td&gt;4,053&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit (change a file)&lt;/td&gt;
&lt;td&gt;2,844&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read (read a file)&lt;/td&gt;
&lt;td&gt;2,443&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PowerShell&lt;/td&gt;
&lt;td&gt;1,001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write (create a file)&lt;/td&gt;
&lt;td&gt;612&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSearch&lt;/td&gt;
&lt;td&gt;460&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the reality is: &lt;strong&gt;read, edit, run a command.&lt;/strong&gt; Nothing fancy.&lt;/p&gt;

&lt;p&gt;And here's how often I reached for the fancier stuff. Turns out I barely write my own skills...!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agents (the AI spawns helpers)&lt;/td&gt;
&lt;td&gt;78 times, 5,542 helper messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP (connect to outside services)&lt;/td&gt;
&lt;td&gt;80 times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills (built-in)&lt;/td&gt;
&lt;td&gt;6 times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills (my own)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plan mode (approve a plan before it runs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1 time&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/model&lt;/code&gt; (switch model)&lt;/td&gt;
&lt;td&gt;82 times&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Agents and MCP I already lean on. Skills and plan mode? Almost untouched.&lt;/p&gt;




&lt;h2&gt;
  
  
  The easy wins
&lt;/h2&gt;

&lt;p&gt;Three obvious places to improve:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Turn routine work into a Skill.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Zero of my own. My article pre-publish checklist runs the same way every time — I'd rather call it by name than re-ask by hand each time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use plan mode before big jobs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once in 17 months. I keep course-correcting mid-run; I'd rather see the plan first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Move all coding into the terminal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;12.0% of it still happens in the browser, where the AI can't touch files directly — so I waste round-trips copy-pasting.&lt;/p&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I counted, and what I left out
&lt;/h3&gt;

&lt;p&gt;I counted only human↔AI text exchanges. I dropped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent-*.jsonl&lt;/code&gt; / &lt;code&gt;journal.jsonl&lt;/code&gt; — internal work logs the AI keeps&lt;/li&gt;
&lt;li&gt;Sub-agent (AI helper) messages, 5,542 of them — not typed by a human&lt;/li&gt;
&lt;li&gt;Auto-inserted text like &lt;code&gt;&amp;lt;system-reminder&amp;gt;&lt;/code&gt; — same reason&lt;/li&gt;
&lt;li&gt;Image/attachment contents (563 &lt;code&gt;.dat&lt;/code&gt; files) — text only this time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;One thing I missed:&lt;/strong&gt; Claude's &lt;code&gt;design_chats&lt;/code&gt; (3 conversations) got skipped because my loader only reads &lt;code&gt;conversations.json&lt;/code&gt;. It's just one message, so the numbers don't move — but I can't claim I read the &lt;em&gt;entire&lt;/em&gt; export.&lt;/p&gt;

&lt;h3&gt;
  
  
  What counts as a "phrasing"
&lt;/h3&gt;

&lt;p&gt;I picked these up with regular expressions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What I counted&lt;/th&gt;
&lt;th&gt;Example words (Japanese source)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Specify steps or format&lt;/td&gt;
&lt;td&gt;first / next / finally / "in a table" / bullet points / JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reject the output&lt;/td&gt;
&lt;td&gt;wrong / not that / that's off / not working&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask for the reasoning&lt;/td&gt;
&lt;td&gt;really? / is that right / evidence / source / why&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask it to verify&lt;/td&gt;
&lt;td&gt;test / run it / verify / double-check / reproduce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's counting surface phrasing, not actual competence — treat it as a rough proxy. The comparison used conversations from May 2026 onward, where I use both tools: 348 browser vs 102 terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not keeping work data in the first place
&lt;/h3&gt;

&lt;p&gt;Nearly half the history was work. That isn't even really &lt;em&gt;my&lt;/em&gt; data — it belongs to the other party.&lt;/p&gt;

&lt;p&gt;At load time I classify by folder name; for work, I throw away the body and keep only a character count.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# keep the body only when it's confirmed personal
&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;personal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The classification keywords are literally client/partner names, so they don't live in the code — they're read from a config file that never enters Git. &lt;strong&gt;The classifier itself is more sensitive than the data it classifies&lt;/strong&gt; — an obvious point I only noticed during the pre-publish check.&lt;/p&gt;

&lt;p&gt;Because of this design, I could only read 75 of the 12,530 terminal conversations. A design that protects me also trimmed my own analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  The measurement flipped three times
&lt;/h3&gt;

&lt;p&gt;My first pass showed every metric &lt;em&gt;dropping&lt;/em&gt;. Read naively: "I got sloppy." But it was the denominator's fault.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;How I measured&lt;/th&gt;
&lt;th&gt;What it showed&lt;/th&gt;
&lt;th&gt;The problem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rate per message&lt;/td&gt;
&lt;td&gt;All metrics fall&lt;/td&gt;
&lt;td&gt;Shorter messages drag it down automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate per conversation&lt;/td&gt;
&lt;td&gt;Still falls&lt;/td&gt;
&lt;td&gt;Picking up the shifting browser/terminal mix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split browser vs terminal&lt;/td&gt;
&lt;td&gt;Terminal higher on everything&lt;/td&gt;
&lt;td&gt;← adopted this&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I couldn't compare past-me to present-me directly: the tools swapped underneath me, so a skill difference and a place difference get tangled together.&lt;/p&gt;

&lt;h3&gt;
  
  
  A junked experiment: blind-judging old vs new requests
&lt;/h3&gt;

&lt;p&gt;I hid the dates and had qwen2.5 compare an old request against a new one. Result: old won 10, new won 3. But it was browser-vs-browser, so I was really comparing "browser when I used it seriously" against "browser after it became an afterthought." It says nothing about whether I improved. (I judged each pair twice with the order flipped, counting a win only when both agreed — a fix for the position bias I found in the Day 14 cat-meow quiz.)&lt;/p&gt;

&lt;p&gt;Speed notes, since I got stuck:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Snag&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Big model won't fit&lt;/td&gt;
&lt;td&gt;qwen2.5 72B: only 39.2 of 62.9 GB on the GPU → 7 min per judgment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model switching&lt;/td&gt;
&lt;td&gt;The previous model lingers; timeouts right after a switch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt length&lt;/td&gt;
&lt;td&gt;621 chars = 8s, 918 chars = 22s, 1,500 chars didn't finish in 60s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Switching to 32B (19 GB) fit entirely on the GPU → a few seconds each. Fitting or not fitting changes the order of magnitude. And the scoring isn't very reliable: of 25 browser-vs-browser pairs only 16 were usable, and for browser-old vs terminal-new, 17 of 25 timed out. No cloud API was used, so this analysis cost $0 (electricity aside).&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The AI Fluency Framework — Rick Dakan, Joseph Feller, and Anthropic (CC BY-NC-SA 4.0): &lt;a href="https://aifluencyframework.org/" rel="noopener noreferrer"&gt;https://aifluencyframework.org/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;Line up 17 months and you can see exactly what you were doing each month. Re-uploading all of that somewhere to analyze it feels like a bit much. Keeping it on my own machine is well suited to moments like this.&lt;/p&gt;

&lt;p&gt;Soon, I'd like to build a Skill of my own.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>productivity</category>
    </item>
    <item>
      <title>[Day 16] I made a theme song for my cat — lyrics, melody, and singing voice, all AI, all local</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Sat, 18 Jul 2026 14:34:28 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-16-i-made-a-theme-song-for-my-cat-lyrics-melody-and-singing-voice-all-ai-all-local-c3g</link>
      <guid>https://dev.to/peppercorn_llm/day-16-i-made-a-theme-song-for-my-cat-lyrics-melody-and-singing-voice-all-ai-all-local-c3g</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 16!&lt;/p&gt;

&lt;p&gt;Today's experiment: &lt;strong&gt;make a theme song for my family's cat&lt;/strong&gt; — and see if it actually feels like &lt;em&gt;their&lt;/em&gt; song, not a generic AI tune.&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;ACE-Step 1.5&lt;/strong&gt;, a local music-generation AI (think "a Suno you can run at home"). It produced the whole package: lyrics, melody, and a singing voice. Everything here was generated locally on my own machine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: DGX Spark (my home AI machine) / ACE-Step 1.5 XL (music-generation AI).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Today's experiment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I wanted to do
&lt;/h3&gt;

&lt;p&gt;One theme song for my cat (Japanese vocals, ~3 minutes). Plus a few background tracks on the side, to measure how fast generation really is.&lt;/p&gt;

&lt;h3&gt;
  
  
  The approach
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;First, the AI &lt;strong&gt;interviews me about my cat&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;turns my answers into lyrics&lt;/strong&gt; (there's a "template" for which answer goes where)&lt;/li&gt;
&lt;li&gt;Hand the lyrics and a style prompt to ACE-Step 1.5 to &lt;strong&gt;generate the song&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The goal
&lt;/h3&gt;

&lt;p&gt;Not "huh, the AI made something," but &lt;strong&gt;a song I'd actually recognize as my cat's&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  STEP 1: the AI interviewed me about my cat
&lt;/h2&gt;

&lt;p&gt;Writing the lyrics started with me answering six questions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;My answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;What do you call them in the song?&lt;/td&gt;
&lt;td&gt;"Our Nekko" (our little cat)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Personality in a word?&lt;/td&gt;
&lt;td&gt;Super tsundere, super timid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Favorite gesture or habit?&lt;/td&gt;
&lt;td&gt;Getting the base of their tail tapped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;One memorable episode?&lt;/td&gt;
&lt;td&gt;It takes at least six months to truly bond&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;How do you feel about them?&lt;/td&gt;
&lt;td&gt;Calm — when they're nearby&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Any song requests?&lt;/td&gt;
&lt;td&gt;Bright and upbeat, female vocal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Tsundere"&lt;/strong&gt; = prickly on the outside, secretly sweet on the inside. A very cat thing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  STEP 2: the template that turns answers into lyrics
&lt;/h2&gt;

&lt;p&gt;Each answer lands in a fixed place in the song.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Interview answer&lt;/th&gt;
&lt;th&gt;Where it goes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The name&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Chorus&lt;/strong&gt; (repeated in the catchiest spot)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personality&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Verse 1&lt;/strong&gt; (introduce the character with everyday scenes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Habit&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Verse 2&lt;/strong&gt; (sharpen the picture with specifics)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Bridge&lt;/strong&gt; (the emotional beat late in the song)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feeling&lt;/td&gt;
&lt;td&gt;The chorus's tone + the overall mood&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Song request&lt;/td&gt;
&lt;td&gt;Not lyrics — the &lt;strong&gt;style prompt&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With that template, my cat's chorus came out like this (Japanese, with a rough English gloss):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uchi-no-Nekko wa tsun-tsun-tsundere      (Our Nekko is tsun-tsun-tsundere)
Kokoro no tobira wa katai kedo           (The door to their heart stays shut, but)
Kiiroi hitomi de chiratto ichibetsu      (Those yellow eyes shoot one quick glance)
Dere wa kibun de nen ni suukai           (The sweet side? A few times a year, if you're lucky)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  STEP 3: generate. A 3-minute Japanese-vocal song
&lt;/h2&gt;

&lt;p&gt;Hand ACE-Step 1.5 XL the lyrics and a style prompt (upbeat J-pop, female vocal, Japanese), and out came a &lt;strong&gt;full 3-minute song with Japanese singing&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed: the audio is 10 seconds, but "composing" takes 8 minutes
&lt;/h3&gt;

&lt;p&gt;The interesting part was the time breakdown.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What I made&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BGM (90s, no vocals) ×4&lt;/td&gt;
&lt;td&gt;quick&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;40s total&lt;/strong&gt; (~10s each)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Theme song (3min, vocals)&lt;/td&gt;
&lt;td&gt;quick&lt;/td&gt;
&lt;td&gt;~10–15s each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Theme song (3min, vocals)&lt;/td&gt;
&lt;td&gt;with composition planner&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~8 min&lt;/strong&gt; (first run ~23 min)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's the "composition planner"?&lt;/strong&gt; = the language model (LM) built into ACE-Step 1.5. It thinks through the song's blueprint (structure, metadata) &lt;em&gt;before&lt;/em&gt; rendering audio. Better quality, but the thinking costs time.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Bonus: I made an MV too
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/gZaiTgSVTJU"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;How it's built: &lt;strong&gt;Whisper&lt;/strong&gt; transcribes the lyric timings → an &lt;strong&gt;image AI&lt;/strong&gt; (AnythingV5) makes a character and backgrounds → a &lt;strong&gt;video AI&lt;/strong&gt; (LTX-2.3) animates the character → &lt;strong&gt;rembg&lt;/strong&gt; cuts it out and composites it onto the background with subtitles (finer steps in the fold below).&lt;/p&gt;

&lt;p&gt;Honestly, the video came out pretty cursed — the lower body suddenly morphs into a tail, the eyes go a little feral — so it's mildly unsettling. That's on the "improve later" list.&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The slow part isn't the sound, it's the thinking&lt;/strong&gt;: the audio itself renders in ~10s; the long wait is the AI planning the song's structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old tools converged&lt;/strong&gt;: anime-style image (Day 11) + Whisper (Day 14) + video generation (Day 15) came together into a single MV&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;p&gt;:::details Environment and models&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Machine: DGX Spark (128GB unified memory)&lt;/li&gt;
&lt;li&gt;ACE-Step 1.5: set up from the official repo with &lt;code&gt;uv&lt;/code&gt;. &lt;strong&gt;DGX Spark (ARM64 + CUDA 13) is listed as an officially supported target&lt;/strong&gt;, and it just ran&lt;/li&gt;
&lt;li&gt;Model setup: DiT is &lt;code&gt;acestep-v15-xl-turbo&lt;/code&gt; (4B, 8 steps), planner LM is the 4B version. ~36GB of downloads total&lt;/li&gt;
&lt;li&gt;Peak memory during generation ~27GB. Generated via the REST API server (easier to reproduce)&lt;/li&gt;
&lt;li&gt;MV side: ComfyUI (LTX-2.3 22B distilled fp8) + AnythingV5 (character &amp;amp; backgrounds) + rembg 2.0.76 (cutout, anime model isnet-anime) + Whisper large-v3 (timings) + ffmpeg (assembly)
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;:::details Full lyrics and the style prompt&lt;br&gt;
Style prompt (Caption):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;upbeat J-pop, energetic and heartwarming, female vocal, bright synth,
acoustic guitar, catchy chorus, 128 bpm, Japanese lyrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lyrics are passed with structural tags like &lt;code&gt;[Verse]&lt;/code&gt; and &lt;code&gt;[Chorus]&lt;/code&gt; (the words are Japanese; the &lt;code&gt;（にゃー）&lt;/code&gt; bits are meow ad-libs sung as backing vocals):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Intro]
（にゃー）

[Verse 1]
目が合った瞬間 ぷいっとそっぽ向く（にゃっ）
呼んでも来ないのに 気づけばそばにいる
物音ひとつで ロケットダッシュ（にゃー！）
ビビリなくせに 顔は堂々

[Pre-Chorus]
ツンとすまして 知らんぷり
それでもしっぽは 正直もの（にゃ？）

[Chorus - catchy]
うちのネッコは ツンツンツンデレ（にゃにゃ！）
心の扉は かたいけど
黄色い瞳で ちらっと一瞥
デレは気分で 年に数回（にゃー）

[Verse 2]
撫でられるのは 好きじゃないくせに
しっぽの付け根を とんとん叩けば
目を細めて 喉を鳴らして
もっと続けてと 視線で命令（にゃっ）

[Bridge - emotional]
心が通じるまで 半年かかった
ゆっくりゆっくり 縮めた距離は
いまでは世界で いちばん近い
気づけば隣が 定位置になった

[Chorus - anthemic]
うちのネッコは ツンツンツンデレ（にゃにゃ！）
ビビリなところも ご愛嬌
とんとんのリズムで しっぽが揺れる
今日も我が家の 王様です（にゃー！）

[Outro - fade out]
うちのネッコ（にゃー）
うちのネッコ（にゃー にゃー）
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tip from the official docs: put the fine-grained style control in the Caption, and keep the lyric tags simple.&lt;br&gt;
:::&lt;/p&gt;

&lt;p&gt;:::details How the MV was built (the finer steps)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses the intro through the second chorus (~70s). Per-line timings from Whisper large-v3 (since I already know the lyrics, a mis-hear here and there is fine — I only use the timestamps)&lt;/li&gt;
&lt;li&gt;20+ cuts. One lyric line = one cut, each assigned a clip whose motion fits the words&lt;/li&gt;
&lt;li&gt;The character image is a single frame: my "anime-fied cat" from Day 11, redrawn into a mascot with an image AI (AnythingV5). That's handed to the video AI (LTX-2.3) to animate. ~30s per clip; motion is described in plain requests ("turn away," "walk cycle," etc.)&lt;/li&gt;
&lt;li&gt;Cutout compositing is frame-by-frame: rembg (isnet-anime) cuts out the character → composite onto a slowly zooming/panning background → draw subtitles → re-encode at 24fps&lt;/li&gt;
&lt;li&gt;Keeping the character's scale &lt;strong&gt;fixed within a cut&lt;/strong&gt; mattered. Matching to the per-frame silhouette makes it shrink the instant it stretches (learned the hard way)
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;:::details Gotchas&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The dash comes last&lt;/strong&gt;: when I asked LTX-2.3 to "suddenly bolt away," the motion tended to land in the clip's final 0.5s. Worked around it by shifting the segment I use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feed the input image in the aspect ratio you want out&lt;/strong&gt;: at first I fed a portrait (512×768) character image into a landscape (768×512) generation, and every clip came out as an upper-body zoom that cut off the ears. Rebuilt the input as a landscape full-body image to fix it. Video AIs inherit the input image's composition quite strongly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The planner may have been running slower than it should&lt;/strong&gt;: digging through the logs, the LM's fast-execution component (vLLM) seems to have failed to compile due to a missing &lt;code&gt;Python.h&lt;/code&gt;, so it may have quietly fallen back to a slow path (I didn't fully chase down the cause on Day 16). I hit the same root cause on Day 15 (&lt;code&gt;python3.12-dev&lt;/code&gt; not installed) — could be a recurring Spark gotcha
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;:::details License notes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ACE-Step 1.5&lt;/strong&gt;: code and weights both MIT. Commercial use OK&lt;/li&gt;
&lt;li&gt;Planner LM (4B, Qwen3-4B based): also MIT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The generated music is explicitly cleared for commercial use&lt;/strong&gt; in the official docs (training data is licensed + royalty-free + synthetic)&lt;/li&gt;
&lt;li&gt;MV-side tools (LTX-2.3, AnythingV5, rembg, Whisper) are the ones I already vetted on Days 11–15
:::&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ace-step/ACE-Step-1.5" rel="noopener noreferrer"&gt;ACE-Step 1.5 (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/ACE-Step/acestep-v15-xl-turbo" rel="noopener noreferrer"&gt;ACE-Step/acestep-v15-xl-turbo (Hugging Face)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;Lyrics, melody, singing, and a music video — all AI, all on my own machine. See you next time!&lt;br&gt;
Thanks for reading!&lt;/p&gt;

&lt;h1&gt;
  
  
  100ExperimentsWithDGX #LocalLLM
&lt;/h1&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>music</category>
    </item>
    <item>
      <title>[Day 15] A cat photo became a video — with a meow. Two local video AIs compared: 30s vs 475s</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Mon, 13 Jul 2026 06:14:04 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-15-a-cat-photo-became-a-video-with-a-meow-two-local-video-ais-compared-30s-vs-475s-g70</link>
      <guid>https://dev.to/peppercorn_llm/day-15-a-cat-photo-became-a-video-with-a-meow-two-local-video-ais-compared-30s-vs-475s-g70</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 15!&lt;/p&gt;

&lt;p&gt;Today's experiment: &lt;strong&gt;turn one photo of my family's cat into a video that moves — and meows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An image-to-video AI takes a single photo and imagines how the scene continues. I ran two of them and compared the results.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: DGX Spark (my home AI machine) / two local video AIs (LTX-2.3 and Wan 2.2) / one photo of my family's cat.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Today's experiment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I wanted to find out
&lt;/h3&gt;

&lt;p&gt;Give both AIs the same cat photo and the same request — "look at the camera and meow once, about 4–5 seconds" — and compare them.&lt;/p&gt;

&lt;h3&gt;
  
  
  The two models
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wan 2.2&lt;/strong&gt;: a well-established favorite for local video. Video only, &lt;strong&gt;no audio&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LTX-2.3&lt;/strong&gt;: a newer model (March 2026) that generates &lt;strong&gt;video and audio together in one pass&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Same photo, same kind of prompt. Measured: &lt;strong&gt;generation time / memory / quality / sound&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The goal
&lt;/h3&gt;

&lt;p&gt;Decide which one to use when — with actual numbers from my own machine, not vibes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The result first: 30 seconds vs 475 seconds
&lt;/h2&gt;

&lt;p&gt;For a ~4–5 second video, LTX-2.3 took &lt;strong&gt;30 seconds&lt;/strong&gt;. Wan 2.2 took &lt;strong&gt;475 seconds&lt;/strong&gt;. Roughly a 16x gap.&lt;/p&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%2Fvq4p38i2h8rc1wxof6gz.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%2Fvq4p38i2h8rc1wxof6gz.png" alt="Generation time for one cat photo to a 4-5s video: Wan 2.2 standard 475s, with speed LoRA 70s, LTX-2.3 30s with audio" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's a "step"?&lt;/strong&gt; The number of refinement passes the AI makes while drawing the video. More steps = more careful but slower.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Time wasn't the only difference, though. Here is each result in turn.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wan 2.2 — nearly photoreal, but 8 minutes per video
&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%2F74sxomq74xa2s7u2gojk.gif" 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%2F74sxomq74xa2s7u2gojk.gif" alt="Wan 2.2 result: the cat opens its mouth and meows, nearly photoreal" width="300" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wan 2.2's output (475s, silent).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The fur, markings, and face are &lt;strong&gt;almost indistinguishable from the real photo&lt;/strong&gt;, and it meows exactly as asked. But one video takes &lt;strong&gt;475 seconds&lt;/strong&gt; (~8 minutes), with &lt;strong&gt;no sound&lt;/strong&gt; (by design).&lt;/p&gt;

&lt;p&gt;A speed-up add-on cut it to &lt;strong&gt;70 seconds&lt;/strong&gt;, at the cost of tamer motion — more of a tongue flick than a proper meow.&lt;/p&gt;




&lt;h2&gt;
  
  
  LTX-2.3 — 30 seconds, and it came with a meow
&lt;/h2&gt;

&lt;p&gt;Here is LTX-2.3's output, sound included (volume on 🔊):&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/yjT-xcIB518"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Done in &lt;strong&gt;30 seconds&lt;/strong&gt; — and along with the video, it generated &lt;strong&gt;an audio track with actual meows&lt;/strong&gt;. I asked for one meow; it enthusiastically gave two, short cat-like "myah" sounds roughly in sync with the mouth. (And it sounds startlingly like my family cat's real meow.)&lt;/p&gt;

&lt;p&gt;Image quality looks about the same as Wan's on screen. The difference is in the motion: Wan moved more, and more realistically; LTX was a bit more subdued.&lt;/p&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%2Fix83k9rxd86m55tee52e.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%2Fix83k9rxd86m55tee52e.png" alt="One frame from each video generated from the same photo: Wan 2.2 on the left, LTX-2.3 on the right" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: give it the first and last frame, and you can design the camera work
&lt;/h2&gt;

&lt;p&gt;LTX-2.3 can take a &lt;strong&gt;first frame and a last frame, and fill in everything between&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I gave it the wide shot as the first frame, and a face close-up (a crop of the same photo) as the last.&lt;/p&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%2Fgrxm02ss18kj3ifwabjx.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%2Fgrxm02ss18kj3ifwabjx.png" alt="First frame: wide shot. Last frame: face close-up. The model filled in a zoom with a meow in the middle" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&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%2F5hjmeh1suaryl6busdoz.gif" 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%2F5hjmeh1suaryl6busdoz.gif" alt="The generated video zooms from wide shot to close-up with a meow along the way" width="300" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Generated in 35 seconds.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The video starts wide, meows while zooming in, and ends on the close-up I specified.&lt;/p&gt;




&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Peak memory&lt;/th&gt;
&lt;th&gt;Audio&lt;/th&gt;
&lt;th&gt;Quality impression&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wan 2.2 (standard)&lt;/td&gt;
&lt;td&gt;475s&lt;/td&gt;
&lt;td&gt;~49GB&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;near-photoreal, faithful motion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wan 2.2 + speed LoRA&lt;/td&gt;
&lt;td&gt;70s&lt;/td&gt;
&lt;td&gt;~46GB&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;clean but tamer motion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTX-2.3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~57GB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;on par with Wan; tamer motion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTX-2.3 (designed zoom)&lt;/td&gt;
&lt;td&gt;35s&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;same as above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Resolutions and frame counts follow each model's recommended settings, so this isn't a strictly identical-conditions benchmark (details below).&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Distinct characters&lt;/strong&gt;: Wan for faithful motion, LTX for speed and sound. Not "which is better" but "which for what"&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Specifying the first and last frame let me design the camera work&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The time gap is really a retry-count gap&lt;/strong&gt;: at 30 seconds, "one more try" is easy&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment and models
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Machine: DGX Spark (128GB unified memory)&lt;/li&gt;
&lt;li&gt;ComfyUI v0.24.0 (native LTX-2.3 support) + Lightricks' official custom nodes&lt;/li&gt;
&lt;li&gt;Both models in their fp8 (weight-reduced) versions; ~91GB of downloads total

&lt;ul&gt;
&lt;li&gt;Wan 2.2 I2V-A14B (high-noise/low-noise pair, 14GB each) + its text encoder&lt;/li&gt;
&lt;li&gt;LTX-2.3 22B distilled (28GB) + Gemma 3 12B as its text encoder (13GB)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The speed add-on for Wan is the lightx2v 4-step LoRA&lt;/li&gt;
&lt;li&gt;Attention backend unified on PyTorch SDPA for both models&lt;/li&gt;
&lt;li&gt;Runs were headless ComfyUI (API calls) driven by a small runner script that logs time and memory for every run&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comparison-condition fine print
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Wan 2.2: 480×640, 81 frames, 16fps (~5.1s), 20 steps&lt;/li&gt;
&lt;li&gt;LTX-2.3: 512×768, 97 frames, 24fps (~4.0s), 8 steps&lt;/li&gt;
&lt;li&gt;Each model ran its official template's representative settings, so resolution/frames/steps differ — it's a comparison of each model's everyday settings&lt;/li&gt;
&lt;li&gt;Per-step time: ~23s for Wan, ~2.6s for LTX&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The one gotcha I hit (LTX)
&lt;/h3&gt;

&lt;p&gt;My hand-built workflow crashed inside an LTX helper node (LTXVCropGuides).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cause: with audio+video generation, the latent is a special combined tensor (a NestedTensor), and this node calls an operation it doesn't support&lt;/li&gt;
&lt;li&gt;Fix #1: for plain image-to-video, the node isn't needed at all — removing it solved the crash&lt;/li&gt;
&lt;li&gt;Fix #2: for the first/last-frame trick the node &lt;em&gt;is&lt;/em&gt; required, so I moved it to run &lt;strong&gt;after&lt;/strong&gt; the audio/video split, where the tensor is ordinary again&lt;/li&gt;
&lt;li&gt;The known audio-VAE NaN bug I had braced for never appeared&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  License notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wan 2.2&lt;/strong&gt;: Apache 2.0. Commercial use OK, generated content is yours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LTX-2.3&lt;/strong&gt;: LTX-2 Community License. Free commercial use under $10M annual revenue. &lt;strong&gt;Disclosing that content is AI-generated is mandatory&lt;/strong&gt;. Also has an unusual remote-access restriction clause&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 3&lt;/strong&gt; (LTX's text encoder): Gemma Terms of Use. Commercial use OK, outputs belong to the user, subject to the prohibited-use policy&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Lightricks/LTX-2" rel="noopener noreferrer"&gt;Lightricks LTX-2 (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B" rel="noopener noreferrer"&gt;Wan-AI/Wan2.2-I2V-A14B (Hugging Face)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;One photo, and 30 seconds later my family's cat was meowing on screen. See you next time!&lt;br&gt;
Thanks for reading!&lt;/p&gt;

&lt;h1&gt;
  
  
  100ExperimentsWithDGX #LocalLLM
&lt;/h1&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>[Day 14] I quizzed an AI on cat meows. It scored worse than random guessing</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Thu, 09 Jul 2026 04:57:52 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-14-i-quizzed-an-ai-on-cat-meows-it-scored-worse-than-random-guessing-42j5</link>
      <guid>https://dev.to/peppercorn_llm/day-14-i-quizzed-an-ai-on-cat-meows-it-scored-worse-than-random-guessing-42j5</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 14!&lt;/p&gt;

&lt;p&gt;Today's experiment: &lt;strong&gt;play cat meows to an AI and have it guess what the cat wants.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I really wanted to use my own cat's meows, but couldn't get recordings — so I used a public research dataset (meows from 21 cats).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: DGX Spark (my home AI machine) / Whisper (speech-to-text AI) / Qwen2-Audio (an AI that can listen to audio directly) / 440 cat meows (public dataset).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Today's experiment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I wanted to find out
&lt;/h3&gt;

&lt;p&gt;Can an AI tell how a cat feels, from its meow alone?&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A research dataset of meows with situation labels (21 cats, 440 clips)&lt;/li&gt;
&lt;li&gt;Hide the labels → 90-question 3-choice quiz → grade it&lt;/li&gt;
&lt;li&gt;Detour: see what Whisper makes of raw meows&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The goal
&lt;/h3&gt;

&lt;p&gt;Find out, by experiment, whether a general-purpose audio AI can understand cat voices — judged by an actual score, not a feeling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Result first: this AI could not read cat feelings
&lt;/h2&gt;

&lt;p&gt;On a 3-choice quiz, its accuracy was &lt;strong&gt;23.3%&lt;/strong&gt; — below random guessing (33.3%).&lt;/p&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%2F5zqe7utvfb7hs4h8d4a3.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%2F5zqe7utvfb7hs4h8d4a3.png" alt="3-choice meow quiz accuracy. The specialized 2019 ML model got 95.9%, Qwen2-Audio got 23.3% and 32.2%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I dug in, it turned out the AI &lt;strong&gt;wasn't really listening to the meows in the first place&lt;/strong&gt;. Here's what happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's material: 440 meows from 21 cats
&lt;/h2&gt;

&lt;p&gt;I used &lt;strong&gt;CatMeows&lt;/strong&gt;, a research dataset of cat vocalizations (credit at the end of this post). Every meow comes with an &lt;strong&gt;answer label&lt;/strong&gt; — the situation it was recorded in.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Label&lt;/th&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Brushing&lt;/td&gt;
&lt;td&gt;Being brushed by the owner&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Waiting for food&lt;/td&gt;
&lt;td&gt;Food is being prepared&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;Left alone in an unfamiliar room&lt;/td&gt;
&lt;td&gt;221&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Experiment 1: first, a speech-to-text AI
&lt;/h2&gt;

&lt;p&gt;First, a detour: what happens if you hand raw cat meows to &lt;strong&gt;Whisper&lt;/strong&gt;, the speech-to-text AI?&lt;/p&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%2Flrikiizrkbfyr7yue93w.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%2Flrikiizrkbfyr7yue93w.png" alt="Waveform of a cat meow. Whisper transcribed it as "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Japanese mode, &lt;strong&gt;10 of the 12 meows&lt;/strong&gt; came back as 「ご視聴ありがとうございました」("Thank you for watching!") — plus one 「チャンネル登録をお願いいたします。」(&lt;strong&gt;"Please subscribe to my channel."&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;Whisper is trained on subtitled video audio, so unrecognizable sounds tend to come back as the stock phrases that end videos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment 2: a 90-question quiz for the AI
&lt;/h2&gt;

&lt;p&gt;Next, I handed meows one at a time to &lt;strong&gt;Qwen2-Audio&lt;/strong&gt; (an AI that listens to audio directly) and asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a recording of a domestic cat meowing.&lt;br&gt;
In which situation was this meow most likely recorded?&lt;br&gt;
(A) The cat is being brushed by its owner.&lt;br&gt;
(B) The cat is waiting for food.&lt;br&gt;
(C) The cat is isolated alone in an unfamiliar room.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;90 questions total — 30 meows from each situation. Random guessing would score 33.3%.&lt;/p&gt;

&lt;p&gt;The result: &lt;strong&gt;23.3% (21 out of 90)&lt;/strong&gt;. It lost to random guessing.&lt;/p&gt;

&lt;p&gt;Here's the per-situation breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&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;Brushing&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 / 30&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Waiting for food&lt;/td&gt;
&lt;td&gt;9 / 30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;12 / 30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Brushing: &lt;strong&gt;zero&lt;/strong&gt;. The AI never once picked "(A) brushing" in all 90 questions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Experiment 3: shuffle the option order
&lt;/h2&gt;

&lt;p&gt;"Never picks A" is odd. So I checked: &lt;strong&gt;same meows, same question, only the option order swapped&lt;/strong&gt; — another 90 questions.&lt;/p&gt;

&lt;p&gt;If the AI answers by listening, changing the order shouldn't change its answers.&lt;/p&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%2Fc87g8yl4vfbbhvn6buhi.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%2Fc87g8yl4vfbbhvn6buhi.png" alt="Which option the AI picked. Run 1: B 40 times, C 50 times. Run 2: B 89 times, C once. Option A: zero in both runs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, the answers flipped completely.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;The AI's answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before the swap&lt;/td&gt;
&lt;td&gt;"isolation" ×50 / "food" ×40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After the swap&lt;/td&gt;
&lt;td&gt;almost all "food" (×89)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Option A (first)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0&lt;/strong&gt; in both runs (0 out of 180)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The AI was answering by option order, not by the meow.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(The post-swap 32.2% accuracy only looks better because answering "food" every time gets exactly the food questions right.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Meanwhile, a meow-only AI scored 96%
&lt;/h2&gt;

&lt;p&gt;The research team behind this dataset built a &lt;strong&gt;purpose-built meow classifier&lt;/strong&gt; (2019) that scores &lt;strong&gt;95.9%&lt;/strong&gt; on the same 3-way task (trained on this data, so not the same conditions as my zero-shot run).&lt;/p&gt;

&lt;p&gt;General-purpose audio AI seems to be &lt;strong&gt;strong at words, still weak at what non-word sounds mean&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quizzes get gamed&lt;/strong&gt;: an audio LLM may answer by option position instead of listening. Shuffle the options and rerun to catch it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't trust accuracy alone&lt;/strong&gt;: the 32.2% run was all-in on one answer. Check the answer distribution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whisper hallucinates&lt;/strong&gt;: non-speech sounds come back as stock phrases from its training data ("Thank you for watching!")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Niche tasks want specialists&lt;/strong&gt;: for reading meows, a purpose-built model is the right tool&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Models and dataset
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Speech-to-text: openai/whisper-large-v3-turbo (MIT license)&lt;/li&gt;
&lt;li&gt;Quiz: Qwen/Qwen2-Audio-7B-Instruct (Apache 2.0), run locally via transformers, greedy decoding (do_sample=False) so answers are reproducible&lt;/li&gt;
&lt;li&gt;Dataset: CatMeows (CC BY 4.0). 21 cats, 440 clips, 8kHz mono. The first letter of each filename is the ground-truth label (B=brushing, F=food, I=isolation)&lt;/li&gt;
&lt;li&gt;Machine: DGX Spark. A 7B audio model fits in memory with plenty of room; a few seconds per question&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How I asked the quiz (prompt)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This is a recording of a domestic cat meowing.
In which situation was this meow most likely recorded?
Choose exactly one:
(A) The cat is being brushed by its owner.
(B) The cat is waiting for food.
(C) The cat is isolated alone in an unfamiliar room.
Answer with only the single letter A, B, or C.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;30 clips per label, stratified sampling with a fixed random seed → 90 questions&lt;/li&gt;
&lt;li&gt;The control run swapped the wording of (A) and (C) only; everything else identical&lt;/li&gt;
&lt;li&gt;Grading: extract the first A/B/C that appears in the model's reply&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Environment notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I reused an existing Python environment and added librosa for audio loading, which bumped numpy past what another library wanted (core stayed fine). Lesson re-learned: &lt;strong&gt;separate environments per experiment is the safer way&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The recordings are 8kHz (phone quality); the models expect 16kHz, so audio is resampled on load. Could that hurt? Maybe — but the specialized model hit 96% on the same 8kHz clips, so it's not much of an excuse&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Meow dataset: CatMeows: A Publicly-Available Dataset of Cat Vocalizations (Zenodo, DOI: &lt;a href="https://zenodo.org/records/4008297" rel="noopener noreferrer"&gt;10.5281/zenodo.4008297&lt;/a&gt;, CC BY 4.0)&lt;/li&gt;
&lt;li&gt;Original paper: Ntalampiras et al., "Automatic Classification of Cat Vocalizations Emitted in Different Contexts," Animals 9(8), MDPI, 2019 (the 95.9% figure is from this paper)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;I set out to have an AI listen to cats' feelings, and instead found an AI that answers by option order. The day it understands my own cat's meows still seems a way off.&lt;/p&gt;

&lt;p&gt;Next up: a completely different experiment. Thanks for reading!&lt;/p&gt;

&lt;p&gt;#100ExperimentsWithDGX #LocalLLM&lt;/p&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>[Day 13] I got a cat to "talk." The biggest wall: the AI couldn't recognize the cat's face</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Fri, 03 Jul 2026 07:06:10 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-13-i-got-a-cat-to-talk-the-biggest-wall-the-ai-couldnt-recognize-the-cats-face-1o8l</link>
      <guid>https://dev.to/peppercorn_llm/day-13-i-got-a-cat-to-talk-the-biggest-wall-the-ai-couldnt-recognize-the-cats-face-1o8l</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 13!&lt;/p&gt;

&lt;p&gt;Today's experiment: take &lt;strong&gt;a single cat image, lay human facial motion on top of it, and make a "talking cat."&lt;/strong&gt; It's the usual "talking avatar" idea, except I use a cat instead of my own face. The tool is &lt;strong&gt;LivePortrait&lt;/strong&gt; (still image + a "driving video" of motion → it transfers the video's expressions onto the still).&lt;/p&gt;

&lt;p&gt;The result: a &lt;strong&gt;cat that properly talks&lt;/strong&gt;. The hard part wasn't the animation — it was the step before it, &lt;strong&gt;getting the AI to recognize the cat's face&lt;/strong&gt;. Here's where it snagged, and how I got past it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: DGX Spark (my home AI machine) / LivePortrait / one cat image (AI-generated) / a driving video (ships with LivePortrait).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Result first: a talking cat
&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%2F4n1mxrj5y9wmoq6t4c6c.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%2F4n1mxrj5y9wmoq6t4c6c.png" alt="The finished " width="512" height="768"&gt;&lt;/a&gt;&lt;/p&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%2F4s108df8tbm2ly7ehwni.gif" 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%2F4s108df8tbm2ly7ehwni.gif" alt="Mouth and eyes both move — a properly talking cat (generated result, GIF)" width="400" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The mouth and eye movement from a human talking-video landed on the cat's face. But there were a few snags on the way here.&lt;/p&gt;




&lt;h2&gt;
  
  
  Snag #1: it won't recognize a "face" at all
&lt;/h2&gt;

&lt;p&gt;To make a cat talk, there's a first step: &lt;strong&gt;find where the face is in the image (the position of eyes, nose, mouth)&lt;/strong&gt; — the &lt;em&gt;face detector&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The tool I reached for first &lt;strong&gt;didn't have a single one installed&lt;/strong&gt;, so it stopped with an error every time. I added detectors and tried again, but the answer didn't change:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Detector I added&lt;/th&gt;
&lt;th&gt;Commercial use&lt;/th&gt;
&lt;th&gt;Recognized the cat's face?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MediaPipe&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;❌ no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InsightFace&lt;/td&gt;
&lt;td&gt;Not allowed (non-commercial)&lt;/td&gt;
&lt;td&gt;❌ no&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None could recognize the cat's face. They're not broken — they're all built to find &lt;strong&gt;human faces&lt;/strong&gt;, so a cat's face doesn't register as a "face."&lt;/p&gt;




&lt;h2&gt;
  
  
  Snag #2: why it couldn't recognize the face
&lt;/h2&gt;

&lt;p&gt;The "animal mode" of the tool I started with only swaps the &lt;strong&gt;motion part&lt;/strong&gt; for an animal version — &lt;strong&gt;the crucial "find the animal's face" detector was never bundled in&lt;/strong&gt;. All that's left is the human one.&lt;/p&gt;

&lt;p&gt;That was why work had stalled here last time, too. Not disk space, not the GPU — just a tool that couldn't look for an animal's face.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix: use the original tool, and skip the build
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;upstream (original) LivePortrait&lt;/strong&gt; does ship an animal-specific face detector, called &lt;strong&gt;XPose&lt;/strong&gt;. So I set it up in a separate folder and used that.&lt;/p&gt;

&lt;p&gt;The catch: XPose normally needs you to &lt;strong&gt;compile (build) a part on your own machine&lt;/strong&gt;, and on this new-generation machine there was no guarantee the build would go through. So, reading the code, I found a &lt;strong&gt;slower but no-build spare part&lt;/strong&gt; tucked inside. I rewrote three files to route to it, dodging compilation entirely. For a short clip, the slowness doesn't matter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The exact files and edits are in "The details" at the bottom.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  It worked — but at first it just looked like the cat stuck its tongue out
&lt;/h2&gt;

&lt;p&gt;The detector ran, the cat's face was recognized, and a video generated. But the first result looked like the &lt;strong&gt;cat just gave a little tongue-out blep&lt;/strong&gt;. The expression transfer was clearly working — so why?&lt;/p&gt;

&lt;p&gt;The cause was the &lt;strong&gt;driving video&lt;/strong&gt; (= the input you feed the AI). My first one was a short "just open the mouth" sample — and if the reference only opens its mouth, the cat only opens its mouth. Swapping in a video of someone &lt;strong&gt;actually talking&lt;/strong&gt; gave me a cat whose eyes and mouth both moved.&lt;/p&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%2Fu0pld2xnnne4cgvgi1ab.gif" 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%2Fu0pld2xnnne4cgvgi1ab.gif" alt="Left: a " width="640" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The quality of the driving video pretty much decides the result.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's takeaway
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The hard part isn't the motion engine, it's &lt;strong&gt;recognizing the animal's face&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A tool's "animal support" can be &lt;strong&gt;a label with the actual part (the detector) missing&lt;/strong&gt;. When it won't run, tracking down which part is missing is the fast path.&lt;/li&gt;
&lt;li&gt;If you hit a "must compile" part on a too-new machine, &lt;strong&gt;look first for a no-build fallback route&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The quality of the result is mostly decided by &lt;strong&gt;the quality of the input (the driving motion)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A note on licensing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The detectors that could recognize the cat's face (XPose / InsightFace) are both &lt;strong&gt;non-commercial&lt;/strong&gt; licenses. So I avoid commercial use of the footage itself, and this article keeps the focus on the method and the gotchas.&lt;/li&gt;
&lt;li&gt;The commercially-OK detector (MediaPipe) couldn't recognize the cat this time.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What was missing, and how it was solved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The "animal mode" of the ComfyUI node I used first only swaps in the animal &lt;strong&gt;motion&lt;/strong&gt; model; the &lt;strong&gt;animal face detector (XPose) is not bundled&lt;/strong&gt;. Human detectors (InsightFace / MediaPipe / FaceAlignment) can't detect a cat's face, so it stops at &lt;code&gt;No face detected&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The fix: set up &lt;strong&gt;upstream KwaiVGI/LivePortrait&lt;/strong&gt; in a separate folder, fetch the official weight set (including &lt;code&gt;xpose.pth&lt;/code&gt;), and use &lt;code&gt;inference_animals.py&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The InsightFace and landmark models I already had could be reused.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The no-compile patch for XPose
&lt;/h3&gt;

&lt;p&gt;XPose is built to &lt;strong&gt;compile its own CUDA custom op&lt;/strong&gt; called &lt;code&gt;MultiScaleDeformableAttention&lt;/code&gt;. On the newest GPU/CUDA generation there's no guarantee that build succeeds, so I &lt;strong&gt;routed it to the bundled pure-PyTorch fallback&lt;/strong&gt; instead.&lt;/p&gt;

&lt;p&gt;Three files edited (under XPose's &lt;code&gt;ops/&lt;/code&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;functions/ms_deform_attn_func.py&lt;/code&gt;: wrap the compiled-version &lt;code&gt;import&lt;/code&gt; in try/except, set the flag to &lt;code&gt;False&lt;/code&gt; on failure.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;modules/ms_deform_attn.py&lt;/code&gt;: when that flag is &lt;code&gt;False&lt;/code&gt;, branch &lt;code&gt;forward&lt;/code&gt; through the pure-PyTorch &lt;code&gt;ms_deform_attn_core_pytorch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;(if needed) add &lt;code&gt;weights_only=False&lt;/code&gt; to the &lt;code&gt;torch.load&lt;/code&gt; in &lt;code&gt;animal_landmark_runner.py&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now animal detection runs with no compilation at all. It's slower, but for a short clip it's fine (one generation finished in ~8 seconds).&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each time I swapped detectors, the base library (numpy) version see-sawed (mediapipe wants numpy&amp;lt;2, insightface wants 2.x). The existing core (cv2/torch) survived, but &lt;strong&gt;the clean approach is to keep upstream LivePortrait in its own isolated environment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When stopping the server, killing by process name &lt;strong&gt;took out my own command too&lt;/strong&gt;. Stopping by port number was safe.&lt;/li&gt;
&lt;li&gt;Disk and GPU had plenty of headroom the whole time — not once was the snag a resource shortage.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next up
&lt;/h2&gt;

&lt;p&gt;Next time I'm switching things up again with a different kind of experiment 🎬&lt;/p&gt;

&lt;h1&gt;
  
  
  100ExperimentsWithDGX #LocalLLM
&lt;/h1&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>[Day 12] I tried to build a line-art LoRA from video frames, and the characters' heads fused together</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Tue, 23 Jun 2026 00:41:38 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-12-i-tried-to-build-a-line-art-lora-from-video-frames-and-the-characters-heads-fused-3go</link>
      <guid>https://dev.to/peppercorn_llm/day-12-i-tried-to-build-a-line-art-lora-from-video-frames-and-the-characters-heads-fused-3go</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 12!&lt;/p&gt;

&lt;p&gt;This time I tried to take my own hand-drawn animation (a short video) and build a&lt;br&gt;
&lt;strong&gt;line-art LoRA that learns its art style and characters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The plan was a little lazy, honestly. The usual way to train this is to prepare&lt;br&gt;
character stills one by one, by hand. But I thought:&lt;br&gt;
&lt;strong&gt;"I already have the video — why not just rip frames out of it and collect the&lt;br&gt;
training material the easy way?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Short version: &lt;strong&gt;the lines got clean, but the one thing that mattered — actually&lt;br&gt;
reproducing my characters — failed completely.&lt;/strong&gt; And the &lt;em&gt;reason&lt;/em&gt; it failed is what I&lt;br&gt;
actually took away from today.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: my home AI machine (DGX Spark) + a training tool (Kohya) + my own&lt;br&gt;
hand-drawn animation (two characters).&lt;br&gt;
Note: everything shown here is &lt;em&gt;LoRA-generated line art only&lt;/em&gt;. I'm not showing the&lt;br&gt;
source video itself or where it's published.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Result first: failure on the left, and the right is &lt;em&gt;also&lt;/em&gt; a failure
&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%2Fzl409tzbll5078bcyf4u.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%2Fzl409tzbll5078bcyf4u.png" alt="Left: v1 trained on video frames (heads fuse). Right: v2 after rebuilding the dataset (clean lines, but a different person)" width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Left is the first attempt. A second body grows upside-down out of the top of the head.&lt;br&gt;
Right is after I tracked down the causes and rebuilt the data — &lt;strong&gt;the lines came out&lt;br&gt;
clean&lt;/strong&gt;. But as you'll see, it's also a failure: it looks &lt;strong&gt;nothing like my original&lt;br&gt;
characters&lt;/strong&gt; — it's a totally different person.&lt;/p&gt;

&lt;p&gt;How can "the breakage got fixed" still be a failure? Let me walk through it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I did: rip frames from the video and train (v1)
&lt;/h2&gt;

&lt;p&gt;Simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract still frames from my hand-drawn animation video&lt;/li&gt;
&lt;li&gt;Roughly select ~300 of them as training material&lt;/li&gt;
&lt;li&gt;Train a LoRA on them&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The training itself took &lt;strong&gt;17 minutes on the DGX&lt;/strong&gt;. Lightning fast.&lt;br&gt;
"Oh, this is easy," I thought — for about five minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then I generated, and it was a mess
&lt;/h2&gt;

&lt;p&gt;Asking the finished LoRA for "single character" and "two-person scenes" gave me this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;How it broke&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fused heads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A second body sprouts from the head / multiple faces merge into one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backgrounds won't go away&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Even asking for "white background," blue/pink backgrounds show up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thick, muddy lines&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No clean line art, everything is heavy and blurry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ghost text&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Meaningless characters (leftover captions) get baked into the image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It &lt;em&gt;could&lt;/em&gt; tell the characters apart (A and B were recognized as different people).&lt;br&gt;
But the looks were wrecked. Lined up, the actual outputs were quite the horror show:&lt;/p&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%2F0xevt3k0nq306nkaya8v.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%2F0xevt3k0nq306nkaya8v.png" alt="Line art where bodies tangle together, you can't tell whose limbs are whose" width="512" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;▲ When two characters show up, you can't tell what's what anymore&lt;/em&gt;&lt;/p&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%2Fzeusi0o7hnn90flo62fu.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%2Fzeusi0o7hnn90flo62fu.png" alt="A dinner-table scene where multiple faces fuse and multiply" width="512" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;▲ Faces fuse and multiply&lt;/em&gt;&lt;/p&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%2Fau6i2xqwhmnebfzv8kpc.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%2Fau6i2xqwhmnebfzv8kpc.png" alt="Line art covered in meaningless floating text" width="512" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;▲ Leftover captions bake in as "ghost text" all over the frame&lt;/em&gt;&lt;/p&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%2Fqh5kmmk9srjvbndn8w14.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%2Fqh5kmmk9srjvbndn8w14.png" alt="A figure screaming as its body dissolves" width="512" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;▲ Train on high-motion frames and everything just melts&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why did it break? (the real point)
&lt;/h2&gt;

&lt;p&gt;The culprit was &lt;strong&gt;using video frames as the source itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A video, if you think about it, is footage with &lt;em&gt;many things happening at once&lt;/em&gt;. Rip a&lt;br&gt;
frame out of it and you don't just learn the character's shape — you learn all the&lt;br&gt;
surrounding noise too.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fused heads&lt;/td&gt;
&lt;td&gt;Video has lots of frames where &lt;strong&gt;two people move in the same shot&lt;/strong&gt;. The model learns an instant where bodies overlap as "one single body"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backgrounds stick&lt;/td&gt;
&lt;td&gt;Tons of background-laden frames get in. It learns "character = with this colored background" as a set, and you can't override it later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thick lines&lt;/td&gt;
&lt;td&gt;Mid-motion frames are blurred; that blur bakes in as a "thick-line style"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ghost text&lt;/td&gt;
&lt;td&gt;Caption text sitting on the frames sneaks into the material and gets learned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In one sentence: &lt;strong&gt;video is noise-laden material — motion, overlap, backgrounds, text&lt;br&gt;
all baked in&lt;/strong&gt; — and it's a poor way to cleanly extract just a character's shape.&lt;/p&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%2Fuogk03gzxmsq9cvqgsuv.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%2Fuogk03gzxmsq9cvqgsuv.png" alt="Line art with a pink background that won't go away, character multiplied" width="512" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;▲ Even asking for "white background," the training background color (pink) won't peel off — and the character multiplies for good measure&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Fixing it (v2)
&lt;/h2&gt;

&lt;p&gt;Now that I knew the causes, I &lt;strong&gt;rebuilt the data side&lt;/strong&gt; and trained again.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Auto-remove frames with caption text&lt;/li&gt;
&lt;li&gt;Drop "no character" frames — pure backgrounds, transition frames (~300 → 141 frames)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split into three groups&lt;/strong&gt; — "A only," "B only," "the two together" — to stop the
characters from bleeding into each other&lt;/li&gt;
&lt;li&gt;Switch the base model to an anime one (good at line art) and tune the settings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;v1 (first)&lt;/th&gt;
&lt;th&gt;v2 (rebuilt)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fused heads&lt;/td&gt;
&lt;td&gt;✗ frequent&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓ gone&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thick/muddy lines&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓ thin and clean&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Line-art look&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓✓ clearly line art&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backgrounds&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;△ white now, but color bleeds onto clothes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ghost text&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;△ far less, a little remains&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resemblance to my characters (the whole point)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✗ a different person — sometimes missing an arm&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Look at just the top of that table and you think "oh, it's fixed!" I did too, for a second.&lt;br&gt;
&lt;strong&gt;The noise problems (fused heads, thick lines, background color) really were fixed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But look closer and there's &lt;strong&gt;no trace of my original characters.&lt;/strong&gt; The lines are clean,&lt;br&gt;
but what comes out is "some vaguely anime-style stranger." The worst ones are even missing&lt;br&gt;
an arm.&lt;/p&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%2Fubm0tzzjmq66jz8ch5el.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%2Fubm0tzzjmq66jz8ch5el.png" alt="v2 line art of a single character cooking" width="512" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;▲ The lines are clean, sure — but it looks nothing like my original character. A stranger.&lt;/em&gt;&lt;/p&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%2Fkx1amiumewvtl0qzfgbh.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%2Fkx1amiumewvtl0qzfgbh.png" alt="v2 line art of a single character standing" width="512" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;▲ Stable as a drawing. Still zero resemblance (and color still bleeds onto the clothes)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The real wall I never got past
&lt;/h2&gt;

&lt;p&gt;Even with the noise gone, &lt;strong&gt;the one thing I actually wanted — reproducing my own&lt;br&gt;
characters — was completely out of reach.&lt;/strong&gt; All I got was a clean-looking stranger.&lt;/p&gt;

&lt;p&gt;And since &lt;strong&gt;even a single character came out this much of a different person, "two of them&lt;br&gt;
together, in a scene with a relationship" was even more hopeless.&lt;/strong&gt; Ask for the two of them&lt;br&gt;
and only one shows up, or it falls apart.&lt;/p&gt;

&lt;p&gt;I chased down why two-person was &lt;em&gt;especially&lt;/em&gt; bad, too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;There were only 9 real frames&lt;/strong&gt; in the entire set where the two were naturally side
by side&lt;/li&gt;
&lt;li&gt;I tried to get more by re-checking another 478 frames, but every "two-person" hit was
&lt;strong&gt;a false positive&lt;/strong&gt; (the detector reacting to on-screen text or body fragments)&lt;/li&gt;
&lt;li&gt;→ In other words, &lt;strong&gt;you cannot grow "two-person scenes" out of video material&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the composition you want (the two of them cleanly together) doesn't happen to exist in&lt;br&gt;
the video, you can't extract it after the fact. Obvious in hindsight — but it really sank&lt;br&gt;
in once I'd hit the wall.&lt;/p&gt;




&lt;h2&gt;
  
  
  Today's takeaway
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ripping frames from a video teaches the model "vaguely anime-ish" at best.&lt;br&gt;
It couldn't reproduce &lt;em&gt;my&lt;/em&gt; characters even for a single figure (let alone two / a&lt;br&gt;
relationship). In the end, you have to hand-draw the stills you want it to learn.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This isn't a sour-grapes conclusion — it's the answer after exhausting every way to grow&lt;br&gt;
the data. I took the long way around trying to be lazy, but because of it I now&lt;br&gt;
&lt;strong&gt;understand, first-hand, why hand-drawn stills are necessary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Someday I'll act on this conclusion and &lt;strong&gt;prepare the compositions by hand&lt;/strong&gt; — but that's&lt;br&gt;
a project for another day.&lt;/p&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Training settings (v1 → v2)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;v1&lt;/th&gt;
&lt;th&gt;v2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base model&lt;/td&gt;
&lt;td&gt;SD1.5 (plain)&lt;/td&gt;
&lt;td&gt;anime-style (good at line art)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clip_skip&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;~300 lumped together&lt;/td&gt;
&lt;td&gt;split into 3 groups (68 / 64 / 9)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Epochs&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;LoRA dim / alpha&lt;/td&gt;
&lt;td&gt;32 / 16&lt;/td&gt;
&lt;td&gt;32 / 16 (kept)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Training time (DGX)&lt;/td&gt;
&lt;td&gt;~17 min&lt;/td&gt;
&lt;td&gt;~14 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In v2 I pinned a required character-name tag to the front of each group's captions&lt;br&gt;
(keep_tokens) to suppress the characters bleeding into each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why "two-person scenes" couldn't be grown
&lt;/h3&gt;

&lt;p&gt;I re-tagged another 478 video frames looking for "two people in frame." Co-occurrence&lt;br&gt;
flagged 25, but on full-resolution inspection almost all of them held only one person —&lt;br&gt;
the tagger was misfiring on on-screen text labels and body fragments. The real "two&lt;br&gt;
together" frames were the 9 I'd hand-picked at the start, basically the whole supply.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's still left (the homework)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Color bleeding onto clothes (likely from color tags in some groups)&lt;/li&gt;
&lt;li&gt;Leftover ghost text (a little text-like noise remains)&lt;/li&gt;
&lt;li&gt;And the big one: &lt;strong&gt;reproducing the characters at all. Neither single figures nor pairs
actually look like "my" characters → hand-draw the compositions I need&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next up
&lt;/h2&gt;

&lt;p&gt;Next time I'm switching things up with a completely different experiment 🎬&lt;/p&gt;

&lt;h1&gt;
  
  
  100ExperimentsWithDGX #LocalLLM
&lt;/h1&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>stablediffusion</category>
    </item>
    <item>
      <title>[Day 11] I turned my cat into anime art — and the AI drew a human girl instead. One photo through IPAdapter pulls it back to a cat</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Thu, 04 Jun 2026 04:13:23 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-11-i-turned-my-cat-into-anime-art-and-the-ai-drew-a-human-girl-instead-one-photo-through-4dkp</link>
      <guid>https://dev.to/peppercorn_llm/day-11-i-turned-my-cat-into-anime-art-and-the-ai-drew-a-human-girl-instead-one-photo-through-4dkp</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 11! Back to cats 🐱&lt;/p&gt;

&lt;p&gt;I took one photo of my cat (a black-and-white tuxedo boy) as a reference and had AI restyle him into anime, ukiyo-e, oil painting, and more.&lt;/p&gt;

&lt;p&gt;The goal: change only the style while keeping "my cat" recognizable. But left alone, the AI started drawing humans instead of a cat. Here's what I did, step by step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: my home AI machine (DGX Spark) + an image-generation tool (ComfyUI) + one photo of my cat.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The reference is this one photo
&lt;/h2&gt;

&lt;p&gt;A tomcat my family looks after for me, with yellow eyes and a slightly grumpy look.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fp8jxkykpjz4zow2i2hh0.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.amazonaws.com%2Fuploads%2Farticles%2Fp8jxkykpjz4zow2i2hh0.png" alt="The reference photo of my cat" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Love that face. I'll turn him into various styles while keeping him recognizable as "my cat."&lt;/p&gt;




&lt;h2&gt;
  
  
  First, anime from text alone → a human
&lt;/h2&gt;

&lt;p&gt;I started with no photo, just text: "a tuxedo cat, anime key visual." I clearly said &lt;em&gt;cat&lt;/em&gt;.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F52m7gp8s46ajvka5ha2x.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.amazonaws.com%2Fuploads%2Farticles%2F52m7gp8s46ajvka5ha2x.png" alt="Anime from text alone, no reference photo" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what came out. …A human girl.&lt;/p&gt;

&lt;p&gt;Black hair, white collar. My cat's tuxedo pattern (black body, white chest) turned straight into clothing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next, I added the reference photo → still human
&lt;/h2&gt;

&lt;p&gt;So I hand over the cat photo as a &lt;em&gt;visual reference&lt;/em&gt;. The tool that applies it is IPAdapter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's the reference-photo trick (IPAdapter)?&lt;/strong&gt; A tool that lets you pass a reference &lt;em&gt;image&lt;/em&gt;, separate from the text prompt, and say "make it look like this." It's what preserves my cat's colors and face.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Surely &lt;em&gt;this&lt;/em&gt; makes it a cat… nope. Still human.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Far610w7ixrcand0etspy.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.amazonaws.com%2Fuploads%2Farticles%2Far610w7ixrcand0etspy.png" alt="Even with the reference photo added, still a human" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this habit wasn't limited to anime. Ask the same anime-style model for ukiyo-e or oil painting, and you still get anime-ish humans. It hijacks not just the subject (the cat), but the art style too.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F1ccueo6n78a1bm7x221m.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.amazonaws.com%2Fuploads%2Farticles%2F1ccueo6n78a1bm7x221m.png" alt="Ask for ukiyo-e or oil painting, you still get anime-style humans" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Left: an "ukiyo-e" that's really an anime woman in a kimono. Right: an "oil painting" that's an anime woman in a tuxedo. Both are "humans painted in the cat's colors."&lt;/p&gt;




&lt;h2&gt;
  
  
  I tuned the settings → finally a cat
&lt;/h2&gt;

&lt;p&gt;On top of the photo, I turned up its strength and added "don't draw humans" to the negatives (details below). That's when it finally became a sitting cat.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fsge68cvgiaxp3hcaf9z9.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.amazonaws.com%2Fuploads%2Farticles%2Fsge68cvgiaxp3hcaf9z9.png" alt="Photo plus tuned settings finally gives a cat" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why does it turn into a human?
&lt;/h2&gt;

&lt;p&gt;Two reasons, as far as I can tell.&lt;/p&gt;

&lt;p&gt;One: anime-savvy models tend to draw people, girls especially. Even with "cat" in the prompt, they drift toward a human if you let them.&lt;/p&gt;

&lt;p&gt;Two: my cat's pose. He sits bolt upright, almost like a person, so the harder you push the reference, the more that upright posture rides along — tipping toward an "anthropomorphized" cat. The pop-art piece later is exactly that leftover.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cyberpunk flipped to a cat with the photo alone
&lt;/h2&gt;

&lt;p&gt;The interesting part: whether the photo alone was enough depended on the model. Anime was stubborn and needed tuning, but cyberpunk became a cat just by adding the photo.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F4or2x14ierhix5j9bc6c.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.amazonaws.com%2Fuploads%2Farticles%2F4or2x14ierhix5j9bc6c.png" alt="Cyberpunk: same prompt, with vs. without the reference photo" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Left (no reference): a human man in a neon city. Right (with reference): a cat with glowing ears.&lt;/p&gt;

&lt;p&gt;I didn't change a single character of the prompt — the photo being there or not is the only difference between human and cat.&lt;/p&gt;




&lt;h2&gt;
  
  
  The styles that came out
&lt;/h2&gt;

&lt;p&gt;Here's the gallery after the human problem was fixed — all with the reference photo, my cat as the base.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F9glp3evvd2l40s34qbxy.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.amazonaws.com%2Fuploads%2Farticles%2F9glp3evvd2l40s34qbxy.png" alt="Gallery of 7 styles" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Top row, left to right: anime, ukiyo-e, oil painting (Van Gogh-ish), stained glass. Bottom row: cyberpunk, 3D (Pixar-ish), pop art.&lt;/p&gt;




&lt;h2&gt;
  
  
  "Likeness" and "style" are a tug-of-war
&lt;/h2&gt;

&lt;p&gt;The oddly real 3D Pixar one shows this little trade-off nicely.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F043tslc6hn8dqxw4awbc.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.amazonaws.com%2Fuploads%2Farticles%2F043tslc6hn8dqxw4awbc.png" alt="3D style: without (left) and with (right) the reference" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Left (no reference): a cute 3D cat, but "some cat." Right (with reference): it becomes my cat's face, but the 3D look washes out into basically a real photo.&lt;/p&gt;

&lt;p&gt;Weaken the reference and the style shows but it's a different cat; strengthen it and it's my cat but the style fades. Finding that grip per style is what the tuning really is.&lt;/p&gt;




&lt;h2&gt;
  
  
  The boss I couldn't beat: storybook watercolor
&lt;/h2&gt;

&lt;p&gt;"Gentle storybook watercolor" was the one style I never got to be a cat. Here's the result of seven retries.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fhtbx4k3etvbyoxdl385o.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.amazonaws.com%2Fuploads%2Farticles%2Fhtbx4k3etvbyoxdl385o.png" alt="Storybook failures: a person, two cats, a cat-girl" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A human, then somehow two cats, then a cat-eared girl holding a cat. "Single + watercolor + cat" wouldn't line up. Lower the reference → human; raise it → two cats. "Storybook" must be soaked in human imagery. Carrying this over.&lt;/p&gt;




&lt;h2&gt;
  
  
  The details
&lt;/h2&gt;

&lt;p&gt;Here are the details.&lt;/p&gt;

&lt;h3&gt;
  
  
  The reference-photo mechanism (IPAdapter)
&lt;/h3&gt;

&lt;p&gt;I added a custom node called &lt;code&gt;ComfyUI_IPAdapter_plus&lt;/code&gt; to ComfyUI. It lets you hand over a reference image as a "visual guide," separate from the text prompt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model used: &lt;code&gt;ip-adapter_sd15&lt;/code&gt; (44.6MB, from h94/IP-Adapter)&lt;/li&gt;
&lt;li&gt;The part that reads the image features: &lt;code&gt;CLIP-ViT-H&lt;/code&gt; (reused an existing one)&lt;/li&gt;
&lt;li&gt;The reference photo is cropped to a 768px square before handing it over&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A number called the "reference strength (weight)" controls how closely it mimics. I moved between roughly 0.7 and 0.85 depending on the style.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I did to suppress the "human" problem
&lt;/h3&gt;

&lt;p&gt;I started at weight 0.7 plus words like "key visual" and "big eyes," which strongly invited humans. Three fixes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Raise the reference strength to 0.85&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;human, girl, person, 1girl, humanoid&lt;/code&gt; to the "things I don't want drawn" list&lt;/li&gt;
&lt;li&gt;Strip human-summoning words from the request and emphasize &lt;code&gt;tuxedo cat, full body, animal&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That corrected anime, ukiyo-e, and oil painting into cats. One catch: the phrase "tuxedo cat" itself tends to put an actual tuxedo (a suit) on the cat, so it cut both ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  The base models I used
&lt;/h3&gt;

&lt;p&gt;I switched the underlying image model by style.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anime / illustration: &lt;code&gt;AnythingV5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Realistic / 3D: &lt;code&gt;Realistic Vision V6&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Plain base: &lt;code&gt;SD 1.5&lt;/code&gt; (base)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When storybook failed, switching to the plain base gave a real cat but weak watercolor feel, and raising the strength split it into two cats — a real bind. The base model's "habits" matter a lot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common generation settings
&lt;/h3&gt;

&lt;p&gt;Across all styles: 768px, 30 steps, sampler &lt;code&gt;dpmpp_2m karras&lt;/code&gt;, cfg 7, seed fixed at 110011. I only varied the text request and the reference strength, keeping everything else equal for a fair comparison. Generation is fired at ComfyUI from a small script I wrote.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next up
&lt;/h2&gt;

&lt;p&gt;Next time it's cats again — and this time I'm planning video generation 🐱&lt;/p&gt;

&lt;h1&gt;
  
  
  100ExperimentsWithDGX #LocalLLM
&lt;/h1&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>stablediffusion</category>
    </item>
    <item>
      <title>[Day 10] Building my own personal weather officer AI, and teaching it my body's sense of cold over the next 100 days</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Mon, 01 Jun 2026 01:31:58 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-10-building-my-own-personal-weather-officer-ai-and-teaching-it-my-bodys-sense-of-cold-over-26d3</link>
      <guid>https://dev.to/peppercorn_llm/day-10-building-my-own-personal-weather-officer-ai-and-teaching-it-my-bodys-sense-of-cold-over-26d3</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 10!&lt;/p&gt;

&lt;p&gt;This time I'm starting a longer-running experiment. Meet the "weather officer AI" — a bot that texts me every morning saying "wear this today." The plan is to build a weather assistant that's tuned to &lt;em&gt;me&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What I'm building today is just v0.1 (the very first version). From here through Day 100, I'll keep teaching it "too cold / just right / too warm" every morning, so it gradually learns my preferences. The experiment is: how smart does it get after 100 days?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: my home AI machine (DGX Spark) + free weather data + a phone messaging app (Telegram)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Today's task
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I wanted
&lt;/h3&gt;

&lt;p&gt;I live somewhere with a big daily temperature swing, and "what do I wear today?" is a small but real daily headache. Weather apps tell you the temperature, but whether &lt;em&gt;I&lt;/em&gt; feel cold is a different question.&lt;/p&gt;

&lt;p&gt;So the starting point was: can I build a clothing AI that's tuned to "how I feel," not just "the temperature"?&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach
&lt;/h3&gt;

&lt;p&gt;I kept the design dead simple.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every morning at 7, automatically fetch today's weather&lt;/li&gt;
&lt;li&gt;Decide "this morning's outfit" from the apparent temperature and push it to my phone&lt;/li&gt;
&lt;li&gt;I just tap back "cold / just right / warm"&lt;/li&gt;
&lt;li&gt;As these feelings pile up, the AI learns "this person runs cold" and corrects its suggestions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The goal this time
&lt;/h3&gt;

&lt;p&gt;Not a "perfect forecast AI," but a "routine I can actually keep up every day." The smarts get grown over the next 100 days. Today is just laying the rails.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 How much does the temperature actually move in a day?
&lt;/h2&gt;

&lt;p&gt;Before building anything, I pulled a week of apparent temperatures for where I live and graphed it.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fgciz2ogzi4dvc7meb2cl.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.amazonaws.com%2Fuploads%2Farticles%2Fgciz2ogzi4dvc7meb2cl.png" alt="Apparent temperature through the day"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A beautiful zigzag. Every day repeats "cold in the morning → way up by midday → down again at night." The average daily swing is 13°C, and on the biggest day it moved 20°C.&lt;/p&gt;

&lt;p&gt;So I narrowed the suggestion down to "one outfit, at 7 a.m., matched to the apparent temperature at that hour." I record just once in the morning too. To keep something up for 100 days, simplicity matters most.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The AI doesn't learn the temperature swing itself. But by deciding to "focus on the morning," the suggestion and the feedback line up in time, so later I can cleanly check "was the morning suggestion right?"&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔧 How it works (the morning loop)
&lt;/h2&gt;

&lt;p&gt;The finished weather officer runs on this loop.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F9b01emkwxk15l19ksyh6.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.amazonaws.com%2Fuploads%2Farticles%2F9b01emkwxk15l19ksyh6.png" alt="The weather officer's morning loop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At 7 a.m. it grabs the weather, decides the outfit, and pushes a notification. I tap back my feeling, and that gets recorded. Once those records pile up, step 5 — learning "you run cold / warm" — kicks in, and the suggestions gradually become mine.&lt;/p&gt;

&lt;p&gt;Here's what the actual notification looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;☀️ Weather Officer AI — good morning

👕 This morning's outfit: long sleeves

This morning feels like: 13°C (highs up to 20°C today)
Rain: 4%  /  Wind: 13 km/h

How does it feel this morning? ↓ tap to tell me
   [🥶 cold]  [😊 just right]  [🥵 warm]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It suggests one outfit, but adds "highs up to 20°C today" so I can decide whether to throw on a layer myself. Tapping a button changes it to "✅ recorded," and the feeling is saved to my home AI.&lt;/p&gt;

&lt;p&gt;The notifications go through the messaging app I already use (Telegram).&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ The details
&lt;/h2&gt;

&lt;p&gt;Below are the specifics.&lt;/p&gt;

&lt;p&gt;The weather data&lt;/p&gt;

&lt;p&gt;Weather comes from &lt;a href="https://open-meteo.com/" rel="noopener noreferrer"&gt;Open-Meteo&lt;/a&gt;, a free weather API. No API key needed, historical data available, and commercial use is OK (CC BY 4.0) — very generous.&lt;/p&gt;

&lt;p&gt;I mostly use "apparent temperature" — not the raw air temperature, but a number adjusted for wind and humidity to reflect how it actually feels, which is better for deciding what to wear. I take the average apparent temperature from 7–9 a.m. as "this morning's feel." The coordinates stay only on my home machine; I don't write the specific location in the article or the code.&lt;/p&gt;

&lt;p&gt;The clothing rule&lt;/p&gt;

&lt;p&gt;A plain rule that splits apparent temperature into 7 bands, each mapped to an outfit (e.g. 13–20°C → long sleeves, 20–26°C → short sleeves).&lt;/p&gt;

&lt;p&gt;There's one "personal offset" number baked in. It's zero for now (v0.1). Going forward, if "cold" keeps coming back, I'll push the offset negative so the same temperature suggests warmer clothes — growing it from the feedback.&lt;/p&gt;

&lt;p&gt;The notification and buttons&lt;/p&gt;

&lt;p&gt;The notification uses a Telegram "Bot" (a thing that sends messages automatically), built with the &lt;code&gt;python-telegram-bot&lt;/code&gt; library to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;send a message at a fixed time every morning at 7&lt;/li&gt;
&lt;li&gt;attach three buttons below it and record which one is pressed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This bot sits waiting inside my home AI machine and fires in the morning.&lt;/p&gt;

&lt;p&gt;The shape of the feeling log (the 100-day foundation)&lt;/p&gt;

&lt;p&gt;Each record is one line per day, with these fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;date&lt;/li&gt;
&lt;li&gt;that day's forecast (morning/daytime apparent temperature, wind, rain chance)&lt;/li&gt;
&lt;li&gt;the outfit the AI suggested&lt;/li&gt;
&lt;li&gt;my feeling (cold / just right / warm)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kept in this consistent shape, I can later graph "monthly hit rate" or "my personal bias." I'll report this trend in the Day 21 / 36 / 54 / 74 / 87 check-ins.&lt;/p&gt;

&lt;p&gt;Keeping it running&lt;/p&gt;

&lt;p&gt;So the 7 a.m. notification reliably fires, I set the bot to launch automatically when the machine boots (systemd). Even after a reboot it comes back on its own, and the morning notification keeps going.&lt;/p&gt;

&lt;p&gt;The feeling log and the notification settings (the Telegram token, etc.) are all stored only on my home machine — none of it goes anywhere external like GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 100-day growth plan
&lt;/h2&gt;

&lt;p&gt;I'll keep growing this weather officer across the series — it'll pop up here and there.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Day 10 (now)&lt;/td&gt;
&lt;td&gt;v0.1 done; the recording loop starts turning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 21&lt;/td&gt;
&lt;td&gt;First "my personal bias" report from 11 days of data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 36&lt;/td&gt;
&lt;td&gt;Graph the monthly hit rate and take a look&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 54 / 74 / 87&lt;/td&gt;
&lt;td&gt;Mid-reviews: seasonal changes in feel, how the correction works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 100&lt;/td&gt;
&lt;td&gt;The 100-day accuracy trend and the finished version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Just tap a button every morning. I'm curious how "mine" it'll feel after 100 days.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next time: Day 11
&lt;/h2&gt;

&lt;p&gt;Next time it's a hard pivot back to cats 🐱 I'll convert photos of my cat into picture-book, anime, and photorealistic styles — the theme being whether I can keep "that's-our-cat-ness" while changing only the style.&lt;/p&gt;

&lt;h1&gt;
  
  
  LocalLLM #100ExperimentsWithDGX
&lt;/h1&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>python</category>
    </item>
    <item>
      <title>[Day 9] A local Japanese sentiment AI (BERT) read 8 years of a LINE chat, and the ups and downs surfaced from numbers alone</title>
      <dc:creator>PEPPERCORN</dc:creator>
      <pubDate>Fri, 29 May 2026 22:39:10 +0000</pubDate>
      <link>https://dev.to/peppercorn_llm/day-9-a-local-japanese-sentiment-ai-bert-read-8-years-of-a-line-chat-and-the-ups-and-downs-4951</link>
      <guid>https://dev.to/peppercorn_llm/day-9-a-local-japanese-sentiment-ai-bert-read-8-years-of-a-line-chat-and-the-ups-and-downs-4951</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Day 9. Today is less about model internals and more of a personal experiment: have a local AI analyze the entire chat history with one LINE friend. (LINE is the dominant messaging app in Japan.)&lt;/p&gt;

&lt;p&gt;When I exported it, 8 years were sitting there — from the very first message to today. It started, we talked a lot, it went quiet for a while, then picked up again. That whole arc is in there.&lt;/p&gt;

&lt;p&gt;Because the content is what it is, nothing left my machine: everything ran locally on my DGX Spark.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What I used: my home AI box (DGX Spark) + a Japanese sentiment model (for tone) + a bigger local model (to guess events from numbers).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Today's setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I wanted to do
&lt;/h3&gt;

&lt;p&gt;Re-reading 8 years of messages one by one isn't realistic. So instead of reading the content, I looked only at the "shape" of the conversation — when, how much, and in what tone we talked.&lt;/p&gt;

&lt;p&gt;Concretely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;monthly message volume&lt;/li&gt;
&lt;li&gt;the trend of tone (positive / negative)&lt;/li&gt;
&lt;li&gt;then asking an AI to find "when something big happened"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Heads-up (the result)
&lt;/h3&gt;

&lt;p&gt;From message counts and tone alone, the 8-year arc came out clearly on a chart. Started, went quiet, came back — the flow was visible without me re-reading a thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Pipeline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LINE chat export (text)
        │
        ▼
 1. Parse: split each message into {datetime, who, type, text}
        │   (from here on, message text never leaves the machine)
        ▼
 2. Aggregate: monthly counts, time-of-day, reply gaps
        │
        ▼
 3. Tone scoring: classify each of 66k messages pos/neu/neg
        │
        ▼
 4. Turning-point detection: from sudden changes in the numbers
        │   + also show ONLY the numbers to a bigger AI and ask it to guess
        ▼
 5. Answer check: compare against the real timeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can export a LINE chat as text from the chat screen ("send chat history").&lt;/p&gt;

&lt;p&gt;Data size:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Span&lt;/td&gt;
&lt;td&gt;~8 years 2 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total messages&lt;/td&gt;
&lt;td&gt;87,621&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text messages&lt;/td&gt;
&lt;td&gt;66,329&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stickers&lt;/td&gt;
&lt;td&gt;15,605&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Photos&lt;/td&gt;
&lt;td&gt;3,982&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;15,605 stickers… that's a lot.&lt;/p&gt;

&lt;h3&gt;
  
  
  The two AIs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;What it sees&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3. Tone&lt;/td&gt;
&lt;td&gt;Japanese sentiment model (&lt;code&gt;koheiduck/bert-japanese-finetuned-sentiment&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;scores each message pos/neu/neg&lt;/td&gt;
&lt;td&gt;66k message texts (scores averaged per month)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Turning points&lt;/td&gt;
&lt;td&gt;a bigger local model (&lt;code&gt;Qwen2.5&lt;/code&gt; 72B)&lt;/td&gt;
&lt;td&gt;guesses "what happened to these two?"&lt;/td&gt;
&lt;td&gt;only the per-month table of counts + tone scores (no conversation, no words)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both run locally on my own machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  📊 Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The 8-year arc of volume and tone
&lt;/h3&gt;

&lt;p&gt;This chart is the highlight. Top: monthly message count. Bottom: tone (up = positive, down = negative). The x-axis is months since the conversation started. (Axis labels are in Japanese.)&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fnvlmf9tb0ah7rwakmqo3.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.amazonaws.com%2Fuploads%2Farticles%2Fnvlmf9tb0ah7rwakmqo3.png" alt="8-year message volume and tone"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plotted, it isn't a steady climb or a flat line — it splits cleanly into "chapters": ramp-up → an 8-month silence → a second peak → a stable plateau. Four phases, at a glance.&lt;/p&gt;

&lt;p&gt;Tone has two peaks of about +0.6, around the start and around when things resumed (overall mean ≈ 0, slightly negative in the later years). The interesting part: in the month &lt;em&gt;before&lt;/em&gt; the silence, tone had already dropped to −0.1. The mood dimmed before the volume did.&lt;/p&gt;

&lt;p&gt;There are two dips into negative tone. The one before the silence was an "omen." The other is the recent years — not an omen, but the effect of logistics-y messages ("what time are you home?") piling up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Mini-note: how is "tone" turned into a number?&lt;br&gt;
The scoring is done by a Japanese sentiment model. Roughly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pre-trained on lots of Japanese text labeled positive / negative&lt;/li&gt;
&lt;li&gt;judges with context, not just by spotting keywords&lt;/li&gt;
&lt;li&gt;returns a probability of "positive-ness" / "negative-ness" per message&lt;/li&gt;
&lt;li&gt;I used the difference as a per-message score&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What kinds of messages scored how?
&lt;/h3&gt;

&lt;p&gt;A few actual judgments (short, name- and place-free one-liners):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Message&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;「楽しかったね！」 (that was fun!)&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;「これめちゃうまい」 (this is so good)&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;「おはようございます」 (good morning)&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;「もうお家？」 (home already?)&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;「全く集中できない」 (can't focus at all)&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;「それは悔しいな、、」 (that's frustrating…)&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(a long trip-planning message)&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(a snappy one-liner sent in a huff)&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plain happy lines score positive; logistics ("good morning", "home already?") score neutral; tiredness or irritation scores negative. Even long, businesslike planning messages lean neutral.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mornings are when we talk
&lt;/h3&gt;

&lt;p&gt;Message density by weekday × hour (brighter = more).&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fzdnp6m5ynqfxirqyvw9i.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.amazonaws.com%2Fuploads%2Farticles%2Fzdnp6m5ynqfxirqyvw9i.png" alt="weekday × hour density"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A clear concentration at 7–9 a.m.!&lt;/p&gt;

&lt;h3&gt;
  
  
  Could the AI guess the turning points?
&lt;/h3&gt;

&lt;p&gt;First, the simple method: mechanically pick the points where message volume jumped or dropped, then check against the real timeline.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Real event&lt;/th&gt;
&lt;th&gt;Auto-detected timing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;When it started&lt;/td&gt;
&lt;td&gt;exact match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When it went quiet&lt;/td&gt;
&lt;td&gt;exact match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When it resumed&lt;/td&gt;
&lt;td&gt;exact match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When it got lively again&lt;/td&gt;
&lt;td&gt;a few months off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A big life milestone&lt;/td&gt;
&lt;td&gt;hard to detect (barely shows in counts)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sharp volume changes were nailed. But "a big life milestone" got missed. So I showed the &lt;em&gt;same numbers&lt;/em&gt; to the bigger local model and asked "what happened?" — and got back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"around when it started" → roughly matches&lt;/li&gt;
&lt;li&gt;"a stretch of going silent" → matches the quiet period&lt;/li&gt;
&lt;li&gt;"a major life change" → almost exactly before the real milestone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than hunting for a single spike, it reads the whole sequence of numbers as a "flow," so it could pick up even an event that barely moves the counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Takeaways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Volume + tone alone reveal the arc
&lt;/h3&gt;

&lt;p&gt;Counts and tone were enough to see the 8-year shape. Silence marks the quiet stretch; a surge marks the resumption — straight off the chart.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A local model reads a story out of numbers
&lt;/h3&gt;

&lt;p&gt;Given only monthly numbers, the model inferred even a barely-visible event ("something big around here"), and it lined up with reality. It connects scattered points into one flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A "negative" tone doesn't mean a bad relationship
&lt;/h3&gt;

&lt;p&gt;The slight negative lean in later years isn't about getting along badly. Logistics messages ("what time are you home?") just don't score high. Low score ≠ trouble. It isn't that sentiment analysis is poor — the scores need to be read together with context.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Technical details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Parsing &amp;amp; aggregation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;LINE export format is a date header plus &lt;code&gt;time&amp;lt;TAB&amp;gt;name&amp;lt;TAB&amp;gt;text&lt;/code&gt;. Multi-line messages (4,987 of them) are merged back into the previous message.&lt;/li&gt;
&lt;li&gt;Speakers normalized to "A / B" by message count (no real names in anything public). Temporary group members and system lines excluded.&lt;/li&gt;
&lt;li&gt;Messages tagged by type (text / sticker / photo / call / unsent…). Tone uses text only; volume counts use all types.&lt;/li&gt;
&lt;li&gt;Aggregation and plotting in Python (pandas / matplotlib).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tone (sentiment)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;koheiduck/bert-japanese-finetuned-sentiment&lt;/code&gt;, a 3-class (pos / neu / neg) Japanese model.&lt;/li&gt;
&lt;li&gt;66,329 texts scored on GPU in batches; per message I take P(pos) − P(neg) in [−1, +1], then average per month.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Turning-point detection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rule-based: long near-zero stretches (silence), large month-over-month surges, and tone peaks — all from numbers only.&lt;/li&gt;
&lt;li&gt;Plus: the per-month table of counts + tone scores fed to a bigger local model (Qwen2.5-72B via ollama) to guess events. No message text was given.&lt;/li&gt;
&lt;li&gt;Real event dates were kept in a local note only, used for annotation and the answer check.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Privacy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Every file containing message text (raw export, parsed data, scores) stays in a non-public folder.&lt;/li&gt;
&lt;li&gt;Only aggregate numbers and charts are published. The chart x-axis is relativized to "months since the conversation started," hiding actual dates.&lt;/li&gt;
&lt;li&gt;Apart from a few short, name- and place-free one-liners shown as scoring examples, no conversation content, real names, specific dates, or long text appears in the article or charts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tomorrow: Day 10
&lt;/h2&gt;

&lt;p&gt;Weather forecasts say one temperature, but everyone &lt;em&gt;feels&lt;/em&gt; it differently. Same degrees, different "do I need a coat?" So next I'm building my own personal "weather officer" AI: from past weather data, it'll tell me each morning something like "coat + beanie today." Over the next 100 days I'll teach it my own sense of cold — the start of a longer project.&lt;/p&gt;

&lt;h1&gt;
  
  
  100ExperimentsWithDGX #LocalLLM
&lt;/h1&gt;

</description>
      <category>localllm</category>
      <category>ai</category>
      <category>dgxspark</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
