<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on Code Now</title>
    <link>https://blog.0xnullpath.cc/posts/</link>
    <description>Recent content in Posts on Code Now</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Mon, 20 Apr 2026 04:27:43 +0000</lastBuildDate>
    <atom:link href="https://blog.0xnullpath.cc/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>【模型推理】浅谈CUDA Graph(WIP)</title>
      <link>https://blog.0xnullpath.cc/posts/note-snippet-15-%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86%E6%B5%85%E8%B0%88cuda-graphwip/</link>
      <pubDate>Mon, 20 Apr 2026 04:27:43 +0000</pubDate>
      <guid>https://blog.0xnullpath.cc/posts/note-snippet-15-%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86%E6%B5%85%E8%B0%88cuda-graphwip/</guid>
      <description>&lt;p&gt;CUDA Graph 算是在推理优化里被提到最多的特性之一了。原理倒是不复杂：把原本一次次 launch 的 kernel 打包成一张静态图，一次性扔给 GPU，省掉中间的调度开销。&lt;/p&gt;&#xA;&lt;p&gt;但这个&amp;quot;打包&amp;quot;具体怎么做？PyTorch 里怎么用？以及最重要的是——在 H200 上到底能快多少？&lt;/p&gt;&#xA;&lt;p&gt;这篇文章就是把这几个问题摸清楚的过程记录。&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-llm-推理在忙什么&#34;&gt;1. LLM 推理在忙什么&lt;/h2&gt;&#xA;&lt;p&gt;Graph 能减少 kernel launch 的开销，但为什么 LLM 推理特别需要这个？先简单说下背景。&lt;/p&gt;&#xA;&lt;p&gt;老生常谈的问题，LLM 推理分两个阶段：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Prefill&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;输入是用户的一整段 prompt&lt;/li&gt;&#xA;&lt;li&gt;一次性做完整的 self-attention 计算，然后讲产生的KVCache写入显存或者其他存储设备，从而在Decode阶段使用。&lt;/li&gt;&#xA;&lt;li&gt;特点是计算密集，kernel消耗的时间比较多，GPU 利用率相对高，同时对内存IO要要求比较低。总之就compute-bound，算力是决定他的速度的关键。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Decode&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;自回归过程，每次只生成一个 token&lt;/li&gt;&#xA;&lt;li&gt;要用到之前计算的 KV Cache，只做必要的更新，对于单个请求推理的时候我们输入model的Tensor维度为&lt;code&gt;[1, D]&lt;/code&gt;, 其中D是Embedding的维度。&lt;/li&gt;&#xA;&lt;li&gt;特点是memory-bound, 单个请求计算量少，但是需要load KVCache巨大，而且推理的时间越长，KVCache load的压力就越大。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;对于这个推理过程，我们可以认为CPU step2step的将模型每一层计算所需要的kernel相关数据和元数据准备好，调用launch通知GPU执行，然后异步的准备下一次计算所需要的信息的过程。&lt;/p&gt;&#xA;&lt;p&gt;此时因为不同Prefill和Decode不同特点，CPU与GPU直接的协作和等待有不同的表现&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Prefill 由于Kernel计算时间比较长，因此CPU侧的开销其实基本可以被GPU的运算Overlap掉，因此关注点核心应该是GPU Kernel如何跑更快。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;img width=&#34;1100&#34; height=&#34;344&#34; alt=&#34;Image&#34; src=&#34;https://github.com/user-attachments/assets/eabf12ac-5f47-4f78-a7af-d02f49004a45&#34; /&gt;&#xA;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;Decode阶段，其实就是两个地方了，首先Kernel计算量比较小，因此开销时间很少，这种情况就出现了GPU等CPU等情况，这也是就是为什么我们常说Decode阶段需要开CUDA Graph的，原因，CUDA Graph的目标就是消除GPU等CPU的问题，让GPU计算更高效。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;img width=&#34;1094&#34; height=&#34;456&#34; alt=&#34;Image&#34; src=&#34;https://github.com/user-attachments/assets/06e9887f-bd43-48bb-928b-34e9402e66e0&#34; /&gt;&#xA;&lt;p&gt;如今其实也有主流的推理框架也支持Prefill阶段的CUDA Graph，这个主要是因为在推理过程中模型有大量的小kernel，比如说Norm这类的Kernel，这种其实也类似于Decode阶段的样子，GPU也需要等待CPU，因此CUDA Graph也是可以计算的，至于细节后面我会再写一篇专门介绍Prefill Piecewise CUDA Graph的一些细节。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;2-pytorch在推理的时候cpu纠结在做什么&#34;&gt;2. Pytorch在推理的时候CPU纠结在做什么？&lt;/h2&gt;&#xA;&lt;p&gt;因为像Pytorch这种框架一般对于一个算子支持很多Kernel的实现，因此为了支持这么多Kernel的实现就回有大量的dispatch的代码用来运行时自动路由到不同的kernel上，同时也会有很多准备工作，所有工作完成之后就调用cudaLaunchKernel交给CUDA的runtime层做一些准备工作并发送给GPU。在GPU运行的时候其实也会有一些操作，比如说GPU侧做Kernel环境的准备，在执行后也会有一些清理状态的后处理。&#xA;总结来说就如图，一个Kernel从Pytorch发起执行到做完运行有这几个步骤：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;CPU:&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Pytorch C++ Dispatch&lt;/li&gt;&#xA;&lt;li&gt;Kernel Launcher Prepare&lt;/li&gt;&#xA;&lt;li&gt;cudaLaunchKernel&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;GPU:&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Command Processor&lt;/li&gt;&#xA;&lt;li&gt;Kernel Execute&lt;/li&gt;&#xA;&lt;li&gt;Post-Process&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;img width=&#34;1538&#34; height=&#34;478&#34; alt=&#34;Image&#34; src=&#34;https://github.com/user-attachments/assets/4a84879b-cc43-47ae-8b42-432bc97113d0&#34; /&gt;&#xA;&lt;h3 id=&#34;21-pytorch-dispatch--kernel-prepare&#34;&gt;2.1. Pytorch Dispatch &amp;amp; Kernel Prepare&lt;/h3&gt;&#xA;&lt;p&gt;Pytorch Dispatch: 这个其实是所有支持多种Kernel+使用Python实现执行前端框架的通病。&lt;/p&gt;</description>
    </item>
    <item>
      <title>如何让hugo支持infographic</title>
      <link>https://blog.0xnullpath.cc/posts/note-snippet-13-%E5%A6%82%E4%BD%95%E8%AE%A9hugo%E6%94%AF%E6%8C%81infographic/</link>
      <pubDate>Tue, 06 Jan 2026 03:33:16 +0000</pubDate>
      <guid>https://blog.0xnullpath.cc/posts/note-snippet-13-%E5%A6%82%E4%BD%95%E8%AE%A9hugo%E6%94%AF%E6%8C%81infographic/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;infographic&lt;/code&gt; 是蚂蚁Antv的新组建，其目标是为AI提供更易用的可视化工具，定位类似于mermaid。其提供了开发者/AI易用的DSL，通过快捷的编写DSL就可以很快的快速可视化，各种图表例如折线图/饼图之类的，同时也可以可视化一些类似于PPT需要使用到的grid图/swot图之类的工具，非常的易用。因此我希望可以引入我的hugo博客，并且可以通过markdown code block快速的通过&lt;code&gt;infographic&lt;/code&gt; 可视化我的想法。&lt;/p&gt;&#xA;&lt;h2 id=&#34;怎么做&#34;&gt;怎么做?&lt;/h2&gt;&#xA;&lt;p&gt;Hugo提供了&lt;a href=&#34;https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_markup/render-codeblock-goat.html&#34;&gt;embedded code block render hook&lt;/a&gt;.&#xA;可以通过模版对特定的language的 code block进行渲染，Hugo中的Mermaid等图都可以使用这种方法。&lt;/p&gt;&#xA;&lt;h3 id=&#34;step1-增加js-import&#34;&gt;step1: 增加js import&lt;/h3&gt;&#xA;&lt;p&gt;在全局header中增加对infographic的包的引用，可以在&lt;code&gt; layouts/partials/foot_custom.html&lt;/code&gt; 增加对AntV Infographic的引用&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://unpkg.com/@antv/infographic@latest/dist/infographic.min.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step2-增加自定义渲染block的hook&#34;&gt;step2: 增加自定义渲染block的hook&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;增加&lt;code&gt;layouts/_default/_markup/render-codeblock-infographic.html&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;通过模版和js脚本对整体html进行渲染和注入&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;infographic-{{ .Ordinal }}&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;infographic-container&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;style&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;min-height: 500px; width: 100%;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/div&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;container&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;getElementById&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;infographic-{{ .Ordinal }}&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;syntax&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`{{ .Inner | safeJS }}`&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;renderInfographic&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (window.&lt;span style=&#34;color:#a6e22e&#34;&gt;AntVInfographic&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;Infographic&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; window.&lt;span style=&#34;color:#a6e22e&#34;&gt;AntVInfographic&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;infographic&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Infographic&lt;/span&gt;({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;container&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;container&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;100%&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;500px&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;infographic&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;render&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;syntax&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// Retry if library not loaded yet&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;renderInfographic&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (document.&lt;span style=&#34;color:#a6e22e&#34;&gt;readyState&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;complete&amp;#39;&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;renderInfographic&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    window.&lt;span style=&#34;color:#a6e22e&#34;&gt;addEventListener&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;load&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;renderInfographic&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/script&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ .&lt;span style=&#34;color:#a6e22e&#34;&gt;Page&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Store&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hasInfographic&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;效果&#34;&gt;效果&lt;/h2&gt;&#xA;&lt;p&gt;example1: 基础图&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零开始LLM生活-如何编写一个Agent</title>
      <link>https://blog.0xnullpath.cc/posts/note-snippet-9-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8Bllm%E7%94%9F%E6%B4%BB-%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99%E4%B8%80%E4%B8%AAagent/</link>
      <pubDate>Tue, 27 May 2025 16:04:10 +0000</pubDate>
      <guid>https://blog.0xnullpath.cc/posts/note-snippet-9-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8Bllm%E7%94%9F%E6%B4%BB-%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99%E4%B8%80%E4%B8%AAagent/</guid>
      <description>&lt;blockquote&gt;&#xA;  &lt;p&gt;本文希望使用通俗的语言表述什么是LLM Agent 和使用python实现一个简单的Agent&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;1-agent介绍&#34;&gt;1. Agent介绍&lt;/h2&gt;&#xA;&lt;h3 id=&#34;11-什么是agent&#34;&gt;1.1 什么是Agent？&lt;/h3&gt;&#xA;&lt;p&gt;LLM Agent并没有一个明确的定义，但是有一个核心特点就是&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;  &lt;p&gt;An LLM serves as the main controller or &amp;ldquo;brain&amp;rdquo; that controls a flow of operations needed to complete a task or user request. &amp;mdash;《Prompt Engineering Guide》&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;LLM Agent 是一个基于大型语言模型（LLM）的系统，它不仅仅被动地响应查询，更能&lt;strong&gt;自主地制定计划、利用工具/资源与外部环境交互，并根据观察结果调整行为，以达成特定目标&lt;/strong&gt;。与简单的LLM调用相比，Agent更强调其自主性、规划能力和工具使用能力.&lt;/p&gt;&#xA;&lt;h3 id=&#34;12-llm-agent可以解决什么样的问题&#34;&gt;1.2 LLM Agent可以解决什么样的问题？&lt;/h3&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;  &lt;p&gt;If that deterministic workflow fits all queries, by all means just code everything! This will give you a 100% reliable system with no risk of error introduced by letting unpredictable LLMs meddle in your workflow. For the sake of simplicity and robustness, it&amp;rsquo;s advised to regularize towards not using any agentic behaviour.&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零开始的 LLM Agent编程生活--MCP 篇</title>
      <link>https://blog.0xnullpath.cc/posts/note-snippet-5-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%9A%84-llm-agent%E7%BC%96%E7%A8%8B%E7%94%9F%E6%B4%BB-mcp-%E7%AF%87/</link>
      <pubDate>Mon, 21 Apr 2025 08:23:57 +0000</pubDate>
      <guid>https://blog.0xnullpath.cc/posts/note-snippet-5-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%9A%84-llm-agent%E7%BC%96%E7%A8%8B%E7%94%9F%E6%B4%BB-mcp-%E7%AF%87/</guid>
      <description>&lt;h2 id=&#34;1-什么是mcpmodel-context-protocol&#34;&gt;1. 什么是MCP(Model Context Protocol)&lt;/h2&gt;&#xA;&lt;p&gt;MCP（Model Context Protocol，模型上下文协议）是由Anthropic公司于2024年推出并开源的一种通信协议，旨在解决大型语言模型（LLM）与外部数据源及工具之间的连接问题。定义了 Model 与外部接口/数据/Prompt 通信之间的协议。 工具/资源提供方只需要实现 MCP 协议就可以和实现 MCP 客户端的 LLM APP 链接，LLM APP在运行过程中自动根据协议中返回工具列表/Prompt/资源列表，通过JsonRpc 从 MCP 服务器中获取。&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;  &lt;p&gt;一个简单的例子：接入高德地图，让模型通过高德地图的 API 查询相关天气/路径/地图周围相关信息的能力。&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;没有 MCP：需要自己实现对高德 OpenAPI 的调用 Tools，自己写 Prompt 组织对 LLM 的请求。&lt;/li&gt;&#xA;&lt;li&gt;有 MCP 后：直接使用 MCP Client 写上高德 MCP Service 的 Endpoint 和 Key，LLM 会在运行期间主动通过 MCP 查询高德相关资源，并使用高德已经组织好的 Prompt 反馈给 LLM。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;2-mcp定义了什么&#34;&gt;2. MCP定义了什么：&lt;/h2&gt;&#xA;&lt;p&gt;MCP 定义的原语：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Tools：FunctionCall&lt;/li&gt;&#xA;&lt;li&gt;Resouce：资源&lt;/li&gt;&#xA;&lt;li&gt;Prompts：提供结构化模板&lt;/li&gt;&#xA;&lt;li&gt;Sampling：允许服务器请求客户端调用 LLM&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;21-工具&#34;&gt;2.1 工具：&lt;/h3&gt;&#xA;&lt;p&gt;经常有人拿 FunctionCall 和 MCP 做对比，甚至发出既生FunctionCall 又生 MCP 的感叹，我个人认为 FunctionCall和 MCP 并不冲突，FunctionCall 其实是 MCP 的一个子集，MCP 也支持 FunctionCall，只不过 MCP 还支持 Resource/Prompt 等定义，并在协议层对其获取/调用/更新做了明确 Protocol 约束。&lt;/p&gt;</description>
    </item>
    <item>
      <title>讲给老婆的数据系统设计-ACID事务</title>
      <link>https://blog.0xnullpath.cc/posts/note-snippet-3-%E8%AE%B2%E7%BB%99%E8%80%81%E5%A9%86%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1-acid%E4%BA%8B%E5%8A%A1/</link>
      <pubDate>Sun, 09 Mar 2025 16:16:44 +0000</pubDate>
      <guid>https://blog.0xnullpath.cc/posts/note-snippet-3-%E8%AE%B2%E7%BB%99%E8%80%81%E5%A9%86%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1-acid%E4%BA%8B%E5%8A%A1/</guid>
      <description>&lt;h2 id=&#34;为什么会有事务&#34;&gt;为什么会有事务&lt;/h2&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;  &lt;p&gt;“在数据系统的残酷现实中，很多事情都可能出错：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;数据库软件、硬件可能在任意时刻发生故障（包括写操作进行到一半时）。&lt;/li&gt;&#xA;&lt;li&gt;应用程序可能在任意时刻崩溃（包括一系列操作的中间）。&lt;/li&gt;&#xA;&lt;li&gt;网络中断可能会意外切断数据库与应用的连接，或数据库之间的连接。&lt;/li&gt;&#xA;&lt;li&gt;多个客户端可能会同时写入数据库，覆盖彼此的更改。&lt;/li&gt;&#xA;&lt;li&gt;客户端可能读取到无意义的数据，因为数据只更新了一部分。&lt;/li&gt;&#xA;&lt;li&gt;客户之间的竞争条件可能导致令人惊讶的错误。”&#xA;&amp;mdash; 《数据密集型应用系统设计》&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;在并发编程中，我们常常会关注多线程/进程/协程对同一块内存进行修改时候的正确性，这个时候我们因为了原子变量和锁来实现临界区去解决这个问题。在实际生活和开发中更多是操作主体总是会通过一系列的操作来完成一件事，这个时候我们也希望能提供类似于原子变量的工作来讲我们一系列操作进行保护，从而防止出现多个主体操作的非预期情况。&lt;/p&gt;&#xA;&lt;p&gt;想象一下，你在用手机银行给朋友转账。这个过程其实有两步：从你的账户扣钱，然后把钱加到朋友的账户上。那么问题来了：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;如果只完成一半怎么办？【原子】&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;假设银行系统在扣完你的钱后突然断电了，没能把钱加到朋友账户上。这时候钱就凭空消失了！你的账户少了钱，但朋友并没有收到。&lt;/p&gt;&#xA;&lt;p&gt;事务就是为了解决这个问题：&lt;strong&gt;它确保一组相关操作要么全部完成，要么一个都不做&lt;/strong&gt;。就像是给这些操作绑了个保险绳，不让它们半途而废。&lt;/p&gt;&#xA;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;多人同时操作怎么办？【隔离】&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;想象你和家人共用一个银行账户，账户里有1000元。你在商场准备刷卡买800元的东西，同一时间，你爱人在超市也想用这个账户买500元的菜。&lt;/p&gt;&#xA;&lt;p&gt;如果系统没有适当控制，可能会出现这种情况：系统先检查账户有1000元（够付800元），然后你老婆那边系统也检查有1000元（够付500元）。结果你们俩同时消费了1300元，超出了账户里的1000元！&lt;/p&gt;&#xA;&lt;p&gt;事务能够解决这个问题，&lt;strong&gt;它会确保在一个人操作的时候，其他人要么看到操作前的状态，要么看到操作后的状态，不会看到中间状态，避免大家操作冲突&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;数据会不会突然丢失？【持久】&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;假设你正在网上填写一个很长的表单，好不容易填完提交，系统提示&amp;quot;保存成功&amp;quot;，但第二天你登录发现信息不见了！原来是系统在显示成功后，还没来得及真正保存到硬盘上就崩溃了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;事务确保一旦系统告诉你&amp;quot;操作成功&amp;quot;，那么即使下一秒系统崩溃，你的数据也不会丢失&lt;/strong&gt;。它就像是给你的重要资料上了一个保险锁。&lt;/p&gt;&#xA;&lt;p&gt;总结来说事务模型就是为多种操作提供原子化/隔离性/持久性的一个编程模型。也就是事务的ACID模型。 【A：Atomicity，C：Consistency，I：Isolation，D：Durability】&lt;/p&gt;&#xA;&lt;h2 id=&#34;acid事务特性&#34;&gt;ACID事务特性：&lt;/h2&gt;&#xA;&lt;h3 id=&#34;原子性&#34;&gt;原子性：&lt;/h3&gt;&#xA;&lt;p&gt;这个原子性其实是计算机中“原子”概念的衍生，计算机的原子性指一个操作或者数据没有中间状态，只有开始和结束，没有中间状态。而事务提到的原子其实是有中间状态，但是中间状态对于其他的操作主体是无法干预的，同时如果中止，则在事务中所有操作都会被回滚。DDIA书中更多的称为&lt;strong&gt;可中止性（abortability）&lt;/strong&gt;：“能够在错误时中止事务，丢弃该事务进行的所有写入变更的能力”&lt;/p&gt;&#xA;&lt;h3 id=&#34;一致性&#34;&gt;一致性：&lt;/h3&gt;&#xA;&lt;p&gt;事务一致性是ACID特性中最为核心却也最容易被误解的概念。从形式化的角度看，一致性指的是事务执行前后，数据库必须从一个一致性状态转变为另一个一致性状态。所谓一致性状态，是指数据库中的数据满足所有预定义的完整性约束(integrity constraints)。&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;实体完整性(Entity Integrity)&lt;/strong&gt;: 通过主键约束实现，确保每个实体具有唯一标识&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;参照完整性(Referential Integrity)&lt;/strong&gt;: 通过外键约束实现，保证实体间引用关系的有效性&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;域完整性(Domain Integrity)&lt;/strong&gt;: 通过数据类型、CHECK约束等实现，确保属性值满足预定义规则&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;用户定义完整性(User-Defined Integrity)&lt;/strong&gt;: 通过触发器、存储过程等实现特定业务规则&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;一致性可被视为一个不变量(invariant)的保持过程，它确保数据库状态转换符合业务规则和领域逻辑。与原子性和隔离性不同，一致性的保证不仅依赖于数据库系统本身，还需要应用程序正确实现业务逻辑。同时原子性、隔离性和持久性是实现一致性的技术手段&lt;/p&gt;&#xA;&lt;h3 id=&#34;隔离性&#34;&gt;隔离性：&lt;/h3&gt;&#xA;&lt;p&gt;隔离性指的是两个事务之间的操作和中间状态和变量互相看不见，即使一个用户做了一个写操作其他用户也看不到。&lt;/p&gt;&#xA;&lt;pre class=&#34;mermaid&#34;&gt;&#xA;  sequenceDiagram&#xA;    participant User1 as User 1&#xA;    participant DB as Database&#xA;    participant User2 as User 2&#xA;&#xA;    &#xA;    User1-&amp;gt;&amp;gt;DB: get counter&#xA;    DB--&amp;gt;&amp;gt;User1: 42&#xA;    &#xA;    Note over User1: [42 + 1 = 43]&#xA;    &#xA;    User2-&amp;gt;&amp;gt;DB: get counter&#xA;    DB--&amp;gt;&amp;gt;User2: 42&#xA;    &#xA;    Note over User2: [42 + 1 = 43]&#xA;    &#xA;    User1-&amp;gt;&amp;gt;DB: set counter = 43&#xA;    DB--&amp;gt;&amp;gt;User1: ok&#xA;    &#xA;    User2-&amp;gt;&amp;gt;DB: set counter = 43 &#xA;    DB--&amp;gt;&amp;gt;User2: ok&#xA;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;最简单的时间就是事务之间完全串行，这样的话就会完全保证事务之间完全是看不到中间状态，但是在实际情况情况下为了提高效率，一般会将隔离优化为4个等级：&lt;/p&gt;</description>
    </item>
    <item>
      <title>树状数组（Binary Indexed）</title>
      <link>https://blog.0xnullpath.cc/posts/note-snippet-12-%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84binary-indexed/</link>
      <pubDate>Tue, 10 Mar 2020 17:30:00 +0000</pubDate>
      <guid>https://blog.0xnullpath.cc/posts/note-snippet-12-%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84binary-indexed/</guid>
      <description>&lt;blockquote&gt;&#xA;  &lt;p&gt;首先很感谢B站up主鹤翔万里的视频, 推荐看 &lt;a href=&#34;https://www.bilibili.com/video/av69667943?from=search&amp;amp;seid=10916758362943551299&#34;&gt;https://www.bilibili.com/video/av69667943?from=search&amp;amp;seid=10916758362943551299&lt;/a&gt; 本文为这个视频集合 算法笔记 写的总结&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;问题引入： 给出了一个长度为&lt;code&gt;n&lt;/code&gt;的数组，完成以下两种操作&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;输出区间&lt;code&gt;[x, y]&lt;/code&gt;内每个数字的和&lt;/li&gt;&#xA;&lt;li&gt;将第&lt;code&gt;x&lt;/code&gt;个数加上&lt;code&gt;k&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;最基础的算法：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;维护一个&lt;code&gt;sum&lt;/code&gt;数组，其&lt;code&gt;sum[i]&lt;/code&gt;记录的为从0-i的和。其递推计算关系为&lt;code&gt;sum[i] = sum[i-1] + nums[i]&lt;/code&gt;其中&lt;code&gt;sum[0] = nums[0]&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;计算区间和即可使用&lt;code&gt;sum_xy = sum[y] - sum[x-1]&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;将第&lt;code&gt;x&lt;/code&gt;个数加上&lt;code&gt;v&lt;/code&gt;, 因为要更新&lt;code&gt;sum[x]...sum[n]&lt;/code&gt;所有值，因此其时间复杂度为$O(n)$&lt;/li&gt;&#xA;&lt;li&gt;如果进行&lt;code&gt;k&lt;/code&gt;次操作&lt;code&gt;add&lt;/code&gt;则时间复杂度为$O(kn)$，&lt;code&gt;k&lt;/code&gt;次求区间和的时间复杂度为$O(k)$&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;在更新频繁的很多场景下$O(kn)$的时间复杂度是不可接受的。&lt;/p&gt;&#xA;&lt;p&gt;因此引入树状数组来解决此问题。&lt;/p&gt;&#xA;&lt;p&gt;树状数组(Binary Indexed Tree)：单点修改$O(logn)$，区间查询$O(logn)$，因此在区间内多次修改查询的速度为$O(klogn)$&lt;/p&gt;&#xA;&lt;h2 id=&#34;lowbit-运算&#34;&gt;LowBit 运算&lt;/h2&gt;&#xA;&lt;p&gt;非负证数n在二进制表示下最低为1及其后面的0构成的数值。&#xA;例如:&#xA;&lt;/p&gt;&#xA;$$lowbit(20) = lowbit(10100) = (100) = 4$$&lt;p&gt;如何操作呢:&lt;/p&gt;&#xA;&lt;p&gt;c++代码&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c++&#34; data-lang=&#34;c++&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lowbit&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/**&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     *  10100  20&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     *  01100  20的补码&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     * &amp;amp;------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     *  00100-&amp;gt;lowbit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;    */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; complement_n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;n &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 求补码，如果是int的话直接 -n即可&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; complement_n; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 或者&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lowbit&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;n)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;python代码&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
