<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Engineering Notes | YuyaoGe's Website</title><link>https://geyuyao.com/category/engineering-notes/</link><atom:link href="https://geyuyao.com/category/engineering-notes/index.xml" rel="self" type="application/rss+xml"/><description>Engineering Notes</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Thu, 11 Jun 2026 00:00:00 +0000</lastBuildDate><image><url>https://geyuyao.com/media/icon_hucac340dfc176d8b4c8a8aa7a23204f12_18561_512x512_fill_lanczos_center_3.png</url><title>Engineering Notes</title><link>https://geyuyao.com/category/engineering-notes/</link></image><item><title>Does a More Complex Agent Deliver Better Performance?</title><link>https://geyuyao.com/post/swe-agent-vs-mini-swe-agent-en/</link><pubDate>Thu, 11 Jun 2026 00:00:00 +0000</pubDate><guid>https://geyuyao.com/post/swe-agent-vs-mini-swe-agent-en/</guid><description>
&lt;div class="travel-langswitch" role="group" aria-label="Language">
&lt;span class="travel-langswitch__btn is-active" aria-current="true">English&lt;/span>
&lt;a class="travel-langswitch__btn" href="https://geyuyao.com/post/swe-agent-vs-mini-swe-agent/">中文&lt;/a>
&lt;/div>
&lt;p>TL;DR: On SWE-bench Pro, the more elaborate SWE-agent underperforms mini-swe-agent and suffers from instances that hang indefinitely.&lt;/p>
&lt;p>PS: less than 20% of this article is AI-generated.&lt;/p>
&lt;p>Let me start with a common intuition: &lt;strong>the more complete an agent framework is, the stronger it should perform.&lt;/strong> Everyone seems to take this for granted, yet nobody has rigorously demonstrated it. I therefore wanted to check whether the intuition actually holds on SWE tasks 🤔.&lt;/p>
&lt;p>💡 The idea is straightforward:&lt;/p>
&lt;p>Evaluate two agent frameworks of differing complexity on SWE-bench Pro and compare their scores.&lt;/p>
&lt;p>The outcome, however, was that &lt;strong>the simpler agent framework scored higher&lt;/strong> 🤯.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
&lt;p>&lt;strong>Background&lt;/strong>&lt;/p>
&lt;p>&lt;strong>What is an SWE task?&lt;/strong> An SWE (Software Engineering) task measures an agent&amp;rsquo;s end-to-end, real-world development ability: given a real code repository and a GitHub issue, the agent must autonomously read the code, localize the problem, edit across files, and produce a patch, which tests then judge as &amp;ldquo;resolved&amp;rdquo; or not. SWE-bench Pro is a benchmark built for exactly this task.&lt;/p>
&lt;p>&lt;strong>SWE-agent and mini-swe-agent&lt;/strong> are two agent frameworks aimed at the SWE setting:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>SWE-agent&lt;/strong> is built around the Agent-Computer Interface (ACI): a carefully designed set of dedicated tools for the agent, each with its own interface. Execution is handed off to a separate &lt;a href="https://github.com/SWE-agent/SWE-ReX" target="_blank" rel="noopener">&lt;code>SWE-ReX&lt;/code>&lt;/a> backend, which uses a persistent pexpect interactive shell (working directory and environment variables are preserved across commands) and pre-parses every command with bashlex (splitting, syntax validation, and precise exit-code extraction).&lt;/li>
&lt;li>&lt;strong>mini-swe-agent&lt;/strong> is the minimal implementation of SWE-agent (the entire agent class is about 100 lines of Python): it has a single &amp;ldquo;tool&amp;rdquo;, bash, and does not even use the model&amp;rsquo;s tool-calling interface; each command runs through &lt;code>subprocess.run&lt;/code>, and every action is fully independent.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;p>With the background in place, the question becomes: &lt;strong>under the same model and the same benchmark, does SWE-agent&amp;rsquo;s &amp;ldquo;more complete&amp;rdquo; engineering pay off relative to mini-swe-agent?&lt;/strong>&lt;/p>
&lt;h2 id="the-more-complete-swe-agent-is-not-the-stronger-one">The more complete SWE-agent is not the stronger one&lt;/h2>
&lt;p>Using Claude Sonnet 4.5, I ran both agent frameworks over all 731 problems of SWE-bench Pro, capping the number of calls at 50 per problem.&lt;/p>
&lt;p>The results:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Agent framework&lt;/th>
&lt;th>N&lt;/th>
&lt;th>resolved&lt;/th>
&lt;th>resolve rate&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>mini-swe-agent&lt;/strong>&lt;/td>
&lt;td>731&lt;/td>
&lt;td>322&lt;/td>
&lt;td>&lt;strong>44.0%&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SWE-agent&lt;/strong>&lt;/td>
&lt;td>731&lt;/td>
&lt;td>302&lt;/td>
&lt;td>&lt;strong>41.3%&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>For reference, the officially reported result for Sonnet 4.5 is around 43.6%&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>; mini&amp;rsquo;s 44.0% matches it closely, which lends credibility to this setup.&lt;/p>
&lt;/blockquote>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://geyuyao.com/post/swe-agent-vs-mini-swe-agent/figures/lang_compare.png" alt="Per-language resolve rate comparison" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>The minimalist mini-swe-agent comes out 2.7 points ahead of SWE-agent. More surprisingly, &lt;strong>after SWE-agent had worked through 722 of the 731 instances, the final 9 hung outright&lt;/strong> — containers stayed up for 5 to 12 hours with no log activity for hours on end, and had to be killed. Running those same 9 problems, mini-swe-agent showed no such issue.&lt;/p>
&lt;p>For me, the surprise in the numbers mattered less than the question of why those nine instances hung 🤔.&lt;/p>
&lt;h2 id="why-did-9-containers-hang">Why did 9 containers hang?&lt;/h2>
&lt;p>Before killing the containers, I grabbed the &lt;code>docker logs&lt;/code> of each one, which showed:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">INFO ... 200 OK POST /run_in_session
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">🦖 ERROR Bashlex fail: here-document at line 0 delimited by end-of-file (wanted &amp;#34;&amp;#39;EOF&amp;#39;&amp;#34;)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The containers were not dead and the &lt;code>swerex-remote&lt;/code> process was still returning &lt;code>200 OK&lt;/code> — &lt;strong>the agent was simply spinning in place.&lt;/strong> Following the agent&amp;rsquo;s trace, it was &lt;strong>writing a large file with a heredoc&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat &amp;gt; some_file.go &lt;span class="s">&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">... a large block of Go code ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That pins down the root cause: SWE-agent&amp;rsquo;s execution backend, &lt;code>swe-rex&lt;/code>, first parses every command with &lt;code>bashlex&lt;/code> (a bash parser written in pure Python) before sending it into the container.&lt;/p>
&lt;p>And that is exactly where the trouble lies. &lt;code>bashlex&lt;/code>&amp;rsquo;s support for heredocs is incomplete; when it encounters a large block write such as &lt;code>cat &amp;lt;&amp;lt;'EOF' … a large block of code … EOF&lt;/code>, parsing fails outright and raises &lt;code>Bashlex fail&lt;/code>.&lt;/p>
&lt;p>Once parsing collapses, swe-rex cannot tell whether the command finished or what its exit code was. The agent receives a broken observation, does not rescue itself by trying a different formulation, and just retries the same command again and again — leaving the container stuck for 5 to 12 hours.&lt;/p>
&lt;h2 id="why-does-swe-agent-go-to-the-trouble-of-parsing-commands-first">Why does SWE-agent go to the trouble of parsing commands first?&lt;/h2>
&lt;p>swe-rex keeps a long-lived shell session alive so that state such as the working directory, environment variables, and any activated virtual environment carries over from one command to the next.&lt;/p>
&lt;p>The price is that when commands run inside a continuously flowing session, &amp;ldquo;where a command ends and what its return code is&amp;rdquo; is no longer as self-evident as it is when a standalone process finishes. The only way out is to have &lt;code>bashlex&lt;/code> split the command apart and inject sentinel strings so the exit code can be fished back out of the output stream.&lt;/p>
&lt;p>The upside is that once a command has been parsed into a structure, the backend can layer on safety checks, command rewriting, and other fine-grained wrapping — precisely the Agent-Computer Interface idea that SWE-agent champions. It spends extra complexity to buy stronger session semantics.&lt;/p>
&lt;p>mini-swe-agent goes the opposite way, taking the minimalist route. It maintains no session whatsoever; every command is handed straight to the system&amp;rsquo;s real shell with a single &lt;code>subprocess.run(shell=True)&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># minisweagent/environments/local.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">command&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shell&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># hand it to the system shell (/bin/sh -c)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cwd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">cwd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">timeout&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stdout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PIPE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stderr&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">STDOUT&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Doing so throws away session state — every command starts from scratch, and the agent has to spell out paths and environment itself — but for that very reason it sidesteps every headache of parsing bash yourself. A heredoc, however large, is the real shell&amp;rsquo;s own business; once the command finishes and the process exits, the exit code is simply there.&lt;/p>
&lt;p>So the same large-file-writing command that runs into &lt;code>bashlex&lt;/code>&amp;rsquo;s limitation and drags the container to a halt under SWE-agent passes through mini-swe-agent without incident. This is a textbook engineering trade-off: swe-rex spends more complexity on stronger session semantics and thereby takes on an extra class of failures, while mini gives up the convenience of a session in exchange for a smaller, more controllable space of errors.&lt;/p>
&lt;h2 id="closing-thoughts">Closing thoughts&lt;/h2>
&lt;p>In a broad sense, this finding is a nod to Occam&amp;rsquo;s razor: the simpler thing turns out to be the more effective one (perhaps a first-principles matter as well).&lt;/p>
&lt;p>That said, I regard this as a very simple toy experiment. It does not establish that more complex, more sophisticated agents perform poorly; it may just be that SWE-agent happens to have this one bug. A carefully tuned, more elaborate agent could well beat a mini agent.&lt;/p>
&lt;p>Either way, these are only conjectures for now. Next I plan to dig into the opening question with more rigorous experiments — stay tuned if you are interested.&lt;/p>
&lt;h2 id="appendix">Appendix&lt;/h2>
&lt;h3 id="per-language-comparison">Per-language comparison&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Language&lt;/th>
&lt;th>mini&lt;/th>
&lt;th>SWE-agent&lt;/th>
&lt;th>Winner&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>go&lt;/td>
&lt;td>95/280 = &lt;strong>34%&lt;/strong>&lt;/td>
&lt;td>78/280 = 28%&lt;/td>
&lt;td>mini +6pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>python&lt;/td>
&lt;td>139/266 = 52%&lt;/td>
&lt;td>143/266 = &lt;strong>54%&lt;/strong>&lt;/td>
&lt;td>SWE-agent +2pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>js&lt;/td>
&lt;td>77/165 = &lt;strong>47%&lt;/strong>&lt;/td>
&lt;td>73/165 = 44%&lt;/td>
&lt;td>mini +3pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ts&lt;/td>
&lt;td>11/20 = &lt;strong>55%&lt;/strong>&lt;/td>
&lt;td>8/20 = 40%&lt;/td>
&lt;td>mini (N=20, small sample, unstable)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Almost all of mini&amp;rsquo;s advantage comes from Go, where it leads by 6 percentage points, a difference of 17 problems — and within Go, a single repository, &lt;code>gravitational/teleport&lt;/code>, accounts for most of it (16 solved only by mini versus just 5 solved only by SWE-agent). This is not really surprising: Go problems tend to be large repositories with substantial edits, exactly where SWE-agent&amp;rsquo;s heredoc hang is easiest to trigger. Switch to Python, though, and SWE-agent is actually 2 points ahead.&lt;/p>
&lt;h3 id="paired-significance">Paired significance&lt;/h3>
&lt;p>Next, paired significance. Aligning all 731 problems one-to-one by instance_id splits them into four groups:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Count&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Both solved&lt;/td>
&lt;td>245&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Only mini solved&lt;/td>
&lt;td>77&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Only SWE-agent solved&lt;/td>
&lt;td>57&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Neither solved&lt;/td>
&lt;td>352&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>What genuinely separates the two are the 134 problems solved by exactly one side, of which mini takes 77 and SWE-agent 57. The tilt does favor mini, but McNemar&amp;rsquo;s exact test gives p = 0.10, which falls short of significance. The other two groups are more telling: 245 problems both can solve and 352 neither can, showing that the problems they cover overlap heavily.&lt;/p>
&lt;h3 id="fairness-accounting-for-the-hangs">Fairness accounting for the hangs&lt;/h3>
&lt;p>Finally, a fairness check. Going through the 9 hung instances one by one (5 from flipt, 2 from teleport, plus one each from vuls and tutanota), only 2 really count as &amp;ldquo;unfair&amp;rdquo; losses — the problems mini solved on which SWE-agent was scored 0 purely because it hung: &lt;code>vuls e4728e38&lt;/code> and &lt;code>teleport 47530e1f&lt;/code>. Even crediting those 2 back, SWE-agent only climbs from 302 to 304 (41.6%), while mini stays at 44.0%; the gap actually narrows, and the &amp;ldquo;not significant&amp;rdquo; conclusion is entirely unchanged. Besides, looked at another way, the stability of the execution backend is itself part of an agent&amp;rsquo;s end-to-end capability, so under a &amp;ldquo;measure the whole scaffold&amp;rdquo; standard, scoring 0 is hardly a miscarriage of justice 😅.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>SWE-bench Pro, &lt;a href="https://arxiv.org/abs/2509.16941" target="_blank" rel="noopener">https://arxiv.org/abs/2509.16941&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Screening and Summarizing HuggingFace Daily Papers with an Agent Skill</title><link>https://geyuyao.com/post/auto_paper_reader-en/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://geyuyao.com/post/auto_paper_reader-en/</guid><description>
&lt;div class="travel-langswitch" role="group" aria-label="Language">
&lt;span class="travel-langswitch__btn is-active" aria-current="true">English&lt;/span>
&lt;a class="travel-langswitch__btn" href="https://geyuyao.com/post/auto_paper_reader/">中文&lt;/a>
&lt;/div>
&lt;p style="display:flex;gap:16px;flex-wrap:wrap;align-items:center;justify-content:center;margin:10px 0 30px;">
&lt;a href="https://github.com/YuyaoGe/Paper_Agent_Skill" target="_blank" rel="noopener">
&lt;img src="https://img.shields.io/badge/SKILL-2563EB?style=for-the-badge&amp;logo=github&amp;logoColor=white" alt="SKILL" style="height:56px;">
&lt;/a>
&lt;a href="https://yuyaoge.github.io/paper_reader/" target="_blank" rel="noopener">
&lt;img src="https://img.shields.io/badge/WebSite-16A34A?style=for-the-badge&amp;logo=googlechrome&amp;logoColor=white" alt="WebSite" style="height:56px;">
&lt;/a>
&lt;/p>
&lt;h2 id="motivation">Motivation&lt;/h2>
&lt;p>As graduate students, we read a lot of papers every day. But beyond reading the papers themselves, &lt;strong>deciding which papers to read is just as time- and energy-consuming&lt;/strong>.&lt;/p>
&lt;p>There is no shortage of tools for analyzing papers, for example:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>&lt;a href="https://www.alphaxiv.org/" target="_blank" rel="noopener">AlphaXiv&lt;/a>&lt;/strong>: reads a single paper very systematically.&lt;/li>
&lt;li>&lt;strong>&lt;a href="https://papers.cool/" target="_blank" rel="noopener">papers.cool&lt;/a>&lt;/strong>: built by &lt;a href="https://kexue.fm/" target="_blank" rel="noopener">Su Jianlin&lt;/a>; it crawls new arXiv papers daily, uses Kimi to produce Chinese explanations, and lets you keep asking follow-up questions inside Kimi.&lt;/li>
&lt;/ol>
&lt;p>These tools go deep on &lt;em>reading&lt;/em> a paper, but they share one shortcoming: &lt;strong>they do not help with filtering&lt;/strong>. They can read a given paper in great detail, yet &amp;ldquo;picking the few worth reading out of the dozens each day&amp;rdquo; is still on me, and they will not tag papers either. Worse, even when I find a promising paper and want to follow up on its work, I often discover that its GitHub repo is empty, or that there is no link at all — and the effort is wasted.&lt;/p>
&lt;p>The sunk cost here is high: when following a paper, I might write code for a long time only to find that some input does not match, or that a reproduction step simply does not work.&lt;/p>
&lt;p>So I wanted to build something genuinely useful and actually usable day to day, so that my time goes to the work that matters.&lt;/p>
&lt;h1 id="idea">Idea&lt;/h1>
&lt;h2 id="requirements">Requirements&lt;/h2>
&lt;p>Concretely, I want it to filter the papers in &lt;a href="https://huggingface.co/papers" target="_blank" rel="noopener">HuggingFace Daily Papers&lt;/a> every day, and organize them by category.&lt;/p>
&lt;blockquote>
&lt;p>Papers in HuggingFace Daily Papers are submitted by the authors themselves. That initiative tends to make daily-paper submissions more complete, more credible, and higher in quality than arXiv at large.&lt;/p>
&lt;/blockquote>
&lt;p>The product needs to do three things:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Auto-classification&lt;/strong>: tag every paper.&lt;/li>
&lt;li>&lt;strong>Verify a paper&amp;rsquo;s authenticity and how &amp;ldquo;easy to follow&amp;rdquo; it is&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>Auto-generate a paper summary&lt;/strong> (similar to &lt;a href="https://papers.cool/" target="_blank" rel="noopener">papers.cool&lt;/a>).&lt;/li>
&lt;/ol>
&lt;p>Here &amp;ldquo;easy to follow&amp;rdquo; is defined as:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>(a)&lt;/strong> the paper should have a corresponding GitHub repo;&lt;/li>
&lt;li>&lt;strong>(b)&lt;/strong> the code in that repo must be substantial — a repo with only a README, for instance, is hard to reproduce and should be excluded;&lt;/li>
&lt;li>&lt;strong>(c)&lt;/strong> datasets and other resources should be open-sourced as well.&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://geyuyao.com/post/auto_paper_reader/figures/hf.png" alt="HuggingFace Daily Papers list" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;em>HuggingFace Daily Papers: dozens of papers a day that you have to click into and sift through one by one.&lt;/em>&lt;/p>
&lt;h2 id="tex-source-vs-pdf">TeX Source vs. PDF&lt;/h2>
&lt;p>On authenticity and implementation, I had two considerations:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Authenticity&lt;/strong>: the publishing institution should be a leading university or lab.&lt;/li>
&lt;li>&lt;strong>Pipeline&lt;/strong>: to keep analysis simple, the Agent should read the paper&amp;rsquo;s &lt;strong>TeX source&lt;/strong> directly rather than the PDF.&lt;/li>
&lt;/ol>
&lt;p>The reason to avoid PDFs is that PDF parsing breaks in all sorts of ways, and string matching is hard (e.g., searching the body for a &lt;code>github.com/&lt;/code> link).&lt;/p>
&lt;hr>
&lt;h1 id="implementation">Implementation&lt;/h1>
&lt;h2 id="python-pre-filter-first-then-hand-off-to-the-agent">Python Pre-filter First, Then Hand Off to the Agent&lt;/h2>
&lt;p>I want it to crawl automatically every day, so I get the day&amp;rsquo;s papers effortlessly. The whole thing is two steps: &lt;strong>a Python script does a coarse pre-filter first, then an Agent handles judgment and organizing&lt;/strong>.&lt;/p>
&lt;p>For the pre-filter: HuggingFace has a public API, and you can fetch a given day&amp;rsquo;s list by date, so I wrote a zero-dependency script &lt;a href="https://github.com/YuyaoGe/Paper_Agent_Skill/blob/main/scripts/fetch_hf_papers.py" target="_blank" rel="noopener">&lt;code>fetch_hf_papers.py&lt;/code>&lt;/a> that filters coarsely by keyword rules:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># scripts/fetch_hf_papers.py — call the public HF API directly, no API key needed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;https://huggingface.co/api/daily_papers?date=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">date_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Titles matching these keywords → excluded&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TITLE_EXCLUDE_KEYWORDS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;benchmark&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;benchmarking&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;bench&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;speech&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;audio&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;video&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;3d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;compiler&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;cuda&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;kernel&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;triton&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;tpu&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;xla&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;quantization&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;quantisation&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;distillation&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Abstract must match at least one → confirm it is in the LLM/VLM space&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ABSTRACT_REQUIRE_ANY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;large language model&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;llm&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;vision language model&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;vlm&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;multimodal&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;reasoning&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;reinforcement learning&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;instruction tuning&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;fine-tuning&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;alignment&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;chain-of-thought&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;in-context learning&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The standard library (&lt;code>urllib&lt;/code> + &lt;code>json&lt;/code>) is enough, and no API key is needed. This step narrows dozens of papers down to a dozen or so; the rest is left for the Agent to weigh.&lt;/p>
&lt;p>Next comes judging and analyzing the papers. Since today&amp;rsquo;s models already strike a good balance between instruction-following and cost, I decided not to build much of a harness and to hand full judgment to the Agent instead. To that end, I wrote the requirements up as a &lt;a href="https://github.com/YuyaoGe/Paper_Agent_Skill/blob/main/SKILL.md" target="_blank" rel="noopener">Skill&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="skill-pipeline-design">SKILL Pipeline Design&lt;/h2>
&lt;p>The SKILL splits each candidate paper into three steps:&lt;/p>
&lt;p>&lt;strong>Step 1&lt;/strong>, extract the GitHub link: prefer the &lt;code>githubRepo&lt;/code> field from the HF API; if it is empty, search the paper&amp;rsquo;s arXiv TeX source for &lt;code>github.com/&lt;/code>.&lt;/p>
&lt;p>&lt;strong>Step 2&lt;/strong>, call the GitHub Contents API to verify whether the repo has substantial code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">API: https://api.github.com/repos/{owner}/{repo}/contents
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Keep (any one of):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - .py / .sh / .ipynb files in the repo root
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - directories such as src / scripts / train / model / code
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Drop (any one of):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - only non-code files like README.md / LICENSE / assets
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - API returns 404 (repo missing or empty)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - repo name / description contains &amp;#34;coming-soon&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Step 3&lt;/strong>, write a Chinese summary that is &lt;strong>understandable at a glance&lt;/strong>, then assign tags from a fixed set so that papers can be filtered:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">RL · Fine-tuning · Training-free · Long-context · VLM · MeM (Agent Memory) · API · Diffusion
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Finally it lands as one JSON record:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;date&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2026-03-12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Prism-Δ: Differential Subspace Steering for Prompt Highlighting in Large Language Models&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;arxiv_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2603.10705&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;github&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/YuyaoGe/PRISM-DELTA&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;abstract&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;PRISM-Δ is a prompt-highlighting method that makes an LLM prioritize user-specified text spans during generation. The core idea is to decompose the difference between the positive and negative cross-covariance matrices to maximize discriminative energy and eliminate shared directions; each attention head gets a continuous softplus importance weight (weak-but-useful heads contribute at reduced strength), and the method is extended to the Value representation to capture content-channel signals. Across 4 benchmarks and 5 models, PRISM-Δ matches or surpasses the best existing methods in 19 of 20 configurations, with relative gains up to +10.6%, fluency loss halved, and up to +4.8% relative gain in long-context retrieval.&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tags&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Training-free&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="harness-engineering-design">Harness Engineering Design&lt;/h2>
&lt;p>With the SKILL in place, the next question is the calling relationship between the Agent (or Subagent) and the SKILL.&lt;/p>
&lt;p>There are two choices:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Master–worker&lt;/strong>: one master Agent dispatches multiple sub-Agents.&lt;/li>
&lt;li>&lt;strong>Parallel&lt;/strong>: spin up an independent Agent for each day.&lt;/li>
&lt;/ul>
&lt;p>For master–worker, I implemented it with OpenClaw, capping sub-agents at 5.&lt;/p>
&lt;p>Timeouts, however, kept happening. When I asked Claw to survey ten days of papers, it would indeed launch several sub-agents, but timeouts and context overruns came up constantly — the whole thing was extremely unstable.&lt;/p>
&lt;p>So I went with the latter option: one independent Agent per day, running in parallel, each writing its own JSON, with a main program merging them at the end. One paper list per day — and as it turns out, the simpler the setup, the more stable it is.&lt;/p>
&lt;p>Parallelism is just &lt;code>xargs -P&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># backfill_papers.sh — batch-backfill historical dates, default concurrency 6&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">printf&lt;/span> &lt;span class="s1">&amp;#39;%s\n&amp;#39;&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">MISSING&lt;/span>&lt;span class="p">[@]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="p">|&lt;/span> xargs -P &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$CONCURRENCY&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> -I&lt;span class="o">{}&lt;/span> bash &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$SCRIPT_DIR&lt;/span>&lt;span class="s2">/run_kimi_one_day.sh&amp;#34;&lt;/span> &lt;span class="o">{}&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PAPER_READER_DIR&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The merge script &lt;code>merge_batches.py&lt;/code> also only does deterministic work: scan &lt;code>paper_batches/*.json&lt;/code>, skip dates already present, and append the missing ones in date order.&lt;/p>
&lt;h2 id="choosing-the-agent-framework">Choosing the Agent Framework&lt;/h2>
&lt;p>There is one hard requirement for the Agent: &lt;strong>it must launch non-interactively from the command line&lt;/strong>, rather than requiring manual operation inside a terminal.&lt;/p>
&lt;p>For example:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Cursor &amp;amp; Claude Code&lt;/strong>: require a GUI or terminal interaction.&lt;/li>
&lt;li>&lt;strong>&lt;a href="https://moonshotai.github.io/kimi-cli/" target="_blank" rel="noopener">Kimi CLI&lt;/a>&lt;/strong>: lets you pass the prompt as an argument to the launch command — easy to invoke, and Kimi is cheap.&lt;/li>
&lt;/ul>
&lt;p>So processing a single day is one line:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># run_kimi_one_day.sh — process a single day with Kimi CLI, write the batch JSON&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">kimi --print --quiet &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --work-dir &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PAPER_READER_DIR&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --add-dir /Users/yuyaoge/Project/Paper_Agent_Skill &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -p &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PROMPT&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &amp;gt; &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$LOG_FILE&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> 2&amp;gt;&lt;span class="p">&amp;amp;&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Even so, the workflow still has to be started manually each day, and the point is for it to run invisibly. So the next piece is an auto-start script tailored to macOS.&lt;/p>
&lt;h2 id="auto-start-script-on-macos">Auto-start Script on macOS&lt;/h2>
&lt;p>The auto-start uses macOS &lt;code>launchd&lt;/code>, configured via &lt;code>com.yuyaoge.paper-daily-fetch.plist&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Run once at login / load --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;key&amp;gt;&lt;/span>RunAtLoad&lt;span class="nt">&amp;lt;/key&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;true/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Then run again every 2 hours --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;key&amp;gt;&lt;/span>StartInterval&lt;span class="nt">&amp;lt;/key&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;integer&amp;gt;&lt;/span>7200&lt;span class="nt">&amp;lt;/integer&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Two features in &lt;code>daily_fetch.sh&lt;/code> are worth mentioning:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>It does not process today&amp;rsquo;s papers&lt;/strong>: HuggingFace updates the same-day list in real time as authors submit, so crawling today would miss papers submitted later in the day. By default it grabs the seven days ending yesterday rather than today, which conveniently backfills any days the machine was off.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Idempotency&lt;/strong>: it should not rely on a fixed daily trigger, since there is no guarantee the machine is on at that moment. So it runs every 2 hours after boot — skipping a day that already has results, but running once more when the result is empty; and if git shows no changes, it does not push.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="quick-start">Quick Start&lt;/h1>
&lt;blockquote>
&lt;p>&lt;strong>Requirements&lt;/strong>: macOS, Python 3, and an installed and logged-in &lt;a href="https://moonshotai.github.io/kimi-cli/" target="_blank" rel="noopener">Kimi CLI&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>1. Clone the repos&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/YuyaoGe/Paper_Agent_Skill.git &lt;span class="c1"># Skill + scripts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone https://github.com/YuyaoGe/paper_reader.git &lt;span class="c1"># data + frontend&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>2. Install the Skill into Kimi&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> Paper_Agent_Skill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p ~/.kimi/skills
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ln -sfn &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PWD&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> ~/.kimi/skills/hf-paper-filter
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>3. Verify the pipeline manually&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># run_kimi_one_day.sh YYYY-MM-DD [paper_reader path]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./scripts/run_kimi_one_day.sh 2026-06-01 /path/to/paper_reader
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>4. (Optional) Backfill a historical range&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># backfill_papers.sh START_DATE END_DATE [CONCURRENCY] [paper_reader path]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./scripts/backfill_papers.sh 2026-04-25 2026-05-26 &lt;span class="m">6&lt;/span> /path/to/paper_reader
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python3 ./scripts/merge_batches.py /path/to/paper_reader
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>5. Install the scheduled job for unattended runs&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cp scripts/com.yuyaoge.paper-daily-fetch.plist ~/Library/LaunchAgents/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">launchctl load -w ~/Library/LaunchAgents/com.yuyaoge.paper-daily-fetch.plist
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h1 id="frontend">Frontend&lt;/h1>
&lt;p>The final list is aggregated into &lt;code>paper_list.md&lt;/code>, so the Agent can easily append to the end of the file and the frontend can easily parse it.&lt;/p>
&lt;p>The &lt;a href="https://yuyaoge.github.io/paper_reader/" target="_blank" rel="noopener">frontend&lt;/a> is designed as a pure static page: at runtime it pulls the Markdown down and parses it into cards, supporting filtering by tag and search by date:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// The frontend fetches the Markdown data source and parses it at runtime
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">resp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;paper_list.md&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Each entry: - **Title** `[Tag]` — [id](url) | [GitHub](url)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;gt; Chinese summary
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">currentPapers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">tags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">links&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">desc&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It is hosted on GitHub Pages; the scheduled script pushes the updated &lt;code>paper_list.md&lt;/code> to the cloud every day, and the page updates in sync.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://geyuyao.com/post/auto_paper_reader/figures/paper_reader.png" alt="paper_reader UI" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;em>paper_reader: what it looks like after filtering, tagging, and generating Chinese summaries — filterable by tag and sortable by date.&lt;/em>&lt;/p>
&lt;h1 id="overall-pipeline">Overall Pipeline&lt;/h1>
&lt;p>Putting it all together, the full pipeline is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"> macOS launchd ──▶ daily_fetch.sh (every 2h, idempotent)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ split by day, run in parallel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> run_kimi_one_day.sh × N (xargs -P 6)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Kimi CLI loads the hf-paper-filter Skill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ fetch_hf_papers.py Python pre-filter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ GitHub Contents API verify code presence
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ write Chinese summary + tags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ one JSON per day
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> paper_batches/YYYY-MM-DD.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ merged by merge_batches.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> paper_list.md ──git push──▶ GitHub Pages (frontend fetch + render)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The pieces involved are all pretty ordinary: the Python standard library for the pre-filter, Kimi CLI for judgment, &lt;code>xargs -P&lt;/code> for parallelism, &lt;code>launchd&lt;/code> for scheduling, a single &lt;code>paper_list.md&lt;/code> as the data source, and a static page for display.&lt;/p>
&lt;!-- # Project Links
- Agent Skill (core logic): [github.com/YuyaoGe/Paper_Agent_Skill](https://github.com/YuyaoGe/Paper_Agent_Skill)
- Frontend source: [github.com/YuyaoGe/paper_reader](https://github.com/YuyaoGe/paper_reader)
- Live demo: [yuyaoge.github.io/paper_reader](https://yuyaoge.github.io/paper_reader/)
- Tools used: [Kimi CLI](https://moonshotai.github.io/kimi-cli/) · [HuggingFace Daily Papers](https://huggingface.co/papers) --></description></item></channel></rss>