<?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: fu jun</title>
    <description>The latest articles on DEV Community by fu jun (@fu_jun_f808a5e1cce91795ee).</description>
    <link>https://dev.to/fu_jun_f808a5e1cce91795ee</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%2F4187971%2F369ac52e-8359-44c4-ad5e-b18d6019a6fc.png</url>
      <title>DEV Community: fu jun</title>
      <link>https://dev.to/fu_jun_f808a5e1cce91795ee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fu_jun_f808a5e1cce91795ee"/>
    <language>en</language>
    <item>
      <title>From a Pile of Office Files to a Searchable Local Knowledge Base: I Built an Open-Source Skill</title>
      <dc:creator>fu jun</dc:creator>
      <pubDate>Mon, 02 Nov 2026 02:57:52 +0000</pubDate>
      <link>https://dev.to/fu_jun_f808a5e1cce91795ee/from-a-pile-of-office-files-to-a-searchable-local-knowledge-base-i-built-an-open-source-skill-3opd</link>
      <guid>https://dev.to/fu_jun_f808a5e1cce91795ee/from-a-pile-of-office-files-to-a-searchable-local-knowledge-base-i-built-an-open-source-skill-3opd</guid>
      <description>&lt;h1&gt;
  
  
  From a Pile of Office Files to a Searchable Local Knowledge Base: I Built an Open-Source Skill
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Self-hosted knowledge base skill v4.0 is out: &lt;a href="https://github.com/furoute/workshop/tree/main/skills/self-hosted-kb" rel="noopener noreferrer"&gt;https://github.com/furoute/workshop/tree/main/skills/self-hosted-kb&lt;/a&gt;&lt;br&gt;
Companion skill: Everything file search: &lt;a href="https://github.com/furoute/workshop/tree/main/skills/everything-http-search" rel="noopener noreferrer"&gt;https://github.com/furoute/workshop/tree/main/skills/everything-http-search&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I had tens of GB of Office/PDF files sitting around: proposals, contracts, notices, monthly reports. When I needed something, all I had was filenames and memory. Even a well-named file can't answer "what was the winning bid for that project?" or "how does XX's proposal handle operations?"&lt;/p&gt;

&lt;p&gt;Off-the-shelf knowledge base options either live in the cloud (your data leaves home) or require standing up a stack of services. What I wanted was small: files stay on my own disk, I can find the original text and open the original file, and when I ask a question the model answers with sources. That tiny list of requirements meant building the pipeline myself.&lt;/p&gt;

&lt;p&gt;So I made a skill: &lt;strong&gt;self-hosted-kb&lt;/strong&gt;, a complete hands-on record, MIT licensed. Not a product — a workbook you can follow step by step and get running.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline: seven steps + two retrieval paths
&lt;/h2&gt;

&lt;p&gt;One sentence: &lt;strong&gt;files stay in place, read-only; the index and vectors live separately; at query time, two paths retrieve and fuse.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seven steps (read-only first, changes later):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Full probe (read-only)&lt;/strong&gt;: inventory + magic-number sniffing, four-way triage (healthy / format-faked / damaged / encrypted-or-locked)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SHA-256 dedup (read-only)&lt;/strong&gt;: exact duplicates only count by hash — same name and size doesn't count&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stratified sample reads (read-only)&lt;/strong&gt;: sample 400–500 files by directory × year × size; recompute all SHAs after the reads and require zero changes — proof I didn't touch the files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-triage into three piles (labels only)&lt;/strong&gt;: useful / junk / undecided; undecided files get grouped into "file families", because nobody can review tens of thousands of rows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch adjudication (labels only)&lt;/strong&gt;: 28 rules, one final state per family&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity cards&lt;/strong&gt;: five entity types, fifteen YAML fields + nine body sections; evidence grades never get inflated, money figures use a fixed convention&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation layer (optional)&lt;/strong&gt;: index + property views + Q&amp;amp;A pages in Obsidian, for humans&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Retrieval stands on two legs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FTS5 (keywords)&lt;/strong&gt;: model numbers, IDs, amounts, dates — exact literal matches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vectors (semantics)&lt;/strong&gt;: fuzzy questions like "how do I handle this"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RRF rank fusion&lt;/strong&gt;: what ranks high on both paths ranks highest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scale from the real run: the largest batch was 37,782 files / 27.7 GB → after inventory and dedup, 2,066 files / 10.7 GB; 400k chunks, a 950 MB index.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuning: the data was in the base, the model said it wasn't
&lt;/h2&gt;

