<?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>Forem: Wouter Van Schandevijl</title>
    <description>The latest articles on Forem by Wouter Van Schandevijl (@laoujin).</description>
    <link>https://forem.com/laoujin</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1289778%2F412a61d8-14e8-4fb7-98e6-3d93b71ea1d8.png</url>
      <title>Forem: Wouter Van Schandevijl</title>
      <link>https://forem.com/laoujin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/laoujin"/>
    <language>en</language>
    <item>
      <title>Building a self-hosted deep-research agent with Claude Code</title>
      <dc:creator>Wouter Van Schandevijl</dc:creator>
      <pubDate>Fri, 01 May 2026 20:01:10 +0000</pubDate>
      <link>https://forem.com/laoujin/building-a-self-hosted-deep-research-agent-with-claude-code-jha</link>
      <guid>https://forem.com/laoujin/building-a-self-hosted-deep-research-agent-with-claude-code-jha</guid>
      <description>&lt;p&gt;Every year the same problem... what to buy for her birthday. While standing in line at the bakery, I started a GitHub Issue. By the time I got back to my desk, a fancy html overview was published to my GitHub Pages with a fallback to the dry markdown research. In there was the gem "&lt;em&gt;Hunt A Killer: a six-month serialized murder-mystery&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%2F2qusugww09y14g6zlwiz.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.amazonaws.com%2Fuploads%2Farticles%2F2qusugww09y14g6zlwiz.gif" alt="Atlas published research" width="600" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/Laoujin/Scout" rel="noopener noreferrer"&gt;Scout&lt;/a&gt;, an MIT open-source research engine that runs Claude Code on your hardware and takes your one-line topic from a GitHub Issue to a cited markdown on a site you own. This post is about the design decisions behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pain
&lt;/h2&gt;

&lt;p&gt;Most of my research curiosities show up on a phone — on a train, in line,&lt;br&gt;
mid-conversation. I'd start a conversation in Claude web, then by the time&lt;br&gt;
I'm at my desk in Claude Code I can't find the thread — and when I do, it's&lt;br&gt;
a Q&amp;amp;A scroll without synthesis. Useful raw, useless as context for whatever&lt;br&gt;
I'm building.&lt;/p&gt;

