<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh">
    <title>bigfish的网络博客 - 插件</title>
    <subtitle>记录技术和生活</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.bigfisharc.cn/tags/cha-jian/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.bigfisharc.cn"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-15T09:00:00+08:00</updated>
    <id>https://blog.bigfisharc.cn/tags/cha-jian/atom.xml</id>
    <entry xml:lang="zh">
        <title>从一个 helloworld 动态库到 rpi package：ABI 插件是怎样被加载的</title>
        <published>2026-10-15T09:00:00+08:00</published>
        <updated>2026-10-15T09:00:00+08:00</updated>
        
        <author>
          <name>bigfish</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.bigfisharc.cn/blog/20261015-0900-rpicha-jian-abijia-zai-ji-zhi/"/>
        <id>https://blog.bigfisharc.cn/blog/20261015-0900-rpicha-jian-abijia-zai-ji-zhi/</id>
        
        <summary type="html">&lt;p&gt;很多人第一次看到 rpi 的 Rust 插件，会把它理解成“编译出一个动态库，然后让 rpi 找到它”。这句话方向没错，但省略了最关键的部分：动态库如何证明自己是一个 rpi 插件？注册的工具如何跨过 Rust 边界？一次调用结束后，插件里的内存由谁释放？源码目录里的 crate 又是怎样变成项目里的 package 的？&lt;/p&gt;
&lt;p&gt;这篇文章从 &lt;code&gt;examples/plugin-stub&lt;/code&gt; 这样的最小 demo 开始。我们可以先从这个简单例子入手，再沿着 &lt;code&gt;rpi-plugin-sdk&lt;/code&gt;、&lt;code&gt;rpi-extensions&lt;/code&gt; 和 &lt;code&gt;rpi-cli&lt;/code&gt; 的实现，解释这条链路：&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;mermaid&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;flowchart&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt; LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    A&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;插件源码\nRust crate&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt; --&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; B&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;cdylib\n.dll / .so / .dylib&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    B &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; C&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;rpi 加载器\nlibloading&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    C &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; D&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;rpi_plugin_register&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    D &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; E&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;HostApi + ExtensionRegistry&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    E &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; F&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;RegistrySnapshot&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    F &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; G&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;Agent 会话中的工具\n事件与其他扩展能力&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的“插件”指 rpi 的 Rust 原生 ABI 扩展，不是 MCP Server，也不是一个通过 HTTP 或 stdio 运行的外部进程。我们可以先把它看成宿主进程里的一段本机可信代码：它和宿主运行在同一个进程里，因此能力强、开销低，但也意味着 ABI、内存所有权和生命周期必须非常明确。&lt;/p&gt;
&lt;p&gt;本文讨论的是 rpi 的原生 ABI 插件机制。由于 ABI、loader 和 package 命令会随仓库版本变化，下面的文件名、符号名和命令都应与所读源码版本一起核对；代码块中明确标注为“示意”的部分，不等于可直接复制运行的最小插件。文章关注一条主线：插件如何从动态库进入注册表，再被会话驱动；不把它扩展成完整的 Rust FFI 教程。&lt;/p&gt;</summary>
        
    </entry>
    <entry xml:lang="zh">
        <title>rpi 这些扩展到底怎么选？一张工具地图看懂 16 个 Rust 插件</title>
        <published>2026-10-11T09:03:00+08:00</published>
        <updated>2026-10-11T09:03:00+08:00</updated>
        
        <author>
          <name>bigfish</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.bigfisharc.cn/blog/20261011-0903-rpigong-ju-di-tu/"/>
        <id>https://blog.bigfisharc.cn/blog/20261011-0903-rpigong-ju-di-tu/</id>
        
        <summary type="html">&lt;p&gt;rpi 已经有一批常见的原生 Rust 插件。数量上还没有成熟生态那么多，但任务规划、代码分析、网络访问、运行观测、消息渠道和语音交互等日常场景，基本都已经有对应能力可以使用。&lt;/p&gt;
&lt;p&gt;如果只看包名，&lt;code&gt;rpi-packages&lt;/code&gt; 很容易变成一张“功能名词表”：&lt;code&gt;todo&lt;/code&gt;、&lt;code&gt;goal&lt;/code&gt;、&lt;code&gt;lens&lt;/code&gt;、&lt;code&gt;MCP&lt;/code&gt;、&lt;code&gt;Langfuse&lt;/code&gt;、&lt;code&gt;voice&lt;/code&gt;……每个词都认识，真正要装哪个却不容易判断。&lt;/p&gt;
&lt;p&gt;这篇文章先把目前已有的扩展整体展示出来，再按使用场景说明它们分别解决什么问题。你可以把它当成一张选型地图：先从任务找到需要的能力，再决定安装哪些包，而不是把整个扩展目录全部装上。后续如果你正在使用其他原生 Rust 插件，也欢迎补充。&lt;/p&gt;</summary>
        
    </entry>
</feed>