&lt;p&gt;The most valuable part. Once, a user asked about something that was definitely in the base, and the model answered "not found" — but a direct FTS5 query returned 10 precise hits. The data wasn't missing; it was buried. Two root causes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. FTS5's default trigram tokenizer kills two-character Chinese words.&lt;/strong&gt; Trigrams index 3-character sliding windows, so two-character Chinese words like "合同" (contract) or "预算" (budget) can never form a complete trigram — in testing, 11 two-character queries returned 0 hits, all 11 of them. The fix: jieba segmentation + unicode61, tokenize before feeding FTS5. Lesson: on Chinese corpora, the tokenization strategy matters an order of magnitude more than the index structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. One giant Excel outweighed 130 PDFs.&lt;/strong&gt; One spreadsheet's extracted text cut into 50,000 chunks, while the library-wide average was just 365 words per chunk — no matter the question, that spreadsheet's chunks filled the candidate pool and pushed the right documents down. That's "retrieval pollution", not "retrieval inaccuracy": the fix isn't better scoring, it's stopping big documents from hogging the pool. Five fixes: chunk-count downweighting, table-density downweighting, a cap of 20 chunks per document, directory-path evidence, and cutting query terms from 60 to 12.&lt;/p&gt;

&lt;p&gt;Before vs after: latency 96–203 ms → 24–96 ms (it got faster, because the candidate pool shrank from thousands to hundreds); three groups of previously unanswerable questions all hit the right documents.&lt;/p&gt;

&lt;p&gt;The mindset in one line: &lt;strong&gt;don't "score the right answer higher" — "keep the wrong answers out of the candidate pool".&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything: finding files is a different layer, don't mix it with retrieval
&lt;/h2&gt;

&lt;p&gt;"Does file X exist?" needs no vectorization at all — Everything reads the NTFS master file table and locates files among hundreds of thousands in milliseconds, zero tokens. The order should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Does X exist?" → Everything (milliseconds, zero cost)&lt;/li&gt;
&lt;li&gt;"What does X say?" → knowledge base retrieval (chunking + vectors)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;80% of "find a file" scenarios need no vectorization at all. The companion skill is published separately: it runs over an HTTP API, one curl line to query; set up autostart + silent tray + always-on HTTP once. Note: this is a Windows + NTFS solution; on Linux/macOS, use grep/find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud vs local models: the bottleneck is retrieval, not the model
&lt;/h2&gt;

&lt;p&gt;Same retrieval chain, two answering models: cloud DeepSeek API vs local Ollama qwen3.5:9b (RTX 4060 Laptop, network cable unplugged throughout).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: cloud steady at 0.7–1.1 s; local jittery at 0.9–4.1 s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality&lt;/strong&gt;: understanding, summarizing, reading proposals — roughly tied; but the local small model has two hard flaws: multi-digit multiplication goes wrong, and tables squeezed into one line get their states mixed up; when information is insufficient, the cloud model leans toward "not sure", the local one leans toward confabulating&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key finding&lt;/strong&gt;: questions where retrieval handed over the wrong material were unanswerable on both sides — a bigger model can't conjure a document that never entered the candidate pool. Optimize recall quality, not model size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One hands-on pitfall&lt;/strong&gt;: qwen3.5 has thinking on by default, and the chain of thought eats the whole num_predict budget, leaving an empty answer — you have to turn it off explicitly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Data boundary (honest version)
&lt;/h2&gt;

&lt;p&gt;The accurate statement: &lt;strong&gt;data doesn't leave home in bulk, but the snippets hit at query time — and your question — do go out.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stays home&lt;/strong&gt;: the original corpus, 400k chunks, the 950 MB index, the vectors — all on my own disk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goes out&lt;/strong&gt;: the top-k snippets selected at query time + the user's question, sent to the cloud model for understanding and organization&lt;/li&gt;
&lt;li&gt;Looking up a technical manual: fine. Looking up a client list, a quote, contract terms: those passages do go out — so judge before putting sensitive material in the base&lt;/li&gt;
&lt;li&gt;The only way to keep everything home is a local model (5–10× slower, one tier weaker in Chinese)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;This isn't SaaS; there's no one-click installer. The right way: &lt;strong&gt;hand the skill folder to your agent and let it follow SKILL.md&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/furoute/workshop/tree/main/skills/self-hosted-kb" rel="noopener noreferrer"&gt;https://github.com/furoute/workshop/tree/main/skills/self-hosted-kb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SKILL.md is a workbook written for an agent to follow, not a manual for humans; companion scripts, references and architecture diagrams are in the same directory. The Everything skill is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/furoute/workshop/tree/main/skills/everything-http-search" rel="noopener noreferrer"&gt;https://github.com/furoute/workshop/tree/main/skills/everything-http-search&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest boundaries
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One complete hands-on record, not a general product; a different corpus means re-tuning&lt;/li&gt;
&lt;li&gt;Windows + NTFS is the precondition for the Everything layer; legacy Office formats (.doc/.xls/.ppt) aren't parsed by the current scripts — only their filenames are searchable&lt;/li&gt;
&lt;li&gt;Hands-on ability is a precondition: you run the scripts, build the index, and debug it yourself&lt;/li&gt;
&lt;li&gt;Example data is de-identified; replace personal configuration with your own&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The original intent was simple: my files, my retrieval. However convenient a cloud knowledge base is, once data leaves, it's gone; once this local chain works, finding it, opening it, and citing the source is enough.&lt;/p&gt;