&lt;p&gt;What I actually wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consume later.&lt;/strong&gt; Kick research off from anywhere; come back home (or
after a night's sleep) with the synthesis ready to drop into a Claude
Code session. Bonus: by then my 5-hour Pro window has reset, so the
research didn't eat the quota I need for the actual work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readable anywhere.&lt;/strong&gt; If I'm not returning soon, I can read the
published markdown on my phone — plus (optionally) a styled HTML view.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On-the-go capture, desk-side payoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is, in one diagram
&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.amazonaws.com%2Fuploads%2Farticles%2F3b5phm34e1nutg1kbb1n.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%2F3b5phm34e1nutg1kbb1n.png" alt="Scout flow" width="719" height="876"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;→ Open a GitHub Issue&lt;br&gt;
→ Sharpen &amp;amp; Sub-Topics selection (optional)&lt;br&gt;
→ Fancy HTML pages selection (optional)&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Repositories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scout&lt;/strong&gt; — the engine. Forked once, rarely touched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atlas&lt;/strong&gt; — your published research. All your content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compass&lt;/strong&gt; — the Jekyll theme. A submodule of Atlas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Scout is your fork, I can't do a rugpull, updates only affect me until&lt;br&gt;
you decide to pull in the changes.&lt;br&gt;&lt;br&gt;
With the Compass submodule I can add additional features like full text search,&lt;br&gt;
filtering, sorting etc and all you have to do is update the submodule to get&lt;br&gt;
these enhancements for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use X?
&lt;/h2&gt;

&lt;p&gt;No shortage of deep-research tools out there&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/assafelovic/gpt-researcher" rel="noopener noreferrer"&gt;gpt-researcher&lt;/a&gt; ⭐ 26.6k&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/stanford-oval/storm" rel="noopener noreferrer"&gt;storm&lt;/a&gt; ⭐ 28.1k&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/langchain-ai/open_deep_research" rel="noopener noreferrer"&gt;open_deep_research&lt;/a&gt; ⭐ 11.2k&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also Perplexity Pages, ChatGPT Deep Research, Gemini Deep Research. Scout's pitch is narrower:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output you own.&lt;/strong&gt; Markdown on a Jekyll site you host. No vendor pivot can paywall it or change the URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No incremental cost.&lt;/strong&gt; Runs against your existing Claude Pro/Max quota instead of an API bill or a second subscription.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forkable patterns.&lt;/strong&gt; The sharpening prompt, decomposition strategy, and citation rule are editable text files in &lt;code&gt;skills/scout/&lt;/code&gt; — not a black-box service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other Claude Code skills exist for the raw research call (&lt;a href="https://github.com/199-biotechnologies/claude-deep-research-skill" rel="noopener noreferrer"&gt;199-biotechnologies/claude-deep-research-skill&lt;/a&gt;, &lt;a href="https://github.com/weizhena/Deep-Research-skills" rel="noopener noreferrer"&gt;weizhena/Deep-Research-skills&lt;/a&gt;). Scout adds the publishing layer and the loop around the model — sharpen, decompose, cite, synthesize and add pretty views.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision 1: GitHub Issues as the UX
&lt;/h2&gt;

&lt;p&gt;Best part of using GitHub Issues as the UX: I didn't have to build one.&lt;br&gt;&lt;br&gt;
I already have the account. It's tightly linked with the source code,&lt;br&gt;
ships Auth, and gives me a comment thread to converse with Claude Code plus checkboxes to&lt;br&gt;
kick research off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision 2: Sharpen before research
&lt;/h2&gt;

&lt;p&gt;The biggest single quality lever in the system isn't the model or the search backend — it's the sharpening step that runs &lt;strong&gt;before&lt;/strong&gt; any research starts.&lt;/p&gt;

&lt;p&gt;A user types "best NAS to buy in 2026." That's three different questions in a trench coat: best for what budget? Replacing what? Optimizing for storage capacity, idle wattage, or both? Without sharpening, Claude charges off in one direction and the output ignores two thirds of what the user actually meant.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;skills/scout/sharpen.md&lt;/code&gt; runs first: read the topic, propose a tightened framing as a comment ("Survey for replacing a Synology NAS in 2026, optimised for AVX2 + ECC, comparing UGREEN / Beelink / Minisforum and TrueNAS / Unraid / Proxmox…"), wait for &lt;code&gt;tick ✓&lt;/code&gt; or a steering reply. Only then does research start.&lt;/p&gt;

&lt;p&gt;The lesson generalizes: &lt;strong&gt;agents that ask one clarifying question before working produce output an order of magnitude better than agents that don't.&lt;/strong&gt; Sharpening is cheap (one model call), and it eliminates the most common (and expensive!) failure mode — answering the wrong question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision 3: Decompose wide topics into parallel sub-agent expeditions
&lt;/h2&gt;

&lt;p&gt;Some questions are atomic ("best static-site generator in 2026"). Some are containers ("everything I need to leave Synology"). Trying to answer the latter in one pass produces a 4-page jumble.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;expedition&lt;/code&gt; depth, the sharpener also proposes 2–8 sub-topics ("hardware shortlist", "OS/storage stack", "DSM migration playbook", "remote access"). Each becomes its own independent expedition, dispatched as a sub-agent in parallel. After they all finish, a synthesis pass produces a parent overview page that links each child.&lt;/p&gt;

&lt;p&gt;This is map-reduce, but applied at the &lt;em&gt;research-question&lt;/em&gt; level rather than the data level. Two non-obvious things matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sub-topics must be independently coherent.&lt;/strong&gt; If sub-topic 3 needs sub-topic 1's conclusions, you've decomposed wrong — collapse them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The synthesis pass is where most value lives.&lt;/strong&gt; Without it, you have N disconnected pages. With it, the parent page reconciles disagreements between children and ends with a recommendation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Decision 4: "Every claim carries its URL inline" as a skill rule
&lt;/h2&gt;

&lt;p&gt;The single rule that does the most work: every factual claim, number, or quote must have its source URL &lt;strong&gt;next to the claim&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why: when claims and citations are decoupled — a "References" dump at the end — the citation block is decorative. Readers can't tell which source backs which sentence.&lt;/p&gt;

&lt;p&gt;Forcing inline URLs (&lt;code&gt;[[1]](https://...)&lt;/code&gt;) doesn't stop a model from hallucinating, but it makes every claim &lt;em&gt;locally auditable&lt;/em&gt;: one click verifies one sentence. Scout enforces this with a &lt;code&gt;citations.jsonl&lt;/code&gt; ledger — every &lt;code&gt;[[n]]&lt;/code&gt; marker has a matching entry pointing at a URL the model actually fetched, and a self-check reconciles the two before publish. A claim without a fetched URL fails the check.&lt;/p&gt;

&lt;p&gt;The hallucinations that survive are the ones where the URL is real and reachable but doesn't say what the model claims it does — smaller, and catchable in review. Cheap and effective hallucination defense.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;Measured per-run from my own Atlas (Sonnet 4.6 API):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Cost median (range)&lt;/th&gt;
&lt;th&gt;n&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one-page&lt;/td&gt;
&lt;td&gt;~2 min&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0.83&lt;/strong&gt; ($0.73–$0.93)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;survey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;single page&lt;/td&gt;
&lt;td&gt;5–10 min&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$2.56&lt;/strong&gt; ($1.99–$3.99)&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expedition&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;single deep&lt;/td&gt;
&lt;td&gt;12–20 min&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$8.64&lt;/strong&gt; ($7.23–$14.90)&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expedition&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;multi-angle&lt;/td&gt;
&lt;td&gt;35–70 min&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$28&lt;/strong&gt; ($12–$35)&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Small samples — these are my own runs, not a benchmark.&lt;/p&gt;

&lt;p&gt;On a Pro/Max subscription, runs consume your existing quota — no API bill, but a multi-angle expedition will eat a chunk of a Pro user's 5-hour window. Max handles them comfortably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Laoujin/Scout/main/install.sh &lt;span class="se"&gt;\&lt;/span&gt;
  | bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;s5.cartography.v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If piping curl to bash makes you twitch, &lt;a href="https://github.com/Laoujin/Scout/blob/main/install.sh" rel="noopener noreferrer"&gt;read it first&lt;/a&gt; (~250 lines) or follow the &lt;a href="https://github.com/Laoujin/Scout/blob/main/INSTALL.md" rel="noopener noreferrer"&gt;manual install&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/Laoujin/Scout" rel="noopener noreferrer"&gt;github.com/Laoujin/Scout&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live example Atlas: &lt;a href="https://laoujin.github.io/Atlas/" rel="noopener noreferrer"&gt;laoujin.github.io/Atlas&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Landing page &amp;amp; config picker: &lt;a href="https://laoujin.github.io/Scout/" rel="noopener noreferrer"&gt;laoujin.github.io/Scout&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's reusable
&lt;/h2&gt;

&lt;p&gt;Sharpen first. Decompose wide topics. Inline citations as a hard rule. Use GitHub Issues so you don't build a UI. Those four ideas port to any agent that produces long-form output — the rest of Scout is plumbing.&lt;/p&gt;

&lt;p&gt;MIT-licensed, three-repo split (Scout / Atlas / Compass), one-line install. Fork it, take the patterns, make it your own.&lt;/p&gt;

&lt;p&gt;⭐ &lt;a href="https://github.com/Laoujin/Scout" rel="noopener noreferrer"&gt;github.com/Laoujin/Scout&lt;/a&gt; if this is useful - helps me prioritize what to build next.&lt;/p&gt;

&lt;p&gt;PS: She's all giddy about next month's episode ;)&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
