<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>木然轩 Blog</title>
        <link>https://muranxuan.top/blog</link>
        <description>木然轩 Blog</description>
        <lastBuildDate>Sat, 26 Sep 2026 11:49:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-Hans</language>
        <item>
            <title><![CDATA[openpyxl性能优化实战小记]]></title>
            <link>https://muranxuan.top/blog/openpyxl-perf-tweaks</link>
            <guid>https://muranxuan.top/blog/openpyxl-perf-tweaks</guid>
            <pubDate>Sat, 26 Sep 2026 11:49:00 GMT</pubDate>
            <description><![CDATA[最近做了个Excel文件处理小工具给一个项目使用，但是一个Excel文件要处理大概两分钟，我觉得这也太慢了，虽然电脑性能一般。因为就是读一个Excel文件，然后写一个Excel文件，也不涉及什么数据库、网络之类的，应该很快才对。]]></description>
            <content:encoded><![CDATA[<p>最近做了个Excel文件处理小工具给一个项目使用，但是一个Excel文件要处理大概两分钟，我觉得这也太慢了，虽然电脑性能一般。因为就是读一个Excel文件，然后写一个Excel文件，也不涉及什么数据库、网络之类的，应该很快才对。
于是，我让AI去做性能优化，毕竟小工具的代码也是Vibe Coding出来的。起初我并没有太在意，不过，当我发现AI为了做性能优化开始修改openpyxl的源码，并且效果很显著时，我觉得有必要看一下了，学习一下。
学习了下本次的性能优化过程，整理了本次性能优化的改动点，然后就有了本篇文章。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="用-cprofile-找热点">用 cProfile 找热点<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E7%94%A8-cprofile-%E6%89%BE%E7%83%AD%E7%82%B9" class="hash-link" aria-label="用 cProfile 找热点的直接链接" title="用 cProfile 找热点的直接链接" translate="no">​</a></h2>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cProfile</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> pstats</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> io</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pr </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cProfile</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Profile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">enable</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># ... 业务代码 ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">disable</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> io</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">StringIO</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pstats</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Stats</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pr</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> stream</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sort_stats</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"cumulative"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">print_stats</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">30</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">getvalue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<!-- -->
<p>表头</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span><br></div></code></pre></div></div>
<p>分别表示：函数调用次数，函数自身消耗时间，平均每次调用函数自身耗时，函数从进入到退出耗时，平均每次调用耗时，函数所在文件行号与函数名。</p>
<p>跑了一下，性能瓶颈主要有：样式复制、工作表读取。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="性能优化">性能优化<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96" class="hash-link" aria-label="性能优化的直接链接" title="性能优化的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="样式复制优化">样式复制优化<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E6%A0%B7%E5%BC%8F%E5%A4%8D%E5%88%B6%E4%BC%98%E5%8C%96" class="hash-link" aria-label="样式复制优化的直接链接" title="样式复制优化的直接链接" translate="no">​</a></h3>
<p>之前跨工作簿复制样式时，是采用逐属性深拷贝的方式，这样非常消耗性能。更好的做法是：先把样式注册到工作簿，然后设置样式。</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">register_style_to_wb</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">src_cell</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dst_wb</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""把 src 的样式注册到 dst wb，返回 dst wb 内的 StyleArray。"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">template_styles </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    register_style_to_wb</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">template_ws</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cell</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">row</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> column</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> out_wb</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> c </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_COLS </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">out_ws</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cell</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">row</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">out_r</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> column</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_style </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> template_styles</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">c </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="只读取需要的sheet">只读取需要的Sheet<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E5%8F%AA%E8%AF%BB%E5%8F%96%E9%9C%80%E8%A6%81%E7%9A%84sheet" class="hash-link" aria-label="只读取需要的Sheet的直接链接" title="只读取需要的Sheet的直接链接" translate="no">​</a></h3>
<p>用户上传的Excel文件有一些程序处理时并不需要的Sheet，但openpyxl会解析所有Sheet。可以给<code>load_workbook</code>加个<code>only_sheets</code>参数，只加载需要的Sheet。</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># openpyxl/reader/excel.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">load_workbook</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">filename</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> only_sheets</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ...</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="屏蔽透视表">屏蔽透视表<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E5%B1%8F%E8%94%BD%E9%80%8F%E8%A7%86%E8%A1%A8" class="hash-link" aria-label="屏蔽透视表的直接链接" title="屏蔽透视表的直接链接" translate="no">​</a></h3>
<p>openpyxl的<code>WorkbookParser.pivot_caches</code>是个<code>@property</code>，每次访问都会触发全部透视表解析。但是我们的场景用不上不需要透视表，因此直接屏蔽即可。</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># openpyxl/reader/workbook.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pivot_caches</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ...</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="启用-lxml-后端">启用 lxml 后端<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E5%90%AF%E7%94%A8-lxml-%E5%90%8E%E7%AB%AF" class="hash-link" aria-label="启用 lxml 后端的直接链接" title="启用 lxml 后端的直接链接" translate="no">​</a></h3>
<p>openpyxl默认用Python标准库的<code>xml.etree.ElementTree</code>写 XML。安装lxml后，只需设一个环境变量就切到lxml后端，写入速度会有一些提升：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> os</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setdefault</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"OPENPYXL_LXML"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"True"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># 必须在 import openpyxl 之前</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> openpyxl</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="修复非规范属性bug">修复非规范属性BUG<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E4%BF%AE%E5%A4%8D%E9%9D%9E%E8%A7%84%E8%8C%83%E5%B1%9E%E6%80%A7bug" class="hash-link" aria-label="修复非规范属性BUG的直接链接" title="修复非规范属性BUG的直接链接" translate="no">​</a></h2>
<p>由Excel/WPS生成的Excel文件有时会有一些不符合OOXML规范里的格式属性，如：<code>defaultColWidthPt</code>，这将会导致openpyxl解析出错：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">TypeError: SheetFormatProperties.__init__() got an unexpected keyword argument 'defaultColWidthPt'</span><br></div></code></pre></div></div>
<p>这个问题长期存在，openpyxl应该并不打算修复。解决方案就是在<code>SheetFormatProperties</code>里显式接受这些字段：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># openpyxl/worksheet/dimensions.py、</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">SheetFormatProperties</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Serialisable</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    defaultColWidthPt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">allow_none</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># 新增</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="补丁">补丁<a href="https://muranxuan.top/blog/openpyxl-perf-tweaks#%E8%A1%A5%E4%B8%81" class="hash-link" aria-label="补丁的直接链接" title="补丁的直接链接" translate="no">​</a></h2>
<p>直接修改openpyxl源码不太好，因为配环境时还需要替换openpyxl的源文件，且不利于库的升级。所以，可以对openpyxl进行patch，我做了一份供参考：</p>
<p><a href="https://github.com/straicat/netdisk/blob/master/gist/openpyxl_patches.py" target="_blank" rel="noopener noreferrer" class="">openpyxl补丁</a></p>
<p>使用时，只需要import一下就会应用patch了。</p>
<p>最后，对比下优化前后的执行效果：</p>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/e704f6b3e1788a1a11217a330f4ee193-8a83afe1c9f796636e506ccf2d78065f.avif" width="2360" height="1360" class="img_ev3q"></p>]]></content:encoded>
            <category>Python</category>
            <category>openpyxl</category>
            <category>性能优化</category>
            <category>cProfile</category>
        </item>
        <item>
            <title><![CDATA[用DeepSeek Harness做了个个人网站工具]]></title>
            <link>https://muranxuan.top/blog/build-site-tool-with-deepseek</link>
            <guid>https://muranxuan.top/blog/build-site-tool-with-deepseek</guid>
            <pubDate>Tue, 25 Aug 2026 10:32:40 GMT</pubDate>
            <description><![CDATA[前言]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="前言">前言<a href="https://muranxuan.top/blog/build-site-tool-with-deepseek#%E5%89%8D%E8%A8%80" class="hash-link" aria-label="前言的直接链接" title="前言的直接链接" translate="no">​</a></h2>
<p>静态博客维护起来有点麻烦，尤其是对于图片的处理。之前为了处理博客里的图片，用Shell脚本写过工具，用Python写过，用Go写过，涉及图片上传到图床、图片压缩等。换来换去，现在基本不用图床了，图片直接和Markdown文件放一起，便于管理。由于我的网站用的国外的服务，访问速度不快，所以我会把图片进行压缩，这样页面加载起来可以快一点。考虑到avif格式图片压缩率比较高，现在我网站的图片基本都用avif格式了。一直以来，我都是用自己写的命令行工具去进行图片的处理的，勉强够用。</p>
<p>除了图片，我还会让AI给我生成博客的slug，这样URL的观感会好一些。最近体验了下DeepSeek Harness，我突然想用它来给我做个个人网站工具网站，这样能方便一些。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="用dsh写工具">用DSH写工具<a href="https://muranxuan.top/blog/build-site-tool-with-deepseek#%E7%94%A8dsh%E5%86%99%E5%B7%A5%E5%85%B7" class="hash-link" aria-label="用DSH写工具的直接链接" title="用DSH写工具的直接链接" translate="no">​</a></h2>
<p>说干就干，我先简单调研了下，因为Docusaurus是基于NodeJS的，所以我想用纯NodeJS做，不想用Python、Go之类的后端语言，于是用NestJS+Vue这样的技术栈。</p>
<p>我整理了下我的需求，写了2000多字的提示词，然后喂给DeepSeek。它自动启用goal模式，并列出任务清单执行，还自动构建、测试。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/03f29a1638660c5960228b6f160208a4-dec44ab2f87c64b90a1c24fb3e37850f.avif" width="942" height="540" class="img_ev3q"></p>
<p>DeepSeek运行了足足有半个多小时，Token花了我大概6元。不过，初次执行的产物就已经可以直接运行了，功能上没有很大的问题，后续再微调下就行。</p>
<p>由于前后端都是使用的NodeJS，所以启动起来非常简单，只需要cd到项目的目录，然后运行：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev</span><br></div></code></pre></div></div>
<p>就行了。反正我是自己用，就不用构建了，直接dev模式就行。</p>
<p>下面介绍下我给我的网站做的工具网站，在本地运行，功能有：Wiz转博客、图片管理、工具箱。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wiz转博客">Wiz转博客<a href="https://muranxuan.top/blog/build-site-tool-with-deepseek#wiz%E8%BD%AC%E5%8D%9A%E5%AE%A2" class="hash-link" aria-label="Wiz转博客的直接链接" title="Wiz转博客的直接链接" translate="no">​</a></h2>
<p>我习惯先在为知笔记里写，然后用为知笔记导出Markdown。但是，要发布到博客需要经过一些处理，例如：添加front matter、处理图片等。整个过程有多个步骤，但是用工具的话，都可以帮我处理：</p>
<p><img decoding="async" loading="lazy" src="data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAAD5bWV0YQAAAAAAAAAvaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAFBpY3R1cmVIYW5kbGVyAAAAAA5waXRtAAAAAAABAAAAHmlsb2MAAAAARAAAAQABAAAAAQAAASEAABq2AAAAKGlpbmYAAAAAAAEAAAAaaW5mZQIAAAAAAQAAYXYwMUNvbG9yAAAAAGppcHJwAAAAS2lwY28AAAAUaXNwZQAAAAAAAARcAAABqgAAABBwaXhpAAAAAAMICAgAAAAMYXYxQ4EEDAAAAAATY29scm5jbHgAAgACAAIAAAAAF2lwbWEAAAAAAAAAAQABBAECgwQAABq+bWRhdAoLAAAAJURb1J9fMAgypjUUAKQAENhuvYic5xjdf//nNZsFuwN5xpg8a0hhc9NtduE9Mcsimb2MU3kbWga+NnndjINRCbeeNc0TXJOE+TMnpMwVBaZ07VwsAG8Db221cPG0CJlhihRuiJcmi0K2bRml0ZAjzzESCcpPV1dwYAztmdQHWi285BwJvGSJdo5DyswihRFo88pYH8qrKgBNh5WEYMbMUj38IYqCEA4gbhjWTBsb61L2owQATo753RuGXPhM3yG7FjDrRvkAvPL/QSUMCccF09zxs5WvZEc/vZYlCDxebGZ2aOukwJwSL2Ka1nIAau1HUOPy+C/IpUXlUyfJtksnyFinks28AMa/GvjukTpcMyLB+MUX9GLdpEpwQna1wBzSHpk4AwVwSuKlnwxXDwvVmWZBmPkAJ+P6QBAujZUuzHEdS6hD1vVTe0lcLebGb1W0+g9qK7cObJ2a6sD9hlP7z0u1bLZ3nnLd3AeEhXcUWpVjdkGh+VLvvR6Jc1dhTP4kc84xvB6GDTcOYyl4UbYUzV5PSlIxL0LderudUok//fQ/hBpU6Ojz4IzcYsM4Oomusu5+m4qIZHX+OQl0zra7y+78LA35vL9Hihc0HAu/67r/tyHYIFdwHXIRL2IpQC/WrkN2tEaAZCKIZuOUrXMalr7d+M1Jmb1QlUJrWRp8oLhtzqENZM0T4aVZvUby1NzjeG48Yqy7a0bHJrVS3YsqBOAHeYaoE18+SAbaUrn24EofzA2Hm2Ys2xSQ1shxRvgAXqm1+mFWAa9H4uM+5T7qK9KqR+V4mWizjwh1RtTwr/kAsfE+To7XsEqxfBwg9E/Bc2BQin0JKzfqzL7UzYi5BLY49PMTycX1XiCcbn3HeeBfAv4+Qj/x5i1eGVODDVNhwf+pogALT+55CExHLRPymLEeru8W46yAo2agcmDHnC86C2FKv0vAeekJmFwc+qgPEck4vbUj3J1fjbWIId0lsMnFX4l3SWLuBpLzK3Eygyq/fTLCN9ryq1FJTZ+Zlw/K0dUyC+1v2PGYa96hgKTTTrSvwXdKRX2f2CCXc/eA+C47QXWO9MJF7YaLVkyAEE7JQHzwoE9nS47I+cONMVYTc7O8DSgUQmTK3Z6w1PhAqh9xOY39Oo3qLQ4F8Vl95tnoGu4tIydUjyNzHl8fLenlAt0+7nITgeUt7+KDzllHBpP6XnxFIKJe8QLm82aSfwnLg8HBJ1EXhU8R434eyDeoSoz42oB9YJo909iNevy3Adn7F7lICe3S4N5+X9nQkJclTJKBahvyp0PznsN/Huy9HvzotByQVAYebtLrmqWQuHOWR58/dAWxYljAjNfG/0XKRsikad9ZzCaTvyzL/PsMAjS+/sMMk9VzOuecK4E5h3v71QMxvAa33cqkP2pxFhp8QfXvC2mRnXSyaGpOBduGQer5zR7ya89rTO1P4C8XQsLBi66z/OsiwcCWaCS9ze3PAOzx0lWyPtTAdr0Hfm4j5Rd4cs2djHDpulEBlV1BkU/KITJNIE5RqMX32ofw0yHkFt00nDPu0W5BDhd8wOtFODFUON4Vh+hyRAyAm11qe/1uJQqippEb2nEyyHKqcg4X/BE0Eq/gwW6DVQBYnE5KdtgeMw+3dlL0GY6GM+n8V3lQpeSOnw0XkFb6yZpJJ9A7R9B3J8aNrHVMsWZSwjqzD1XjDJTbQdrdC3RyUIbz7H/H1ZjORctQ08IO6LyHLtGN6qEDwWuEMAx8NNoSSBbd2s324rSR0wwl9OaphO7O3ncKR5AaF0zRj+8LwLcNrTGPqiMc7tD2aVgR4P69yblIz24rtdx6mFzUoHLZ4eQuXkpBPd2z3wBJSSbu7D0lYfXoGBXTKKYaTjuyL2Qcc0o1HARDZ9F5W3I3gS8Ba85eOOcTzxUJErMrvoKPbukGNPtwLi2cGHByn8PlQUw6/DDimP5LOek5wij2A3eqonxMQ+6wb3hK4T7frgAzO5wU5vsj7+ntdLnt4WlIfi1Lx1I0yv55IjLeVYbTv2jw7myNyGhFzAqa0lAI7Kgh63WBHHZpIXzVlPG/oFyEE7BERImHAZ3QlPJ9ScGIS35iiUMuGWqXWMf5nSzlLmrnXv8+7QhsGMeXUccqO7NgnOC0wxy2BMl1uaq5OtPmw6evAeqXWx8mRIbhDi056kY/W88J/zH7i1HQpV8m6NHnxj2zrx4OT04TCksX5HtxzRH9NJMu6Lvy1y/Y80eRc4GdPIkSy95I0UsXmys78Cl+eSl348C89CCR6FSul+bwweBfFX6zDqCrB8Rt7z+pgK25Z34q8DDrk4MRF7NQFss4/Kjr1xEX/dAdaj6Z+iCMnvS1K2f0+lH684an7cZguJUwUtpsUlKhdsn0MY8QLtgog8giacjK2dXm81J1BfyOQjKKNPPbMOyUHV52gIMPziFTxO8nTmdluhQETPVk4XMLbrx1W9RNRTy0NQ/5mndmwxQYdoVwvgaCIhNMnsGl79fVouNN/B5LyAUxhk5MvO7JrVTiIqrEQvkj4BHdxNQ122JoLzsyCJMm6DawP89tY+G2V9wlZdZSCbZlIC61WYx2yFlrLiquTgDYehHOV2VfyDAQ0NH7NO+bz/Ls+tYL7BvJoXrDHi+w4h/2zazlQmHsLikfS1UFOqocrvJLlLwMXeDuvAtNR5qhUstZMlx18y5UbGOvsSECe6hHew/G0i4KZ06RXKOWLMkNne6qsRqz5Nlg1LMyG+fsq+KM+zhiW0cFJCMTdSR+LDhv3pBRjClsfkOjcsfyWWF6kXwXt82cJvHuMHkawdLx7KJSMzgmqhyv6ROoyiHWGY4X432t/73UiBMXKwAK+FdCrtOjOFTOYMnhjpb3Ugkix8u4D3/LMGA8w2kgZEgzPpCIzJxkL+Mon/E8OP78MQsofEIMelhrtmAUY6mPi7hUZ8s6qXVYVShekpD/Jy8xTMEL09ZK62qS7DeaiaRgQxlnJ9c/RqOfW1do/DfMvw9fMFuvbufFmB7nm53J9AVQRzdOw9LypuviEgGCml6Q+wKsQDNfDw39OfWWPTk102EdaKI4TVci45SAq0AN4QjVvbOWwSb6P2TA8PZ1e1dvDCeiLx+bPniScA4Q4SSwStRKNG+wO4pwj1dM1Gx6BAEynO138fCfMNOZKY6EctB8DvGoW2YxIDd42Rakx39ZJsK6DDQyavpjEA4+qAILdWsG6u7Pe/kc30TYi1oexO45jqnlj/8Xmpvg183YvKK00SpmS8uJmv8tJWXjlp7cyVGfsihG09II7VCsszMWKLVoXtgwhZXv3PAP0EYuNEp2HF8iZ3smcs+C/iHiwOc08k3Jo+4iDWWCK/Brdl77vKhHqcT0Iuj+86rQHb2dRjkxGLMjN6DxF56mrXxW1Wavw9RNFCUEwWrr4AN4rdAfJq1ReO4bDuE5mJoGwfvNh5nxfvI59UOcJTzlfO4hOvBNd8C2bMCJZgtdTGfvH4kiFzNEr3aMjUvJ++4gLI9QogTLeW5GN/GlTMhJb/+huWzu3IkR5A4t5JspsOEVlQnkz1BgKaxSL1vtj4dSs4oFf/oKrMqrWqIfStzM1yS/ozFDdVy8MQWBCkrJxcQnKoJ1jVc0RjcHlGmQi6nPa4wOYE2FRpncqOE1YbQSWuc4dY/qWG7DGwaPbmSFdkyZXERqAdXb7Z/3lvXT2KTFBqwl+YQtXf+6jC9aX+TSFRwTY30MO4ThZLZKRp5wZycUVT1vVJtPNmlge+Vy0BRYXxfCBHtUDBgBkEv7nYKV+Pu9kfwyGx4xRYL0utc5dUUay7hVtbkxKb/gIFiKV04mAwhj9is2ePJ2Op3hFD3vsHuqFq1suOPpNfgtRWrVoYgReRWTYThRmurgn+sMQnqKiMV83xiocIKOZK7UqHNpg64k3Kz+N2bEazFQIR9Je0tR4XovRO1YMcJLeg9GP89iZDQW5d/S64yfJguqLwM/gKZrNiQkAiYjuIgrWoVlPVK7tvolsTvg9s8s7NO39flMB4mTiJLcoh7c7wT25r2Y5kDYi5cJTVnCxH4Yci/5opSpZUN8WUo1W7QPgjzOOMC2QhKiHC5YN8jTR4E8k/DsBLPRYsryQG36rT+fqdC/Qh/yYvoLP/CK9Ctl3pxKD9IcSQ04D5kyyB9XKGdRd60zP8HI9hhH6drc2AUbvzFUDg3sgXb+799jJ7wq0ND+/gAVNDsjmEz2Q4f7ZGtYKK+I2LPeiCuboTCBfOexTY+fdab1SJDiwkl/bwXOAudl5pkdr407FGBD4GRYdnmHWCyUcH9yxCc7TeRul8lQVhP7vRqI1AfAXul8w7d29tXCNU6b3sq95wINM0LRBwhLO+CCPsFzrUXOOZ5IEhXGLL9sdA0mfqO3xiXU6d6WdkZ/L3O/IkRDGRRIehOIPinDbSMQ4BFZ7tjxZB3eOLrVfAsjnGwDAo0PFkgbSv7o+T5wx0xr0O5udsiDKdXJ9i4JgQZnd7sMS8G8Ae9aIlBnMF+JjVbBuC58OamPmtr3QR0YlYLTd9szko142SGdEoMcju8/xCnhU+qYXZOVYgTaYUahMEAMPMO4Hpp6cFLiqnONauavTsSy56bAsp9nbHZ//kapeC+TBVPgM3CPhPAqnU8ewz6acizylKb9dnck+aUSe+2lt9HKNsEm10hBOhfK4h3QAqz03YHihKJRdRLPtQwuiLdvYmllZhp/9Ns1t+mdd1Gx87nKURgaF5LKfGlABsgFhviaihueKPIg3nIyCCM590rfpWCAjZK1r/xDy+th15F4Tp5SFMGEu+tWf3QS92QezLNkB5FzrZ1sBBFavHdC/YLDUnQxnLapW4w7gudGPctx8YpvbgtVz53fok68Z8dRafpmjpn/wurag3gB67Njlvo+aaAt1t8/pA1ZdLPOJBbvnHhzZ1GZOw/yk6Y6ocfilIRXvwsj1QZ6g0IoWdYQHuTa4aS/e9YgcK+KAEbqCEbLG/l6/k8vEC0h62xg/SGe2g4X/DwO8Fi87YiVZBKUFZFa29/gjQ83lcEQgd6PfFyGSkKOBh313Fg1S0E6yzUX/tRc7r1iS23zQcrulmdnc268SpMTMAArR5WydtJ2WduLR4vqoB4Zc+vvPIp2eZhO59zCFVNMVmMLUGq6TzwK9eb/aMmev5JJCYfkNYOBXEyF3B9kJn8QfyfnJiQQaefr9ObpzZRsj4fVmkaTJAh4hYqEAArJq2qOB8B3UZgwsvKq3W0QGcA02mwj8YUCqlR3UA0kKH9b+1cqFRuFYf8hCtASqH6FXasIbltnvxEovEJ5T9m3/FRpWvFUsg/GcBvXgrSqDgzim5+gTuF4xiiRp1kw7miZZq36XKnv7RCLiPgk3U3VXuYqJjh0wjjxvJbYezUFYrPZLp8gc6qy6yEwtpUCcE1/Bl0kbeV08O73hh8pbdnTIh3Ihljhn2evc+8qILn3tTSS2OgV5wuVn3oIYuRYvCglfKOjIafkAJVjFp/1OTV+B3Uuj9zBc91zTzDm1ONCPxFC5cCQPWOQs7sX8XSh8K6rHCWp3CQgcFj81CvtUvmYasSSOLwaJOu24niEBIKlpqhb8Fipry1ZJ0gmX5W7rrE2f9xNJJjgn4rB8J2L92FTVjYsiCK6lr3j7wCsrg7GktUGAesa8IA8BMCbwRU7VXcv2Lt+ZgEKEO2ZKUn2GbeLmbZPksGO47yA1yzTfHksCJy6fu40WJov9r7Zeb7sOPy5HrZ2CYF5H0sesCF+NP30ZnPGfLfA4UF4KUjr7lU/7OyjLPk+TQJqHnJsoorQL+5gv+kyxU/r3vod/33jaa7NAwOxaGfhWfwpu+BymYFaP9vVzuOI5M7aXvnknZ9KKPxIKd9wnfniqalxj1kTe7BaTW8O13cOHuz0uYdRHkSFOOCsJuDr4LkFtF5NVQlK5DsXUtfvi5XtMMENrIFmwGxrQTTD4sVEdFY8PTiKK1i9fKyBc0MT4KXV0PmFPNzckEDMG2f03PnBKYll+w/BAjISK6YswN4vaui3SAV5i5rVul+JUmalanSn5SrYRP1sV1hLcgHoj9iY6liW/AJKyrTantjzdiKwo0A7w1SNiOHlLVhto3aOY7W1EKLCjX54LBw1UpsqoSg6Aib90dpDeAiuZIKwBSI8WWKWg1dWTUmK1cNGO2LgEu8HqzKg6u7kXl3z0fTtbb/D5o49GIZsBOcnCT3vXqE5j8sxdp8CUB0Xll/RQSgo0/uAzyz5e14AKZmKVmL50itBo1dly6fdiwitUvhv/D5XN/wwQaMGEizNI0tj24HsTzAWbkPlcpidGDdK61/Fa2n+xynafDZcdoNAfk7MZ9XmQ3MYri83M0uIlSe9uhLNmGq0S6UK/7z/qY5dawU8r29a2A9RjbTccmgfTa9qsGOgFf+aHsv09h+aexEHTwJo7AdzjHoWY8RNn86e87zAhWas3pFBkf+07vPLFJbaKZVC89lPVwi3lMeURZPG5+SCNoA9IuHl8C3I9v7cE3153gIDXbOixaoNgNlQrMKMlP34cmZHjf/XVtHwq6Rjpmmw2nST2AGpYak5NQRCOPNGyXbsfEZHVsEtZRzdZSfZMgp5HYO2aKxUPrF2nflXZDomqZnFfJsVIFuKnxnXcOj4b6xEQaJ6T8qoJ/c4Ql/WRmcacYRYw2tiyck/TTU/EQArp6mAAjB2+6clSs1p9LxTONz7QH9z4nyuJvkdEHCzA3nieLxvS9imB9eE7lActMNIuxIWlhvxl3TPzdDFoRKW0wMyjakMJhar6lU5JtC6PmRNVqrcMr2KtMNZZfKkXdOYzA2yOwUJfHOAVfVQdwGeGKFOnaFx6x3iuAd2RyRobm/KmZ2fdhf93L/8Feso/7SXPQTl1TV26cyJMnlcEKfpZP03MVGSwuDnKFSDiXVUQW1TEDe/6TvjmQtPAu4Yq0rQtUFq0KQ1Meki+VahKZYSYpq2a+74tKQkkLYPYqZopQ06jWScz0/kq+T20NvtLZqT9/iflbD7JE8OskvmULc1kOP9Jxf3/s50GW7KdkuVM/gaZlBGRVfrUMmdrBHMGHByk7d/lP1IURkSZQJhpEoZvaBGys+pRis/NX/BV1jKBavO26578etafpaSwqv+v67RvF6YrzPVPgUXuqLS01NZ6Me9C30iw8jGksKfiLcYHMSPrh5PzvaoaVIRrVQSwffaYsYLEd2JQihNktDCCn2zv6mNr9mZGw2gQX7n/upFABNzYdYOGoRNrSKrHyOwNZ0EowAbFKKaikIp+GbZaHrDuMx3GkAfdeHDlu9zEE6GInGLklglZ7YCFufpFi/OZOiYppvJJy+JyCjliCA6m9dqoUaCSeV7r9szWXZTTAO9IxXp3bhsKKqtjVL7zLLWn/qJPKdRy6Q1lu4DtRP4r1NMlEEMyyKn+vqNaxAS/r2pR832ZYRBP7kvbFl69uJ9BISeMBZRQtGHNT/xni43frEAEiuNQVixwrK381tUOXRnvdlaie3mB7yFwq1UTEYpEZ1FFgdKPscPF7CqHRVzFbVVQ//OOfYquCFU0+632oDQIg9zepNHR9Xft4ss4t1xd9rpnAslZWt6sZExEw6tpcH5NTVyPNhQcwCOt6FSZAKFZrf2Zxd3U4lh6v4qhKJY6hFHxQz3E8XvcbMeSdvO52PVXCdbO9KrjSHgG3MsV+89LHG4jbxLCi7+ffwWP5erk1GVIYWMcK/RGaPnAxhQs7kZiOt2N0i1oZ/l2p3aFqNte8dcinYzoj49k8Jkg2+W8bbmcI5XnfQ6Ym4V+eOFMh/p0iVjpPJO/r1l92aImgwNPAFHLIlPKAPW70xyIqnSP+yBS+oCQoq5TUjhhhzKZcUB2YvbMO5sly8rvPOrIzXLihPnrK9vapEya5Uq+XjVElZB/3Zd2/TIaVUiU4/G4NDEkMpzfExCQRT70jNWr+4az0A0zk8uJxuGE/7RU9rihcifUaVWYDbBUPqduAtljE3Ly0LI3OMhBoUEZS9KXlkzVrsjIt2tr1t/oFPBcjOm9HqQZ6ulodsDvPKhgS389Cpyq6rgvoXQ4p363N5QvWyYWQk+NWnFOZC/VP8C/kVEORKcD9X2EM5ssFgITf9XfogBVjKNpEZkEujM/WDo5w8sxRQtbxo81GtGzHPTVYiRhC84cCtloX2belvd25/Xo5MMS7RNiK0WPe1o6pQrCppCocSAQdg5kpoMlcvJTvHbt4BHZrW5Vpuf6rXczb3sJzTDwpxJaFCIBmMhPkxnHyg1zIMhW6bgKdThvaZ6gCBorqRM8kD1VQFbpm0gTmkVXQv7K7UhKFESTiZdqlqJNa8G+8ZVTISXPoJfUA6IRLtwE0Dpo+ThmUz/5a2rk9vLZrMyl0zvZgmAYZfgzrJpGMWX73lJKsEn5BtRtufxZRzo7QXlAEMyYDQXF4PAdFHvhJfvl9rFPSPRUmS/xY+uYIP7tn4PUS2hgZ/bstpJAVHLgDZJ/f/UPD7s1w4sf1j1TMKT2WddRZnBvd7paovm2WnFKNOCBe5klu8dAwThej71RDmF0Mfcj8sLDAGGvW12tc6e7ynd6cSSCnrL2WpLs947i3xGK7tHQoNuS6qmqup4Hd6NjyfSlrTgu9ydMaaXQn4BsHiZpNmiP4mF/0rrjpr7FHc7GRv7nFn06/NLuuVpR07PVpWBCJYjK3douY0N2K0ph2IBYuHOx4pJgRKdbBOkftVLO1+igpFhZnuAdZwvVTSefhnxpslLRbDXqTCMKgls+/Dm7w+VwZK0++whQZyPNCBPAhfdwUOxa5TLIlVVGbA1D2xvGHQPhHQvpysFVG9zvA/SsU3iuMOUjV/vrmsDu4CSIclDgRz92F1MemJQuP5zzzMmAP3Gv0m5QF/uHh2kUvUH00LU3AH22IX/iT6UtFHydhPoiGlOX9JuZjaXc3MotBGuMT/5Kt5CogUuRgop0ZofQgpcsembwsaiPlRLjZ6eEOUxAorUUgjK+C24DkNN2ys28MFGpFGlw+O1XPYx8He0VclOdBRRwlUbSaYx6cGwSh714q3w+qDeLhZmcoa0tHOAy/RPSiZ/uybcW2NDePojMTRSaJt32tJvmQvk311QzqPCeJpZn8ZtLyseGJqrk7jEMZnSFAU63ObYMu+AZg4=" width="1116" height="426" class="img_ev3q"></p>
<p>在处理元数据这步，会调用大模型生成标签和slug（就是图里的文件名，我将文件名作为slug）。还会自动确定合适的阅读更多插入的位置，这个是用的一套基于元素高度计算的算法确定的，而不是大模型，因为大模型不擅长这个，加上这个就变得很慢了。</p>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/6f0ecdc324a6c726c9a4ad2a75a9d74c-8c9c60ef9918feb522b7c4ee2c05814e.avif" width="1117" height="418" class="img_ev3q"></p>
<p>在处理图片这步，会调用ffmpeg来对图片进行压缩。ffmpeg可不止是可以转换视频格式，也可以转换图片格式，功能超级强。在这里可以看到压缩前后大小对比，还可以设置缩放和CRF。这样比起我之前使用命令行工具一次性处理要精细化很多。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="图片管理">图片管理<a href="https://muranxuan.top/blog/build-site-tool-with-deepseek#%E5%9B%BE%E7%89%87%E7%AE%A1%E7%90%86" class="hash-link" aria-label="图片管理的直接链接" title="图片管理的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/92c119a7b57d689161cea33aa888e170-ed1bcf59bab5cae20c278ae376147b37.avif" width="1118" height="526" class="img_ev3q"></p>
<p>图片管理可以看每篇博客或笔记关联的图片，然后也可以对这些图片进行处理，同样也可以设置缩放和CRF。还可以检测冗余图片、检测图片坏链，保障图片不多不少。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="工具箱">工具箱<a href="https://muranxuan.top/blog/build-site-tool-with-deepseek#%E5%B7%A5%E5%85%B7%E7%AE%B1" class="hash-link" aria-label="工具箱的直接链接" title="工具箱的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/60f94475cd7f92e3dfa19882dcb2a52d-8c69611ec80b28cd789261bc3421fe76.avif" width="1173" height="580" class="img_ev3q"></p>
<p>至于工具箱，我暂时就想到一个打包网站的功能，这样我可以备份网站。可以看到，虽然网站上有上百篇博客，但整体大小只有5.84MB，在手机随便拍张照片就有几MB的今天，这个整体大小有点微不足道了。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一些感慨">一些感慨<a href="https://muranxuan.top/blog/build-site-tool-with-deepseek#%E4%B8%80%E4%BA%9B%E6%84%9F%E6%85%A8" class="hash-link" aria-label="一些感慨的直接链接" title="一些感慨的直接链接" translate="no">​</a></h2>
<p>AI的写代码能力如今已经变得很强大了，用AI写一些小工具非常便捷。AI的出现对程序员也产生了深远的影响，一方面提升了效率，另一方面也取代了一部分工作。我们无法阻止AI的潮流，只有顺应AI前进的步伐。</p>]]></content:encoded>
            <category>DeepSeek Harness</category>
            <category>AI编程</category>
            <category>个人网站</category>
            <category>NodeJS</category>
            <category>工具开发</category>
        </item>
        <item>
            <title><![CDATA[Docusaurus主题定制：给归档页加上博文计数]]></title>
            <link>https://muranxuan.top/blog/docusaurus-theme-customization</link>
            <guid>https://muranxuan.top/blog/docusaurus-theme-customization</guid>
            <pubDate>Mon, 24 Aug 2026 23:46:27 GMT</pubDate>
            <description><![CDATA[网站迁移到 Docusaurus 有一段时间了，整体感觉还行，虽然不那么花里胡哨，不过现在也懒得把网站整得花里胡哨了。用过Hexo、Hugo，也用过 Typecho，用过别人的主题，也自己魔改过，如今换到Docusaurus了，打算长期就用这个了。]]></description>
            <content:encoded><![CDATA[<p>网站迁移到 Docusaurus 有一段时间了，整体感觉还行，虽然不那么花里胡哨，不过现在也懒得把网站整得花里胡哨了。用过Hexo、Hugo，也用过 Typecho，用过别人的主题，也自己魔改过，如今换到Docusaurus了，打算长期就用这个了。</p>
<p>不过，Docusaurus 的归档页有个让我这个强迫症很难受的地方，归档页的标题和副标题都是“历史博文”，显得很累赘。我想把副标题改为博文计数，经过一番折腾，终于做到了，效果如下：</p>
<p><img decoding="async" loading="lazy" src="data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAAD5bWV0YQAAAAAAAAAvaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAFBpY3R1cmVIYW5kbGVyAAAAAA5waXRtAAAAAAABAAAAHmlsb2MAAAAARAAAAQABAAAAAQAAASEAABBiAAAAKGlpbmYAAAAAAAEAAAAaaW5mZQIAAAAAAQAAYXYwMUNvbG9yAAAAAGppcHJwAAAAS2lwY28AAAAUaXNwZQAAAAAAAANZAAABEwAAABBwaXhpAAAAAAMICAgAAAAMYXYxQ4EBDAAAAAATY29scm5jbHgAAgACAAIAAAAAF2lwbWEAAAAAAAAAAQABBAECgwQAABBqbWRhdAoLAAAADMaxEj6+YBAy0iAUACQACSQg4oXEmkAudK+zDXJPsBETsAdtBZ3ewqO1YulhAsRJY/v5n56sTKlOnvZY9fRuPU7tN7FpKdqygW53G4agmysnUbI35v8EM4P9/gygJ3eMwIVjTPI+kEiaLu2bM8BCC/SF8xWw9t9u/t3fe7K75lCqBtCpleKeePvoUNBzFjK/oszqs3jIMpsBGbJc6QnV44MIf3N/15TmWKlEsNW0iTRsarcFSZV4FA9yPSoQBWi1mSL99oAb+kROGhkz+K975ZWZlNAtCy5OrH3fhKeuSmxXvt4gzHlRaYm75L1v+4IaaRinkuayBlciUfCFulbYeMcxLeABmSk5dcb462600vh//3Ctp5evZjLVX5jqRbiVmhH6L8dQuYMdUQXItvHVnL34JBnEsJr2QM/GljscnjS67F7DRk6TQl68k1atJZlc6pJ6d1KDtIKPwrMjEN+zkp3RefMCdGttXz/6Hf4yr53W+G+IIxkb7xQRFg39utoAhwTET/SG9byHjbhGfiMBzdW5GlzRReg8PcNY2xjmqOVCWFEKoVSHJcadYTOByrhYzg6Ro6QI/bljBptjaW0j1HqepVh9ZbIkk9vzB8seUzMLkWmoUgmYlifAcu/hMk0SQT/vjfbKTh8g/nLDcNb/JRh0gWQoTL3sZTTbyyFkItLpDxZ9Y/T1Fs8gGBOh+wHl54zY4fLI8PFJSR0jsafCKyYY29K1gf4aTUMch1ZGhMM9jMhxWEL3ix+qDLj3VjGk/omSCGzpEGgWineAYBtjUOOS19bgft2fIuLWrp3B3S3I9inYN7sq4PhawcLtNDDTIWsvUByDr5ZmWfpantLiibmCtBjJCS3NV4LP/e8voXLDAyb5wWmb3QHbUecvWXdvHu2GhN74i681TSqE2Se+KXkZRe0kxoIAsDFJVOuHOiCoWDq84Nu2Kz8V+Y2STaCMwk2agYCWVxZuOPpFAghxw2/SZ2G9ZuerJNqVsI9ApFFxADO54lY6Jv6cj3ZxYA7Ts8yQ1CUKX5OpWx9+FP2fAXPc++ey0AhGX0Uw508Vrb8Uo0A/FXXPaf1r46L9VSDcuFMWwbCAvQszJDFPJip0eE/+91ZNYDA3mEgWCqSh8XJhA8kdHWbpQyBTId5GJbUZ4BynpJVb07PgtNT8X/OSD1lHoUw/W3ukWw5EeKzFR9OqNpfGBgF5tV6h5cZr/ypDN4VyWpNIghwx3Wxi1AFFyIJ9G5urGv2v7MCqnRmX14QqaWtNR67qdIjYWob2/G6tHhAgTJOTAp8a9Uy/x7eO4Krm+9G3lp0PUSXkG5NHoA3B4a5FEMbUHIuXPhGv20gpCmoRXtuC4zWQ7UCK2UmX/7UyqWZ+YMd+QiG1gyyhSvJEAPbvD28hvRgeaIdn8/+M47UD1wFelxzsM0aFY7psLB9lNSzpWZ6u6+46XX14iHp21v+zwKUSU9hzE/jOL0J0KydSdTH88GiqZG/MerOSxBKfwuBjENCKa+7imii7+U6LkLpiT17r6lh8895diiYpBYV7vAMXlgfE0ClTUKJpAnTpqJ521+2jWIOMpimr7tYgnOxU38VncAADJoed9gRwXAaUbxB31qOrbK6pp13DV0Gc2KlAVE6GJ1mPpa1TK1eaImpybLdRxGimbsmr5nLJk8qk6B7/dg9PB2LO0FdNpMPlYYkohwqnGOY95nJIUnAiv8negNC5JN5zuETS9rp2RXbbSbCEcOOyDL1KRn4dcEmR///41XWFrNn3Zcml/u0mG84crXPHKcESaHRkV3I249066NNldME0a0Y6y1DPn68QvfKzVGLcxtlcNrLOs50ud0iHZ8dKOwEyLcM94r9VBR+i1VgOXsjGgQ0Ud2L/4gtW1hnLBoLAFVtwhhy2UHVSI7t2BruJFfKGOea6r9+PyLZiqLHHypF/m8Stv6Rz8BEoyP8bFKxJF3SowILbt4U0obQeqRLnQeykC+NPFxOcJrNHwHFkE2udI/vmJX1mKueDTIscuE/ZlkhdEQet7Z9HC4/Uvrot5eIGPxcrLppvy0URjmguqj3U2bjKLtWPKpIamqCQDSLiWwaXjYfzw79n5GWWMFY7UiakcUguwDyQHciWW8bCaelp0/nEhBUvpVLHEOUy6r7+Qu+cqzqAxt56SGe1MYcX62lBqNIGlyXhU6Yd0gTztaL7eE/iybHkT3CbJ/+yXvPxcfxkf5MbP5vwtOVRlSfXNK0A7B1quqHVFXwl/PyV49oHX3Qz+PI5qBzb+I3rLDLNHWI4/v8i4tlQ5mj6Flz9FChf5rEUlW3DMNr5mdMOdTYD/eOzA2AXY6YqFKEQk83GhMtYI8ZRCoCrgJrdW87pI4sLIlomurWMMjhQuqtZTs8csorc9vDHnF0wNelZrl1djKkEJVGNkSN2eLpBlf1ncPv8O6ZuQfn8bDq6WxNR08PT1oSHMzRNVc/+FmluE9zn4gb9hW4mmNX0lIoVhqKfdGHDSA9BE7z1Z7cegWQPVQ1GQA24Hw+L5fpYWZCnVyqy7KADXHMehIjMn9xXpuY+2cbYSzO6+RgL7IYqWA6OV7wjIbuL4hFhXH80T2dhHXeHm2xjJgQLCRrc3gE/OfOKJM2Lrk+GfQtN2leU2M4XrzgrOPnwxJy/W4xzIt01MT96u7yEKwqX8HW8NLTMVJXtAPU0bmRjB320VhIf9ecILTNawZEUunjLijJi0lgrndWVX2DA8SMCW3LYchpcGFc6EnWXMHPIoAFaQRg+UhMYg46M3LsRCm8A4edcnO93J/P1aCLvcr1rXH05TV6bMb5BivMRi9TXq8XvPk77ZKRpOsRMx9g7c56/6VetnCtog1eQJdDcZ4hNYgPbvMXsKa0/0zhxi187SoES7/TfigbmG5KPZcxeDLoEwStNtCWkiFl9n/E5PV2QdFxGPQPLhSKLEHrvYUQxKav6ujZ7qWxHsW3rurWOO/L3ht9KhpITk75sa9Cx2L16Ubf3pVXYBurVEXYIfnT39JJDck+D2KiVUBMapvWTptV1Et4TdYd0KyaHhKZKpuhoiz3MLUQ5OAmMhH3io5P12dLs6fJh7X20Wwp0V0/BVL3Cj8ciEPF0c3hRwdncGVe34LGDT7ce/o+8OLaxDkYD2/UIosILNOouThhceGpatnqFIp03QiHBs0rOAsC4OR6pKtambPi6vuZ8BHpakUuzAStKAWnDDeFllsIPwFVaoNeEyVFsDd97XHiaMTwud1YaWSOm3eztWucP3+bkmC6ezkMvkXhKG5UZfsRRdInROWsjlI4SclgucxdCRJCfjVXxOdzXl3sKglMXvUkSNZD2Qo+1r6L+5TA91K+8q/CZaHFkTe9+2gkHBqOX4W5Ro9kU3CkVyyzPj/hxgg+i3DrTEWbvcdd9G4g8LzQG4J7694CH0T54eDeQjfA2gE5pPBPXFssJ9M5aTtjOAFo31+jgT4ZAdnLoT6axe9acroEKGNlTuMWUOPWjjL2fPYXOM4AHtO2GTP7RLy8bokZZXo40+xYlFLK/P64DyG6PV0maNpXvZmCP7vRiWEvHkqeIUUNRCjSvo1DqIycN+ehT6ga9dypRsJacdvqtRtel1frruDElOu0vSiucdvaCtXTSp99fd8i4wDNzfgRghcU/JfrMNzRwGoCDoUbSWarucbZq6N3/j8pfnCLPtsA//46o6PdV9jHZ7+804CYf4fw76ZCNakZkltGnUMq3MXZw3yAhL7MX+t19sKe3ioDS78dn+4i4f2sHiqTzboatO+gs29boO4xyTI2BXO8vYgzc5mR1KUiNpFBQsMcgq7ysIcAe6SJtmGKU+J2V7i8BvRnNApzYxCC7zQCHam4q0nsPVAvNgJ7JSIqPm9yRffP+UBk/DsehWnBXXPXDBT0jDBcPddLC1+/URHZg1x4KJXKWaMkvoD3GPyWnQc5nLLea100B18mYO3ndp0/aid0tI1iGUoWTyp1tQrJ8tn55JSQ2qMlD4YONhz/3RA2QCmfyejOHkFKsPPZN4yKcfOYFkDhHuQiutjZSLB0+4ZuB6cZCDZP4iyBLJqK2GGYU7p5/JIwV9VXR0TjJ8YulGmyDGZzi/5hFPn4ThXZJhe2yw5djHbLjXB1UJmQ/hBCfEy0ckwMaCjnLAuceCLxAM3ZThs3Hbv5ue8c4/8jHpOOsyA3lm8Gda2igMx5fdG8ws1HA+MsKCU3Z+4BPq49BigYMMQRX8sXSV77M6Klt8dn0svCyyage7KoWsJf/mO7/MopOtpP0IcuF6es7Q6/h+FCzq8uvcaJ53fIr5sysipLOHOG2IXHL6yTmFtSdu2iL64pbhx9apfR2AONUcr1Lc2rrOMya6JhK/6vfbzXrdYI0NsMTc4rFCpvpxM1JgcaEP6AeTtZ5QPSAxrsnpwPvhbuBuZzyoo08X5HiU6z9RtILBcJ+BeNf64CCa9VzRtnFxNzi/V0dWIpv3s04b60w2Z7/3Cp2kUSAmpJRcR0CzZIgMQBz+rTxtwdo3Ipm4Dn/73wSYiNvhM2wKM0RTq00HXlGrsdk9eD21EsSrTDaQ5qQ+ynC5X3aWo2CCcYUGBl2OxOvuOlLrgNFNpBZzqhRkY78CZnaBV9FnNJsMXVbt0+rblNeek5eQLazdpWhA2T9tFfbeC3CvNvhGIkzS4gspHNhcRVZ90nBPZF39avgOX2Zaejt3UEYbhaIfoR0fatWGD8FiJFaISUH4fARF8OQGhyz+YNlGdEPlX97LwT5VMUoavch1W9ynRu5fqDpnfZPIv2V5UA5ih98CU0hUfEpFZ1iIluuOMa2qeR+10itVoMwjNU6v5DGN3LUUxlyVc1a7h/+ICfXqEDx4sPlnxoZfI5IJtzW5eyoChxHyn7PuuQjQv35o17U5qqm6HtVoe/W1Ik09lwvj/ScRxiKYLy6dsKkGno5qx5EjarTMDiomHvPNuHiDEcpO+Vm+9fcVMSQ//0znLHhUtruNY7j0aKS7CLGXj+zF9bLU8jZHQ4nwr2q1pPQexjUCxtV8tBWXjO6XsZDMg5azF/TpYCuhjxHtfBKGQZ0tavGk55Cg8P4Vldog0d8SviARdQ9yWxRzbQiU0gxjGqr50aIkaFFeaV5LZ99DHLZ9GbP64QD2q6yjR+lmK2D5Nr8zBLuE9M1rKNtbT2Z2yDQr3QXwaoSguQFU93jTi1yAStqtwuV1ONNj8tGq/CGjIXPiHE8itj+FU0xoMq9p4vLQR+GF0GFQ6Vw2JfTnVJ1zNumtGhuYMV1V97VOR6IjxWwGGPPtb09GwT8RCF4JrHcz2iiS1QwZPQPEie7gvDBuul+7vyomLoGReV9itPDijMwkQR58pASwjncbdLeT/d7fgkjypoyuL5qRjy9wr0qqTE56slXw7jrqnQKYs9E1nSIOVqhYfUJe2UcnOZztVHV+Kl1aVVaqaoAdUbK4i43ZhhR2Lh1XBUYMmKk6CykXF4veuz3fYAEMFFdUJrOfpb31PlJp16LEoZ9gQNi0POqeOVzrFwXJN2qXUHTG8CP7CYRExmlIZfnVz09GelTXpYUrLw8Yp5CeQvZl/fJny4lwQ/BBz4/fx+WXujwO8vDygS8gA==" width="857" height="275" class="img_ev3q"></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="实现">实现<a href="https://muranxuan.top/blog/docusaurus-theme-customization#%E5%AE%9E%E7%8E%B0" class="hash-link" aria-label="实现的直接链接" title="实现的直接链接" translate="no">​</a></h2>
<p>我第一反应是加个自定义的 js，通过修改 DOM 实现，但是不太可行，因为 Docusaurus 是 SPA，从首页点进归档页时，走React Router 客户端路由，不会发起浏览器GET请求去下载新的HTML文件，脚本并不会执行。思来想去，还是打算用正经的方法实现，也更优雅点。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="swizzle">swizzle<a href="https://muranxuan.top/blog/docusaurus-theme-customization#swizzle" class="hash-link" aria-label="swizzle的直接链接" title="swizzle的直接链接" translate="no">​</a></h3>
<p>Docusaurus 要做定制，就涉及 swizzle 了。这是官方文档介绍：<a href="https://docusaurus.io/docs/swizzling" target="_blank" rel="noopener noreferrer" class="">Swizzling | Docusaurus</a></p>
<p>简单来说，Swizzling 就是允许你用自己的代码替换掉主题自带的 React 组件，从而实现比改 CSS 更深入的定制 —— 不只改外观，而是直接改组件的行为和结构。Swizzling 有两种模式：</p>
<ul>
<li class="">Eject: 把原始组件复制一份，完全重写</li>
<li class="">Wrap: 在原始组件外面套一层包装组件，只做增强</li>
</ul>
<p>执行</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run swizzle</span><br></div></code></pre></div></div>
<p>可进入交互式选择界面。博客归档页是<code>BlogArchivePage</code> ，归档页标题相关的原始代码：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">PageMetadata</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">title</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">title</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">description</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">description</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Layout</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">header</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">hero hero--primary</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">container</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Heading</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">as</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">h1</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">hero__title</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">title</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Heading</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">hero__subtitle</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">description</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">header</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">main</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">years</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">YearsSection</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">years</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">years</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">main</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Layout</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>我不想用Eject，但整个归档页又是一个整体，不好Wrap。毕竟我是要修改，不是在前后加内容。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="useeffect">useEffect<a href="https://muranxuan.top/blog/docusaurus-theme-customization#useeffect" class="hash-link" aria-label="useEffect的直接链接" title="useEffect的直接链接" translate="no">​</a></h2>
<p><code>useEffect</code> 是 React 官方内置的 Hook，属于 React 运行时机制。在组件挂在时执行effect函数，在依赖里的值发生变化时重新执行effect，在组件卸载时执行effect return的清理函数。</p>
<p>所以，我依然使用Wrap，然后通过useEffect来修改DOM。</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> React</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> useEffect</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> useRef </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> OriginalBlogArchive </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@theme-original/BlogArchivePage'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">BlogArchive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> totalPosts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">archive</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">blogPosts</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> timer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">querySelector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'p.hero__subtitle'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">el</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">textContent </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">已经写了 </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">totalPosts</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c"> 篇</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">timer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">timer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">totalPosts</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">OriginalBlogArchive </span><span class="token punctuation" style="color:#393A34">{</span><span class="token operator" style="color:#393A34">...</span><span class="token plain">props</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>尽管useEffect触发时React已经渲染，但React hydrate修补DOM可能还有一微小时差，所以用短轮询增加容错。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="写在最后">写在最后<a href="https://muranxuan.top/blog/docusaurus-theme-customization#%E5%86%99%E5%9C%A8%E6%9C%80%E5%90%8E" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接" translate="no">​</a></h2>
<p>突然想看看我每年写了多少篇，于是让AI统计了下：</p>
<p><img decoding="async" loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjQwIiBoZWlnaHQ9IjgwMCIgdmlld0JveD0iMCAwIDEyNDAgODAwIj48ZGVmcz4KICA8bGluZWFyR3JhZGllbnQgaWQ9ImJhckdyYWQiIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxIj4KICAgIDxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZiY2ZlOCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2Y0NzJiNiIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPGZpbHRlciBpZD0ic2hhZG93IiB4PSItMTAlIiB5PSItMTAlIiB3aWR0aD0iMTMwJSIgaGVpZ2h0PSIxMzAlIj4KICAgIDxmZURyb3BTaGFkb3cgZHg9IjAiIGR5PSI4IiBzdGREZXZpYXRpb249IjE4IiBmbG9vZC1jb2xvcj0iIzBmMTcyYSIgZmxvb2Qtb3BhY2l0eT0iMC4wNSIvPgogIDwvZmlsdGVyPgo8L2RlZnM+PHJlY3QgeD0iMjAiIHk9IjIwIiB3aWR0aD0iMTIwMCIgaGVpZ2h0PSI3NjAiIHJ4PSIyOCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjZjFmNWY5IiBmaWx0ZXI9InVybCgjc2hhZG93KSIvPjxnIG9wYWNpdHk9IjAuNSI+PGNpcmNsZSBjeD0iMTEwMCIgY3k9Ijk1IiByPSI3MCIgZmlsbD0iI2ZiY2ZlOCIvPjwvZz48cGF0aCBkPSJNMCwtMTMgQzIsLTUgNSwtMiAxMywwIEM1LDIgMiw1IDAsMTMgQy0yLDUgLTUsMiAtMTMsMCBDLTUsLTIgLTIsLTUgMCwtMTMgWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTE1NiwxNzApIiBmaWxsPSIjZmJjZmU4IiBvcGFjaXR5PSIwLjU1Ii8+PGNpcmNsZSBjeD0iNTYiIGN5PSI3NDgiIHI9IjUiIGZpbGw9IiNmYmNmZTgiIG9wYWNpdHk9IjAuNCIvPjxjaXJjbGUgY3g9IjY4IiBjeT0iNzU2IiByPSI0IiBmaWxsPSIjZmJjZmU4IiBvcGFjaXR5PSIwLjMiLz48Y2lyY2xlIGN4PSI0NCIgY3k9Ijc1OCIgcj0iMyIgZmlsbD0iI2ZiY2ZlOCIgb3BhY2l0eT0iMC4zIi8+PHRleHQgeD0iNjQiIHk9IjExNiIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjQyIiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMzM0MTU1Ij7mr4/lubTlhpnkuoblpJrlsJHnr4fljZrmloc8L3RleHQ+PHRleHQgeD0iNjYiIHk9IjE1NiIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE5IiBmaWxsPSIjOTRhM2I4Ij4yMDE1IH4gMjAyNiDCtyDlhbEgMTE0IOevhyDCtyAyMDI2IOS4uui/m+ihjOS4rTwvdGV4dD48bGluZSB4MT0iMTEwIiB5MT0iNjkwIiB4Mj0iMTE3MCIgeTI9IjY5MCIgc3Ryb2tlPSIjZTJlOGYwIiBzdHJva2Utd2lkdGg9IjEuNSIvPjx0ZXh0IHg9Ijk2IiB5PSI2OTUiIHRleHQtYW5jaG9yPSJlbmQiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNSIgZmlsbD0iI2NiZDVlMSI+MDwvdGV4dD48bGluZSB4MT0iMTEwIiB5MT0iNjEwIiB4Mj0iMTE3MCIgeTI9IjYxMCIgc3Ryb2tlPSIjZjFmNWY5IiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB4PSI5NiIgeT0iNjE1IiB0ZXh0LWFuY2hvcj0iZW5kIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiNjYmQ1ZTEiPjU8L3RleHQ+PGxpbmUgeDE9IjExMCIgeTE9IjUzMCIgeDI9IjExNzAiIHkyPSI1MzAiIHN0cm9rZT0iI2YxZjVmOSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgeD0iOTYiIHk9IjUzNSIgdGV4dC1hbmNob3I9ImVuZCIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE1IiBmaWxsPSIjY2JkNWUxIj4xMDwvdGV4dD48bGluZSB4MT0iMTEwIiB5MT0iNDUwIiB4Mj0iMTE3MCIgeTI9IjQ1MCIgc3Ryb2tlPSIjZjFmNWY5IiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB4PSI5NiIgeT0iNDU1IiB0ZXh0LWFuY2hvcj0iZW5kIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiNjYmQ1ZTEiPjE1PC90ZXh0PjxsaW5lIHgxPSIxMTAiIHkxPSIzNzAiIHgyPSIxMTcwIiB5Mj0iMzcwIiBzdHJva2U9IiNmMWY1ZjkiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHg9Ijk2IiB5PSIzNzUiIHRleHQtYW5jaG9yPSJlbmQiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNSIgZmlsbD0iI2NiZDVlMSI+MjA8L3RleHQ+PGxpbmUgeDE9IjExMCIgeTE9IjI5MCIgeDI9IjExNzAiIHkyPSIyOTAiIHN0cm9rZT0iI2YxZjVmOSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgeD0iOTYiIHk9IjI5NSIgdGV4dC1hbmNob3I9ImVuZCIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE1IiBmaWxsPSIjY2JkNWUxIj4yNTwvdGV4dD48bGluZSB4MT0iMTEwIiB5MT0iMjEwIiB4Mj0iMTE3MCIgeTI9IjIxMCIgc3Ryb2tlPSIjZjFmNWY5IiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB4PSI5NiIgeT0iMjE1IiB0ZXh0LWFuY2hvcj0iZW5kIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiNjYmQ1ZTEiPjMwPC90ZXh0PjxyZWN0IHg9IjEyNy4xNjY2NjY2NjY2NjY2NiIgeT0iNjI2IiB3aWR0aD0iNTQiIGhlaWdodD0iNjQiIHJ4PSIxMiIgZmlsbD0idXJsKCNiYXJHcmFkKSIvPjx0ZXh0IHg9IjE1NC4xNjY2NjY2NjY2NjY2NiIgeT0iNjEyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiM2NDc0OGIiPjQ8L3RleHQ+PHRleHQgeD0iMTU0LjE2NjY2NjY2NjY2NjY2IiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAxNTwvdGV4dD48cmVjdCB4PSIyMTUuNDk5OTk5OTk5OTk5OTciIHk9IjY1OCIgd2lkdGg9IjU0IiBoZWlnaHQ9IjMyIiByeD0iMTIiIGZpbGw9InVybCgjYmFyR3JhZCkiLz48dGV4dCB4PSIyNDIuNDk5OTk5OTk5OTk5OTciIHk9IjY0NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjNjQ3NDhiIj4yPC90ZXh0Pjx0ZXh0IHg9IjI0Mi40OTk5OTk5OTk5OTk5NyIgeT0iNzIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTciIGZpbGw9IiM5NGEzYjgiPjIwMTY8L3RleHQ+PHJlY3QgeD0iMzAzLjgzMzMzMzMzMzMzMzMiIHk9IjU2MiIgd2lkdGg9IjU0IiBoZWlnaHQ9IjEyOCIgcng9IjEyIiBmaWxsPSJ1cmwoI2JhckdyYWQpIi8+PHRleHQgeD0iMzMwLjgzMzMzMzMzMzMzMzMiIHk9IjU0OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjNjQ3NDhiIj44PC90ZXh0Pjx0ZXh0IHg9IjMzMC44MzMzMzMzMzMzMzMzIiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAxNzwvdGV4dD48cmVjdCB4PSIzOTIuMTY2NjY2NjY2NjY2NyIgeT0iMjEwIiB3aWR0aD0iNTQiIGhlaWdodD0iNDgwIiByeD0iMTIiIGZpbGw9InVybCgjYmFyR3JhZCkiLz48dGV4dCB4PSI0MTkuMTY2NjY2NjY2NjY2NyIgeT0iMTk2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiM2NDc0OGIiPjMwPC90ZXh0Pjx0ZXh0IHg9IjQxOS4xNjY2NjY2NjY2NjY3IiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAxODwvdGV4dD48cmVjdCB4PSI0ODAuNSIgeT0iMjU4IiB3aWR0aD0iNTQiIGhlaWdodD0iNDMyIiByeD0iMTIiIGZpbGw9InVybCgjYmFyR3JhZCkiLz48dGV4dCB4PSI1MDcuNSIgeT0iMjQ0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiM2NDc0OGIiPjI3PC90ZXh0Pjx0ZXh0IHg9IjUwNy41IiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAxOTwvdGV4dD48cmVjdCB4PSI1NjguODMzMzMzMzMzMzMzMyIgeT0iNTMwIiB3aWR0aD0iNTQiIGhlaWdodD0iMTYwIiByeD0iMTIiIGZpbGw9InVybCgjYmFyR3JhZCkiLz48dGV4dCB4PSI1OTUuODMzMzMzMzMzMzMzMyIgeT0iNTE2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiM2NDc0OGIiPjEwPC90ZXh0Pjx0ZXh0IHg9IjU5NS44MzMzMzMzMzMzMzMzIiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAyMDwvdGV4dD48cmVjdCB4PSI2NTcuMTY2NjY2NjY2NjY2NiIgeT0iNTE0IiB3aWR0aD0iNTQiIGhlaWdodD0iMTc2IiByeD0iMTIiIGZpbGw9InVybCgjYmFyR3JhZCkiLz48dGV4dCB4PSI2ODQuMTY2NjY2NjY2NjY2NiIgeT0iNTAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiM2NDc0OGIiPjExPC90ZXh0Pjx0ZXh0IHg9IjY4NC4xNjY2NjY2NjY2NjY2IiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAyMTwvdGV4dD48cmVjdCB4PSI3NDUuNDk5OTk5OTk5OTk5OSIgeT0iNjU4IiB3aWR0aD0iNTQiIGhlaWdodD0iMzIiIHJ4PSIxMiIgZmlsbD0idXJsKCNiYXJHcmFkKSIvPjx0ZXh0IHg9Ijc3Mi40OTk5OTk5OTk5OTk5IiB5PSI2NDQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iIzY0NzQ4YiI+MjwvdGV4dD48dGV4dCB4PSI3NzIuNDk5OTk5OTk5OTk5OSIgeT0iNzIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTciIGZpbGw9IiM5NGEzYjgiPjIwMjI8L3RleHQ+PHJlY3QgeD0iODMzLjgzMzMzMzMzMzMzMzMiIHk9IjU3OCIgd2lkdGg9IjU0IiBoZWlnaHQ9IjExMiIgcng9IjEyIiBmaWxsPSJ1cmwoI2JhckdyYWQpIi8+PHRleHQgeD0iODYwLjgzMzMzMzMzMzMzMzMiIHk9IjU2NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjNjQ3NDhiIj43PC90ZXh0Pjx0ZXh0IHg9Ijg2MC44MzMzMzMzMzMzMzMzIiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAyMzwvdGV4dD48cmVjdCB4PSI5MjIuMTY2NjY2NjY2NjY2NiIgeT0iNjU4IiB3aWR0aD0iNTQiIGhlaWdodD0iMzIiIHJ4PSIxMiIgZmlsbD0idXJsKCNiYXJHcmFkKSIvPjx0ZXh0IHg9Ijk0OS4xNjY2NjY2NjY2NjY2IiB5PSI2NDQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iIzY0NzQ4YiI+MjwvdGV4dD48dGV4dCB4PSI5NDkuMTY2NjY2NjY2NjY2NiIgeT0iNzIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTciIGZpbGw9IiM5NGEzYjgiPjIwMjQ8L3RleHQ+PHJlY3QgeD0iMTAxMC40OTk5OTk5OTk5OTk5IiB5PSI2MTAiIHdpZHRoPSI1NCIgaGVpZ2h0PSI4MCIgcng9IjEyIiBmaWxsPSJ1cmwoI2JhckdyYWQpIi8+PHRleHQgeD0iMTAzNy41IiB5PSI1OTYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iIzY0NzQ4YiI+NTwvdGV4dD48dGV4dCB4PSIxMDM3LjUiIHk9IjcyMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IidOb3RvIFNhbnMgQ0pLIFNDJywnUGluZ0ZhbmcgU0MnLCdNaWNyb3NvZnQgWWFIZWknLCdXZW5RdWFuWWkgTWljcm8gSGVpJyxzYW5zLXNlcmlmIiBmb250LXNpemU9IjE3IiBmaWxsPSIjOTRhM2I4Ij4yMDI1PC90ZXh0PjxyZWN0IHg9IjEwOTguODMzMzMzMzMzMzMzMyIgeT0iNTk0IiB3aWR0aD0iNTQiIGhlaWdodD0iOTYiIHJ4PSIxMiIgZmlsbD0idXJsKCNiYXJHcmFkKSIvPjx0ZXh0IHg9IjExMjUuODMzMzMzMzMzMzMzMyIgeT0iNTgwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iJ05vdG8gU2FucyBDSksgU0MnLCdQaW5nRmFuZyBTQycsJ01pY3Jvc29mdCBZYUhlaScsJ1dlblF1YW5ZaSBNaWNybyBIZWknLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiM2NDc0OGIiPjY8L3RleHQ+PHRleHQgeD0iMTEyNS44MzMzMzMzMzMzMzMzIiB5PSI3MjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNyIgZmlsbD0iIzk0YTNiOCI+MjAyNjwvdGV4dD48dGV4dCB4PSIxMTgwIiB5PSI3NzAiIHRleHQtYW5jaG9yPSJlbmQiIGZvbnQtZmFtaWx5PSInTm90byBTYW5zIENKSyBTQycsJ1BpbmdGYW5nIFNDJywnTWljcm9zb2Z0IFlhSGVpJywnV2VuUXVhbllpIE1pY3JvIEhlaScsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iI2NiZDVlMSI+5pyo54S26L2pIMK3IG11cmFueHVhbi50b3AvYXJjaGl2ZTwvdGV4dD48L3N2Zz4=" width="1240" height="800" class="img_ev3q"></p>
<p>写得不多，不过好在勉强算坚持下来了。从最初自学计算机，那时写的很少；然后读研期间比较活跃，一年最多写了30篇；后面工作了，篇数就明显减少。有很多时候有计划写，但因为各种原因耽搁了，不过工作后写的质量还是要稍微高一些的。但愿后面可以多抽些时间，多写写，或许若干年后又会有不一样的感觉。</p>]]></content:encoded>
            <category>Docusaurus</category>
            <category>主题定制</category>
            <category>React</category>
        </item>
        <item>
            <title><![CDATA[DeepSeek Harness折腾与简单体验]]></title>
            <link>https://muranxuan.top/blog/deepseek-harness-experience</link>
            <guid>https://muranxuan.top/blog/deepseek-harness-experience</guid>
            <pubDate>Sun, 23 Aug 2026 23:58:46 GMT</pubDate>
            <description><![CDATA[DeepSeek官方终于出了自己的Agent工具——DeepSeek Harness（简称dsh），类似与Claude Code、Codex等工具。不同的是，DeepSeek Harness默认是web界面的，而不是TUI或GUI。它强调一切皆插件，这边简单体验了下，感觉还不错，简单记录下折腾的过程。]]></description>
            <content:encoded><![CDATA[<p>DeepSeek官方终于出了自己的Agent工具——DeepSeek Harness（简称dsh），类似与Claude Code、Codex等工具。不同的是，DeepSeek Harness默认是web界面的，而不是TUI或GUI。它强调一切皆插件，这边简单体验了下，感觉还不错，简单记录下折腾的过程。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="安装与启动">安装与启动<a href="https://muranxuan.top/blog/deepseek-harness-experience#%E5%AE%89%E8%A3%85%E4%B8%8E%E5%90%AF%E5%8A%A8" class="hash-link" aria-label="安装与启动的直接链接" title="安装与启动的直接链接" translate="no">​</a></h2>
<p>全局安装：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-g</span><span class="token plain"> @deepseek-ai/dsh@latest</span><br></div></code></pre></div></div>
<p>启动 Web 界面：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">dsh web</span><br></div></code></pre></div></div>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/ee3947a88c0bc4b50784bba161b9b3ba-41301c2cbb440cf1b6a5dfb47f9ff167.avif" width="958" height="538" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="装插件">装插件<a href="https://muranxuan.top/blog/deepseek-harness-experience#%E8%A3%85%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="装插件的直接链接" title="装插件的直接链接" translate="no">​</a></h2>
<p>dsh 有很多插件，这里附上两个插件合集供参考：</p>
<ul>
<li class=""><a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/blob/main/README.zh.md" target="_blank" rel="noopener noreferrer" class="">awesome-dsh-plugin</a></li>
<li class=""><a href="https://www.dsh.plus/zh/plugins/" target="_blank" rel="noopener noreferrer" class="">插件库 · dsh.plus</a></li>
</ul>
<p>装插件之前先把 pnpm 装上，否则有些插件安装时会提示缺失 pnpm：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-g</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pnpm</span><br></div></code></pre></div></div>
<p>这里推荐一些我觉得还不错的插件。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="dsh-better-sidebar">dsh-better-sidebar<a href="https://muranxuan.top/blog/deepseek-harness-experience#dsh-better-sidebar" class="hash-link" aria-label="dsh-better-sidebar的直接链接" title="dsh-better-sidebar的直接链接" translate="no">​</a></h3>
<p>侧边栏增强：<a href="https://github.com/omdsh-dev/DSH-better-sidebar" target="_blank" rel="noopener noreferrer" class="">omdsh-dev/DSH-better-sidebar</a></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">dsh plugin </span><span class="token parameter variable" style="color:#36acaa">--profile</span><span class="token plain"> web </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> dsh-better-sidebar@latest</span><br></div></code></pre></div></div>
<p>不过第一次装的时候，构建脚本被 pnpm 默认拦截了，需要先手动 approve 一下构建，否则装不上：</p>
<p>Windows:</p>
<div class="language-batch codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-batch codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token command keyword" style="color:#00009f">cd</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">/d</span><span class="token command"> </span><span class="token command variable" style="color:#36acaa">%UserProfile%</span><span class="token command">\.dsh\profiles\web</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token command keyword" style="color:#00009f">pnpm</span><span class="token command"> approve-builds </span><span class="token command parameter attr-name" style="color:#00a4db">--all</span><br></div></code></pre></div></div>
<p>macOS/Linux:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token builtin class-name">cd</span><span class="token plain"> ~/.dsh/profiles/web </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> approve-builds </span><span class="token parameter variable" style="color:#36acaa">--all</span><br></div></code></pre></div></div>
<p>approve 完重新跑一遍上面的安装命令就可以了。后面遇到构建脚本被 pnpm 默认拦截了的问题都是这样处理。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="dsh-web-ui-all">dsh-web-ui-all<a href="https://muranxuan.top/blog/deepseek-harness-experience#dsh-web-ui-all" class="hash-link" aria-label="dsh-web-ui-all的直接链接" title="dsh-web-ui-all的直接链接" translate="no">​</a></h3>
<p>界面增强全家桶：<a href="https://github.com/zhu1090093659/dsh-web-ui/blob/main/packages/dsh-web-ui-all/README.zh.md" target="_blank" rel="noopener noreferrer" class="">dsh-web-ui</a></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">dsh plugin </span><span class="token parameter variable" style="color:#36acaa">--profile</span><span class="token plain"> web </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> @linxin666/dsh-web-ui-all@latest</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="余额挂件">余额挂件<a href="https://muranxuan.top/blog/deepseek-harness-experience#%E4%BD%99%E9%A2%9D%E6%8C%82%E4%BB%B6" class="hash-link" aria-label="余额挂件的直接链接" title="余额挂件的直接链接" translate="no">​</a></h3>
<p>顺手还装了个显示余额的小挂件：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">dsh plugin </span><span class="token parameter variable" style="color:#36acaa">--profile</span><span class="token plain"> web </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> github:MeteorNOX/DeepSeek-Balance-Whale-Widget</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="桌面端">桌面端<a href="https://muranxuan.top/blog/deepseek-harness-experience#%E6%A1%8C%E9%9D%A2%E7%AB%AF" class="hash-link" aria-label="桌面端的直接链接" title="桌面端的直接链接" translate="no">​</a></h2>
<p>Web 端用着挺方便，不过 Windows / macOS 上还有桌面版，用起来更顺手：<a href="https://github.com/anywhere-labs/deepseek-harness-desktop" target="_blank" rel="noopener noreferrer" class="">deepseek-harness-desktop</a> 。桌面版应用的名字是 DSH Desktop。</p>
<p>最后来张 dsh 的截图：</p>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/842fbf746365c3ab473bb4bc52c20dea-31f36790cf4ef6926e935d5482c5723a.avif" width="958" height="576" class="img_ev3q"></p>
<p>桌面端使用desktop的profile，所以前面我们使用web的profile安装的插件在桌面端就不显示了，可以在设置里切换到web的profile。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="简单体验">简单体验<a href="https://muranxuan.top/blog/deepseek-harness-experience#%E7%AE%80%E5%8D%95%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="简单体验的直接链接" title="简单体验的直接链接" translate="no">​</a></h2>
<p>简单体验了下，和别的Agent工具的区别是，DeepSeek Harness更加开放，特别是轨迹功能：</p>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/957fe8f7bab746d7c26e5433ecb2057a-b188aa9c90538c40375392d69877f17e.avif" width="958" height="552" class="img_ev3q"></p>
<p>界面上非常像浏览器的开发者工具，可以看到对话的细节，包括工具调用的细节，这样也便于审计。</p>
<p>不仅如此，DeepSeek Harness也很容易配置别的供应商，而其它Agent工具要么不能配置（例如AI办公类Agent工具），要么不方便配置，需要借助一些工具（例如使用 CC Switch 等工具进行切换）。</p>
<p>不过，由于现在还是开发者预览版，插件在短时间内井喷式发展，插件的兼容性稳定性等缺乏沉淀，在安装插件时可能遇到各种问题，这一定程度上也提高了DeepSeek Harness的使用门槛。</p>]]></content:encoded>
            <category>DeepSeek Harness</category>
            <category>AI Agent</category>
            <category>插件</category>
            <category>开发工具</category>
        </item>
        <item>
            <title><![CDATA[在本地部署大模型，顺便感慨]]></title>
            <link>https://muranxuan.top/blog/local-llm-setup-notes</link>
            <guid>https://muranxuan.top/blog/local-llm-setup-notes</guid>
            <pubDate>Sat, 22 Aug 2026 23:08:12 GMT</pubDate>
            <description><![CDATA[前言]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="前言">前言<a href="https://muranxuan.top/blog/local-llm-setup-notes#%E5%89%8D%E8%A8%80" class="hash-link" aria-label="前言的直接链接" title="前言的直接链接" translate="no">​</a></h2>
<p>其实早在去年DeepSeek-R1开源那会儿，有朋友就和我聊可以本地部署大模型玩玩。不过那时我对本地部署不太感兴趣，虽然我知道可以用Ollama部署，但没有付诸实践，因为我觉得本地只能部署一些参数量很小的大模型，其能力较满血版差多了，直接用API调用简单又方便，何必费那个劲呢？</p>
<p>现在看来，这理由没错，但是也不全对。我当时可能不只是拒绝尝试本地部署这件事，而是变得不想折腾的感觉。毕竟工作比较忙，没多少时间折腾，渐渐地就开始用是否值得、有没有意义等理由来衡量一切，而与折腾的初心渐行渐远。</p>
<p>所以这篇博客，本来去年就可以有的，现在补上，既是技术记录，也是响应下喜欢折腾的初心。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ollama">Ollama<a href="https://muranxuan.top/blog/local-llm-setup-notes#ollama" class="hash-link" aria-label="Ollama的直接链接" title="Ollama的直接链接" translate="no">​</a></h2>
<p>Ollama官网：<a href="https://ollama.com/" target="_blank" rel="noopener noreferrer" class="">Ollama</a></p>
<p>Ollama安装比较简单，去Ollama官网下载安装包，安装时一路Next就行。Ollama会默认安装在C盘，如果要修改安装路径，要使用命令行安装：</p>
<div class="language-batch codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-batch codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token command keyword" style="color:#00009f">OllamaSetup</span><span class="token command">.exe /DIR=D:\AppData\Local\Programs\Ollama</span><br></div></code></pre></div></div>
<p>Ollama默认把模型放在C盘，可以通过设置环境变量<code>OLLAMA_MODELS</code> 来设置Ollama模型的保存位置。</p>
<p>Ollama有自己的模型库：<a href="https://ollama.com/search" target="_blank" rel="noopener noreferrer" class="">https://ollama.com/search</a>，不过模型偏少。例如，运行个<code>qwen3:4b</code> 玩玩：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">ollama run qwen3:4b</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/b001e9da733de5217f22a21cbd621bb6-455543c02506decb648480b11ac72b3c.avif" width="958" height="670" class="img_ev3q"></p>
<p>如果之前没有下载过，Ollama会先下载模型，然后运行。Ollama也有GUI工具可以使用，像Chat助手。图里因为我之前下载过，所以没有下载的过程。</p>
<p>Ollama默认会监听11434端口，验证：</p>
<div class="language-batch codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-batch codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token command keyword" style="color:#00009f">C</span><span class="token command label property" style="color:#36acaa">:\Users\jiang&gt;netstat</span><span class="token command"> -ano | findstr </span><span class="token command number" style="color:#36acaa">11434</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token command keyword" style="color:#00009f">TCP</span><span class="token command">    </span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">:</span><span class="token command number" style="color:#36acaa">11434</span><span class="token command">          </span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">:</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">              LISTENING       </span><span class="token command number" style="color:#36acaa">77440</span><br></div></code></pre></div></div>
<p>用LangChain本地调用下：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> langchain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chat_models </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> init_chat_model</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'__main__'</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    model </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> init_chat_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        model</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'ollama:qwen3:4b'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        base_url</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'http://localhost:11434'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">invoke</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'什么是LangChain，100字以内'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div></code></pre></div></div>
<p>输出如下：</p>
<div class="language-Plain language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">LangChain是开源AI框架，用于构建应用，通过连接大模型与工具实现链式推理，支持多模态数据处理和智能决策。（58字）</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="llamacpp">llama.cpp<a href="https://muranxuan.top/blog/local-llm-setup-notes#llamacpp" class="hash-link" aria-label="llama.cpp的直接链接" title="llama.cpp的直接链接" translate="no">​</a></h2>
<p>Ollama的推理内核使用的是llama.cpp，但曾长期缺失llama.cpp的版权声明。而且考虑到llama.cpp的性能更好，也可以使用llama.cpp来本地部署大模型。</p>
<p>llama.cpp有CPU版和GPU版，从GitHub下载：<a href="https://github.com/ggml-org/llama.cpp/releases" target="_blank" rel="noopener noreferrer" class="">Releases · ggml-org/llama.cpp</a>。</p>
<p>因为我电脑有N卡，所以下载CUDA版，CUDA DLLs也需要下载。下载之后解压，CUDA DLLs解压后放到llama的目录。</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://muranxuan.top/assets/images/23d3e027dd97496f69495a59ab122010-10aaf25e25bd2a03ca2ade28107d43f5.avif" width="1608" height="516" class="img_ev3q"></p>
<p>然后就是下载模型了，一般在<a href="https://huggingface.co/" target="_blank" rel="noopener noreferrer" class="">Hugging Face – The AI community building the future.</a>上下载。不过那个网站很慢，所以可以使用镜像站。设置环境变量：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">HF_ENDPOINT</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">https://hf-mirror.com</span><br></div></code></pre></div></div>
<p>就可以在命令行或代码里使用镜像站了。</p>
<p>不过，有时这个镜像站依然还是很慢（唉，网络是国内开发者的痛）。可以看看<a href="https://modelscope.cn/models" target="_blank" rel="noopener noreferrer" class="">模型库首页 · 魔搭社区</a>，在HuggingFace上找的模型可以到魔搭社区（ModelScope）下载，例如：<a href="https://modelscope.cn/models/empero-ai/Qwen3.8-4B-Distill-GGUF/files" target="_blank" rel="noopener noreferrer" class="">Qwen3.8-4B-Distill-GGUF · 模型库</a>。</p>
<p>llama.cpp有cli：</p>
<div class="language-batch codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-batch codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token command keyword" style="color:#00009f">llama</span><span class="token command">-cli.exe </span><span class="token command parameter attr-name" style="color:#00a4db">-m</span><span class="token command"> models\Qwen3.</span><span class="token command number" style="color:#36acaa">8</span><span class="token command">-4B-Q4_K_M.gguf -ngl all </span><span class="token command parameter attr-name" style="color:#00a4db">-c</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">4096</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">-b</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">2048</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">--flash-attn</span><span class="token command"> auto </span><span class="token command parameter attr-name" style="color:#00a4db">-t</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">8</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">--temp</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">6</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">--top-p</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">95</span><span class="token command"> -cnv</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/9daee66bc951eff473c5bdc26c0bafd7-3cf29010fd9ae6387caff7738d189bab.avif" width="958" height="640" class="img_ev3q"></p>
<p>llama.cpp也可以部署为服务：</p>
<div class="language-batch codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-batch codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token command keyword" style="color:#00009f">llama</span><span class="token command">-server.exe </span><span class="token command parameter attr-name" style="color:#00a4db">-m</span><span class="token command"> models\Qwen3.</span><span class="token command number" style="color:#36acaa">8</span><span class="token command">-4B-Q4_K_M.gguf -ngl all </span><span class="token command parameter attr-name" style="color:#00a4db">-c</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">4096</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">-b</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">2048</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">--flash-attn</span><span class="token command"> auto </span><span class="token command parameter attr-name" style="color:#00a4db">-t</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">8</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">--host</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command">.</span><span class="token command number" style="color:#36acaa">0</span><span class="token command"> </span><span class="token command parameter attr-name" style="color:#00a4db">--port</span><span class="token command"> </span><span class="token command number" style="color:#36acaa">11434</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://muranxuan.top/assets/images/dda3205d26d56672db6bc727b4377b38-42b02cb1bad846eb996400cac7a3eb07.avif" width="958" height="612" class="img_ev3q"></p>
<p>用LangChain本地调用下：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> langchain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chat_models </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> init_chat_model</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'__main__'</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    model </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> init_chat_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        model</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'Qwen3.8-4B-Q4_K_M.gguf'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        base_url</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'http://localhost:11434/v1'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        model_provider</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        api_key</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'xxx'</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 随便填个，不能为空</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">invoke</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'什么是LangChain，100字以内'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>输出如下：</p>
<div class="language-Plain language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">LangChain 是构建 LLM 应用的框架。它提供 Prompt、Memory、Tools 等模块，帮助开发者简化复杂工作流，构建具备推理、规划及工具调用能力的 AI 应用，降低开发门槛。</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一些感慨">一些感慨<a href="https://muranxuan.top/blog/local-llm-setup-notes#%E4%B8%80%E4%BA%9B%E6%84%9F%E6%85%A8" class="hash-link" aria-label="一些感慨的直接链接" title="一些感慨的直接链接" translate="no">​</a></h2>
<p>折腾，本身就是意义。不是所有事都需要“有意义”才“值得”去做，有时，“想做”本身就是理由。</p>
<p>很长时间里，我都希望自己的博客内容“有含金量”，这导致我常常很久才憋一篇，有时想写又觉得没什么意义，然后随着工作忙就放弃了。现在很多人遇到问题就直接问AI了，搜索引擎也很少用了，我也是这样。在这种情况下，个人博客就更没什么人看了，我也不在意我的博客有多少人看了，那写博客就没什么意义了吗？</p>
<p>博客的意义，也许从来就不是让别人看到，而是让自己看见。</p>
<p>看见自己曾经折腾过什么，比如曾经为了本地部署大模型，折腾过Ollama，折腾过llama.cpp。我在折腾的过程中遇到的一些问题以及解决方案，也许会帮助到其他人。</p>
<p>所以，我觉得不必强求博客要“有含金量”，它更多的是一种记录，提醒自己保持对技术的热忱，保持对折腾的兴致。</p>]]></content:encoded>
            <category>AI</category>
            <category>LLM</category>
        </item>
    </channel>
</rss>