&lt;p&gt;Welcome to try it — issues go straight to the repo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; this wasn't a solo effort — it was built by six AI agents across three machines, with task split, review and merge all running over ACP-lite (Agent Connect Personal), an open-source file-mailbox-style interconnection scheme: &lt;a href="https://github.com/furoute/acp-lite" rel="noopener noreferrer"&gt;https://github.com/furoute/acp-lite&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>selfhosted</category>
      <category>rag</category>
      <category>sqlite</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How Do AI Agents on Different Computers Talk to Each Other? I Built a Tiny Open-Source Fix</title>
      <dc:creator>fu jun</dc:creator>
      <pubDate>Fri, 16 Oct 2026 06:16:05 +0000</pubDate>
      <link>https://dev.to/fu_jun_f808a5e1cce91795ee/how-do-ai-agents-on-different-computers-talk-to-each-other-i-built-a-tiny-open-source-fix-4dk7</link>
      <guid>https://dev.to/fu_jun_f808a5e1cce91795ee/how-do-ai-agents-on-different-computers-talk-to-each-other-i-built-a-tiny-open-source-fix-4dk7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update (Nov 2026):&lt;/strong&gt; v0.7.1 is out — delivery facts are now derived from the signed envelope, tickets are bound to (msg_id, requires, data), and 0.7.0 tickets need re-signing. The SSH doorbell, mailbox design and pitfalls below are unchanged. Related new work from the same author: a self-hosted knowledge-base skill (FTS5 + vectors) and an Everything file-search skill: &lt;a href="https://github.com/furoute/workshop" rel="noopener noreferrer"&gt;https://github.com/furoute/workshop&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  How Do AI Agents on Different Computers Talk to Each Other? I Built a Tiny Open-Source Fix
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;ACP-lite (Agent Connect Personal) v0.7.0 is out:&lt;/strong&gt; &lt;a href="https://github.com/furoute/acp-lite" rel="noopener noreferrer"&gt;https://github.com/furoute/acp-lite&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: unrelated to Zed's Agent Client Protocol and OpenAI+Stripe's Agentic Commerce Protocol — same acronym, different project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I run 6 AI agents across 3 machines: Codex and DSH on my desktop, Hermes and OpenClaw on a netbook. Human-to-agent is easy — WeChat, DingTalk, done. But how do agents talk to &lt;em&gt;each other&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Search for answers and you get enterprise stacks: Google's A2A, RabbitMQ, Kafka. All built for data centers. My need is much smaller: two home computers, both behind NAT, no public IP, no server — agents passing messages and tasks to each other. Nobody builds for that.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;ACP-lite (Agent Connect Personal)&lt;/strong&gt;: a minimal implementation of cross-host agent interconnect. v0.7.0 just shipped, MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture: one post office per machine
&lt;/h2&gt;

&lt;p&gt;The whole idea in one sentence: &lt;strong&gt;each machine runs a "post office"&lt;/strong&gt;: agents only ever read and write local files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send: agent A writes a letter (JSON) to its local outbox&lt;/li&gt;
&lt;li&gt;Transport: Syncthing syncs the file to the other machine — battle-tested decentralized file sync, no need to reinvent it&lt;/li&gt;
&lt;li&gt;Receive: agent B picks it up from its local inbox&lt;/li&gt;
&lt;li&gt;Trust: Ed25519-signed envelopes + out-of-band fingerprint verification + a hash-chained audit log. Tampered letters are rejected outright.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No central server, no persistent network connections. The &lt;code&gt;watch&lt;/code&gt; process only moves files — it never calls a model, so idling costs zero tokens. That matters for personal use: run it for a month, pay nothing extra.&lt;/p&gt;

&lt;p&gt;How it relates to the big names: A2A is the enterprise-grade full protocol, ACP-lite is the personal minimal implementation — different leagues. MCP connects agents to &lt;em&gt;tools&lt;/em&gt;; ACP-lite connects agents to &lt;em&gt;agents&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  New in v0.7.0: cross-host wake-up (the SSH doorbell)
&lt;/h2&gt;

&lt;p&gt;A mailbox alone isn't enough: the letter arrives, but the agent on the other side is asleep. v0.7.0 adds the "doorbell" — SSH wake-up.&lt;/p&gt;

&lt;p&gt;The architecture rule is fixed: &lt;strong&gt;SSH is the doorbell, ACP is the mailbox; the doorbell carries only a pointer, the letter body always travels via ACP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The wake-up command passes just the absolute path of the received letter file; the prompt goes through a file + stdin (measured ~20x fewer tokens than passing it as a CLI argument, and no shell-mangled encoding).&lt;/p&gt;

&lt;p&gt;Last week's dual-channel integration test, timeline included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Desktop rebooted: &lt;code&gt;sshd / Syncthing / watch&lt;/code&gt; all came back up with zero manual intervention, IP unchanged&lt;/li&gt;
&lt;li&gt;Netbook delivered 2 letters; 18 seconds later the doorbell rang; both wake scripts pulled up Codex and DSH within a second&lt;/li&gt;
&lt;li&gt;Codex first moved in 6 seconds, DSH in 9; each wrote artifacts + broadcast + signed replies; the audit chain verified end-to-end&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The completion criteria are threefold, all required: artifact on disk, longer audit chain, signed receipt. Don't trust exit code 0 alone — an agent woken in read-only sandbox mode will happily "talk about doing the work" and exit 0 with nothing done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls worth knowing (four favorites)
&lt;/h2&gt;

&lt;p&gt;All earned the hard way; the README has the full list (Q5-Q13). Four highlights:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ALL_PROXY hijacks everything.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since Syncthing 2.1, it honors only &lt;code&gt;ALL_PROXY&lt;/code&gt; and ignores &lt;code&gt;NO_PROXY&lt;/code&gt; — a local Clash setup silently captured all dials and everything looked correct but never connected. Debugging order for a new machine: check &lt;code&gt;proxy env vars&lt;/code&gt; first, suspect the firewall second.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Never pass non-ASCII text as CLI arguments.&lt;/strong&gt; Windows shells read UTF-8 as GBK and you get mojibake. Prompts always go through a file + stdin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't omit &lt;code&gt;--root&lt;/code&gt;.&lt;/strong&gt; Omit it and the exit code is still 0 — looking like success — while the letter lands in the machine-private directory where nobody else can see it. The criterion is always "a file really exists in outbox/", never the exit code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The wake-up sandbox level decides whether it works or performs.&lt;/strong&gt; Codex wake-ups need &lt;code&gt;workspace-write&lt;/code&gt;; DSH wake-ups need &lt;code&gt;DSH_PERMISSION_MODE=danger-full-access&lt;/code&gt; baked into the script. Wrong level, and the agent performs diligence theater.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;Be clear about this: it's not SaaS, there's no one-click installer. The one correct usage — &lt;strong&gt;hand the repo link to your coding agent and let it install from the README&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The README is step-by-step: step one do this, step two do that, each step only "what to do + commands to copy verbatim". After reading it, an agent can set the same thing up on your own two machines. There's also an &lt;code&gt;llms.txt&lt;/code&gt; if you'd rather feed the whole thing to an LLM at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest boundaries
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A 0-star personal project, only recently made public — don't expect plug-and-play&lt;/li&gt;
&lt;li&gt;You'll configure Syncthing and SSH yourself; hands-on ability is a prerequisite.&lt;/li&gt;
&lt;li&gt;Built for the personal multi-machine scenario; for enterprise needs, use A2A directly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I built this
&lt;/h2&gt;

&lt;p&gt;The motivation is simple: big-vendor solutions are all built for enterprises, nobody builds cross-machine agent interconnect for individual users. ACP-lite is one minimal answer for the "a few of my own computers, a few of my own agents" scenario.&lt;/p&gt;

&lt;p&gt;Try it out — issues go directly to the repo. For this project, the first issue from a stranger matters more than a hundred stars.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
      <category>multiagent</category>
    </item>
  </channel>
</rss>
