<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>网络 on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/%E7%BD%91%E7%BB%9C/</link><description>Recent content in 网络 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 03:56:46 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/%E7%BD%91%E7%BB%9C/index.xml" rel="self" type="application/rss+xml"/><item><title>Postman 越来越臃肿了，我换了个开源的，还能让 AI 帮我写测试</title><link>https://xiaobox.github.io/p/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de/</link><pubDate>Mon, 13 Apr 2026 03:56:46 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/cover.jpg" alt="Featured image of post Postman 越来越臃肿了，我换了个开源的，还能让 AI 帮我写测试" /&gt;&lt;p&gt;Postman 我用了好几年了。&lt;/p&gt;
&lt;p&gt;从最早的 Chrome 插件时代开始用的，那时候它还是个轻量小工具，打开就能测接口，干干净净。但最近这两年，怎么说呢，它开始「端着」了。&lt;/p&gt;
&lt;p&gt;打开就让我登录。不登录不让用。&lt;/p&gt;
&lt;p&gt;好不容易登录了，又弹窗让我升级 Team 版。&lt;/p&gt;
&lt;p&gt;集合还给我存到云端去了。我就测个本地接口，你把我的请求数据往你服务器上传干嘛？&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/001-293772cd.png"&gt;&lt;/p&gt;
&lt;p&gt;我一直忍着，直到有一天，团队里两个人同时改了同一个集合，Postman 云同步直接给合并冲突了，还没法像 Git 那样 diff 看变更。那天我就想，不行了，得换。&lt;/p&gt;
&lt;p&gt;然后我遇到了 Bruno。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/002-67da7d91.png"&gt;&lt;/p&gt;
&lt;p&gt;怎么形容呢，就像你一直在用一个越来越臃肿的 IDE，突然有人递给你一个 Vim，告诉你「够用了，而且是你的」。&lt;/p&gt;
&lt;p&gt;Bruno 干了一件特别简单但特别对的事情，它把你的 API 请求存成 .bru 文件，放在你本地文件夹里。&lt;/p&gt;
&lt;p&gt;就是普通的文本文件，打开长这样。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;meta { name: 用户登录 type: http seq: 1}post { url: {{baseUrl}}/auth/login body: json auth: none}headers { Content-Type: application/json}body:json { { &amp;#34;username&amp;#34;: &amp;#34;{{username}}&amp;#34;, &amp;#34;password&amp;#34;: &amp;#34;{{password}}&amp;#34;, &amp;#34;expiresInMins&amp;#34;: 30 }}script:post-response { if (res.status === 200) { bru.setVar(&amp;#34;authToken&amp;#34;, res.body.accessToken); bru.setVar(&amp;#34;userId&amp;#34;, res.body.id); }}tests { test(&amp;#34;登录应该返回 200&amp;#34;, function() { expect(res.status).to.equal(200); }); test(&amp;#34;响应中应该包含 accessToken&amp;#34;, function() { expect(res.body.accessToken).to.be.a(&amp;#34;string&amp;#34;); expect(res.body.accessToken.length).to.be.greaterThan(0); }); test(&amp;#34;响应中应该包含用户基本信息&amp;#34;, function() { expect(res.body.id).to.be.a(&amp;#34;number&amp;#34;); expect(res.body.username).to.equal(&amp;#34;emilys&amp;#34;); expect(res.body.email).to.be.a(&amp;#34;string&amp;#34;); }); test(&amp;#34;响应时间应该小于 3 秒&amp;#34;, function() { expect(res.responseTime).to.be.lessThan(3000); });}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你看，GET 请求、Header、Body、断言，全都是纯文本。你用任何编辑器都能打开它，改完保存就行。&lt;/p&gt;
&lt;p&gt;这玩意最不同的地方在哪？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;它可以用 Git 管理。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以前团队共享 Postman 集合，那叫一个痛苦。谁改了什么不知道，版本对不对不确定，冲突了还没法 resolve。&lt;/p&gt;
&lt;p&gt;现在用 Bruno，接口定义就是文件，扔进 Git 仓库，该 PR 就 PR，该 Code Review 就 Code Review。有人改了某个接口的 Header，diff 里看得一清二楚。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/003-c38b66fd.png"&gt;&lt;/p&gt;
&lt;p&gt;用程序员已经会的工具，解决程序员的问题。不用学新的协作方式，不用付费，不用担心数据被传到哪个云上。&lt;/p&gt;
&lt;p&gt;Bruno 官网上有一句话我印象特别深，大意是**「我们不会同步你的任何数据到云端，甚至连登录的概念都没有。我们看不到你在 Bruno 里输入了什么，也不会用你的数据训练任何 AI 模型」。**&lt;/p&gt;
&lt;p&gt;在这个年代，一个工具敢这么说，我觉得还是挺硬气的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/004-2762e255.png"&gt;&lt;/p&gt;
&lt;p&gt;回到工具本身。说到这里，可能有朋友会想，开源 API 客户端一抓一大把，Insomnia、Hoppscotch、Thunder Client，凭什么是 Bruno？&lt;/p&gt;
&lt;p&gt;我自己用下来，让我有「这玩意不一样」的瞬间，是发现它有 CLI。&lt;/p&gt;
&lt;p&gt;但我说的不是「能在终端里跑测试」这种废话。Newman 也能跑，Postman 自己也有命令行。我说的是另一件事。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bruno 的 CLI，让 Claude Code 和 Cursor 这种 AI 编程工具，第一次能真正帮你写和跑接口测试。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这话有点大，我用两件刚发生的真事讲一下。&lt;/p&gt;
&lt;p&gt;回到 Bruno 的 CLI 本身。装它就一行，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;npm i -g @usebruno/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;装完之后命令叫 &lt;code&gt;bru&lt;/code&gt;，跑整个集合的测试是这样，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;bru run --env production
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出长这样。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/005-95a75ce3.png"&gt;&lt;/p&gt;
&lt;p&gt;干净、彩色、有 ✓ 和 ✗、有总耗时、有失败原因。最重要的是，&lt;strong&gt;这是一段普通的终端命令，输出是普通的文本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;为什么这件事重要？因为这两个特征，正好是 AI 编程 agent 工作的边界。&lt;/p&gt;
&lt;p&gt;Claude Code、Cursor、Codex 这些工具，它们能干什么？它们能读你的代码文件，能写新的文件，能在终端里跑命令，能读命令的输出。它们不能干什么？它们不能点击 Postman 的按钮，不能在你 GUI 里输入 token，不能登录任何账号。(&lt;strong&gt;或者说不方便，成本高，效率低&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;Postman 的核心数据存在云端、操作靠 GUI、协作靠登录，这三件事每一件都把 AI agent 挡在外面。&lt;/p&gt;
&lt;p&gt;而 Bruno 的核心数据是 .bru 文本文件、操作靠 CLI、协作靠 Git。每一件都正好是 AI 最擅长的那种事。&lt;/p&gt;
&lt;p&gt;抽象的说完了，说点具体的。&lt;/p&gt;
&lt;p&gt;我前两天就让 Claude Code 帮我加了一个测试，过程是这样的。&lt;/p&gt;
&lt;p&gt;我跟它说，「我刚加了一个搜索商品的接口，帮我在 bruno 集合里加个测试用例」。&lt;/p&gt;
&lt;p&gt;它干了三件事，全程没问我任何问题。&lt;/p&gt;
&lt;p&gt;第一步，读了我现有的一个 .bru 文件，就是为了搞清楚我用的格式。比如我习惯加哪些 test，断言风格是什么样的。&lt;/p&gt;
&lt;p&gt;第二步，照着这个格式写了一个新的 06-搜索商品.bru 文件，放在我集合的根目录里。请求方法、URL、query 参数、4 条断言，全都给我加上了。&lt;/p&gt;
&lt;p&gt;第三步，它直接执行 &lt;code&gt;bru run 06-搜索商品.bru --env production&lt;/code&gt;，亲眼看着 4 个测试全绿，然后才回我一句「写完了，4/4 通过」。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/006-f94498cf.png"&gt;&lt;/p&gt;
&lt;p&gt;整个过程我没碰 Bruno 的 GUI，没敲一个字符的代码，没解释什么是 .bru 格式。AI 自己读自己写自己跑自己验证。&lt;/p&gt;
&lt;p&gt;你可能会问，这个事换成 Postman 不行吗？&lt;/p&gt;
&lt;p&gt;我得先把话说清楚，Postman 不是没有 CLI。Newman 一直都在，Postman Collection 也是 JSON 文件，AI 理论上能读能写。这条路是通的。&lt;/p&gt;
&lt;p&gt;但你真去试一下就会发现，那条路上全是石头。&lt;/p&gt;
&lt;p&gt;Postman 的 Collection JSON 格式不是给人手写的。我做了个最朴素的对比，同一个 GET 请求加一条「状态码等于 200」的断言，写成 .bru 是 15 行，导出成 Postman Collection JSON 是 44 行。区别在哪？.bru 里 URL 就是一个字符串 &lt;code&gt;https://api.example.com/users&lt;/code&gt;，Postman JSON 里 URL 被拆成 &lt;code&gt;protocol&lt;/code&gt;、&lt;code&gt;host&lt;/code&gt; 数组、&lt;code&gt;path&lt;/code&gt; 数组三个字段。.bru 里测试代码就是普通 JS，Postman JSON 里测试代码以字符串数组的形式塞在 event 里，每一行 JS 都得加引号、转义、再 JSON 序列化一次。还有一堆 &lt;code&gt;_postman_id&lt;/code&gt;、&lt;code&gt;_exporter_id&lt;/code&gt;、&lt;code&gt;schema&lt;/code&gt; 这种内部元数据，跟你的业务接口毫无关系，但你不写就报错。&lt;/p&gt;
&lt;p&gt;让 AI 写 .bru，它跟写 markdown 一样轻松。让 AI 写 Postman JSON，它更像在翻译一份配置文件，token 烧得多，出错概率高，写完你自己 review 都费劲，git diff 出来三行业务变更夹在二十行格式噪音里。&lt;/p&gt;
&lt;p&gt;所以不是 AI 不能写 Postman 的测试，是 Postman 的格式从一开始就没打算让人手写，更没打算让 AI 直接读写。它假设你有一个 GUI 在中间帮你管理这些内部细节。AI 进来之后，那个假设就有点尴尬了。&lt;/p&gt;
&lt;p&gt;我说的还不是最炸的场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;真正让我觉得 Bruno + AI 是 1 + 1 大于 10 的，是调试场景。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我又试了一次，让 Claude Code 帮我加一个购物车接口的测试。它写完跑了一遍，挂了。&lt;/p&gt;
&lt;p&gt;错误信息是这样的。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;✕ 购物车应该有 totalPrice 字段 expected undefined to be a number✕ 购物车应该有 products 数组 expected undefined to be an array
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;它猜错了字段名。这种事很正常，毕竟它没真的去看接口返回。但接下来才有意思。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;它没有问我、没有让我提供文档、也没有放弃。它自己执行了一条 curl 命令，把购物车接口的真实响应拉了下来。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;curl -s https://dummyjson.com/carts/1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看到响应里字段是 &lt;code&gt;total&lt;/code&gt; 和 &lt;code&gt;products&lt;/code&gt;，不是 &lt;code&gt;totalPrice&lt;/code&gt; 和 &lt;code&gt;items&lt;/code&gt;。它马上把测试里的字段名改了，再跑一遍，全绿。&lt;/p&gt;
&lt;p&gt;整个调试过程，我做了什么？我什么也没做。我只是看着它一步一步跑完。&lt;/p&gt;
&lt;p&gt;这就是 Bruno + CLI + AI 的真正价值。你的 API 测试不再是一个孤立的、需要你手动维护的负担，而是变成了 AI 可以读、可以写、可以跑、可以调试的一种代码。&lt;/p&gt;
&lt;p&gt;它和你的源码、你的 Git 历史、你的 CI/CD、你的 AI agent，全都是一体的。&lt;/p&gt;
&lt;p&gt;写到这里我猜有人要拍桌子了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;「等等，国内程序员谁还用 Postman 啊？Apifox 它不香吗？」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;发，我们专门聊聊 Apifox。&lt;/p&gt;
&lt;p&gt;我必须先承认它真的很强。一体化平台，API 文档、调试、Mock、自动化测试、团队协作全在一个工具里搞定，相当于 Postman + Swagger + JMeter 三合一。界面是中文，文档是中文，国内团队几乎零学习成本。如果你的团队 20 人以上，需要权限管理、需要实时协作、需要统一的 API 文档管理，Apifox 是比 Bruno 更合适的选择。这点我不否认。&lt;/p&gt;
&lt;p&gt;而且 Apifox 不傻，它也在跟上 AI 浪潮。它有 apifox-cli，能在终端里跑测试场景。它甚至专门做了 Apifox MCP Server，可以让 Cursor 和 Claude Desktop 读取 Apifox 项目里的 API 文档，帮你写代码。&lt;/p&gt;
&lt;p&gt;那问题来了，Bruno 和 Apifox 在 AI 这件事上到底有啥不一样？&lt;/p&gt;
&lt;p&gt;我读了一圈 Apifox 的官方文档，发现一个挺有意思的差别。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apifox 接 AI 的方式，是让 AI 来连接 Apifox。Bruno 接 AI 的方式，是 Bruno 根本就是 AI 已经会读的格式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;具体说，Apifox 的 AI 工作流是这样的，你的测试数据躺在 Apifox 云端项目里，你装一个 Apifox MCP Server，配置 Cursor 去连这个 MCP，AI 通过 MCP 协议向 Apifox 服务发请求，把 API 文档拿下来，再帮你生成代码。&lt;/p&gt;
&lt;p&gt;Bruno 的 AI 工作流是这样的，你的测试就是项目目录里的 .bru 文件，AI 直接 cat、edit、bru run。完了。&lt;/p&gt;
&lt;p&gt;你看出区别了吗？Apifox 把 AI 当成一个外部工具来对接，所以需要 MCP 这一层中转。而 Bruno 根本不需要 MCP，因为它跟 AI 用的是同一种原生语言：&lt;strong&gt;文本文件加终端命令。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;还有一个更现实的差别。Apifox 的 MCP Server 现在的能力，是让 AI 读取 API 文档来生成代码，它并不能让 AI 直接去编辑你的测试用例。AI 在 Apifox 这边的角色基本上是只读的。&lt;/p&gt;
&lt;p&gt;而我前面演示的那两个场景，Claude Code 帮我写新测试、自动调试、修字段名，那是完整的读写跑改循环。AI 不光在读，还在写，还在跑，还在调试。这是因为 .bru 是普通文件，Edit 工具就能改，Bash 工具就能跑。它中间没有任何一层，所以也没有任何一层会卡住。&lt;/p&gt;
&lt;p&gt;这不是说 Apifox 不好，是两条不同的路。Apifox 选的是「我是平台，AI 来连我」，Bruno 选的是「我什么都不是，我就是文件」。前者适合企业级场景，后者适合代码即测试的开发者工作流。&lt;/p&gt;
&lt;p&gt;我自己是后者。我喜欢我所有的东西都能塞进 Git 仓库，喜欢 AI 直接读我硬盘上的文件，不喜欢任何账号、登录、云端中转。所以我选 Bruno。&lt;/p&gt;
&lt;p&gt;如果你的工作场景更接近企业级 SaaS 那一套，那 Apifox 完全没问题，甚至更合适。但如果你跟我一样，希望让 AI 一句话把接口测试搞定，那 Bruno 这条路确实更顺。&lt;/p&gt;
&lt;p&gt;聊完 Apifox 我们继续。顺便提一下 CI 集成，因为这块也很丝滑。Bruno CLI 支持输出 JUnit XML，GitHub Actions、GitLab CI、Jenkins 直接吃这个格式。我的工作流文件大概长这样。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;- name: 安装 Bruno CLI run: npm install -g @usebruno/cli- name: 跑接口测试 run: bru run --env production --reporter-junit junit.xml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;两步。你的 PR 一提交，所有接口测试自动跑一遍，挂了直接拒绝合并。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/007-56b53ed9.png"&gt;&lt;/p&gt;
&lt;p&gt;生成的测试执行也挺好看的&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/008-cc895180.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/009-90333e8c.png"&gt;&lt;/p&gt;
&lt;p&gt;整套东西免费、开源、本地、Git 友好、AI 友好，不用登录、不用付钱、不用担心数据上云。&lt;/p&gt;
&lt;p&gt;安装很简单。桌面版，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;brew install bruno
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt;```&lt;/span&gt;bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;CLI，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt;```&lt;/span&gt;bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;npm i -g @usebruno/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用了一周之后我把 Postman 卸了。&lt;/p&gt;
&lt;p&gt;不是它不好，是我不再需要一个登录才能用、集合存在别人云上、免费版各种限制、AI 完全帮不上忙的接口测试工具了。&lt;/p&gt;
&lt;p&gt;我只需要一个文件夹、几个 .bru 文件、一条终端命令，和一个能读懂这一切的 AI。&lt;/p&gt;
&lt;p&gt;有时候工具的进步不是功能越加越多，而是把不该有的东西去掉。Bruno 去掉了登录、去掉了云端、去掉了 GUI 锁定，剩下的全是程序员真正需要的东西。&lt;/p&gt;
&lt;p&gt;而当你把这些不需要的东西去掉之后，你会发现一个意外的副作用，AI 进来了。&lt;/p&gt;
&lt;p&gt;好了就说这么多。&lt;/p&gt;
&lt;p&gt;如果你也受够了 Postman，试试 Bruno。说不定你也会像我一样，用完就回不去了。&lt;/p&gt;
&lt;p&gt;ps:我写的 demo 在这里，你可以拉下来试一下，可以 run 的 ：https://github.com/xiaobox/bruno-demo&lt;/p&gt;</description></item><item><title>OpenClaw 爆火背后：它不是聊天机器人，而是一套真正会做事的 AI 系统</title><link>https://xiaobox.github.io/p/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/</link><pubDate>Wed, 11 Mar 2026 10:14:30 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/cover.jpg" alt="Featured image of post OpenClaw 爆火背后：它不是聊天机器人，而是一套真正会做事的 AI 系统" /&gt;&lt;p&gt;如果你最近在 GitHub 上关注过 AI Agent 领域，大概率已经看到过 OpenClaw。到 2026 年 3 月 10 日，它的 GitHub 仓库已经来到约 297k stars，超过了 React 的约 244k 和 Linux 的约 222k。更重要的不是数字本身，而是它火起来的方式：它不是靠一个漂亮网页，也不是靠一个“会聊天的套壳”，而是靠一整套&lt;strong&gt;把大模型接入真实消息渠道、真实设备、真实浏览器、真实文件系统的系统架构&lt;/strong&gt;，硬生生把“AI 助手”做成了一个长期在线的工程系统。&lt;/p&gt;
&lt;p&gt;但如果你只把 OpenClaw 理解成“接了很多 IM 的机器人”，你会完全错过它最有价值的部分。OpenClaw 官方 README 写得很直白：“&lt;strong&gt;The Gateway is just the control plane — the product is the assistant.&lt;/strong&gt;” 这句话几乎就是读懂整个项目的钥匙。它的重点从来不是“有多少入口”，而是：&lt;strong&gt;有没有一个统一控制面，把消息、状态、路由、模型、工具、节点、权限和安全边界收在一起&lt;/strong&gt;。 README、架构文档和 Vision 文档都在强调同一件事：OpenClaw 想做的是“真正会做事的 AI”，运行在你的设备、你的渠道、你的规则之内。&lt;/p&gt;
&lt;p&gt;这篇文章，我想尽量回答七个问题：&lt;/p&gt;
&lt;p&gt;1.它到底是什么？&lt;/p&gt;
&lt;p&gt;2.它为什么会采用现在这套架构？&lt;/p&gt;
&lt;p&gt;3.Gateway 到底在系统里扮演什么角色？&lt;/p&gt;
&lt;p&gt;4.Agent 是怎么运行起来的？&lt;/p&gt;
&lt;p&gt;5.Memory、Workspace、Session 为什么是它的关键设计？&lt;/p&gt;
&lt;p&gt;6.多 Agent、节点、工具体系是怎么拼到一起的？&lt;/p&gt;
&lt;p&gt;7.以及最后，为什么它值得被看作下一代 AI 助手的典型系统样本。&lt;/p&gt;
&lt;p&gt;在回答这些问题之前，我不得不说，现在龙虾有些过热了，对于想 “卖铲子” 的公司当然觉得这是好事，于是他们推波助澜，但对于专业人士不能人云亦云。openClaw 有它优秀的一面，也有被炒作夸大的一面，应该客观地看。&lt;/p&gt;
&lt;h2 id="一openclaw-的本质不是聊天机器人而是个人-ai-助手控制面"&gt;&lt;a href="#%e4%b8%80openclaw-%e7%9a%84%e6%9c%ac%e8%b4%a8%e4%b8%8d%e6%98%af%e8%81%8a%e5%a4%a9%e6%9c%ba%e5%99%a8%e4%ba%ba%e8%80%8c%e6%98%af%e4%b8%aa%e4%ba%ba-ai-%e5%8a%a9%e6%89%8b%e6%8e%a7%e5%88%b6%e9%9d%a2" class="header-anchor"&gt;&lt;/a&gt;一、OpenClaw 的本质，不是聊天机器人，而是“个人 AI 助手控制面”
&lt;/h2&gt;&lt;p&gt;一句话定义 OpenClaw，我会这样说：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenClaw = 一个以 Gateway 为中心的个人 AI 助手控制平面，下面挂着嵌入式 agent runtime、会话系统、工具系统、消息渠道、节点设备和安全边界。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个定义不是我自己拔高出来的，而是官方文档本身就在往这个方向写。&lt;/p&gt;
&lt;p&gt;●README 说它是“你运行在自己设备上的 personal AI assistant”；&lt;/p&gt;
&lt;p&gt;●架构文档说它是一个 single long-lived Gateway，拥有所有 messaging surfaces；&lt;/p&gt;
&lt;p&gt;●Vision 文档则把它描述为“the AI that actually does things”，运行在你的设备、你的渠道、你的规则里。&lt;/p&gt;
&lt;p&gt;把这些信息放在一起看，你会发现 OpenClaw 的设计起点根本不是一个“聊天 UI”，而是一个&lt;strong&gt;长期在线、可被多入口触发、可调用工具、可连接设备、可持续维护状态的 AI 系统。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/001-edb50d9b.png"&gt;&lt;/p&gt;
&lt;p&gt;这也是为什么我认为 OpenClaw 更接近“控制面”而不是“应用层”。在很多 AI 产品里，用户打开网页，输入问题，后端调一下模型，返回一段文本，交互就结束了。OpenClaw 则完全不是这种形态。它默认有一个长期运行的 Gateway 进程，消息渠道接到这个 Gateway，上层的 CLI、Control UI、WebChat 接这个 Gateway，macOS/iOS/Android/headless 节点也接这个 Gateway，甚至定时任务、exec approvals、pairing 和 health 事件都围绕 Gateway 展开。也就是说，&lt;strong&gt;Gateway 不是一个消息转发器，而是系统中枢。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/002-4ba00a5d.png"&gt;&lt;/p&gt;
&lt;h2 id="二gateway-为什么是-openclaw-最关键的设计"&gt;&lt;a href="#%e4%ba%8cgateway-%e4%b8%ba%e4%bb%80%e4%b9%88%e6%98%af-openclaw-%e6%9c%80%e5%85%b3%e9%94%ae%e7%9a%84%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;二、Gateway 为什么是 OpenClaw 最关键的设计
&lt;/h2&gt;&lt;p&gt;OpenClaw 官方架构文档里最重要的一句话，是它把 Gateway 明确成 &lt;strong&gt;single control plane&lt;/strong&gt;。一个长期运行的 Gateway 拥有所有 messaging surfaces；control-plane clients 通过 WebSocket 连进来；nodes 也通过 WebSocket 连进来，但会声明自己是 &lt;code&gt;role: node&lt;/code&gt;；Canvas host 也由 Gateway 的 HTTP server 提供，而且默认和 Gateway 共用 127.0.0.1:18789 这个端口。&lt;/p&gt;
&lt;p&gt;这意味着什么？意味着 OpenClaw 的系统设计不是“每个端各做一套逻辑”，而是“先做一个统一控制面，再让所有端接入它”。这在工程上有三个非常大的好处。&lt;/p&gt;
&lt;p&gt;第一，&lt;strong&gt;状态是统一的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;会话在哪里维护？在 Gateway。&lt;/p&gt;
&lt;p&gt;路由在哪里决策？在 Gateway。&lt;/p&gt;
&lt;p&gt;设备配对、认证 token、事件广播、健康状态、cron、工具审批在哪里收敛？还是在 Gateway。&lt;/p&gt;
&lt;p&gt;这让系统不会因为前端入口变多而出现多套状态、多个事实来源。&lt;/p&gt;
&lt;p&gt;第二，&lt;strong&gt;协议是统一的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Gateway protocol 文档明确写了：OpenClaw 不是“随便传一段 JSON”，而是有明确握手流程和版本约束的 WebSocket 协议。服务端先发 &lt;code&gt;connect.challenge&lt;/code&gt;，客户端再带着 &lt;code&gt;device identity&lt;/code&gt;、&lt;code&gt;role&lt;/code&gt;、&lt;code&gt;scopes&lt;/code&gt;、&lt;code&gt;caps&lt;/code&gt;、&lt;code&gt;auth&lt;/code&gt;、签名等参数发起 connect，通过后才返回 hello-ok。协议版本有 minProtocol/maxProtocol 协商，协议 schema 由 TypeBox 定义，再生成 JSON Schema 以及 Swift model。对一个跨 CLI、网页、桌面、移动端、节点设备的系统来说，这种 typed protocol 的价值非常高。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/003-1f7f4ec8.png"&gt;&lt;/p&gt;
&lt;p&gt;第三，&lt;strong&gt;能力是统一暴露的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;比如 Control UI 不是一个独立后端，而是 Gateway 在同一端口上提供的浏览器管理界面；WebChat 直接连 Gateway WebSocket；nodes 也不是第二套服务，而是带 &lt;code&gt;role:node&lt;/code&gt; 的外围设备。也就是说，OpenClaw 并不是“一个 App + 一堆外挂”，而是“一个控制面 + 多个表面”。&lt;/p&gt;
&lt;p&gt;很多人第一次看 OpenClaw，会把注意力放在“它居然支持这么多渠道”。但真正懂架构的人，会先看 Gateway。因为能不能把多个入口、多种设备、多条事件流、多种工具执行方式，全都压到一个长期运行的控制面里，决定了它到底是“一个功能”还是“一个系统”。OpenClaw 的做法很明确：&lt;strong&gt;先有控制面，再有助手&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="三它最强的抽象不是对话框而是-agentsession-和-route"&gt;&lt;a href="#%e4%b8%89%e5%ae%83%e6%9c%80%e5%bc%ba%e7%9a%84%e6%8a%bd%e8%b1%a1%e4%b8%8d%e6%98%af%e5%af%b9%e8%af%9d%e6%a1%86%e8%80%8c%e6%98%af-agentsession-%e5%92%8c-route" class="header-anchor"&gt;&lt;/a&gt;三、它最强的抽象，不是对话框，而是 Agent、Session 和 Route
&lt;/h2&gt;&lt;p&gt;很多 AI 产品最基础的抽象单位是“聊天窗口”。OpenClaw 不是。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/004-717935e1.png"&gt;&lt;/p&gt;
&lt;p&gt;OpenClaw 的真正基础模型，是：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;谁来回复（Agent）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;回复落在哪段连续上下文里（Session）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;一条消息应该被路由到哪个 agent 和哪个 session（Route）&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="agent一颗完整隔离的大脑"&gt;&lt;a href="#agent%e4%b8%80%e9%a2%97%e5%ae%8c%e6%95%b4%e9%9a%94%e7%a6%bb%e7%9a%84%e5%a4%a7%e8%84%91" class="header-anchor"&gt;&lt;/a&gt;Agent：一颗完整隔离的大脑
&lt;/h3&gt;&lt;p&gt;Multi-Agent 文档里写得很清楚：一个 agent 是一个 &lt;strong&gt;fully scoped brain&lt;/strong&gt;，拥有&lt;/p&gt;
&lt;p&gt;●自己的 workspace&lt;/p&gt;
&lt;p&gt;●自己的 agentDir&lt;/p&gt;
&lt;p&gt;●自己的 auth profiles&lt;/p&gt;
&lt;p&gt;●自己的 session store&lt;/p&gt;
&lt;p&gt;它的文件、人格、配置、认证信息和会话历史都是围绕这个 agent 单独组织的。默认路径也很清晰：&lt;/p&gt;
&lt;p&gt;●workspace 在 &lt;code&gt;~/.openclaw/workspace&lt;/code&gt; 或 &lt;code&gt;workspace-&amp;lt;agentId&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;●session 存在 &lt;code&gt;~/.openclaw/agents/&amp;lt;agentId&amp;gt;/sessions&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;●auth profile 在 &lt;code&gt;~/.openclaw/agents/&amp;lt;agentId&amp;gt;/agent/auth-profiles.json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这件事非常重要。因为这说明 OpenClaw 的多 Agent，不是“在一个上下文里换不同 system prompt 假装多人格”，而是&lt;strong&gt;真的把状态、身份、凭证和工作目录做成了隔离单元&lt;/strong&gt;。但要注意，这种独立是为了让系统跑得更有条理，属于“防君子不防小人”的内部隔离。官方的意思很明确：同一个网关（Gateway）里的 Agent 默认都是“自己人”，不能把互不信任、甚至带有敌意的任务强行塞进同一个网关里，它并没有提供那种级别的安全防御。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/005-b96b6812.png"&gt;&lt;/p&gt;
&lt;h3 id="session上下文连续性的主键"&gt;&lt;a href="#session%e4%b8%8a%e4%b8%8b%e6%96%87%e8%bf%9e%e7%bb%ad%e6%80%a7%e7%9a%84%e4%b8%bb%e9%94%ae" class="header-anchor"&gt;&lt;/a&gt;Session：上下文连续性的主键
&lt;/h3&gt;&lt;p&gt;Session 文档里有一句特别关键的话：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;OpenClaw treats one direct-chat session per agent as primary.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这句话可以理解成：&lt;strong&gt;对每一个 agent，OpenClaw 都认为它有一个“主私聊会话”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenClaw 默认会将一个 Agent 接收到的所有私聊（Direct Message, DM）都汇聚到一个主会话里（即 &lt;code&gt;agent:&amp;lt;agentId&amp;gt;:&amp;lt;mainKey&amp;gt;&lt;/code&gt;）。对于群聊、频道或特定的话题（Thread），则会自动拆分独立处理。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;对于 direct chat， agent 有一个规范意义上的主会话；默认所有 DM 都往这里归并，以保证连续性。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;假设你有一个 agent 叫 main。默认情况下：&lt;/p&gt;
&lt;p&gt;●你在 Web UI 私聊它一次&lt;/p&gt;
&lt;p&gt;●之后又在 CLI 私聊它&lt;/p&gt;
&lt;p&gt;●再后来在手机端私聊它&lt;/p&gt;
&lt;p&gt;如果这些都被识别为 direct chat，而且你没有改 session.dmScope，那么这些私聊会折叠进同一个主 session,这样做的好处是：&lt;strong&gt;agent 会把这些私聊视为同一条连续对话，而不是三个彼此割裂的会话&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;默认的主会话机制在单用户场景下很完美，但在&lt;strong&gt;多用户场景&lt;/strong&gt;下就是一个巨大的安全漏洞。&lt;/p&gt;
&lt;p&gt;如果 Alice 和 Bob 都去私聊同一个 Agent，在默认配置下，他们实际上是在向同一个“上下文沙箱”里写入数据。这就好比两个人共用一个日记本：&lt;/p&gt;
&lt;p&gt;●Alice 刚和 Agent 聊完财务密码。&lt;/p&gt;
&lt;p&gt;●Bob 接着去问 Agent“我们刚才聊了什么？”&lt;/p&gt;
&lt;p&gt;●Agent 就会直接把 Alice 的密码复述给 Bob，造成严重的信息泄露。&lt;/p&gt;
&lt;p&gt;为了应对多用户场景，OpenClaw 提供了 &lt;code&gt;session.dmScope&lt;/code&gt; 配置，允许你在架构层面把私聊的上下文切分成更安全的细粒度：&lt;/p&gt;
&lt;p&gt;●按发信人隔离（&lt;code&gt;per-peer&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;●按频道+发信人隔离。(&lt;code&gt;per-channel-peer&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;●按账号+频道+发信人隔离(&lt;code&gt;per-account-channel-peer&lt;/code&gt;)。&lt;/p&gt;
&lt;p&gt;⚠️ 如果你在开发面向多用户的 AI Agent，&lt;strong&gt;绝对不能盲目使用默认的私聊配置&lt;/strong&gt;。必须根据业务需求，通过调整 &lt;code&gt;dmScope&lt;/code&gt; 将用户的对话状态彻底隔离开，防止你的 Agent 变成一个没有隐私边界的“大喇叭”。&lt;/p&gt;
&lt;h3 id="route决定消息进入哪颗大脑"&gt;&lt;a href="#route%e5%86%b3%e5%ae%9a%e6%b6%88%e6%81%af%e8%bf%9b%e5%85%a5%e5%93%aa%e9%a2%97%e5%a4%a7%e8%84%91" class="header-anchor"&gt;&lt;/a&gt;Route：决定消息进入哪颗大脑
&lt;/h3&gt;&lt;p&gt;Channel Routing 文档把消息路由规则写得非常明确，一条消息发过来，绝对不是“哪个 Agent 闲着就扔给谁”，而是像网关（Gateway）匹配规则一样，必须严格按照优先级一层层往下筛，直到找到唯一确定的接收者。&lt;/p&gt;
&lt;p&gt;我们可以用**“公司收发室分拣快递”**来打个比方，看一下这 5 层降级（Fallback）匹配规则：&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;精准单聊 (Exact peer match)&lt;/strong&gt;：快递单上写着“直接交到张三本人手里”。&lt;/p&gt;
&lt;p&gt;○明确的点对点直接交互，优先级最高。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;跟帖/线程继承 (Parent peer match)&lt;/strong&gt;：快递单没写名字，但备注了“这是昨天那个加急件的补充材料”。收发室一查昨天是李四负责的，直接给李四。&lt;/p&gt;
&lt;p&gt;○识别 Thread 或上下文，让同一个 Agent 连贯处理同一个话题。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;平台级群组与角色 (Discord guild+roles / Slack team)&lt;/strong&gt;：快递写着“给财务部经理”或“给核心开发组”。&lt;/p&gt;
&lt;p&gt;○根据外部平台（如 Discord/Slack）的特定权限组或大团队来分配对应的 Agent。&lt;/p&gt;
&lt;p&gt;4.&lt;strong&gt;账号与频道 (Account / Channel)&lt;/strong&gt;：快递写着“送到 3 楼会议室”或“交给官方客服号”。&lt;/p&gt;
&lt;p&gt;○匹配特定的聊天频道或绑定的公共账号。&lt;/p&gt;
&lt;p&gt;5.&lt;strong&gt;默认兜底 (Default agent)&lt;/strong&gt;：啥也没写清楚的无主件，统统扔给“前台总机”处理。&lt;/p&gt;
&lt;p&gt;○如果上面所有条件都未命中，最后由默认的 Agent 统一接管。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/006-a89a97a1.png"&gt;&lt;/p&gt;
&lt;p&gt;这意味着 OpenClaw 的“消息归属”不是模糊的。一条消息不是“谁在线谁回”，而是经过一套确定性规则，先判定该由哪个 agent 接管，再决定落到哪个 session 里。&lt;/p&gt;
&lt;p&gt;所以 OpenClaw 能天然处理这些现实世界场景：&lt;/p&gt;
&lt;p&gt;●同一个 Gateway 托管多个 agent；&lt;/p&gt;
&lt;p&gt;●一个 Telegram 群给 work agent；&lt;/p&gt;
&lt;p&gt;●一个 WhatsApp 家庭群给 family agent；&lt;/p&gt;
&lt;p&gt;●一个 Slack team 给 support agent；&lt;/p&gt;
&lt;h2 id="四agent-不是外挂调用而是嵌入式运行时"&gt;&lt;a href="#%e5%9b%9bagent-%e4%b8%8d%e6%98%af%e5%a4%96%e6%8c%82%e8%b0%83%e7%94%a8%e8%80%8c%e6%98%af%e5%b5%8c%e5%85%a5%e5%bc%8f%e8%bf%90%e8%a1%8c%e6%97%b6" class="header-anchor"&gt;&lt;/a&gt;四、Agent 不是外挂调用，而是嵌入式运行时
&lt;/h2&gt;&lt;p&gt;很多人对 OpenClaw 最大的误解，是把它当成了一个简单的“任务调度员”——以为它只是在收到消息时，拉起一个外部的子进程（Subprocess）去跑一下，或者通过接口（RPC）远程调一下就完事了。&lt;/p&gt;
&lt;p&gt;但架构文档明确指出：OpenClaw 是将 Agent 运行时“原生内嵌”到自己的网关里的。 它不是把 Agent 当作一个不可控的外部黑盒，而是直接在内部实例化 Agent 的核心会话（AgentSession）。&lt;/p&gt;
&lt;p&gt;Pi Integration Architecture 文档写得非常明确：OpenClaw &lt;strong&gt;不是&lt;/strong&gt; 把 pi 作为 subprocess 或 RPC mode 的外部黑盒去调用，而是直接导入并实例化 pi 的 AgentSession，通过 createAgentSession() 把 agent runtime 嵌入 到自己的消息网关架构里。&lt;/p&gt;
&lt;p&gt;这种“深度内嵌”的架构设计，直接赋予了系统 6 大核心优势：&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;全局生命周期掌控&lt;/strong&gt;： 从对话的创建、挂起、恢复到销毁，网关层拥有绝对的控制权。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;动态能力扩展&lt;/strong&gt;： 可以在运行时，随时把自定义的外部工具“塞”给 Agent 使用。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;“看人下菜碟”的人设&lt;/strong&gt;： 能够根据消息来源（不同的平台渠道或上下文），动态切换 Agent 的系统提示词。&lt;/p&gt;
&lt;p&gt;4.&lt;strong&gt;强悍的记忆管理&lt;/strong&gt;： 不仅能持久化保存对话，还支持高级的“记忆压缩（Compaction）”防止上下文爆满，甚至支持像 Git 一样对对话“开分支（Branching）”。&lt;/p&gt;
&lt;p&gt;5.&lt;strong&gt;智能凭证轮询&lt;/strong&gt;： 在多个账号或 API Key 之间自动无缝切换，轻松应对并发和限流问题。&lt;/p&gt;
&lt;p&gt;6.&lt;strong&gt;模型厂商解绑&lt;/strong&gt;： 底层的大模型想换就换，完全不受单一服务商（如 OpenAI、Anthropic）的绑架。&lt;/p&gt;
&lt;p&gt;简单来说，OpenClaw 走的是“直接收编”的路线，它把 Agent 的核心大脑直接“拔”过来，原生种植在了自己的神经中枢里。这就好比你不再是打电话咨询外部专家，而是直接把这位专家招进了自家的核心指挥部。正因为“人”彻底成了内部员工，你才能拥有上帝视角般的掌控力：你可以全面接管他的作息安排（会话生命周期），随时往他手里塞各种定制兵器（动态注入工具），根据不同场合要求他扮演不同的角色（按渠道切换提示词），像操作代码仓库一样去整理甚至分叉他的记忆（支持压缩与分支的持久化），甚至连他背后的“脑力供应商”（随时无缝切换各家大模型）和权限账号，都能在底层悄无声息地替他自动轮换。说白了，OpenClaw 不是在和 Agent “跨部门合作”，而是直接把 Agent 融为了自己身体的一部分。换句话说，&lt;strong&gt;OpenClaw 不是“在用一个 agent”，而是“在拥有一个 agent runtime，并把它纳入自己的控制面”。这也是它跟很多“外接 Agent SDK 的应用层产品”最大的差别之一。别人只是调用，OpenClaw 是接管&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/007-b6154044.png"&gt;&lt;/p&gt;
&lt;h2 id="五agent-loop一条消息的真实旅程"&gt;&lt;a href="#%e4%ba%94agent-loop%e4%b8%80%e6%9d%a1%e6%b6%88%e6%81%af%e7%9a%84%e7%9c%9f%e5%ae%9e%e6%97%85%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;五、Agent Loop：一条消息的&amp;quot;真实旅程&amp;quot;
&lt;/h2&gt;&lt;p&gt;前面我们讲了 Gateway 如何把消息路由到正确的 Agent。现在让我们跟随一条消息，看看它进入 OpenClaw 后，到底经历了什么。&lt;/p&gt;
&lt;h3 id="不是一次请求而是一个完整生命周期"&gt;&lt;a href="#%e4%b8%8d%e6%98%af%e4%b8%80%e6%ac%a1%e8%af%b7%e6%b1%82%e8%80%8c%e6%98%af%e4%b8%80%e4%b8%aa%e5%ae%8c%e6%95%b4%e7%94%9f%e5%91%bd%e5%91%a8%e6%9c%9f" class="header-anchor"&gt;&lt;/a&gt;不是&amp;quot;一次请求&amp;quot;，而是一个完整生命周期
&lt;/h3&gt;&lt;p&gt;如果你习惯了网页聊天框的&amp;quot;发消息→等回复&amp;quot;模式，OpenClaw 的处理方式会让你有点意外。&lt;/p&gt;
&lt;p&gt;传统模式：&lt;/p&gt;
&lt;p&gt;⚡ 代码片段&lt;code&gt;用户发消息 → 后端调用模型 → 返回文本 → 结束&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;OpenClaw 模式：&lt;/p&gt;
&lt;p&gt;⚡ 代码片段&lt;code&gt;用户发消息 → 分配 runId → 解析 session → 装配上下文 → 运行 agent → 流式返回事件 → 持久化 session → 结束&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这一条链路，实际上就是你给 OpenClaw 发一句话之后，系统内部真实发生的事情。它不是“一次 HTTP 请求”，而是一个完整的运行生命周期。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/008-b98e49e5.png"&gt;&lt;/p&gt;
&lt;p&gt;OpenClaw 把你的消息视为一个&lt;strong&gt;进程而非请求&lt;/strong&gt;。它会给这个进程分配ID、监控生命周期、管理并发、持久化状态。&lt;/p&gt;
&lt;h3 id="并发控制为什么同一聊天窗口的消息要排队"&gt;&lt;a href="#%e5%b9%b6%e5%8f%91%e6%8e%a7%e5%88%b6%e4%b8%ba%e4%bb%80%e4%b9%88%e5%90%8c%e4%b8%80%e8%81%8a%e5%a4%a9%e7%aa%97%e5%8f%a3%e7%9a%84%e6%b6%88%e6%81%af%e8%a6%81%e6%8e%92%e9%98%9f" class="header-anchor"&gt;&lt;/a&gt;并发控制：为什么同一聊天窗口的消息要&amp;quot;排队&amp;quot;？
&lt;/h3&gt;&lt;p&gt;想象一下这个场景：你在 Telegram 连续发了三条消息：&lt;/p&gt;
&lt;p&gt;●“帮我查一下明天天气”&lt;/p&gt;
&lt;p&gt;●“顺便看看日程”&lt;/p&gt;
&lt;p&gt;●“把第一封邮件标为已读”&lt;/p&gt;
&lt;p&gt;如果这三条消息并发执行，会发生什么？&lt;/p&gt;
&lt;p&gt;●Agent 可能先处理了邮件，再处理天气&lt;/p&gt;
&lt;p&gt;●Session 历史会乱序写入&lt;/p&gt;
&lt;p&gt;●工具调用可能互相冲突&lt;/p&gt;
&lt;p&gt;OpenClaw 的解决方案很简单：每个 session 串行化执行。这不是性能问题，而是状态一致性问题。长期在线的助手，必须保证&amp;quot;记忆&amp;quot;不会被乱序操作搞乱。是&lt;strong&gt;防止工具竞争和状态污染&lt;/strong&gt;的工程必要选择。&lt;/p&gt;
&lt;h3 id="流式事件你看到的不是打字动画而是真实的工作过程"&gt;&lt;a href="#%e6%b5%81%e5%bc%8f%e4%ba%8b%e4%bb%b6%e4%bd%a0%e7%9c%8b%e5%88%b0%e7%9a%84%e4%b8%8d%e6%98%af%e6%89%93%e5%ad%97%e5%8a%a8%e7%94%bb%e8%80%8c%e6%98%af%e7%9c%9f%e5%ae%9e%e7%9a%84%e5%b7%a5%e4%bd%9c%e8%bf%87%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;流式事件：你看到的不是&amp;quot;打字动画&amp;quot;，而是真实的工作过程
&lt;/h3&gt;&lt;p&gt;OpenClaw 的流式输出，不是简单的&amp;quot;逐字显示&amp;quot;，而是三种事件流：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/009-19119b2c.png"&gt;&lt;/p&gt;
&lt;p&gt;为什么要这样设计？ 因为这让用户能真正&amp;quot;看到 AI 在工作&amp;quot;。不是动画，不是假进度条，而是系统内部真实发生的事件被推送到前端。它的体验更像一个&amp;quot;正在办公的助手&amp;quot;而非&amp;quot;死寂的输入框&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/010-67135bf9.png"&gt;&lt;/p&gt;
&lt;h2 id="六真正让它像一个人的不是模型而是-workspacesystem-prompt-和-memory"&gt;&lt;a href="#%e5%85%ad%e7%9c%9f%e6%ad%a3%e8%ae%a9%e5%ae%83%e5%83%8f%e4%b8%80%e4%b8%aa%e4%ba%ba%e7%9a%84%e4%b8%8d%e6%98%af%e6%a8%a1%e5%9e%8b%e8%80%8c%e6%98%af-workspacesystem-prompt-%e5%92%8c-memory" class="header-anchor"&gt;&lt;/a&gt;六、真正让它“像一个人”的，不是模型，而是 Workspace、System Prompt 和 Memory
&lt;/h2&gt;&lt;p&gt;很多人体验 OpenClaw 后会有一种明显感觉：它比普通网页聊天更像一个“持续存在的助手”。这种感觉，核心不是来自模型，而是来自它对&lt;strong&gt;工作区、提示词和记忆&lt;/strong&gt;的系统化设计&lt;/p&gt;
&lt;h3 id="workspaceai-的家而不是一个临时目录"&gt;&lt;a href="#workspaceai-%e7%9a%84%e5%ae%b6%e8%80%8c%e4%b8%8d%e6%98%af%e4%b8%80%e4%b8%aa%e4%b8%b4%e6%97%b6%e7%9b%ae%e5%bd%95" class="header-anchor"&gt;&lt;/a&gt;Workspace：AI 的家，而不是一个临时目录
&lt;/h3&gt;&lt;p&gt;简单说，Workspace 就是 AI 的&amp;quot;家&amp;quot;：&lt;/p&gt;
&lt;p&gt;●它有固定的位置（~/.openclaw/workspace/）&lt;/p&gt;
&lt;p&gt;●它有固定的文件结构&lt;/p&gt;
&lt;p&gt;●它是 AI 长期工作的地方，不是临时落脚点&lt;/p&gt;
&lt;p&gt;OpenClaw 在 Workspace 里约定了一整套&amp;quot;说明书文件&amp;quot;：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/011-71f8b973.png"&gt;&lt;/p&gt;
&lt;p&gt;这个设计特别妙。因为它把很多系统会偷偷塞进 prompt 模板或数据库里的东西，变成了&lt;strong&gt;用户可见、可读、可改、可备份&lt;/strong&gt;的文件系统资产。你不是在“配一个人设”，而是在维护一个 AI 的长期工作环境。&lt;/p&gt;
&lt;p&gt;这里有一个非常重要的提醒：&lt;strong&gt;workspace 是默认工作目录，但不是硬沙箱（hard sandbox）；相对路径默认在 workspace 内解析，但绝对路径仍可能访问宿主机其它位置，除非你开启 sandbox。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="system-prompt每次运行都在编译上下文"&gt;&lt;a href="#system-prompt%e6%af%8f%e6%ac%a1%e8%bf%90%e8%a1%8c%e9%83%bd%e5%9c%a8%e7%bc%96%e8%af%91%e4%b8%8a%e4%b8%8b%e6%96%87" class="header-anchor"&gt;&lt;/a&gt;System Prompt：每次运行都在&amp;quot;编译上下文&amp;quot;
&lt;/h3&gt;&lt;p&gt;OpenClaw 不是把用户的问题直接扔给模型，而是每次都重新构建一份完整的上下文：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ 代码片段System Prompt 结构：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;├── Tooling（可用工具列表）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;├── Safety（安全规则）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;├── Skills（技能列表）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;├── Workspace Context（工作区文件）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;├── Documentation（相关文档）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;├── Current Date &amp;amp; Time（当前时间）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;└── Runtime（运行环境信息）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Context 文档还补充了细节：默认会把 AGENTS.md、SOUL.md、TOOLS.md、IDENTITY.md、USER.md、HEARTBEAT.md、BOOTSTRAP.md 等文件作为 Project Context 注入系统提示；技能本身只会注入“技能列表和描述”，真正的 SKILL.md 需要模型按需读取。&lt;/p&gt;
&lt;p&gt;类比：&lt;/p&gt;
&lt;p&gt;●传统聊天：像&amp;quot;临时起意打电话&amp;quot;&lt;/p&gt;
&lt;p&gt;●OpenClaw：像&amp;quot;开会前先发会议议程和背景资料&amp;quot;&lt;/p&gt;
&lt;h3 id="memory真正写到磁盘才算记住"&gt;&lt;a href="#memory%e7%9c%9f%e6%ad%a3%e5%86%99%e5%88%b0%e7%a3%81%e7%9b%98%e6%89%8d%e7%ae%97%e8%ae%b0%e4%bd%8f" class="header-anchor"&gt;&lt;/a&gt;Memory：真正写到磁盘，才算记住
&lt;/h3&gt;&lt;p&gt;Memory 文档里我最喜欢的一句话是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;The files are the source of truth; the model only “remembers” what gets written to disk.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;OpenClaw 默认的记忆结构非常简单，但非常工程化:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ 代码片段workspace/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; ├── memory/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; │ ├── 2026-03-10.md ← 今天的日志
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; │ ├── 2026-03-09.md ← 昨天的日志
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; │ └── ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; └── MEMORY.md ← 长期、精炼的永久记忆
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;两种记忆的区别：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/012-105dfd4a.png"&gt;&lt;/p&gt;
&lt;h3 id="检索机制不是只有文件也不是只有向量"&gt;&lt;a href="#%e6%a3%80%e7%b4%a2%e6%9c%ba%e5%88%b6%e4%b8%8d%e6%98%af%e5%8f%aa%e6%9c%89%e6%96%87%e4%bb%b6%e4%b9%9f%e4%b8%8d%e6%98%af%e5%8f%aa%e6%9c%89%e5%90%91%e9%87%8f" class="header-anchor"&gt;&lt;/a&gt;检索机制：不是&amp;quot;只有文件&amp;quot;，也不是&amp;quot;只有向量&amp;quot;
&lt;/h3&gt;&lt;p&gt;OpenClaw 使用&lt;strong&gt;混合检索&lt;/strong&gt;,它明确暴露了两个 agent-facing tools：&lt;/p&gt;
&lt;p&gt;●memory_search 负责检索&lt;/p&gt;
&lt;p&gt;●memory_get 负责精确读取某个 Markdown 文件或行段&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ 代码片段用户问&amp;#34;我上次出差去哪了？&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; BM25 关键词检索 ← 精确匹配&amp;#34;出差&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; +
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; 向量语义检索 ← 理解&amp;#34;去哪了&amp;#34;是问目的地
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; MMR 重排序 ← 去重、多样化
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt; 返回最相关的几条记忆片段
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/013-94c69879.png"&gt;&lt;/p&gt;
&lt;h3 id="记忆刷新在遗忘前先存档"&gt;&lt;a href="#%e8%ae%b0%e5%bf%86%e5%88%b7%e6%96%b0%e5%9c%a8%e9%81%97%e5%bf%98%e5%89%8d%e5%85%88%e5%ad%98%e6%a1%a3" class="header-anchor"&gt;&lt;/a&gt;记忆刷新：在&amp;quot;遗忘&amp;quot;前先&amp;quot;存档&amp;quot;
&lt;/h3&gt;&lt;p&gt;OpenClaw 有一个很巧妙的设计：&lt;code&gt;pre-compaction memory flush&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;当 session 接近上下文上限（比如对话太长，快塞不进模型窗口了），OpenClaw 会：&lt;/p&gt;
&lt;p&gt;●触发一次&amp;quot;静默回合&amp;quot;（用户看不到）&lt;/p&gt;
&lt;p&gt;●提醒模型：“把值得记住的信息写入记忆文件”&lt;/p&gt;
&lt;p&gt;●然后再压缩上下文&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/014-d04133a7.png"&gt;&lt;/p&gt;
&lt;h2 id="七工具体系分层设计不是堆砌功能"&gt;&lt;a href="#%e4%b8%83%e5%b7%a5%e5%85%b7%e4%bd%93%e7%b3%bb%e5%88%86%e5%b1%82%e8%ae%be%e8%ae%a1%e4%b8%8d%e6%98%af%e5%a0%86%e7%a0%8c%e5%8a%9f%e8%83%bd" class="header-anchor"&gt;&lt;/a&gt;七、工具体系：分层设计，不是堆砌功能
&lt;/h2&gt;&lt;p&gt;如果说 Gateway 是控制面，Session 是状态骨架，那么 Tools / Plugins / Skills 就是 OpenClaw 的执行肌肉。&lt;/p&gt;
&lt;p&gt;OpenClaw 的工具体系有三个层次，很多人会混淆。让我们分清楚：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/015-934c8d77.png"&gt;&lt;/p&gt;
&lt;h3 id="tools第一等公民"&gt;&lt;a href="#tools%e7%ac%ac%e4%b8%80%e7%ad%89%e5%85%ac%e6%b0%91" class="header-anchor"&gt;&lt;/a&gt;Tools：第一等公民
&lt;/h3&gt;&lt;p&gt;OpenClaw 暴露的是 &lt;strong&gt;first-class agent tools&lt;/strong&gt;，不是外挂脚本。 包括 browser、canvas、nodes、cron、gateway、session 相关工具、agents_list、image、pdf、message、exec 等。&lt;/p&gt;
&lt;p&gt;OpenClaw 没有把“能力调用”做成 prompt 技巧，而是做成了&lt;strong&gt;运行时契约&lt;/strong&gt;。Tool list 和 tool schema 会进入模型上下文；tool allow/deny、tool profiles、per-agent 工具策略、provider-specific 工具策略和 sandbox 工具策略共同决定模型实际能拿到哪些工具&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/016-1395dff2.png"&gt;&lt;/p&gt;
&lt;h3 id="plugins扩展系统本身"&gt;&lt;a href="#plugins%e6%89%a9%e5%b1%95%e7%b3%bb%e7%bb%9f%e6%9c%ac%e8%ba%ab" class="header-anchor"&gt;&lt;/a&gt;Plugins：扩展系统本身
&lt;/h3&gt;&lt;p&gt;插件是运行在 Gateway 内部的代码模块，可以：&lt;/p&gt;
&lt;p&gt;●注册新的 RPC 方法&lt;/p&gt;
&lt;p&gt;●添加新的 HTTP 路由&lt;/p&gt;
&lt;p&gt;●注册新的工具&lt;/p&gt;
&lt;p&gt;●启动后台服务&lt;/p&gt;
&lt;p&gt;类比：&lt;/p&gt;
&lt;p&gt;●Skills：像&amp;quot;使用说明书&amp;quot;&lt;/p&gt;
&lt;p&gt;●Tools：像&amp;quot;内置功能&amp;quot;&lt;/p&gt;
&lt;p&gt;●Plugins：像&amp;quot;给系统装新器官&amp;quot;&lt;/p&gt;
&lt;h3 id="skills教-ai-如何做事"&gt;&lt;a href="#skills%e6%95%99-ai-%e5%a6%82%e4%bd%95%e5%81%9a%e4%ba%8b" class="header-anchor"&gt;&lt;/a&gt;Skills：教 AI 如何做事
&lt;/h3&gt;&lt;p&gt;每个 Skill 就是一个目录，核心是 SKILL.md——一份详细的操作指南。Skill 的三个来源（优先级从高到低）：&lt;/p&gt;
&lt;p&gt;●&lt;code&gt;&amp;lt;workspace&amp;gt;/skills/&lt;/code&gt;：当前工作区专属&lt;/p&gt;
&lt;p&gt;●&lt;code&gt;~/.openclaw/skills/&lt;/code&gt;：用户私有技能&lt;/p&gt;
&lt;p&gt;●Bundled skills：系统内置技能&lt;/p&gt;
&lt;p&gt;与Plugins的本质区别:Plugins是给手机增加新硬件（如外接摄像头）；Skills是相机APP里的&amp;quot;夜景模式&amp;quot;说明书。&lt;/p&gt;
&lt;h2 id="八node让-ai-有手有眼"&gt;&lt;a href="#%e5%85%abnode%e8%ae%a9-ai-%e6%9c%89%e6%89%8b%e6%9c%89%e7%9c%bc" class="header-anchor"&gt;&lt;/a&gt;八、Node：让 AI “有手有眼”
&lt;/h2&gt;&lt;p&gt;OpenClaw 严格区分了两个概念：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/017-7e75b671.png"&gt;&lt;/p&gt;
&lt;p&gt;为什么这样设计？&lt;/p&gt;
&lt;p&gt;如果把它们混在一起：&lt;/p&gt;
&lt;p&gt;●Telegram Bot 只能干 Telegram 允许的事&lt;/p&gt;
&lt;p&gt;●WhatsApp Bot 只能干 WhatsApp 允许的事&lt;/p&gt;
&lt;p&gt;每个渠道都要重新实现一遍&amp;quot;控制电脑&amp;quot;的能力&lt;/p&gt;
&lt;p&gt;OpenClaw 的设计：&lt;/p&gt;
&lt;p&gt;●所有消息渠道都汇聚到 Gateway&lt;/p&gt;
&lt;p&gt;●所有设备能力也汇聚到 Gateway&lt;/p&gt;
&lt;p&gt;Gateway 负责调度：“这个 Telegram 消息需要控制 iPhone，我来协调”&lt;/p&gt;
&lt;h3 id="node-是什么"&gt;&lt;a href="#node-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;Node 是什么？
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;node 是 companion device，可以是 macOS、iOS、Android 或 headless 设备；它通过和 operator 一样的 Gateway WebSocket 接入，但使用 role: &amp;ldquo;node&amp;rdquo;，向 Gateway 暴露一组命令面，比如 canvas.&lt;em&gt;、camera.&lt;/em&gt;、device.&lt;em&gt;、notifications.&lt;/em&gt;、system.*，再由 node.invoke 触发。官方还特别强调：nodes are peripherals, not gateways。消息还是落在 Gateway，不是落在 node&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Node 是一台&amp;quot;伴侣设备&amp;quot;，它：&lt;/p&gt;
&lt;p&gt;●通过 WebSocket 连接到 Gateway&lt;/p&gt;
&lt;p&gt;●向 Gateway 暴露一组能力（camera、notifications、system…）&lt;/p&gt;
&lt;p&gt;●等待 Gateway 的指令&lt;/p&gt;
&lt;p&gt;类比：&lt;/p&gt;
&lt;p&gt;●Gateway：大脑&lt;/p&gt;
&lt;p&gt;●消息渠道：耳朵和嘴&lt;/p&gt;
&lt;p&gt;●Node：手和脚&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/018-094dd77e.png"&gt;&lt;/p&gt;
&lt;p&gt;没有 Node 的话：&lt;/p&gt;
&lt;p&gt;●Telegram Bot 无法直接控制你的 iPhone&lt;/p&gt;
&lt;p&gt;●需要你自己手动截图,再发给 Bot&lt;/p&gt;
&lt;p&gt;●AI 无法真正&amp;quot;替你做事&amp;quot;&lt;/p&gt;
&lt;p&gt;有了 Node：&lt;/p&gt;
&lt;p&gt;●AI 可以跨设备协同工作&lt;/p&gt;
&lt;p&gt;●你在 Telegram 发指令,它在你的 Mac 上执行&lt;/p&gt;
&lt;p&gt;●真正的&amp;quot;个人助手&amp;quot;体验&lt;/p&gt;
&lt;h2 id="九安全边界诚实比承诺更重要"&gt;&lt;a href="#%e4%b9%9d%e5%ae%89%e5%85%a8%e8%be%b9%e7%95%8c%e8%af%9a%e5%ae%9e%e6%af%94%e6%89%bf%e8%af%ba%e6%9b%b4%e9%87%8d%e8%a6%81" class="header-anchor"&gt;&lt;/a&gt;九、安全边界：诚实比承诺更重要
&lt;/h2&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/019-9b4d83b0.png"&gt;&lt;/p&gt;
&lt;p&gt;OpenClaw 的安全模型假设的是 &lt;strong&gt;one trusted operator boundary per gateway&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenClaw 的安全文档非常诚实，这句话翻译成人话是：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/020-fe2dc05d.png"&gt;&lt;/p&gt;
&lt;p&gt;这意味着：如果你把Gateway密码给朋友，让他也连进来，你们的对话历史、文件访问、记忆内容默认不隔离。这不是漏洞，是设计选择——为了简化架构，OpenClaw牺牲了多租户隔离，换取单用户场景下的极致能力。&lt;/p&gt;
&lt;h3 id="安全层次"&gt;&lt;a href="#%e5%ae%89%e5%85%a8%e5%b1%82%e6%ac%a1" class="header-anchor"&gt;&lt;/a&gt;安全层次
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="err"&gt;代码片段外层：公网&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;外部消息源&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;↓&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;第一道门：&lt;/span&gt;&lt;span class="n"&gt;Gateway&lt;/span&gt; &lt;span class="err"&gt;入口保护&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="err"&gt;认证&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;challenge&lt;/span&gt; &lt;span class="err"&gt;签名验证&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt; &lt;span class="err"&gt;校验&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pairing&lt;/span&gt; &lt;span class="err"&gt;审批&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;↓&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;第二道门：权限控制&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="err"&gt;角色&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt; &lt;span class="err"&gt;权限范围&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;↓&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;第三道门：执行保护&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="err"&gt;（工具策略）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;approvals&lt;/span&gt;&lt;span class="err"&gt;（执行审批）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="err"&gt;（沙箱隔离）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;allowlist&lt;/span&gt;&lt;span class="err"&gt;（白名单）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;↓&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;内层：高风险边界&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trusted&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;插件和&lt;/span&gt; &lt;span class="n"&gt;Gateway&lt;/span&gt; &lt;span class="err"&gt;同等权限&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/021-58fc1d46.png"&gt;&lt;/p&gt;
&lt;h3 id="sandbox可以隔离也可以放行"&gt;&lt;a href="#sandbox%e5%8f%af%e4%bb%a5%e9%9a%94%e7%a6%bb%e4%b9%9f%e5%8f%af%e4%bb%a5%e6%94%be%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;Sandbox：可以隔离，也可以放行
&lt;/h3&gt;&lt;p&gt;OpenClaw 的沙箱设计非常灵活：&lt;/p&gt;
&lt;p&gt;配置维度：&lt;/p&gt;
&lt;p&gt;●mode：off / non-main / all（是否启用沙箱）&lt;/p&gt;
&lt;p&gt;●scope：session / agent / shared（沙箱范围）&lt;/p&gt;
&lt;p&gt;●workspaceAccess：none / ro / rw（工作区访问权限）&lt;/p&gt;
&lt;p&gt;实际用法举例：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/022-0a65a7e5.png"&gt;&lt;/p&gt;
&lt;h3 id="浏览器隔离不是接管你的-chrome"&gt;&lt;a href="#%e6%b5%8f%e8%a7%88%e5%99%a8%e9%9a%94%e7%a6%bb%e4%b8%8d%e6%98%af%e6%8e%a5%e7%ae%a1%e4%bd%a0%e7%9a%84-chrome" class="header-anchor"&gt;&lt;/a&gt;浏览器隔离：不是接管你的 Chrome
&lt;/h3&gt;&lt;p&gt;OpenClaw不会接管你的日常Chrome（那里面可能有银行登录态），而是拉起独立的Chrome Profile：&lt;/p&gt;
&lt;p&gt;●独立的Cookie、缓存、扩展&lt;/p&gt;
&lt;p&gt;●Agent专用，与你的个人浏览数据隔离&lt;/p&gt;
&lt;p&gt;●支持截图、点击、PDF生成，但无法访问你个人的浏览器历史&lt;/p&gt;
&lt;p&gt;这是&amp;quot;能力&amp;quot;与&amp;quot;安全&amp;quot;的折中：AI需要浏览器自动化，但不能拥有你的全部数字生活。&lt;/p&gt;
&lt;h2 id="十为什么这是个人ai操作系统的雏形"&gt;&lt;a href="#%e5%8d%81%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%99%e6%98%af%e4%b8%aa%e4%ba%baai%e6%93%8d%e4%bd%9c%e7%b3%bb%e7%bb%9f%e7%9a%84%e9%9b%8f%e5%bd%a2" class="header-anchor"&gt;&lt;/a&gt;十、为什么这是&amp;quot;个人AI操作系统&amp;quot;的雏形？
&lt;/h2&gt;&lt;p&gt;OpenClaw 之所以值得研究，不是因为它 GitHub stars 多，而是因为它回答了一个未来会越来越重要的问题：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果 AI 不再是网页对话框，而是一个长期在线、能操作设备、能记住一切的助手，它的系统架构应该长什么样？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenClaw 的答案是：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-11-openclaw-bao-huo-bei-hou-ta-bu-shi-liao-tian-ji-qi-ren-er-sh/023-c6731cdd.png"&gt;&lt;/p&gt;
&lt;p&gt;这套答案不一定是终局，也还远没到“完美”。Vision 文档自己都说，项目还很早，当前重点依然是 security、safe defaults、bug fixes、stability 和 setup reliability。也就是说，它依然在快速迭代，仍然带着实验性。&lt;/p&gt;
&lt;p&gt;但它已经足够有代表性。因为它第一次比较完整地把“个人 AI 助手”这件事，从概念拉成了系统工程：&lt;/p&gt;
&lt;p&gt;●消息不再只是消息，而是事件入口；&lt;/p&gt;
&lt;p&gt;●模型不再只是回答器，而是运行时里的推理核心；&lt;/p&gt;
&lt;p&gt;●工具不再只是 function calling 演示，而是被策略、审批和沙箱约束的系统调用；&lt;/p&gt;
&lt;p&gt;●记忆不再只是“模型好像记得”，而是落到磁盘、可检索、可审计、可 Git 备份的工作区资产&lt;/p&gt;
&lt;h3 id="它具备了操作系统的味道"&gt;&lt;a href="#%e5%ae%83%e5%85%b7%e5%a4%87%e4%ba%86%e6%93%8d%e4%bd%9c%e7%b3%bb%e7%bb%9f%e7%9a%84%e5%91%b3%e9%81%93" class="header-anchor"&gt;&lt;/a&gt;它具备了&amp;quot;操作系统&amp;quot;的味道
&lt;/h3&gt;&lt;p&gt;不是说它替代 Windows 或 macOS，而是说它有那种系统级的感觉：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ 代码片段传统应用：打开 → 用 → 关闭
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;操作系统：开机 → 长期运行 → 管理所有应用 → 关机
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;传统 AI：聊天 → 结束
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;OpenClaw：启动 Gateway → 长期在线 → 管理所有 Agent → 关闭
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后，再强调一次：&lt;strong&gt;OpenClaw 的本质，不是一个接了很多渠道的聊天 Bot，而是一套以 Gateway 为控制面、以 Agent/Session/Memory 为状态骨架、以工具与节点为执行面，把大模型真正接入现实世界的个人 AI 助手系统。&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>嘿，朋友，做了个 AI 模型比价工具，想请你来试试</title><link>https://xiaobox.github.io/p/2026-01-22-hei-peng-you-zuo-le-ge-ai-mo-xing-bi-jia-gong-ju-xiang-qing/</link><pubDate>Thu, 22 Jan 2026 07:51:40 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-01-22-hei-peng-you-zuo-le-ge-ai-mo-xing-bi-jia-gong-ju-xiang-qing/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-01-22-hei-peng-you-zuo-le-ge-ai-mo-xing-bi-jia-gong-ju-xiang-qing-/cover.jpg" alt="Featured image of post 嘿，朋友，做了个 AI 模型比价工具，想请你来试试" /&gt;&lt;p&gt;大家好，我是小盒子。&lt;/p&gt;
&lt;p&gt;这两年 AI 大模型卷得厉害，GPT-4、Claude、Gemini、Llama……模型眼花缭乱，价格也是五花八门。作为一个经常要调用 API 的开发者，我经常想搞清楚一个问题：&lt;strong&gt;到底谁家的模型便宜？性价比高的是哪个？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;说实话，每次想比较价格，我都得打开一堆浏览器标签页：AWS Bedrock 的定价页、Azure OpenAI 的价格表、OpenAI 官网、还有 OpenRouter……然后手动对比，算汇率，头都大了。更要命的是，这些价格还时不时更新，上周看的数据，这周可能就变了。&lt;/p&gt;
&lt;p&gt;不知道你有没有同感：&lt;/p&gt;
&lt;p&gt;●想用 Claude 3.5 Sonnet，但不确定是直接调 Anthropic 便宜，还是走 AWS Bedrock 便宜？&lt;/p&gt;
&lt;p&gt;●项目预算有限，想找个便宜点的模型先跑通，但不知道该选谁？&lt;/p&gt;
&lt;p&gt;●跟老板汇报要说清楚模型成本，却发现各家的计价单位都不一样，有的按 1K tokens，有的按 1M tokens，换算起来很麻烦？&lt;/p&gt;
&lt;p&gt;就因为这些&amp;quot;痛点&amp;quot;，前段时间，我干脆撸起袖子，做了一个工具来解决这个问题。&lt;/p&gt;
&lt;p&gt;于是，&lt;code&gt;Model Price&lt;/code&gt; 就这么诞生了。&lt;/p&gt;
&lt;p&gt;它的目标很简单：&lt;strong&gt;把各大 AI 服务商的模型价格聚合到一起，让你一眼就能看清谁便宜、谁贵、性价比如何。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;代码都在这儿了，开诚布公，欢迎随时来坐坐：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a class="link" href="https://github.com/xiaobox/model-price" target="_blank" rel="noopener"
 &gt;https://github.com/xiaobox/model-price&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;在线演示:&lt;/strong&gt; &lt;a class="link" href="https://modelprice.boxtech.icu" target="_blank" rel="noopener"
 &gt;https://modelprice.boxtech.icu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;（要是觉得还行，顺手点个 Star，就是对我最大的肯定。）&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-01-22-hei-peng-you-zuo-le-ge-ai-mo-xing-bi-jia-gong-ju-xiang-qing-/001-3ae3bd24.png"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="这把锤子我花了些心思去打磨"&gt;&lt;a href="#%e8%bf%99%e6%8a%8a%e9%94%a4%e5%ad%90%e6%88%91%e8%8a%b1%e4%ba%86%e4%ba%9b%e5%bf%83%e6%80%9d%e5%8e%bb%e6%89%93%e7%a3%a8" class="header-anchor"&gt;&lt;/a&gt;这把&amp;quot;锤子&amp;quot;，我花了些心思去打磨
&lt;/h1&gt;&lt;p&gt;我不想只做个&amp;quot;能看&amp;quot;的工具，我希望它能&amp;quot;好用&amp;quot;，甚至让你&amp;quot;爱用&amp;quot;。所以，在几个关键的地方下了功夫。&lt;/p&gt;
&lt;h2 id="首先数据要全"&gt;&lt;a href="#%e9%a6%96%e5%85%88%e6%95%b0%e6%8d%ae%e8%a6%81%e5%85%a8" class="header-anchor"&gt;&lt;/a&gt;首先，数据要全
&lt;/h2&gt;&lt;p&gt;目前 Model Price 覆盖了 &lt;strong&gt;6 家主流 AI 服务商，580+ 个模型&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;服务商&lt;/th&gt;
 &lt;th&gt;模型数量&lt;/th&gt;
 &lt;th&gt;数据来源&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;AWS Bedrock&lt;/td&gt;
 &lt;td&gt;96+&lt;/td&gt;
 &lt;td&gt;公开 API&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Azure OpenAI&lt;/td&gt;
 &lt;td&gt;50+&lt;/td&gt;
 &lt;td&gt;零售价格 API&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenAI&lt;/td&gt;
 &lt;td&gt;53+&lt;/td&gt;
 &lt;td&gt;官网爬虫&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Google Gemini&lt;/td&gt;
 &lt;td&gt;31+&lt;/td&gt;
 &lt;td&gt;官网爬虫&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenRouter&lt;/td&gt;
 &lt;td&gt;339+&lt;/td&gt;
 &lt;td&gt;公开 API&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;xAI (Grok)&lt;/td&gt;
 &lt;td&gt;12+&lt;/td&gt;
 &lt;td&gt;官方文档&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;无论你用的是 GPT-4、Claude 3.5、Gemini Pro 还是 Llama，都能在这里找到对应的价格。&lt;/p&gt;
&lt;h2 id="其次数据要准"&gt;&lt;a href="#%e5%85%b6%e6%ac%a1%e6%95%b0%e6%8d%ae%e8%a6%81%e5%87%86" class="header-anchor"&gt;&lt;/a&gt;其次，数据要准
&lt;/h2&gt;&lt;p&gt;最让我头疼的就是价格变动。所以我给 Model Price 做了自动数据获取机制：&lt;/p&gt;
&lt;p&gt;●对于有公开 API 的服务商（如 AWS、Azure、OpenRouter），直接调接口拿最新数据&lt;/p&gt;
&lt;p&gt;●对于没有 API 的服务商（如 OpenAI、Google），用 Playwright 爬虫自动抓取官网定价&lt;/p&gt;
&lt;p&gt;这样一来，数据基本能保持实时更新，你不用再担心看到的是过时信息。&lt;/p&gt;
&lt;h2 id="查找要快"&gt;&lt;a href="#%e6%9f%a5%e6%89%be%e8%a6%81%e5%bf%ab" class="header-anchor"&gt;&lt;/a&gt;查找要快
&lt;/h2&gt;&lt;p&gt;580+ 个模型，如果只能翻页查看，那体验也太差了。所以我加了多维度筛选：&lt;/p&gt;
&lt;p&gt;●按&lt;strong&gt;提供商&lt;/strong&gt;筛选：只看 OpenAI 的？只看 AWS 的？一键切换&lt;/p&gt;
&lt;p&gt;●按&lt;strong&gt;模型系列&lt;/strong&gt;筛选：只看 GPT-4 系列？只看 Claude 系列？&lt;/p&gt;
&lt;p&gt;●按&lt;strong&gt;能力标签&lt;/strong&gt;筛选：支持视觉的？支持音频的？支持 Function Call 的？&lt;/p&gt;
&lt;p&gt;●按&lt;strong&gt;价格排序&lt;/strong&gt;：从低到高、从高到低&lt;/p&gt;
&lt;p&gt;基本上，三秒内就能找到你想要的模型。&lt;/p&gt;
&lt;h2 id="最后看着要舒服"&gt;&lt;a href="#%e6%9c%80%e5%90%8e%e7%9c%8b%e7%9d%80%e8%a6%81%e8%88%92%e6%9c%8d" class="header-anchor"&gt;&lt;/a&gt;最后，看着要舒服
&lt;/h2&gt;&lt;p&gt;我做了两种视图模式：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;卡片视图&lt;/strong&gt;：信息展示更直观，适合浏览&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;表格视图&lt;/strong&gt;：数据更紧凑，适合对比&lt;/p&gt;
&lt;p&gt;每个模型的价格还有一个小的柱状图，让你一眼就能看出谁贵谁便宜。输入输出价格分开展示，Batch API 价格也有，该有的都有。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-01-22-hei-peng-you-zuo-le-ge-ai-mo-xing-bi-jia-gong-ju-xiang-qing-/002-8abb0741.png"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="技术栈给爱折腾的朋友参考"&gt;&lt;a href="#%e6%8a%80%e6%9c%af%e6%a0%88%e7%bb%99%e7%88%b1%e6%8a%98%e8%85%be%e7%9a%84%e6%9c%8b%e5%8f%8b%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;技术栈，给爱折腾的朋友参考
&lt;/h1&gt;&lt;p&gt;&lt;code&gt;Model Price&lt;/code&gt; 的技术选型很主流，方便大家二次开发：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;后端：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;●Python 3.11+&lt;/p&gt;
&lt;p&gt;●FastAPI（高性能异步框架）&lt;/p&gt;
&lt;p&gt;●Playwright（网页爬虫，用于抓取 OpenAI、Google 官网）&lt;/p&gt;
&lt;p&gt;●httpx（异步 HTTP 客户端）&lt;/p&gt;
&lt;p&gt;●uv（超快的 Python 包管理器）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;前端：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;●React 18&lt;/p&gt;
&lt;p&gt;●TypeScript 5&lt;/p&gt;
&lt;p&gt;●Vite（构建工具）&lt;/p&gt;
&lt;p&gt;●CSS Variables（主题系统）&lt;/p&gt;
&lt;p&gt;代码结构清晰，Provider 采用插件架构，想要接入新的服务商，只需要实现一个 &lt;code&gt;BaseProvider.fetch()&lt;/code&gt; 方法就行。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="这只是个开始"&gt;&lt;a href="#%e8%bf%99%e5%8f%aa%e6%98%af%e4%b8%aa%e5%bc%80%e5%a7%8b" class="header-anchor"&gt;&lt;/a&gt;这只是个开始
&lt;/h1&gt;&lt;h2 id="想邀请你一起来添砖加瓦"&gt;&lt;a href="#%e6%83%b3%e9%82%80%e8%af%b7%e4%bd%a0%e4%b8%80%e8%b5%b7%e6%9d%a5%e6%b7%bb%e7%a0%96%e5%8a%a0%e7%93%a6" class="header-anchor"&gt;&lt;/a&gt;想邀请你一起来添砖加瓦
&lt;/h2&gt;&lt;p&gt;现在 &lt;code&gt;Model Price&lt;/code&gt; 已经能用了，但它离&amp;quot;完美&amp;quot;还差得很远。一个人的力量终究有限，一个好的开源项目，生命力在于社区。&lt;/p&gt;
&lt;p&gt;所以，我诚心地邀请你，无论你是谁，都可以来参与这件事：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;如果你只是想找个工具查价格&lt;/strong&gt;：欢迎直接访问 &lt;a class="link" href="https://modelprice.boxtech.icu" target="_blank" rel="noopener"
 &gt;https://modelprice.boxtech.icu&lt;/a&gt; 使用。如果能顺手在 GitHub 上点个 Star，我会非常开心。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;如果你经常用某个服务商，发现数据有误&lt;/strong&gt;：欢迎提 Issue 告诉我，我会尽快修复。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;如果你和我一样，是个爱折腾的开发者&lt;/strong&gt;：欢迎来读源码，提 PR。比如接入新的服务商、优化爬虫逻辑、改进 UI 交互……都非常欢迎。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;如果你有其他想法&lt;/strong&gt;：比如想要对比历史价格、想要价格变动提醒、想要导出 Excel……都可以提 Issue，我们一起讨论。&lt;/p&gt;
&lt;p&gt;一个优秀的开源项目，就像一场漫长的篝火晚会，需要不断有人添柴，才能一直燃烧下去。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Model Price&lt;/code&gt; 就是我点起的第一根火柴。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;好了，就说这么多。感谢你耐心听我这个老家伙唠叨。&lt;/p&gt;
&lt;p&gt;如果你对 &lt;code&gt;Model Price&lt;/code&gt; 有一点点兴趣，就去看看吧。期待在 GitHub 上，看到你的身影。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub 传送门：&lt;/strong&gt; &lt;a class="link" href="https://github.com/xiaobox/model-price" target="_blank" rel="noopener"
 &gt;https://github.com/xiaobox/model-price&lt;/a&gt;&lt;/p&gt;</description></item><item><title>快速安装 ClickHouse</title><link>https://xiaobox.github.io/p/2025-07-17-kuai-su-an-zhuang-clickhouse/</link><pubDate>Thu, 17 Jul 2025 09:33:05 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-07-17-kuai-su-an-zhuang-clickhouse/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/cover.jpg" alt="Featured image of post 快速安装 ClickHouse" /&gt;&lt;h2 id="概述"&gt;&lt;a href="#%e6%a6%82%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;概述
&lt;/h2&gt;&lt;p&gt;我们使用 docker compose 来安装 ClickHouse&lt;/p&gt;
&lt;p&gt;但我们不是裸装 ClickHouse，实际上我们安装的是 ClickStack。有点儿像 elastic-stack 与 elastic search 的关系 ，但并不完全一样。&lt;/p&gt;
&lt;p&gt;ClickStack 是基于 ClickHouse 构建的完整观察性平台，集成了日志、指标、追踪和会话回放功能，提供统一的用户界面和查询能力。因此，ClickStack 是在 ClickHouse 的基础上，结合 HyperDX 提供的前端界面和 OpenTelemetry Collector 实现的完整解决方案。它不仅仅是一个数据库，而是一个集成的观察性平台。&lt;/p&gt;
&lt;p&gt;安装步骤参考官方文档：https://clickhouse.com/docs/zh/use-cases/observability/clickstack/getting-started?loc=use-case-observability&lt;/p&gt;
&lt;h2 id="安装"&gt;&lt;a href="#%e5%ae%89%e8%a3%85" class="header-anchor"&gt;&lt;/a&gt;安装
&lt;/h2&gt;&lt;h3 id="克隆-hyperdx-仓库"&gt;&lt;a href="#%e5%85%8b%e9%9a%86-hyperdx-%e4%bb%93%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;克隆 HyperDX 仓库
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;git clone https://github.com/hyperdxio/hyperdx.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# switch to the v2 branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;git checkout v2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据自身情况修改配置文件 .env&lt;/p&gt;
&lt;p&gt;我将 &lt;code&gt;HDX_IMAGE_REPO=docker.hyperdx.io&lt;/code&gt; 修改为 &lt;code&gt;HDX_IMAGE_REPO=docker.io&lt;/code&gt; 不然镜像拉不下来&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;# Used by docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;HDX_IMAGE_REPO=docker.hyperdx.io
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NAME=ghcr.io/hyperdxio/hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;LOCAL_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;LOCAL_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;ALL_IN_ONE_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-all-in-one
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;ALL_IN_ONE_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-all-in-one
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;OTEL_COLLECTOR_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-otel-collector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-otel-collector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;CODE_VERSION=2.0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_VERSION_SUB_TAG=.0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_VERSION=2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NIGHTLY_TAG=2-nightly
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_LATEST_TAG=latest
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;# Set up domain URLs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_API_PORT=8000 #optional (should not be taken by other services)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_APP_PORT=8080
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_APP_URL=http://localhost
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_LOG_LEVEL=debug
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_OPAMP_PORT=4320
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;# Otel/Clickhouse config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE=default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="docker-compose-启动"&gt;&lt;a href="#docker-compose-%e5%90%af%e5%8a%a8" class="header-anchor"&gt;&lt;/a&gt;docker compose 启动
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Docker-compose 文件如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hdx-oss&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ONLY USED FOR DEMO SSL SETUP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# nginx:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# image: nginx:1.27.3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# volumes:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - ./docker/nginx/ssl:/etc/nginx/ssl&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - .volumes/nginx_logs:/var/log/nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ports:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 80:80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 443:443&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# networks:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# depends_on:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mongo:5.0.14-focal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/db:/data/db&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# WARNING: Exposing the database port will make it accessible from outside the container,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# potentially allowing unauthorized access. If you uncomment the ports below,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ensure to secure your database (e.g., with strong authentication, proper network rules, and firewalls).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ports:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 27017:27017&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;otel-collector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HDX_IMAGE_REPO}/${OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB}:${IMAGE_VERSION}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;CLICKHOUSE_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://ch-server:9000?dial_timeout=10s&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_LOG_LEVEL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OPAMP_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://app:${HYPERDX_OPAMP_PORT}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;13133:13133&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# health_check extension&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;24225:24225&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# fluentd receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;4317:4317&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# OTLP gRPC receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;4318:4318&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# OTLP http receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;8888:8888&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# metrics extension&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ch-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HDX_IMAGE_REPO}/${IMAGE_NAME_DOCKERHUB}:${IMAGE_VERSION}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${HYPERDX_API_PORT}:${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${HYPERDX_APP_PORT}:${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;FRONTEND_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_URL}:${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_API_KEY}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_API_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_APP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_APP_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_URL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_LOG_LEVEL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MINER_API_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://miner:5123&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MONGO_URI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mongodb://db:27017/hyperdx&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;NEXT_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http://127.0.0.1:${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OPAMP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_OPAMP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hdx-oss-api&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;USAGE_STATS_ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${USAGE_STATS_ENABLED:-true}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DEFAULT_CONNECTIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[{&amp;#34;name&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;host&amp;#34;:&amp;#34;http://ch-server:8123&amp;#34;,&amp;#34;username&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;&amp;#34;}]&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DEFAULT_SOURCES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;otel_logs&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;log&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimestampTime&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;LogAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,SeverityText,Body&amp;#34;,&amp;#34;severityTextExpression&amp;#34;:&amp;#34;SeverityText&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;otel_traces&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;trace&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;SpanAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,StatusCode,round(Duration/1e6),SpanName&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;durationExpression&amp;#34;:&amp;#34;Duration&amp;#34;,&amp;#34;durationPrecision&amp;#34;:9,&amp;#34;parentSpanIdExpression&amp;#34;:&amp;#34;ParentSpanId&amp;#34;,&amp;#34;spanNameExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;spanKindExpression&amp;#34;:&amp;#34;SpanKind&amp;#34;,&amp;#34;statusCodeExpression&amp;#34;:&amp;#34;StatusCode&amp;#34;,&amp;#34;statusMessageExpression&amp;#34;:&amp;#34;StatusMessage&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;metric&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimeUnix&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Metrics&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;metricTables&amp;#34;:{&amp;#34;gauge&amp;#34;:&amp;#34;otel_metrics_gauge&amp;#34;,&amp;#34;histogram&amp;#34;:&amp;#34;otel_metrics_histogram&amp;#34;,&amp;#34;sum&amp;#34;:&amp;#34;otel_metrics_sum&amp;#34;,&amp;#34;_id&amp;#34;:&amp;#34;682586a8b1f81924e628e808&amp;#34;,&amp;#34;id&amp;#34;:&amp;#34;682586a8b1f81924e628e808&amp;#34;},&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;hyperdx_sessions&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;session&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimestampTime&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;LogAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,SeverityText,Body&amp;#34;,&amp;#34;severityTextExpression&amp;#34;:&amp;#34;SeverityText&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;}]&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ch-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;db&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ch-server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;clickhouse/clickhouse-server:24-alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# WARNING: Exposing the database port will make it accessible from outside the container,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# potentially allowing unauthorized access. If you uncomment the ports below,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;82&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ensure to secure your database (e.g., with strong authentication, proper network rules, and firewalls).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;83&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;84&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8123&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8123&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# http api&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;85&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;9050&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;9000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# native&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;86&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# environment:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;87&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# default settings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;88&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;89&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;90&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./docker/clickhouse/local/config.xml:/etc/clickhouse-server/config.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;91&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./docker/clickhouse/local/users.xml:/etc/clickhouse-server/users.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;92&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./empty.xml:/etc/clickhouse-server/users.d/default-password.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;93&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/ch_data:/var/lib/clickhouse&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;94&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/ch_logs:/var/log/clickhouse-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;95&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;on&lt;/span&gt;-&lt;span class="l"&gt;failure&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;96&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;97&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;98&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;99&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：environment 部分我注释掉了，另外 加了一行：&lt;code&gt;./empty.xml:/etc/clickhouse-server/users.d/default-password.xml&lt;/code&gt; 作用是解决 clickhouse 连接异常的问题。&lt;/p&gt;
&lt;p&gt;通过 &lt;code&gt;/data/clickhouse/hyperdx/docker/clickhouse/local/users.xml&lt;/code&gt; 可以看到 clickhouse 的账户信息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;clickhouse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profiles&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;max_memory_usage&amp;gt;&lt;/span&gt;10000000000&lt;span class="nt"&gt;&amp;lt;/max_memory_usage&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;use_uncompressed_cache&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/use_uncompressed_cache&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;load_balancing&amp;gt;&lt;/span&gt;in_order&lt;span class="nt"&gt;&amp;lt;/load_balancing&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;log_queries&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/log_queries&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/profiles&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;users&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password_sha256_hex&amp;gt;&lt;/span&gt;2d964690ad5ac2d2f78bebadc30895bc519969ffcef4d3c9e7ff04ee1c765d96&lt;span class="nt"&gt;&amp;lt;/password_sha256_hex&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;api&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;api&lt;span class="nt"&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/api&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;worker&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;worker&lt;span class="nt"&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/worker&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/users&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quotas&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;interval&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;duration&amp;gt;&lt;/span&gt;3600&lt;span class="nt"&gt;&amp;lt;/duration&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;queries&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/queries&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;errors&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/errors&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;result_rows&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/result_rows&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;read_rows&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/read_rows&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;execution_time&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/execution_time&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/interval&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/quotas&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/clickhouse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;密码用 sha256sum 处理过&lt;/p&gt;
&lt;p&gt;可以这样生成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;echo -n &amp;#39;你的密码&amp;#39; | sha256sum
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="运行"&gt;&lt;a href="#%e8%bf%90%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;运行
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/001-1d333d30.png"&gt;点击登录跳转至首页&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/002-f8c34343.png"&gt;&lt;/p&gt;
&lt;h2 id="客户端连接"&gt;&lt;a href="#%e5%ae%a2%e6%88%b7%e7%ab%af%e8%bf%9e%e6%8e%a5" class="header-anchor"&gt;&lt;/a&gt;客户端连接
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;用户名：default&lt;/li&gt;
&lt;li&gt;密码： 你的密码&lt;/li&gt;
&lt;li&gt;端口：8123&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/003-39f76698.png"&gt;&lt;/p&gt;
&lt;h2 id="数据库初始化"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e5%ba%93%e5%88%9d%e5%a7%8b%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;数据库初始化
&lt;/h2&gt;&lt;p&gt;初始化 sql 脚本&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 1. 创建数据库（如已存在可先 DROP DATABASE IF EXISTS testdb） */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 2. 维度表：用户 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;signup_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 3. 维度表：页面 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 4. 事实表：页面访问日志 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 停留秒数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toYYYYMM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 5. 物化视图：每日 PV / UV 聚合 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MATERIALIZED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VIEW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pv_uv_daily&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SummingMergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toYYYYMM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniqExact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GROUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 6. 演示数据插入 ---------------------------------------- */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 用户维度 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;signup_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Alice&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-06-01&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-07-15&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Cathy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-11-30&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 页面维度 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;landing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/pricing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 页面访问日志 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:17:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:18:07&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:19:02&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14 10:03:45&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14 10:05:22&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 7. 快速验证 --------------------------------------------- */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 查看当前数据库已创建的表 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TABLES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 查询物化视图结果 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pv_uv_daily&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 联表查询示例 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;82&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;83&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>RAG的2024—随需而变，从狂热到理性（下）</title><link>https://xiaobox.github.io/p/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-xia/</link><pubDate>Mon, 25 Nov 2024 06:27:48 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-xia/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-xia/cover.jpg" alt="Featured image of post RAG的2024—随需而变，从狂热到理性（下）" /&gt;&lt;p&gt;首先感谢您的阅读，本篇分享是RAG的市场变化，上篇主要分享了&lt;a class="link" href="http://mp.weixin.qq.com/s?__biz=MzIyOTA5NTM1OA==&amp;amp;mid=2247484953&amp;amp;idx=1&amp;amp;sn=41f673ca9a3f55eb42b6dac29d8eeb16&amp;amp;chksm=e846a4c8df312dde6cebdaf1f12b57cdd5c6a98be80359126039c9718547f8c80d77562db91e&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;RAG技术在2024年的变化&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;本文主要内容：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;ul&gt;
&lt;li&gt;RAG和Fine-tune目前的市场态势&lt;/li&gt;
&lt;li&gt;RAG在这一年的市场需求变化&lt;/li&gt;
&lt;li&gt;这一年的AI从业者观察&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;h4 id="1rag-vs-fine-tune"&gt;&lt;a href="#1rag-vs-fine-tune" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;1.RAG vs Fine-tune&lt;/strong&gt;
&lt;/h4&gt;
 &lt;blockquote&gt;
 &lt;p&gt;2024这一年，RAG技术对应的市场需求变化也是挺大的。在讲变化之前，我觉得有必要分享一下为什么RAG是目前市场上不可或缺的一种大模型应用的技术实现方式，它的优点是什么？以及它和主要竞争技术之间的现状是怎么样的？&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;RAG最开始被大家热推，更多是因为以下三个原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以避开大模型的上下文窗口长度的限制；&lt;/li&gt;
&lt;li&gt;可以更好地管理和利用客户专有的本地资料文件；&lt;/li&gt;
&lt;li&gt;可以更好地控制幻觉。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这三点到现在来看依然还是成立的，但上下文窗口这个优势已经慢慢淡化了，因为各大模型的上下文窗口都在暴涨，如Baichuan2的192K，doubao、GLM-4的128K，过10万tokens的上下文窗口长度已经屡见不鲜，更别说一些特长的模型版本，以及月之暗面这样用长文本占据用户心智的模型。虽然这些模型是否内置了RAG技术不好说，但是RAG解决上下文窗口长度限制的特点已经不太能站得住脚。&lt;/p&gt;
&lt;p&gt;但是第二点管理和利用专属知识文件，以及第三点控制幻觉，现在反而是我认为RAG最大的杀手锏。&lt;/p&gt;
&lt;h5 id="01-专属知识文件管理"&gt;&lt;a href="#01-%e4%b8%93%e5%b1%9e%e7%9f%a5%e8%af%86%e6%96%87%e4%bb%b6%e7%ae%a1%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;01 专属知识文件管理&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;因为RAG这种外挂文件的形式，我们便可以构建一个知识文件管理的系统来维护系统内的知识，包括生效和失效时间，知识的协作，以及便捷地为知识更新内容等。RAG在知识维护上，既不需要像传统NLP那样由人工先理解再抽取问答对，也不需要像微调（fine-tune）那样需要非常专业的技术能力，以及&lt;strong&gt;微调之后的繁琐对齐（alignment）优化&lt;/strong&gt;。所以&lt;strong&gt;如果客户的知识内容更新比较频繁（假设每天需要追加、替换大量实时资讯内容），特别是金融证券、企业情报等场景，RAG知识更新便捷的特性真的非常合适&lt;/strong&gt;。&lt;/p&gt;
&lt;h5 id="02-rag的幻觉控制"&gt;&lt;a href="#02-rag%e7%9a%84%e5%b9%bb%e8%a7%89%e6%8e%a7%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;02 RAG的幻觉控制&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;RAG的幻觉控制是一个有争议的话题，我之前写过类似观点，也有同学斩钉截铁地认为RAG和幻觉控制八竿子打不着，但我现在依然坚持RAG可以有效控制幻觉这个观点。&lt;/p&gt;
&lt;p&gt;首先我们可以来看看LLM幻觉产生的主要原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对于用户的提问输入，LLM内部完全没有相应的知识来做应对。比如你问大模型，上周三我在思考一件事，但是现在想不起来，你帮我想想是什么。例子虽然夸张，但显而易见，LLM也不知道，但是它会一本正经给你一些建议，当然肯定不是你想要的；&lt;/li&gt;
&lt;li&gt;当我们给LLM原始问题，以及多个模棱两可或互相影响的参考材料，那么LLM给出的最终答案也会出错。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;好，那么针对以上问题，是否我们解决好对原始问题的**“理解-检索-召回”**，送到LLM的context足够清晰（指的是没有歧义内容、检索相关度高），结果就会非常准确？根据我们的实践结果，答案是明确的：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;今年9月份我们对一些项目进行了槽位填充（消除模糊问答）和元数据辅助之后，问答准确率可达到98%以上。比直接把大文本扔进同一个LLM测试的问答准确率几乎高出14个百分点。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;有同学会说，LLM幻觉的深层原因是temperature或者说概率引起的。就我纯个人观点来看，现当下的LLM参数足够大、知识量足够多，temperature引起的偏差对于最终结果的正确性影响已经微乎其微了。&lt;/p&gt;
&lt;h5 id="03-市场表现"&gt;&lt;a href="#03-%e5%b8%82%e5%9c%ba%e8%a1%a8%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;03 市场表现&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;你应该看出来了，在RAG和微调之间，我明显站队了，而且从一年前就开始站队了，我们创业的技术方向也是如此。从今天来看，我觉得RAG在2024年的表现确实要强于微调。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-xia/001-a0c64926.png"&gt;&lt;/p&gt;
&lt;p&gt;图：Menlo Ventures在2024年11月20日发布的市场调研报告。&lt;strong&gt;RAG以51%的市场份额在企业市场份额中占据绝对优势&lt;/strong&gt;，Fine-tune和Prompting工程均下降两倍多。Agent今年属于纯增长，目前情况还不错，但在企业应用领域，多Agents的编排依然存在理解能力不足和生成幻觉等问题有待提高。&lt;/p&gt;
&lt;p&gt;来源：https://menlovc.com/2024-the-state-of-generative-ai-in-the-enterprise/&lt;/p&gt;
&lt;p&gt;如果去预测明年的&lt;strong&gt;企业级&lt;/strong&gt;市场趋势，我觉得应用（Application）可能会是最大的关键词，甚至会超过Agent的热度。其实今年下半年已经能明显的看出来，越来越多传统大企业开始将大模型技术引入到业务中，而且他们的特点是“&lt;strong&gt;要求高&lt;/strong&gt;”、“&lt;strong&gt;需求刚&lt;/strong&gt;”、“&lt;strong&gt;付费爽&lt;/strong&gt;”。而一旦大家开始在大模型的应用侧竞赛，RAG在整个业务流程中“&lt;strong&gt;白盒流程多&lt;/strong&gt;”、“&lt;strong&gt;易控&lt;/strong&gt;”等特点愈发会受到企业客户和开发者的热捧，优势进一步拉大。&lt;/p&gt;
&lt;h4 id="2市场变化之2024"&gt;&lt;a href="#2%e5%b8%82%e5%9c%ba%e5%8f%98%e5%8c%96%e4%b9%8b2024" class="header-anchor"&gt;&lt;/a&gt;2.市场变化之2024
&lt;/h4&gt;&lt;p&gt;关于企业AI应用市场在2024年的变化，我之前已经有写过文章《聊个五分钟的企业AI应用需求发展趋势》，这里就简单再总结一下。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;上半年：AI无所不能，大而全&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2024年的上半年，AI市场充斥着激情，那种热情似乎走在街上都会扑面而来，个人感觉最主要的推动者是自媒体和模型厂商。模型厂商的出发点很容易理解，快速打开市场嘛，但考虑到他们是要最终交付的，所以相对还是比较理性。但自媒体就一样了，整个上半年看过太多的文章，大家也都是把最好的一面呈现给了大众，所以很多人会觉得我才几个月没关注，AI已经发展到我不认识的地步了，AI已经无所不能了。所以，在2024年上半年，我们接触到的企业需求中，占主流的是那种大而全的需求，要用AI替代他们业务的全流程或基本流程，气味中充满了使用者的野望。&lt;/p&gt;
&lt;p&gt;但实际情况并不理想，AI或者大模型还真没到这个程度，而且最关键的是范式转换也还需时间。什么是范式转换？最简单的例子就是以前人们用笨重的蒸汽机推动主轴承转动，带动整车间的机器工作。但是换了电动机之后呢，工作方式变了，动力可是变得非常分散，比如你拿在手上吹头发的吹风机。带着微型电动机的吹风机和传统的蒸汽机在工作范式上就完全不同，采用AI大模型之后，企业的业务流程也存在范式改造的过程，并非一朝一夕可以完成的。&lt;/p&gt;
&lt;p&gt;所以，上半年我遇到的、参与的或者听说的那些&lt;strong&gt;大而全&lt;/strong&gt;的AI项目，一半是在可行性推演中没有被验证，一半是交付之后效果很不理想，成功者寥寥。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;下半年：回归理性，小而难&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在今年7月份开始，陆续有一些传统大企业找上门来，包括非常知名的企业，以及世界500强和多家中国500强。如果从时间上来说，他们属于AI投入相对较晚的了，但他们的优势是需求非常明确，要求也极高。比如有些企业仅仅就是解决一个咨询服务的需求，在产品范围上就是一个AI问答，但要求准确率接近100%，就像我们CTO在&lt;a class="link" href="http://mp.weixin.qq.com/s?__biz=MzA5NDc3ODQxNQ==&amp;amp;mid=2247484571&amp;amp;idx=1&amp;amp;sn=4527dcacc967a97da93402dc5f3e70a9&amp;amp;chksm=904821dba73fa8cd82aebd4748698de129591b83dd042fb62fd73105fa8ebb8f738ecc054d9b&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;《AIGC时代的淘金者，TorchV这一年的心路历程》&lt;/a&gt;说到社保咨询一样。&lt;/p&gt;
&lt;p&gt;小而难的好处很明显，我能看到的是下面几点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对企业现有业务流程改造相对较小，内部推动的阻力相对较小，企业客户配合度高；&lt;/li&gt;
&lt;li&gt;切口小，需求明确，建设成果的考核清晰可量化；&lt;/li&gt;
&lt;li&gt;使用功能较小但可用性较高的AI产品，可以让企业内部员工快速接受AI，做进一步业务流程改造的前期预热；&lt;/li&gt;
&lt;li&gt;乐于承接大而全需求的合作厂商多半是外包性质的（这个观点有点伤人，但确实是我看到的现状），而专业的、交付成功率更高的厂商往往更喜欢需求清晰且有难度的任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;关于2025年的预测&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我在上文中已经有提到，2025年会有更多企业需求方采用AI技术，但企业永远不会为你的技术买单，他们只会为他们自己的使用价值买单。比如可以帮助他们提升销售额、业务流转效率更高，或者和竞争对手的竞争中获得优势，还有就是降低成本等等。所以，大模型应用端多端不够，还需要生长出藤蔓围绕着企业流程开花结果，这个任务最终会落在&lt;strong&gt;应用（Application）&lt;/strong&gt;——内化了企业流程、借助了大模型能力的、带有可交互界面的程序。&lt;strong&gt;我自己预测2025年会成为大模型应用或AI应用之争。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;另外还有一个趋势也很明显，就是知识管理和协作。我们都说这波AI浪潮把原来“没用”的非结构化数据给激活了，嗯，所以我们马上会看到那些原来堆在角落里面的“冷”文件和知识（类似wiki）会被大量启用，&lt;strong&gt;“热”文件和知识会爆炸性增长，知识的协作和管理会成为新的问题&lt;/strong&gt;——&lt;strong&gt;就像你有再多的先进坦克和战车，却因为无序的交通都堵在阿登森林了&lt;/strong&gt;。基于大模型的知识管理和协作，会在12月专门写一篇文章好好分享一下我自己的见解，希望能找到共鸣的客户以及开发者。&lt;/p&gt;
&lt;h4 id="3ai从业者观察"&gt;&lt;a href="#3ai%e4%bb%8e%e4%b8%9a%e8%80%85%e8%a7%82%e5%af%9f" class="header-anchor"&gt;&lt;/a&gt;3.AI从业者观察
&lt;/h4&gt;&lt;p&gt;因为我看到的不代表真相，所以这一章节会很短，仅仅分享两个发现。&lt;/p&gt;
&lt;h5 id="01-ai技术的下坡"&gt;&lt;a href="#01-ai%e6%8a%80%e6%9c%af%e7%9a%84%e4%b8%8b%e5%9d%a1" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;01 AI技术的下坡&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-xia/002-89e2dc75.png"&gt;&lt;/p&gt;
&lt;p&gt;图：技术采用生命周期。现阶段的AI大模型市场似乎正处于过高期望之后的下坡过程中。&lt;/p&gt;
&lt;p&gt;有两个感受（非证据）可以说明这一点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;关于AI大模型的自媒体数量在减少，从搜索引擎趋势，加上我和几个业内朋友的blog、公众号以及X的阅读量下降趋势也可以佐证这一点，下半年虽然市场理性回归，但整体热度是在下降的。OpenAI不再持续放大招可能也是重要原因之一；&lt;/li&gt;
&lt;li&gt;我前期接触了很多因为AI热潮而在企业内部抽调精干力量组成的AI小组、AI研究组和AI创新组等团队的成员，但下半年有不少类似团队已经解散，人员回归到原有岗位。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有一点就是上半年加我微信好友的很多独立开发者或在职的个人，多半也已经在寻觅了半年机会之后放弃了继续探索，这一点在和他们交流，以及他们朋友圈的内容变化中可以明显感知。&lt;/p&gt;
&lt;p&gt;但是这并不是坏事，上图已经告诉我们，这是必然规律。&lt;/p&gt;
&lt;h5 id="02-价值开始显现"&gt;&lt;a href="#02-%e4%bb%b7%e5%80%bc%e5%bc%80%e5%a7%8b%e6%98%be%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;02 价值开始显现&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;第二个观察就是目前还奔跑在AI大模型应用赛道的公司，很多已经开始创造出客户价值，有了自己的优势。&lt;/p&gt;
&lt;p&gt;包括在海外风生水起的Dify，在内容提取端的合合，以及肯定会成为国内AI巨无霸的火山引擎。当然我们还看到了一些深耕垂直行业的优秀团队，特别是在法律、医药、教育等行业。我们也在今年6月份开始做了产品转身，现在已经不再烦恼人家问我们“你们和dify/fastgpt/ragflow有什么区别？”，因为赛道已经开始慢慢不一样了，而且这个不一样依然是产品层面的，和服务什么行业无关。关于这一点，也还是在12月的那篇文章再来分享吧。&lt;/p&gt;
&lt;h3 id="三总结"&gt;&lt;a href="#%e4%b8%89%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;三、总结
&lt;/h3&gt;&lt;p&gt;好了，这篇文章就写到这里了，上篇从个人观点分享了RAG技术这一年的变化，下篇分析了RAG的市场发展情况。最后介绍一下我们自己的产品品牌TorchV，我们通过RAG进行扩展延伸，已经服务了不少大型客户。如果您想在企业中引入AI大模型能力来提升业务，欢迎找我们沟通，以下是我的微信二维码。&lt;/p&gt;
&lt;p&gt;ps：我们最近在招聘实习生，如果您对AI发展非常感兴趣，是在杭州的在校大学生，且有一定的vue/Java/Python编程能力，请联系我。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-xia/003-5a2bfd7b.jpg"&gt;&lt;/p&gt;</description></item><item><title>RAG的2024—随需而变，从狂热到理性（上）</title><link>https://xiaobox.github.io/p/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-shang/</link><pubDate>Mon, 25 Nov 2024 06:27:48 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-shang/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-shang/cover.jpg" alt="Featured image of post RAG的2024—随需而变，从狂热到理性（上）" /&gt;&lt;p&gt;首先感谢那些依然关注着“土猛的员外”的朋友们，对你们，肃然起敬！&lt;/p&gt;
&lt;p&gt;转眼到了2024年尾，和小伙伴一起创立TorchV也接近一年。虽然这一年做了很多事情，但从技术层面上来说，RAG肯定是不得不提的，所以今天分享一下作为大模型应用创业者所感知的这一年，RAG技术和市场环境的变化。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;首先声明，本文更多来自于本人主观感受，且内容更多是回顾性的结论，不建议作为其他文章的引用材料。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;主要内容包括：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RAG技术变化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主要架构变化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;技术细节变化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;市场需求变化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;上半年：AI无所不能，大而全；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下半年：回归理性，小而难；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;明年预测：应用才是王道；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从业者变化。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;p&gt;其中技术部分放在上篇，市场需求变化放在下篇。&lt;/p&gt;
&lt;h3 id="一rag技术变化"&gt;&lt;a href="#%e4%b8%80rag%e6%8a%80%e6%9c%af%e5%8f%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;一、RAG技术变化
&lt;/h3&gt;&lt;p&gt;RAG（检索增强生成）其实是由两部分组成的，分别是检索和大模型生成。当然，既然有检索就必然会先有索引，包括chunking、embedding等动作都是为了建立更好的索引。因为我们之前从零开始创建并运营了一个千万级用户的智能问答类产品，所以在2021年左右其实就已经采用Java技术栈在使用RAG里面“RA”的大部分技术了。在2023年年中，RAG这个词突然火了起来，于是我们就立马就扑进去了，而且相信RAG在企业应用领域比纯粹使用大模型会更具实用性，至少在三年之内是这样的（随着最近传闻Scaling Law遇到瓶颈，好像这个时间还有可能被推后）。短短几个月，RAG开始的火爆程度甚至有超过LLM的趋势，在2024年1月我甚至还参加了“&lt;a class="link" href="http://mp.weixin.qq.com/s?__biz=MzI2MTM2MTgxNQ==&amp;amp;mid=2247486569&amp;amp;idx=1&amp;amp;sn=1bb4d0b44745637079846fa69ea5c3c5&amp;amp;chksm=ea5ad591dd2d5c8743030e2b3e40f376b69d03d473c9a6259f161d6a79c8b562706db3ba197a&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;共识粉碎机”的EP15讨论会&lt;/a&gt;，主要话题就是**“2024年是否会成为RAG元年？”**。&lt;/p&gt;
&lt;h4 id="1主要架构变化"&gt;&lt;a href="#1%e4%b8%bb%e8%a6%81%e6%9e%b6%e6%9e%84%e5%8f%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;1.&lt;strong&gt;主要架构变化&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;虽然RAG的火热，各种架构思想就被大师们总结出来，最出名的莫过于下面这张图了：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-shang/001-3df79d17.png"&gt;&lt;/p&gt;
&lt;p&gt;RAG三种技术架构&lt;/p&gt;
&lt;p&gt;图1:RAG三种架构模式，来源于论文Retrieval-Augmented Generation for Large Language Models: A Survey。&lt;/p&gt;
&lt;p&gt;站在现在（2024年11月）再看，其实Advanced RAG应该还是最主流的架构。因为它的效果明显比Naive RAG要好，但比Modular RAG更容易实现。在实际应用中，我们还需要为客户考虑经济成本和维护成本，很多时候基于客户需求，在Advanced RAG上做一些对症下药，远比全家桶更具适应性。&lt;/p&gt;
&lt;h4 id="2技术细节变化"&gt;&lt;a href="#2%e6%8a%80%e6%9c%af%e7%bb%86%e8%8a%82%e5%8f%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;2.&lt;strong&gt;技术细节变化&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;相较于架构变化，技术细节的变化更加“风起云涌”，这一点从各自媒体的文章主题变化和从业者之间的交流内容中就能明显感知到。在RAG这个大框架之下，我们看到了每隔一段时间就会有一些技术细节被热炒，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;知识提取&lt;/li&gt;
&lt;li&gt;索引组织&lt;/li&gt;
&lt;li&gt;检索方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="01-知识提取"&gt;&lt;a href="#01-%e7%9f%a5%e8%af%86%e6%8f%90%e5%8f%96" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;01 知识提取&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;在2024年之前，我们看到的RAG面对的原始知识更多是非常标准的论文，一般是文字版PDF或者HTML，所以那时候内容提取还不是一个大问题。但是在2024年，随着各行各业都开始使用RAG之后，五花八门的文件类型解析变成了从业者们头痛的事情，于是，我们发现有好一波人开始专注于知识提取。&lt;/p&gt;
&lt;p&gt;于是我们看到在2024年，出现了很多专门做知识提取的公司，比如已经在科创板上市合合信息（其实已经是老牌企业），在文件解析方面就非常出色，还有新创的SoMark等，当然大厂在这一块肯定也都涉足，特别是OCR解析，比如百度开源的PaddleOCR，其实还挺良心的。&lt;/p&gt;
&lt;p&gt;我们也是如此，看到现成的python组件在面对多样的知识文件类型时渐感无力，于是就开始在知识提取方面花时间。从多类型文件的解析，包括一些老文件格式（.doc、.xls、.ppt等）的解析，再到比较难的PDF表格解析，需要去处理非常复杂的合并/拆分单元格，并且也有了具备原创知识产权的提取工具组件。&lt;/p&gt;
&lt;p&gt;但是我一直在想，是否可以把整个工作流再前置，如果我们提供知识生产工具，用户可以在我们的知识管理和生产工具上进行知识生产和协作，那么知识提取是不是会更流畅？这个问题可以单独写一篇文章，就放在这个篇文章的上下篇结束之后吧。&lt;/p&gt;
&lt;h5 id="02-索引组织"&gt;&lt;a href="#02-%e7%b4%a2%e5%bc%95%e7%bb%84%e7%bb%87" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;02 索引组织&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;在RAG里面，索引组织可能是相对比较有技巧性的部分了。&lt;/p&gt;
&lt;p&gt;了解RAG的人都听过chunking，不严谨地说，就是把文件切成若干片段，为的是可以在LLM的窗口大小之内进行作业。常见的chunking方式有按固定token数量的，有按Page的，也有通过NLTK等来进行切分的。但我始终觉得使用哪种chunking方法并不是重点，如果您使用的LLM的上下文窗口较大，对于一些4、5页的文件，还切它干嘛呢，直接扔进去就完事了。但在chunking过程中，其实有两个隐含的技巧是可以快速提升准确率的：指代消解和附加元数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;指代消解&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们在实操中常见的是两种，一种是切割的时候，上一页有详细信息，而下一页中只有“这种方法”来指代。这时候最简单的方法就是做chunk叠加；还有一种就是类似合同，甲方乙方的具体名字只在最开头的地方出现，剩下全文都用“甲方”、“乙方”指代，这种情况因为被指代的名称是比较好获取的，可以直接加在chunk中，如下所示（伪代码），chunk_meta里面可以设置甲方乙方的全称：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;chunk&amp;gt; &amp;lt;chunk_meta&amp;gt; ... &amp;lt;/chunk_meta&amp;gt; &amp;lt;chunk_content&amp;gt; ... &amp;lt;/chunk_content&amp;gt; &amp;lt;/chunk&amp;gt; &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;附加元数据&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其实元数据的索引存储结构和上面的指代消解示例中差不多，不同的是，我们需要关心的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;元数据从哪里来？&lt;/li&gt;
&lt;li&gt;在检索的过程中什么情况下激活元数据过滤？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;元数据的来源只可能是&lt;strong&gt;人工标注&lt;/strong&gt;或者&lt;strong&gt;应用侧&lt;/strong&gt;（包括文件处理时）生成的，这里我们肯定先不去考虑人工标注了。从应用侧的设计来看，其实是可以拿到很多元数据的。比如我们上传文件、撰写知识的时候，自然可以拿到时间、文件名称（正常命名都会含有实体），也可以在交互设计中要求作者进行分类选择，简介编写等，无一不是增加元数据的手段。&lt;/p&gt;
&lt;p&gt;但我们在索引中增加了元数据内容之后，也不是强行要激活元数据过滤的。我们在实操中是会设置自定义的&lt;strong&gt;系统槽位（system_slot）&lt;/strong&gt;，如果用户提问的文本中，也包含比较多的意图和实体信息，且与系统槽位存在匹配，才会激活。会先使用BM25进行元数据过滤，再进行dense检索。更具语义信息的dense索引有时候因为维度限制会丢失一些信息，或者说dense检索对于元数据过滤并不那么精准。这种设计一方面可以减少检索的时间，另一方面就是可以提高带有时间和实体（如城市、公司、部门等）意图的提问的准确率。说起来这好像是一种“退步”，因为当LLM（或GPT）所向披靡的时候，我们也是一度有点看不上之前的技术的，但是在企业应用实践中，客户问题的解决率才是一切的根本。所以，在TorchV AI中，我们让NLU和Slot（槽位填充）回归了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-shang/002-725c8649.png"&gt;&lt;/p&gt;
&lt;p&gt;使用槽位填充的对话&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;是否使用Graph&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对了，还有一个大家争论比较多的索引组织方式就是Graph或者叫图数据，将node（实体）和relation（关系）通过图论组织起来。我们团队从2015年开始一直有在使用neo4j开发一些有趣的应用，在寻找人类直观上不太容易识别的点与点关联的方面确实非常出色。但是这个技术非常大的问题就是索引建立，从关系数据库把结构化数据迁移到Graph数据库中还算容易，但是面对成千上万的非结构化数据的时候，说实话目前也就是只能让GPT4以上的LLM来帮忙了。和重度使用过图知识库的朋友聊的过程中，他们的一致感受是：花费太高昂，但是场景太薄弱，依然还存在手工校对的工作。换句话说，如果花了10万的费用，但在实际使用中却没这么多使用场景，得不偿失。&lt;/p&gt;
&lt;p&gt;当然，如果您手头的工作非Graph不能解决，那就大胆去All-in吧。我们目前的系统并没有使用图数据库。&lt;/p&gt;
&lt;h5 id="03-检索方法"&gt;&lt;a href="#03-%e6%a3%80%e7%b4%a2%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;03 检索方法&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;有句话（我说的）不一定说的对，在某种意义上，&lt;strong&gt;知识提取&lt;/strong&gt;决定了专有知识的&lt;strong&gt;完整性&lt;/strong&gt;，&lt;strong&gt;索引组织&lt;/strong&gt;决定了&lt;strong&gt;回答准确率&lt;/strong&gt;，而&lt;strong&gt;检索方法&lt;/strong&gt;则在减少幻觉上有重要意义。&lt;/p&gt;
&lt;p&gt;如果我们可以准确检索召回，把非常明确的内容加上指令prompt，给到LLM处理，那么LLM给出的结果几乎不太会一本正经胡说八道。只有检索召回的内容和用户原问题不相关，或者召回内容存在多个不置可否的知识内容时，LLM才会按心情（概率和 temperature）选择错误知识或者使用预训练时学习的知识进行回答。所以，检索做得好，可以将整个RAG幻觉尽可能多的变为白盒。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hybird&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但是说到检索，我相信现在大部分的RAG系统都已经用上了Hybird检索了吧，一般来说也就是BM25+语义相似检索的混合检索。&lt;/p&gt;
&lt;p&gt;BM25有自己的固有用武之地，就像前面说的元数据过滤，还有就是一些在类似产品型号和专业术语的检索上，其精确度和稳定性是远高于语义相似检索的。&lt;/p&gt;
&lt;p&gt;语义相似检索有很多方法，因为我们主要用的还是Elasticsearch（也有Milvus），所以其实真正的语义相似检索就是ANN，说的更具体就是以HNSW为主的相似度算法。这个HNSW（最小可导航世界）的逻辑解释起来有点麻烦，但是我可以打一个比方：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;比如你需要从一个城市的南部坐公交去北部，我们要选择最短的坐车路线（含换乘），那么有两种方式选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;knn：将所有可能的公交路线（含换乘）做一个整理，假设有28900条路线，然后按花费时间进行排序，选择Top1或者Top n。抛开语义理解错误的问题，这种方法是非常精确的，但是耗时巨大，也可以认为是一种暴力检索；&lt;/li&gt;
&lt;li&gt;ann：还有一种就是相似最近邻，我们这里说到更多的是hnsw。为了让非技术专业的朋友可以听懂，不严谨地说，看着地图，从28900条路线中，选择出发地和目的地两点连线附近的50或100条公交路线。这种方法的效率极高，可能耗时只需要knn的万分之一，但它的问题在于无法确定这50或100条里面哪几条才是最好的。于是，如果你采用ann却不用rerank的话，就会比较拉垮了。&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;RRF Fusion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;RAG-Fusion主要是使用多个不同类型的检索方式进行检索，并按RRF（倒数排序融合）公式进行综合排名的一种检索方式。&lt;/p&gt;
&lt;p&gt;多种检索方式包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sparse（稀疏）检索，比如BM25；&lt;/li&gt;
&lt;li&gt;Dense（稠密）检索，比如语义相似度检索；&lt;/li&gt;
&lt;li&gt;还有就是使用不同配比的混合检索（在TorchV AI中，我们采用alpha值来做BM25和ANN的结果权重配比）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后使用RRF公式&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;RRF(d) = Σ(r ∈ R) 1 / (k + r(d))&lt;/p&gt;
&lt;p&gt;具体公式就不解释了，有兴趣的朋友自己查资料吧。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;来进行再排序，得出一个综合结果。其好处是使用不同的检索器，可以在各类不同问题场景下得到一个“思考”更周密的答案。我们在的系统里面有一个turbo开关，打开就会进行增强检索，其中也包括了该方法，且在召回率方面是有一定效果的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rerank&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其实我应该不用再讲reank了，如果您看过上面hnsw算法的话，就能知道rerank的作用了。TorchV AI在使用rerank的时候也加入了自己的一些优化算法，比如归一化处理和密度函数（舍弃相关性较低的返回结果）。在使用rerank前后，准确率相差确实很大，但你要平心而论，元数据过滤对准确率的提升可能会更明显。因为rerank相对比较被动（根据前序召回的结果，有时候是矮子里拔将军），而元数据过滤则是直接在检索召回环节产生影响，相对更加主动（自己对召回哪些结果起到重要作用）。嗯，这是我自己的理解，不一定对。&lt;/p&gt;
&lt;h4 id=""&gt;&lt;a href="#" class="header-anchor"&gt;&lt;/a&gt;
&lt;/h4&gt;&lt;p&gt;3.&lt;strong&gt;技术部分小结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OK，RAG技术变化我想先做个小结。&lt;/p&gt;
&lt;p&gt;关于RAG的大流程已经有太多文章了，我自己也写了不少，所以本文我更希望是从点而不是面的角度来讲一些技术实践上发现的变化。其实从RAG本身的各环节技术来说，没有出现新的现象级组件，2024这一年，看到更多的，是碰撞实际需求、探索最佳实践、内化到系统能力中的一个过程。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;市场变化的部分，下周差不多时间发布，敬请期待&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;如果您对TorchV AI的产品有兴趣，也可以直接联系我：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-25-rag-de-2024-sui-xu-er-bian-cong-kuang-re-dao-li-xing-shang/003-6d254516.jpg"&gt;&lt;/p&gt;</description></item><item><title>Redis6.0 以后为什么使用了多线程？</title><link>https://xiaobox.github.io/p/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/</link><pubDate>Mon, 18 Nov 2024 02:04:32 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/cover.jpg" alt="Featured image of post Redis6.0 以后为什么使用了多线程？" /&gt;&lt;p&gt;Redis 所谓的单线程并不是所有工作都是只有一个线程在执行，而是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的，Redis 在处理客户端的请求时包括获取 (socket 读）、解析、执行、内容返回 (socket 写） 等都由一个顺序串行的主线程处理。&lt;/p&gt;
&lt;p&gt;这就是所谓的“&lt;strong&gt;单线程&lt;/strong&gt;”。这也是 Redis 对外提供键值存储服务的主要流程。
由于 Redis 在处理命令的时候是单线程作业的，所以会有一个 Socket 队列，每一个到达的服务端命令来了之后都不会马上被执行，而是进入队列，然后被线程的事件分发器逐个执行。如下图：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/001-b7c0094d.png"&gt;&lt;/p&gt;
&lt;p&gt;至于 Redis 的其他功能， 比如持久化、异步删除、集群数据同步等等，其实是由额外的线程执行的。 可以这么说，Redis 工作线程是单线程的。但是在 4.0 之后，对于整个 Redis 服务来说，还是多线程运作的。&lt;/p&gt;
&lt;h2 id="60-之前为什么要使用单线程"&gt;&lt;a href="#60-%e4%b9%8b%e5%89%8d%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e4%bd%bf%e7%94%a8%e5%8d%95%e7%ba%bf%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;6.0 之前为什么要使用单线程
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/002-55bc246f.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在使用 Redis 时，Redis 主要受限是在内存和网络上，CPU 几乎没有性能瓶颈的问题。&lt;/li&gt;
&lt;li&gt;以 Linux 系统为例子，在 Linux 系统上 Redis 通过 pipelining 可以处理 100w 个请求每秒，而应用程序的计算复杂度主要是 O(N) 或 O(log(N)) ，不会消耗太多 CPU。&lt;/li&gt;
&lt;li&gt;使用了单线程后，提高了可维护性。多线程模型在某些方面表现优异，却增加了程序执行顺序的不确定性，并且带来了并发读写的一系列问题，增加了系统复杂度。同时因为线程切换、加解锁，甚至死锁，造成一定的性能损耗。&lt;/li&gt;
&lt;li&gt;Redis 通过 AE 事件模型以及 IO 多路复用等技术，拥有超高的处理性能，因此没有使用多线程的必要&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="60-之后的多线程主要解决什么问题"&gt;&lt;a href="#60-%e4%b9%8b%e5%90%8e%e7%9a%84%e5%a4%9a%e7%ba%bf%e7%a8%8b%e4%b8%bb%e8%a6%81%e8%a7%a3%e5%86%b3%e4%bb%80%e4%b9%88%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;6.0 之后的多线程主要解决什么问题
&lt;/h2&gt;&lt;p&gt;近年来底层网络硬件性能越来越好，Redis 的性能瓶颈逐渐体现在网络 I/O 的读写上，单个线程处理网络 I/O 读写的速度跟不上底层网络硬件执行的速度。&lt;/p&gt;
&lt;p&gt;Redis 在处理网络数据时，调用 epoll 的过程是阻塞的，这个过程会阻塞线程。如果并发量很高，达到万级别的 QPS，就会形成瓶颈，影响整体吞吐能力&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/003-e1c54fcd.png"&gt;&lt;/p&gt;
&lt;p&gt;既然读写网络的 read/write 系统调用占用了 Redis 执行期间大部分 CPU 时间，那么要想真正做到提速，必须改善网络 IO 性能。我们可以从这两个方面来优化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;提高网络 IO 性能，典型实现方式比如使用 DPDK 来替代内核网络栈的方式&lt;/li&gt;
&lt;li&gt;使用多线程，这样可以充分利用多核 CPU，同类实现案例比如 Memcached。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;协议栈优化的这种方式跟 Redis 关系不大，所以最便捷高效的方式就是支持多线程。总结起来，redis 支持多线程就是以下两个原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以充分利用服务器 CPU 的多核资源，而主线程明显只能利用一个&lt;/li&gt;
&lt;li&gt;多线程任务可以分摊 Redis 同步 IO 读写负荷，降低耗时&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;6.0 版本优化之后，主线程和多线程网络 IO 的执行流程如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/004-51bc3556.png"&gt;&lt;/p&gt;
&lt;p&gt;具体步骤如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主线程建立连接，并接受数据，并将获取的 socket 数据放入等待队列；&lt;/li&gt;
&lt;li&gt;通过轮询的方式将 socket 读取出来并分配给 IO 线程；&lt;/li&gt;
&lt;li&gt;之后主线程保持阻塞，一直等到 IO 线程完成 socket 读取和解析；&lt;/li&gt;
&lt;li&gt;I/O 线程读取和解析完成之后，返回给主线程 ，主线程开始执行 Redis 命令；&lt;/li&gt;
&lt;li&gt;执行完 Redis 命令后，主线程阻塞，直到 IO 线程完成 结果回写到 socket 的工作；&lt;/li&gt;
&lt;li&gt;主线程清空已完成的队列，等待客户端新的请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本质上是将主线程 IO 读写的这个操作 独立出来，单独交给一个 I/O 线程组处理。
这样多个 socket 读写可以并行执行，整体效率也就提高了。同时注意 Redis 命令还是主线程串行执行。&lt;/p&gt;
&lt;p&gt;利用多核来分担 I/O 读写负荷。在事件处理线程每次获取到可读事件时，会将所有就绪的读事件分配给 I/O 线程，并进行等待，在所有 I/O 线程完成读操作后，事件处理线程开始执行任务处理，在处理结束后，同样将写事件分配给 I/O 线程，等待所有 I/O 线程完成写操作。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;int handleClientsWithPendingReadsUsingThreads(void) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; /* Distribute the clients across N different lists. */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; listIter li;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; listNode *ln;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; listRewind(server.clients_pending_read,&amp;amp;li);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; int item_id = 0;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; // 将等待处理的客户端分配给 I/O 线程
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; while((ln = listNext(&amp;amp;li))) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; client *c = listNodeValue(ln);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; int target_id = item_id % server.io_threads_num;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; listAddNodeTail(io_threads_list[target_id],c);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; item_id++;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; /* Wait for all the other threads to end their work. */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; // 轮训等待所有 I/O 线程处理完
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; while(1) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; unsigned long pending = 0;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; for (int j = 1; j &amp;lt; server.io_threads_num; j++)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; pending += io_threads_pending[j];
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; if (pending == 0) break;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; return processed;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本质上是利用多核的多线程让多个 IO 的读写加速。&lt;/p&gt;
&lt;h3 id="局限性"&gt;&lt;a href="#%e5%b1%80%e9%99%90%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;局限性
&lt;/h3&gt;&lt;p&gt;6.0 版本的多线程并非彻底的多线程，I/O 线程只能同时执行读或者同时执行写操作，期间事件处理线程一直处于等待状态，并非流水线模型，有很多轮训等待开销。&lt;/p&gt;</description></item><item><title>Milvus实战：如何用一个数据库提升你的AI项目性能</title><link>https://xiaobox.github.io/p/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/</link><pubDate>Fri, 11 Oct 2024 08:22:08 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/cover.jpg" alt="Featured image of post Milvus实战：如何用一个数据库提升你的AI项目性能" /&gt;&lt;h2 id="回顾"&gt;&lt;a href="#%e5%9b%9e%e9%a1%be" class="header-anchor"&gt;&lt;/a&gt;回顾
&lt;/h2&gt;&lt;p&gt;在上一文中我们使用 LlamaIndex 整合 智谱 AI 的 GLM-4 和 Embedding-3 模型一起构建 RAG 应用。&lt;/p&gt;
&lt;p&gt;在上篇文章的最后，我们发现因为 Embedding-3 模型是同步调用的，所以从测试效果看比较慢。每一次运行都产生了大量的 http 同步请求。文末我说解决的办法可以在本地部署一个开源的 embedding 模型，这样就不会产生远程的 http 调用了，而且也比较省钱。&lt;/p&gt;
&lt;p&gt;这是个办法，但实际上还有其他的好办法。&lt;/p&gt;
&lt;p&gt;我们可以将 &lt;strong&gt;文档通过 embedding 模型产生的向量存储起来，这样相同的文档，只有在第一次 embedding 时会慢一些，再次检索时，可以快速地将已经保存好的向量查询出来使用。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="本地文件存储"&gt;&lt;a href="#%e6%9c%ac%e5%9c%b0%e6%96%87%e4%bb%b6%e5%ad%98%e5%82%a8" class="header-anchor"&gt;&lt;/a&gt;本地文件存储
&lt;/h2&gt;&lt;p&gt;利用 LlamaIndex 的 API ，我们可以非常方便地把向量存储到本地文件，以下是一个例子，我把向量存储到项目的 &lt;code&gt;index&lt;/code&gt;目录下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_or_create_index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 检查是否存在有效的持久化索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;正在加载现有索引。..&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;storage_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StorageContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_defaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;persist_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_index_from_storage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;未找到有效的现有索引，正在创建新索引。..&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 使用预定义的 DATA_DIR 常量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 创建新索引，显示 embedding 进度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;show_progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 持久化索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;persist_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;索引已创建并保存到本地。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来代码多，实际上重要的就是这两行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;storage_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StorageContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_defaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;persist_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_index_from_storage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也很容易理解，见文知意。&lt;/p&gt;
&lt;p&gt;索引创建后，&lt;code&gt;index&lt;/code&gt; 会自动创建一些文件来保存向量信息：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/001-33676f25.png"&gt;&lt;/p&gt;
&lt;h2 id="向量数据库"&gt;&lt;a href="#%e5%90%91%e9%87%8f%e6%95%b0%e6%8d%ae%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;向量数据库
&lt;/h2&gt;&lt;p&gt;一般情况下，比如小型项目，将向量数据保存在系统文件中就已经够用了。但是，在中大型项目中，由于数据规模较大，使用人数较多，为了方便管理和扩展，我们会使用专业的向量数据库来存储和管理向量数据。&lt;/p&gt;
&lt;p&gt;你可以借助下图了解下向量数据库在 AIGC 应用架构中的位置和作用&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/002-4d7dc065.png"&gt;&lt;/p&gt;
&lt;h3 id="向量数据库选型"&gt;&lt;a href="#%e5%90%91%e9%87%8f%e6%95%b0%e6%8d%ae%e5%ba%93%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;向量数据库选型
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;RAG 系统的成功在很大程度上取决于其高效地获取和处理海量信息的能力。向量数据库又在其中发挥了不可替代的作用，并构成了 RAG 系统的核心&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;不看不知道，作为一个数据库软件 ，目前向量数据库领域是真卷啊，打眼一看至少有几十个。知名的也得有 10 几个。&lt;/p&gt;
&lt;p&gt;说实话，最开始还真有些茫然，有点儿挑花眼了，我们这里列举几个知名的向量数据库：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Milvus&lt;/strong&gt; 是一个 2019 年开源的纯向量数据库，号称全球最先进的开源向量数据库。它是 &lt;code&gt;LF AI &amp;amp; Data Foundation&lt;/code&gt;（简称 &lt;strong&gt;LFAI，它相当于 CNCF 在云原生界的地位&lt;/strong&gt;）赞助的毕业项目&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Chroma&lt;/strong&gt; 是一个相对较新的向量数据库，目前它的设计确实是以单节点模式为主，主要用于中小型应用或开发测试环境。然而，对于需要更高可用性和横向扩展能力的生产环境，Chroma 当前的版本可能还不完全满足需求。Chroma 内置了 &lt;code&gt;SQLite&lt;/code&gt; 作为其底层存储引擎&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Weaviate&lt;/strong&gt; ：是一个云原生的、开源的向量数据库。专为大规模的向量数据存储和检索设计。它结合了向量搜索和图数据库的优势，适用于机器学习、推荐系统、图像识别和自然语言处理等场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Faiss&lt;/strong&gt; ：由 Facebook AI Research 开发的 Faiss 是一个开源库，用于快速、密集向量相似性搜索和分组&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Qdrant&lt;/strong&gt; 是一个开源的向量数据库，专为高效的大规模向量数据存储和检索设计。它适用于机器学习、推荐系统、图像识别和自然语言处理等场景，提供了高性能和易用性的结合。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PGVector&lt;/strong&gt; 是一个基于 PostgreSQL 的扩展插件，旨在提供强大的向量存储和查询功能，PGVector 可以无缝集成到现有的 PostgreSQL 数据库中，用户无需迁移现有的数据库即可开始使用向量搜索功能。因为是 PostgreSQL 插件，借助 PostgreSQL 的长期开发和优化，PGVector 继承了其可靠性和稳健性，同时在向量化处理方面进行了增强。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;整体上看在向量数据库领域有这么几类玩家：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;专做向量数据库的，大部分是开源的，如 Chroma、Weaviate 等&lt;/li&gt;
&lt;li&gt;做关系型数据库的扩展或插件，如 PGVector&lt;/li&gt;
&lt;li&gt;做 NoSQL 数据库的功能扩展或兼容，如 &lt;code&gt;Elasticsearch&lt;/code&gt;、 &lt;code&gt;Redis&lt;/code&gt;、 &lt;code&gt;ClickHouse&lt;/code&gt; 等&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;太多了，真是太多了，最开始我做选型的时候真是有点儿挑花眼了。最后，一点点缩小范围，最终进入决赛圈的是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Qdrant&lt;/li&gt;
&lt;li&gt;Weaviate&lt;/li&gt;
&lt;li&gt;Milvus&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你可以通过 &lt;a class="link" href="https://zilliz.com.cn/comparison" target="_blank" rel="noopener"
 &gt;https://zilliz.com.cn/comparison&lt;/a&gt; 来了解各向量数据库之间的对比情况&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/003-5681900f.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/004-331f1c43.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最终我选择了 Milvus&lt;/strong&gt; 原因是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;它确实很知名，看了那么多评测，各方面性能都很能打&lt;/li&gt;
&lt;li&gt;我个人觉得比较重要的是它还有数据库管理客户端 &lt;code&gt;attu&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;向量数据库不像我之前使用过的关系型数据库，一般是没有像 &lt;code&gt;Navicat&lt;/code&gt; 、&lt;code&gt;DataGrip&lt;/code&gt; 这样的数据库管理客户端的。一般只有 CRUD 接口或 CLI 客户端。这对于初学者了解和学习向量数据库不太友好，所以我还是特别希望有这样一个有 GUI 图形界面、看得见摸得着的客户端的，而 Milvus 正好是有的。就是 &lt;code&gt;attu&lt;/code&gt; （可以通过 &lt;a class="link" href="https://github.com/zilliztech/attu" target="_blank" rel="noopener"
 &gt;https://github.com/zilliztech/attu&lt;/a&gt; 下载）&lt;/p&gt;
&lt;p&gt;如果你也和我一样在 Qdrant、Weaviate、Milvus 之间纠结的话，可以参考网上一位大哥对它们的评价：&lt;strong&gt;“总结起来就是，Qdrant 开销特别小，Weaviate 支持向量搜索、对象存储和倒排索引的组合，Milvus 性能最强、花活最多。”&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="chroma"&gt;&lt;a href="#chroma" class="header-anchor"&gt;&lt;/a&gt;Chroma
&lt;/h2&gt;&lt;p&gt;LlamaIndex 官方的例子使用的是 Chroma 作为向量数据库进行向量存储。&lt;/p&gt;
&lt;p&gt;默认情况下，Chroma 会将向量数据存储在本地文件系统中。我们就以 Chroma 为例写个例子。&lt;/p&gt;
&lt;p&gt;Chroma 不需要安装外部软件，安装导入相关的库就可了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;import chromadb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;from llama_index.vector_stores.chroma import ChromaVectorStore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在导入了 Chroma 相关的库后，我们将 &lt;code&gt;load_or_create_index()&lt;/code&gt; 方法调整一下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_or_create_index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 初始化客户端，设置数据保存路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chromadb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PersistentClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./chroma_db&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 创建或获取集合&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;chroma_collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_or_create_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;quickstart&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 将 chroma 指定为上下文的 vector_store&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChromaVectorStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chroma_collection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chroma_collection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;storage_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StorageContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_defaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 检查集合是否为空&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chroma_collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 如果集合为空，加载文档并创建新的索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;已创建新的索引&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 如果集合不为空，直接从 vector_store 加载索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_vector_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;已加载现有索引&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到也很简单。程序运行后，&lt;code&gt;chroma_db&lt;/code&gt; 文件夹下会自动创建以下文件：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/005-79a82529.png"&gt;&lt;/p&gt;
&lt;p&gt;前文中我们提到过 chroma 内置了 &lt;code&gt;SQLite&lt;/code&gt; ，这里就体现出来了。&lt;/p&gt;
&lt;h2 id="milvus"&gt;&lt;a href="#milvus" class="header-anchor"&gt;&lt;/a&gt;Milvus
&lt;/h2&gt;&lt;p&gt;在使用 Milvus 前我们需要先安装它。它有多种安装方式，我本地通过 Docker-Compose 安装&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;3.5&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;etcd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;milvus-etcd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;quay.io/coreos/etcd:v3.5.14&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ETCD_AUTO_COMPACTION_MODE=revision&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ETCD_AUTO_COMPACTION_RETENTION=1000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ETCD_QUOTA_BACKEND_BYTES=4294967296&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ETCD_SNAPSHOT_COUNT=50000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CMD&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;etcdctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;endpoint&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;health&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;30s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;20s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;minio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;milvus-minio&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minio/minio:RELEASE.2023-03-20T20-16-18Z&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MINIO_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minioadmin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MINIO_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minioadmin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;9001:9001&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;9000:9000&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minio server /minio_data --console-address &amp;#34;:9001&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CMD&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;curl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-f&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;http://localhost:9000/minio/health/live&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;30s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;20s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;milvus-standalone&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;milvusdb/milvus:v2.3.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;milvus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;run&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;standalone&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;security_opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;seccomp:unconfined&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MINIO_REGION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;us-east-1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ETCD_ENDPOINTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;etcd:2379&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MINIO_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minio:9000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CMD&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;curl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-f&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;http://localhost:9091/healthz&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;30s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;start_period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;90s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;20s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;19530:19530&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;9091:9091&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;etcd&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;minio&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;milvus&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装好以后，可以看到它内部有三个容器：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/006-b694b4d5.png"&gt;&lt;/p&gt;
&lt;p&gt;接着我们安装 attu，它的安装比较简单，下载相关平台的安装文件安装即可&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/007-4bc242b7.png"&gt;&lt;/p&gt;
&lt;p&gt;attu 安装完成后打开进行 Milvus 的连接：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/008-1963366f.png"&gt;&lt;/p&gt;
&lt;p&gt;默认地址是 &lt;code&gt;127.0.0.1:19530&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;接着，我们来到程序这里，进行连接和使用，同样，要先导入库&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;from llama_index.vector_stores.milvus import MilvusVectorStore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后我们调整一下之前的方法，改写一个新的方法来连接 Miluvs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_or_create_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 获取或创建索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; overwrite 设置为 False 意味着如果同名的集合已存在，将不会覆盖它。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; dim 是向量维度，必须与 embedding 模型的维度一致。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MilvusVectorStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;http://localhost:19530&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;overwrite&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;llamaindex_collection&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;storage_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StorageContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_defaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;storage_context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;已成功创建并存储新的索引。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_vector_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我相信如果你阅读了前文，知道这段代码的重要点在哪里。&lt;/p&gt;
&lt;p&gt;当 RAG 应用程序正常运行后，向量数据就被存储到了 Milvus 数据库中：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/009-ef9054d1.png"&gt;&lt;/p&gt;
&lt;p&gt;有了 GUI 界面，就比较直观地能感受到向量数据是个什么样子了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/010-fbd533b2.png"&gt;&lt;/p&gt;
&lt;p&gt;有关在 attu 中进行向量数据的查询等操作可以参数相关文档，本文就不多说了。&lt;/p&gt;
&lt;p&gt;使用向量数据库存储以后，我们再次运行查询，速度就很快了，因为第一次运行的时候就已经把文档 embedding 后的向量存储起来了，只需要从 Milvus 中加载查询就可以了，不用再走 http 远程调用。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-11-milvus-shi-zhan-ru-he-yong-yi-ge-shu-ju-ku-ti-sheng-ni-de-ai/011-14a85a67.png"&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;在本文中，我们深入探讨了如何通过 LlamaIndex 整合智谱 AI 的 GLM-4 和 Embedding-3 模型来构建 RAG 应用，并针对 Embedding-3 模型同步调用导致的性能瓶颈问题，提出了有效的解决方案。我们发现，将文档的向量存储起来，可以显著提高检索速度，避免了重复的 HTTP 同步请求，从而节省了成本和时间。&lt;/p&gt;
&lt;p&gt;通过本地文件存储和向量数据库的选型，我们对比了多种向量数据库的特点和性能，最终选择了 Milvus 作为我们的向量数据库。Milvus 以其卓越的性能和易用性脱颖而出，特别是其数据库管理客户端 attu，为初学者提供了友好的图形界面，使得向量数据库的管理和操作变得更加直观和便捷。&lt;/p&gt;
&lt;p&gt;在实际应用中，我们通过 Docker-Compose 安装了 Milvus，并利用 attu 进行了连接和操作。通过将向量数据存储到 Milvus 数据库中，我们显著提高了查询速度，因为文档的向量在第一次运行时就已经被存储起来，后续的查询可以直接从 Milvus 中加载，无需再次进行远程 HTTP 调用。&lt;/p&gt;
&lt;p&gt;此外，我们还探讨了使用 Chroma 作为向量数据库的方案，它内置了 SQLite，简化了安装和使用过程。通过 LlamaIndex 的 API，我们可以轻松地将向量存储到本地文件或 Chroma 数据库中，进一步增强了 RAG 应用的性能和可扩展性。&lt;/p&gt;
&lt;p&gt;总的来说，通过本文的探讨和实践，我们不仅解决了 RAG 应用中的性能问题，还为中大型项目提供了一种高效、可扩展的向量数据存储和管理方案。随着 AI 技术的不断发展，向量数据库在 AIGC 应用架构中的作用将越来越重要，而 Milvus 等向量数据库的选择和应用，将为构建更加智能和高效的 AI 应用提供强有力的支持。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;本文所涉及的完整代码在该项目中：https://github.com/xiaobox/llamaindex_test 大家可按需自取&lt;/em&gt;&lt;/p&gt;</description></item><item><title>提升RAG应用性能：使用智谱AI的GLM-4和Embedding-3模型优化文档检索</title><link>https://xiaobox.github.io/p/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/</link><pubDate>Tue, 08 Oct 2024 07:59:17 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/cover.jpg" alt="Featured image of post 提升RAG应用性能：使用智谱AI的GLM-4和Embedding-3模型优化文档检索" /&gt;&lt;h2 id="回顾"&gt;&lt;a href="#%e5%9b%9e%e9%a1%be" class="header-anchor"&gt;&lt;/a&gt;回顾
&lt;/h2&gt;&lt;p&gt;上文 &lt;a class="link" href="http://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247489761&amp;amp;idx=1&amp;amp;sn=949b3880cfd5c66ee4596c9f1803a420&amp;amp;chksm=eb6da767dc1a2e71f894da379278995c0af04f22eb0137d8b1cee2c6c54ecd712b83ff15f169&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;提速 RAG 应用：用 DeepSeek API 替换本地 Ollama 模型，LlamaIndex 实战解析&lt;/a&gt; 我们介绍了如何通过 DeepSeek 的 API 调用 &lt;code&gt;DeepSeek v2.5&lt;/code&gt; 模型 替换通过 Ollama 调用本地下载好的 &lt;code&gt;Qwen2.5&lt;/code&gt; 模型。&lt;/p&gt;
&lt;p&gt;这样做的目的是想通过 API 调用远程部署好的 LLM 给我们的 RAG 应用提提速。不然由于本地个人电脑计算资源的不足（我的电脑没有 GPU）会导致 RAG 应用运行缓慢。&lt;/p&gt;
&lt;p&gt;在我们的 RAG 应用中分别使用了两个模型 ，一个是 &lt;code&gt;embedding&lt;/code&gt; 模型，它的作用有这么几点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;文档嵌入（Document Embedding）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表示文档&lt;/strong&gt;：将文档转换为高维向量（embeddings），这些向量能够捕捉文档的语义信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;相似度计算&lt;/strong&gt;：通过计算查询和文档嵌入之间的相似度，找到与查询最相关的文档。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;查询嵌入（Query Embedding）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表示查询&lt;/strong&gt;：将用户的查询转换为高维向量，这些向量能够捕捉查询的语义信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检索相关文档&lt;/strong&gt;：通过计算查询嵌入和文档嵌入之间的相似度，找到与查询最相关的文档&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;文档检索（Document Retrieval）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高效检索&lt;/strong&gt;：通过向量数据库（如 Faiss、Annoy 等），快速找到与查询最相关的文档。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;相关性排序&lt;/strong&gt;：根据相似度得分对检索到的文档进行排序，选择最相关的文档作为生成回答的依据。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;生成回答（Answer Generation）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;融合信息&lt;/strong&gt;：将检索到的相关文档与查询结合，生成高质量的回答。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上下文感知&lt;/strong&gt;：利用检索到的文档作为上下文，生成更加准确和丰富的回答。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中第 4 点，要结合 LLM 来完成。所以这也是我们在 RAG 应用中使用第二个模型&amp;ndash;大语言模型（LLM） 的意义。&lt;/p&gt;
&lt;p&gt;我们再通过回顾 2 张图片来比较直观地了解下 &lt;code&gt;embedding&lt;/code&gt; 和 &lt;code&gt;LLM&lt;/code&gt; 在 RAG 中的作用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;embedding 过程&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/001-8736f38f.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RAG&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/002-08ef5269.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="问题"&gt;&lt;a href="#%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;问题
&lt;/h2&gt;&lt;p&gt;上文遗留的问题很明显，因为我们需要使用的 2 个模型通过 DeekSeek 的 API 只替换了其中的&lt;code&gt;LLM&lt;/code&gt;，而 &lt;code&gt;embedding&lt;/code&gt; 模型仍然用的是本地的。没有替换是因为 DeepSeek 的 API 不支持：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/003-94f77b14.png"&gt;&lt;/p&gt;
&lt;p&gt;引自 deepseek 文档&lt;/p&gt;
&lt;p&gt;虽然我们下载的 &lt;code&gt;embedding&lt;/code&gt; 模型 &lt;code&gt;BAAI/bge-base-zh-v1.5&lt;/code&gt; 比较小巧，在本地运行的速度也还行，但我还是想试一下调用远程部署好的更大更优秀的 &lt;code&gt;embedding&lt;/code&gt; 模型后会怎样？&lt;/p&gt;
&lt;p&gt;于是我将目光转向了另一个很知名，同样很优秀的国产 AI 公司 &lt;code&gt;智谱 AI&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="智谱-ai"&gt;&lt;a href="#%e6%99%ba%e8%b0%b1-ai" class="header-anchor"&gt;&lt;/a&gt;智谱 AI
&lt;/h2&gt;&lt;p&gt;这两年 AI 的发展如火如荼，以 ChatGPT 为代表的一众 AIGC 应用深入人心，这些应用的背后都少不了大语言模型的支持。然而对于国内用户使用这些产品仍然有门槛。大家不禁想找到一个能打的国产 AI 产品。&lt;/p&gt;
&lt;p&gt;去年秋天我还在迷信 ChatGPT 的能力是“宇宙无敌”，直到我体验了 智谱 AI 旗下的 &lt;code&gt;智谱清言&lt;/code&gt; 我才觉得国产 AI 产品在中文语料下的能力并不比别人差。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/004-4b36a375.png"&gt;&lt;/p&gt;
&lt;p&gt;智谱是由清华大学计算机系技术成果转化而来的公司。它的发展很快。目前可供用户使用的各类模型 20 余个。其中包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大规模语言模型 GLM-4&lt;/li&gt;
&lt;li&gt;视频生成模型 CogVideoX&lt;/li&gt;
&lt;li&gt;代码模型 CodeGeeX-4&lt;/li&gt;
&lt;li&gt;图片生成模型 CogView-3&lt;/li&gt;
&lt;li&gt;嵌入式模型 Embedding-3&lt;/li&gt;
&lt;li&gt;&amp;hellip;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;智谱在开源领域也做出了极大贡献，上面列举的这些模型都能在 HuggingFace 或 GitHub 上找到开源的版本。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/005-9ff700e7.png"&gt;&lt;/p&gt;
&lt;p&gt;智谱 AI 最让我们熟悉的产品是其 C 端 AIGC 产品 &lt;code&gt;智谱清言&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/006-479e5fdd.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在中文语料下，它的问答质量不比 GPT-4 差！&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="llamaindex-集成-zhipu-embedding"&gt;&lt;a href="#llamaindex-%e9%9b%86%e6%88%90-zhipu-embedding" class="header-anchor"&gt;&lt;/a&gt;LlamaIndex 集成 Zhipu embedding
&lt;/h2&gt;&lt;p&gt;通过查看智谱 AI 大模型开放平台的文档得知它有两款 &lt;code&gt;embedding&lt;/code&gt; 模型可以通过 API 调用&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/007-050f1b24.png"&gt;&lt;/p&gt;
&lt;p&gt;于是决定将 &lt;code&gt;Embedding-3&lt;/code&gt; 试着集成到 LlamaIndex 中。&lt;/p&gt;
&lt;p&gt;当然，调用 API 首先你要有 API Key 以及可用的 tokens，这个我们在之前的文章我介绍过，一般是需要付费的，智谱 AI 会给新老用户赠送一些 tokens，之前赠送给了我 1000w tokens ，所以下面的示例我就用这些免费的 tokens。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/008-3bbf3053.png"&gt;&lt;/p&gt;
&lt;h3 id="简单-demo"&gt;&lt;a href="#%e7%ae%80%e5%8d%95-demo" class="header-anchor"&gt;&lt;/a&gt;简单 demo
&lt;/h3&gt;&lt;p&gt;我们先根据文档写一个最简单的模型调用 demo&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zhipuai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ZhipuAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ZhipuAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;GLM_4_PLUS_API_KEY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;embedding-3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;美食非常美味，服务员也很友好。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;这部电影既刺激又令人兴奋。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;阅读书籍是扩展知识的好方法。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它响应的输出是这样的：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/009-ba223053.png"&gt;&lt;/p&gt;
&lt;p&gt;这输出的一片数字是啥？&lt;/p&gt;
&lt;p&gt;这里简单解释一下：&lt;strong&gt;嵌入是将文字、图像或其他类型的数据转换成一系列数字（向量）的过程。这个向量在高维空间中代表了原始数据的语义信息。你看到的那一长串数字（如 -0.019210815, -0.0023460388, 0.010299683 等）就是嵌入向量的具体值。每个数字代表向量在某个维度上的值，这些数字虽然看起来没有明显意义，但它们在高维空间中编码了输入文本的语义信息。相似的文本会产生相似的向量，这使得我们可以进行语义相似度比较。这种表示方法使得机器能够更好地&amp;quot;理解&amp;quot;和处理文本数据。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;能够正常输出，代表模型调用成功。&lt;/p&gt;
&lt;h3 id="和-llamaindex-集成"&gt;&lt;a href="#%e5%92%8c-llamaindex-%e9%9b%86%e6%88%90" class="header-anchor"&gt;&lt;/a&gt;和 LlamaIndex 集成
&lt;/h3&gt;&lt;p&gt;在之前的文章中我们已经通过 Custom LLM 的方式将 LlamaIndex 和 GLM-4 集成在一起了，也就是在 RAG 应用中使用的框架是 LlamaIndex ，调用 的 LLM 是 GLM-4。&lt;/p&gt;
&lt;p&gt;同理，现在我们要把 &lt;code&gt;embedding&lt;/code&gt; 模型也同 LlamaIndex 集成起来，这样我们自己写的这个 RAG 应用的技术组合就是 &lt;code&gt;RAG = LlamaIndex +GLM-4 + Embedding-3&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;和 LLM 一样，在 LlamaIndex 文档的 embedding 模型兼容列表中并没有 Zhipu 的 Embedding-3 ，仍然需要通过自定义的方式来实现。&lt;/p&gt;
&lt;p&gt;这是文档中给的自定义 embedding 的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;InstructorEmbedding&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;INSTRUCTOR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.embeddings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseEmbedding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstructorEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseEmbedding&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instructor_model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hkunlp/instructor-large&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Represent the Computer Science documentation or question:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;INSTRUCTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instructor_model_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_instruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instruction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_query_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_text_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_text_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_query_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_query_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_text_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_text_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;仔细看的话，实际上只需要实现 2 个方法即可，下面的方法都会调用这两个方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; def _get_query_embedding(self, query: str) -&amp;gt; List[float]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; embeddings = self._model.encode([[self._instruction, query]])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; return embeddings[0]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; def _get_text_embedding(self, text: str) -&amp;gt; List[float]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; embeddings = self._model.encode([[self._instruction, text]])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; return embeddings[0]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里我们可以新建一个自定义的 embedding 类：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;class ZhipuEmbeddings(BaseEmbedding):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; client: ZhipuAI = Field(default_factory=lambda: ZhipuAI(api_key=API_KEY))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; def __init__(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; self,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; model_name: str = &amp;#34;embedding-3&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; **kwargs: Any,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; ) -&amp;gt; None:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; super().__init__(model_name=model_name, **kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; self._model = model_name
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; def invoke_embedding(self, query: str) -&amp;gt; List[float]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; response = self.client.embeddings.create(model=self._model, input=[query])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; # 检查响应是否成功
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; if response.data and len(response.data) &amp;gt; 0:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; return response.data[0].embedding
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; else:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; raise ValueError(&amp;#34;Failed to get embedding from ZhipuAI API&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; def _get_query_embedding(self, query: str) -&amp;gt; List[float]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; return self.invoke_embedding(query)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; def _get_text_embedding(self, text: str) -&amp;gt; List[float]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; return self.invoke_embedding(text)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; def _get_text_embeddings(self, texts: List[str]) -&amp;gt; List[List[float]]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; return [self._get_text_embedding(text) for text in texts]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; async def _aget_query_embedding(self, query: str) -&amp;gt; List[float]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; return self._get_query_embedding(query)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; async def _aget_text_embedding(self, text: str) -&amp;gt; List[float]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; return self._get_text_embedding(text)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; async def _aget_text_embeddings(self, texts: List[str]) -&amp;gt; List[List[float]]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; return self._get_text_embeddings(texts)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在利用 LlamaIndex 调用时，将 &lt;code&gt;embed_model&lt;/code&gt; 设置为自定义类就可以了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; # 设置 LLM 和嵌入模型
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; Settings.llm = GLM4LLM()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; Settings.embed_model = ZhipuEmbeddings()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样我们的 RAG 应用就把智谱 AI 的 GLM-4 和 Embedding-3 一起使用上了。&lt;/p&gt;
&lt;p&gt;以下是完整代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zhipuai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ZhipuAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.embeddings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseEmbedding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.llms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CustomLLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CompletionResponseGen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.llms.callbacks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;llm_completion_callback&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cached_property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 配置日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 从环境变量获取 API 密钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;GLM_4_PLUS_API_KEY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;GLM_4_PLUS_API_KEY environment variable is not set&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 29&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GLM4LLM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomLLM&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@cached_property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ZhipuAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 34&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 38&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat_with_glm4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;glm-4-plus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 47&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 51&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 52&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 53&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 54&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 55&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 56&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@llm_completion_callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 57&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 58&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_with_glm4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你是一个聪明的 AI 助手&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 59&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;full_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 60&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 61&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 62&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 63&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 64&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;full_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 65&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 66&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@llm_completion_callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 67&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CompletionResponseGen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 68&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_with_glm4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你是一个聪明的 AI 助手&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 69&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 70&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response_generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 71&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 72&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 73&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 74&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response_content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 75&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 76&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response_content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 77&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 78&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 79&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_generator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 80&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ZhipuEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseEmbedding&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 82&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ZhipuAI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ZhipuAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 83&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 84&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 85&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 86&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;embedding-3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 87&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 88&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 89&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 90&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model_name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 91&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 92&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 93&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 94&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 95&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 检查响应是否成功&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 96&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 97&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 98&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 99&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Failed to get embedding from ZhipuAI API&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;100&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;101&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_query_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;102&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;103&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;104&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_text_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;105&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;106&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;107&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_text_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;108&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_text_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;109&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;110&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_aget_query_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;111&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_query_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;112&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;113&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_aget_text_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;114&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_text_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;115&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;116&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_aget_text_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;117&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_text_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;118&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;119&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 设置环境变量，禁用 tokenizers 的并行处理&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;120&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;TOKENIZERS_PARALLELISM&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;false&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;121&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;122&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_glm4_query_with_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;123&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 从指定目录加载文档数据&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;124&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;125&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;126&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 设置 LLM 和嵌入模型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;127&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GLM4LLM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;128&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embed_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ZhipuEmbeddings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;129&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;130&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 创建索引和查询引擎&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;131&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;132&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;streaming&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;133&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;134&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 执行查询&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;135&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;GLM-4 查询结果：&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;136&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;137&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;138&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 处理并输出响应&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;139&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;response_gen&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;140&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 流式输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;141&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_gen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;142&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;143&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# 确保立即输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;144&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;145&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 非流式输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;146&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;147&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;148&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; 查询完成&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="效果"&gt;&lt;a href="#%e6%95%88%e6%9e%9c" class="header-anchor"&gt;&lt;/a&gt;效果
&lt;/h2&gt;&lt;p&gt;从最终的使用效果上看，速度上不如之前使用本地 embedding 模型 &lt;code&gt;BAAI/bge-base-zh-v1.5&lt;/code&gt; 快。因为执行了多次 Http 远程调用：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/010-bbb38cab.png"&gt;&lt;/p&gt;
&lt;p&gt;所以我又查了一下文档看看有没有办法提提速：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-08-ti-sheng-rag-ying-yong-xing-neng-shi-yong-zhi-pu-ai-de-glm-4/011-79b38b4b.png"&gt;&lt;/p&gt;
&lt;p&gt;虽然有一个 &lt;code&gt;dimensions&lt;/code&gt; 参数，虽然我感觉设置的越小维度越小数据也越少，那么速度可能更快，但实际测试下来速度并没有明显变化 。其主要原因还是：它是&lt;strong&gt;同步调用的&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;看来从 API 上没办法提速了，只能在编程模型上想办法了，这里就不多说了。这里我认为，&lt;strong&gt;最好的方式还是在一个资源充足的服务器中部署一个开源的 embedding 模型 ，这样方便模型的微调及不限量的调用。速度也会快许多&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="最后"&gt;&lt;a href="#%e6%9c%80%e5%90%8e" class="header-anchor"&gt;&lt;/a&gt;最后
&lt;/h2&gt;&lt;p&gt;我已将文章中涉及到的相关代码上传至 ：https://github.com/xiaobox/llamaindex_test&lt;/p&gt;
&lt;p&gt;这个仓库中包含了最新几篇文章中的所有 demo 代码，大家可以自行查看。&lt;/p&gt;</description></item><item><title>提速 RAG 应用：用 DeepSeek API 替换本地 Ollama 模型，LlamaIndex 实战解析</title><link>https://xiaobox.github.io/p/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama/</link><pubDate>Sun, 06 Oct 2024 13:27:10 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama-/cover.jpg" alt="Featured image of post 提速 RAG 应用：用 DeepSeek API 替换本地 Ollama 模型，LlamaIndex 实战解析" /&gt;&lt;h2 id="概述"&gt;&lt;a href="#%e6%a6%82%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;概述
&lt;/h2&gt;&lt;p&gt;在上一篇文章中 &lt;a class="link" href="http://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247489727&amp;amp;idx=1&amp;amp;sn=9ff0ec14a1a9c2aa0d52c3ad452b1f03&amp;amp;chksm=eb6da739dc1a2e2f913495acae29cce5460115b6f0a8ad0833d3aac0138c6249e4edc231b771&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;如何用 30秒和 5 行代码写个 RAG 应用？&lt;/a&gt;，我们介绍了如何利用 LlamaIndex 结合 Ollama 的本地大模型和在 Hugging Face 开源的 embedding 模型用几行 Python 代码轻松构建一个 RAG 应用。&lt;/p&gt;
&lt;p&gt;从最终输出的结果上看是满意的，理论上是可以针对本地的知识库内容进行精准的问答。然而执行效率却不尽人意。原因是：无论 LLM 还是 embedding 模型的调用都是在本地，而我本地电脑的性能确属一般（几乎只能利用到 CPU 资源，没有 GPU 资源），这样就导致代码运行速度缓慢。&lt;/p&gt;
&lt;p&gt;本文我们将介绍，如何通过调用国产大模型 DeepSeek 的 API 为我们的 RAG 应用提速，我们将把对本地 Ollama 的模型调用替换成对 DeepSeek API 的调用。&lt;/p&gt;
&lt;p&gt;对比一下上文和本文的方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;上文：LlamaIndex + &lt;code&gt;Ollama(Qwen2:7b)&lt;/code&gt;+ embedding（BAAI/bge-base-zh-v1.5）&lt;/li&gt;
&lt;li&gt;本文：LlamaIndex + &lt;code&gt;DeepSeek API&lt;/code&gt; + embedding（BAAI/bge-base-zh-v1.5）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="deepseek"&gt;&lt;a href="#deepseek" class="header-anchor"&gt;&lt;/a&gt;DeepSeek
&lt;/h2&gt;&lt;p&gt;首先来明确几个问题&lt;/p&gt;
&lt;h3 id="为什么不用-openai-的-api"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e7%94%a8-openai-%e7%9a%84-api" class="header-anchor"&gt;&lt;/a&gt;为什么不用 OpenAI 的 API？
&lt;/h3&gt;&lt;p&gt;当然可以，而且 LlamaIndex 默认支持的就是通过 API Key 访问 OpenAI 的 API。&lt;strong&gt;问题是成本太高了，有更高性价比的所以不用它。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="deepseek-是什么-"&gt;&lt;a href="#deepseek-%e6%98%af%e4%bb%80%e4%b9%88-" class="header-anchor"&gt;&lt;/a&gt;DeepSeek 是什么 ？
&lt;/h3&gt;&lt;p&gt;DeepSeek 这个词在不同的上下文中有不同的含义，为了避免概念和语义的混淆，我们在这里分别说明一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DeepSeek 代表一个公司&lt;/strong&gt;：杭州深度求索人工智能基础技术研究有限公司，专注于大模型研发、AI 技术创新和企业解决方案，是幻方量化的子公司。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DeepSeek 代表一个大语言模型&lt;/strong&gt; ：具有 236B 参数量（2360 亿个参数）的开源大语言模型。严格上讲，DeepSeek 不只是一个单一的模型，而是包含多个针对不同任务和应用场景的模型系列，这些模型在 DeepSeek 的基础上进行了专门的优化和训练，以满足特定的需求，如：&lt;code&gt;DeepSeek-Chat&lt;/code&gt;、 &lt;code&gt;DeepSeek-Math&lt;/code&gt;、&lt;code&gt;DeepSeek-Coder&lt;/code&gt; 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DeepSeek 是一个 API&lt;/strong&gt;: 由 DeepSeek 公司开发对外提供付费的大模型功能的接口，支持文本生成、对话系统、文本摘要、问答系统和多模态任务等。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在本文中，我们利用 DeepSeek 的 API 间接调用 DeepSeek 所提供的模型，具体模型是 &lt;code&gt;DeepSeek V2.5&lt;/code&gt;(&lt;em&gt;DeepSeek V2 Chat 和 DeepSeek Coder V2 两个模型已经合并升级，升级后的新模型为 DeepSeek V2.5&lt;/em&gt;)&lt;/p&gt;
&lt;h3 id="为什么用-deepseek-"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e7%94%a8-deepseek-" class="header-anchor"&gt;&lt;/a&gt;为什么用 DeepSeek ？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;使用 DeepSeek 主要出于成本和效果的综合考虑。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;虽然 DeepSeek 是开源大模型（在大模型领域，类似这样的国产中文开源大模型还有许多），但是部署这样的具有大规模参数的模型是需要很多硬件资源的，我们手上的个人电脑没有这个条件。更别说运维和微调这样的模型。所以通过 API 直接调用已经部署好的模型是最便捷的方式，当然，这是有成本的，人家部署和运维这样规模的模型也是需要成本的，所以这些 API 是需要付费使用的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama-/001-61cee938.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;从成本考量&lt;/strong&gt;，DeepSeek 几乎是最佳方案，因 DeepSeek API 调用价格之便宜曾被戏称为 “AI 界的拼多多”。在 DeepSeek 价格公开后不久，多家模型厂商卷入价格战，现在的模型调用价格是真真正正的被 “打下来”了。多家公司频繁更新自家模型价格，截止目前，可以说 “没有最低，只有更低”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;从效果考量&lt;/strong&gt; ，因之前使用过 &lt;code&gt;deepseek-coder&lt;/code&gt;、和 &lt;code&gt;deepseek-chat&lt;/code&gt; 两个模型，效果上可以说是在中文模型领域的第一梯队。当然这只是我个人的使用体验。&lt;/p&gt;
&lt;p&gt;从权威的角度，通过 &lt;code&gt;LMSYS Chatbot Arena Leaderboard&lt;/code&gt;（LMSYS Chatbot Arena Leaderboard 是一个大型语言模型的评测排行榜，提供了一个匿名竞技场，用于评估和比较不同模型的性能。） 这个大型语言模型的评测排行榜可以了解 DeepSeek 的能力如何&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama-/002-46276e80.png"&gt;&lt;/p&gt;
&lt;p&gt;最近的几个月里，国产模型中与 DeepSeek 排名竞争最激烈的是阿里的 &lt;code&gt;Qwen2.5&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama-/003-f209b64f.png"&gt;&lt;/p&gt;
&lt;h3 id="deepseek-的使用费用"&gt;&lt;a href="#deepseek-%e7%9a%84%e4%bd%bf%e7%94%a8%e8%b4%b9%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;DeepSeek 的使用费用
&lt;/h3&gt;&lt;p&gt;前文中我们提到 DeepSeek 的 API 是需要付费调用的，所以到底收多少钱是一个关键的问题。&lt;/p&gt;
&lt;p&gt;首先，如果你是一个新用户，那么 &lt;strong&gt;DeeepSeek 会送你 500w 个 tokens&lt;/strong&gt; （在自然语言处理中，Token 是指将文本分割成的最小单位。这些单位可以是单词、子词、字符等，具体取决于所使用的分词策略）。简单理解就是 500w 个字。需要注意的是，送的 tokens 有有效期，一个月后就过期了。&lt;/p&gt;
&lt;p&gt;其次，如果送的 tokens 用完了，就需要花真金白银去充值了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama-/004-64228f9f.png"&gt;&lt;/p&gt;
&lt;p&gt;简单说， &lt;strong&gt;10 元 500w tokens&lt;/strong&gt;，如果你是个人使用，一个人放开了用，一个月足够了。&lt;/p&gt;
&lt;h3 id="deepseek-api-的使用"&gt;&lt;a href="#deepseek-api-%e7%9a%84%e4%bd%bf%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;DeepSeek API 的使用
&lt;/h3&gt;&lt;p&gt;无论是通过赠送还是付费，当你拥有了 tokens，你就可以根据文档创建自己的 API key 并进行 API 调用了。&lt;/p&gt;
&lt;p&gt;由于是走网络 API 的这种方式，在编程语言上就没有限制了，你可以选用你觉得合适的语言。DeepSeek 官方也比较贴心的给出了各种语言调用的示例：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama-/005-205fb89f.png"&gt;&lt;/p&gt;
&lt;p&gt;这里我用 &lt;code&gt;Python&lt;/code&gt; 写了一个简单的调用 Demo， 以下是具体代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeepSeekChat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://api.deepseek.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;deepseek-chat&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stream_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_stream_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;full_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;full_response&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;===============我是分隔线===============&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;full_response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 使用示例&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;deepseek_chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DeepSeekChat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[你的 API Key]&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deepseek_chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你是一个聪明的 AI 助手&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;三国演义中战斗力排名前 10 的武将有谁？&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;完整回答：&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到我们只引入了 openai 这一个库，原因是 DeepSeek 的 API 和 OpenAI 的 API 是兼容的。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;DeepSeek API 使用与 OpenAI 兼容的 API 格式，通过修改配置，您可以使用 OpenAI SDK 来访问 DeepSeek API，或使用与 OpenAI API 兼容的软件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; --源自 DeepSeek 文档
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/blockquote&gt;
&lt;p&gt;引入 openai 这个库以后我们不需要再引入其他多余的库就可以进行 API 请求了。&lt;/p&gt;
&lt;p&gt;这段代码比较简单，我的问题是 ：“三国演义中战斗力排名前 10 的武将有谁？” 我们来看一下大模型给我的回答：&lt;/p&gt;
&lt;p&gt;已关注&lt;/p&gt;
&lt;p&gt;Follow&lt;/p&gt;
&lt;p&gt;Replay Share Like&lt;/p&gt;
&lt;p&gt;Close&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;观看更多&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更多&lt;/p&gt;
&lt;p&gt;&lt;em&gt;退出全屏&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;切换到竖屏全屏**退出全屏&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;小盒子的技术分享已关注&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Share Video&lt;/p&gt;
&lt;p&gt;，时长00:19&lt;/p&gt;
&lt;p&gt;0/0&lt;/p&gt;
&lt;p&gt;00:00/00:19&lt;/p&gt;
&lt;p&gt;切换到横屏模式&lt;/p&gt;
&lt;p&gt;继续播放&lt;/p&gt;
&lt;p&gt;进度条，百分之0&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;Play&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;00:00&lt;/p&gt;
&lt;p&gt;/&lt;/p&gt;
&lt;p&gt;00:19&lt;/p&gt;
&lt;p&gt;00:19&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;倍速&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;全屏&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;倍速播放中&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;0.5倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;0.75倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;1.0倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;1.5倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;2.0倍&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;超清&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;流畅&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Your browser does not support video tags&lt;/p&gt;
&lt;p&gt;继续观看&lt;/p&gt;
&lt;p&gt;提速 RAG 应用：用 DeepSeek API 替换本地 Ollama 模型，LlamaIndex 实战解析&lt;/p&gt;
&lt;p&gt;观看更多&lt;/p&gt;
&lt;p&gt;Original&lt;/p&gt;
&lt;p&gt;,&lt;/p&gt;
&lt;p&gt;提速 RAG 应用：用 DeepSeek API 替换本地 Ollama 模型，LlamaIndex 实战解析&lt;/p&gt;
&lt;p&gt;小盒子的技术分享已关注&lt;/p&gt;
&lt;p&gt;Share点赞Wow&lt;/p&gt;
&lt;p&gt;Added to Top Stories&lt;a class="link" href="javascript:;" &gt;Enter comment&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;Video Details&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="rag"&gt;&lt;a href="#rag" class="header-anchor"&gt;&lt;/a&gt;RAG
&lt;/h2&gt;&lt;p&gt;在上一篇文章中我们能够方便地调用 Ollama 进而调用本地下载好的模型，是因为 LlamaIndex 的库封装好了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 设置语言模型，使用 Ollama 提供的 Qwen2 7B 模型，并设置请求超时时间
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;Settings.llm = Ollama(model=&amp;#34;qwen2:7b&amp;#34;, request_timeout=360.0)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在，我们想用在线的模型 DeepSeek，让 LlamaIndex 去调用 DeepSeek API 就不能用之前的方式了。&lt;/p&gt;
&lt;h3 id="llamaindex-支持的-llm-集成方式"&gt;&lt;a href="#llamaindex-%e6%94%af%e6%8c%81%e7%9a%84-llm-%e9%9b%86%e6%88%90%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;LlamaIndex 支持的 LLM 集成方式
&lt;/h3&gt;&lt;p&gt;通过查看 LlamaIndex 的文档，总结来说，它支持的 LLM 集成方式有三种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过 Ollama 调用安装在本地的大模型（一般适用于个人电脑使用）&lt;/li&gt;
&lt;li&gt;通过 API 调用的免费或付费模型&lt;/li&gt;
&lt;li&gt;自定义 LLM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们需要解释一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第一种方式&lt;/strong&gt; : Ollama 无需多言。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第二种方式&lt;/strong&gt; : API 付费调用不是所有市面上的模型 LlamaIndex 都有现成的集成方式，比如 DeepSeek 就没有，具体支持集成哪些模型，在它的文档中有清单：https://docs.llamaindex.ai/en/stable/module_guides/models/llms/modules/ 另外，对于付费模型，模型背后的公司都会提供相应的 API，付费购买就可以了，而开源模型虽然本身代码是开源的，但提供模型调用服务的平台是收费的，比如 &lt;code&gt;Replicate&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-06-ti-su-rag-ying-yong-yong-deepseek-api-ti-huan-ben-di-ollama-/006-80d17203.png"&gt;&lt;/p&gt;
&lt;p&gt;也就是说&lt;strong&gt;第二种方式无论你使用的模型本身是否开源，提供模型调用服务的平台都会收费。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第三种方式&lt;/strong&gt;：自定义 LLM，本文我们使用的就是这种方式 ，这种集成实现方式是 LlamaIndex 留给开发者的一个扩展，我们可以自定义自己需要使用的 LLM 与 LlamaIndex 进行集成。使用这种方式可以实现两类集成：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第一类就是类似 DeepSeek 这种已经有 API 但 LlamaIndex 尚未支持的 LLM。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第二类就是调用我们本地部署的开源大模型，当然一般是部署在服务器上（如果 PC 有足够的计算资源也可以部署在 PC 上）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="custom-llm"&gt;&lt;a href="#custom-llm" class="header-anchor"&gt;&lt;/a&gt;Custom LLM
&lt;/h3&gt;&lt;p&gt;如何通过 Custom LLM 的方式将 DeepSeek 与 LlamaIndex 进行集成呢？&lt;/p&gt;
&lt;p&gt;其实很容易，我们只需要创建一个类并实现三个方法即可（用 python 代码实现）。&lt;/p&gt;
&lt;p&gt;文档中给出的代码是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Mapping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SummaryIndex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.callbacks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CallbackManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.llms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CustomLLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CompletionResponseGen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.llms.callbacks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;llm_completion_callback&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OurLLM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomLLM&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;context_window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3900&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;num_output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;custom&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dummy_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;My response&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Get LLM metadata.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;context_window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context_window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;num_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@llm_completion_callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dummy_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@llm_completion_callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CompletionResponseGen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dummy_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# define our LLM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OurLLM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# define embed model&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embed_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;local:BAAI/bge-base-en-v1.5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Load the your data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SummaryIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Query and print response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;query_text&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;OurLLM 就是要创建的类，要实现的三个方法是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;metadata&lt;/li&gt;
&lt;li&gt;complete&lt;/li&gt;
&lt;li&gt;stream_complete&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实际上一般 metadata 方法可以直接返回 &lt;code&gt;LLMMetadata()&lt;/code&gt; ，最主要的就是实现后面两个方法。&lt;/p&gt;
&lt;h3 id="实例"&gt;&lt;a href="#%e5%ae%9e%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;实例
&lt;/h3&gt;&lt;p&gt;根据上一节 Custom LLM 所述，我将上一篇文章中的 Ollama 模型调用换成自定义的 DeepSeek，以下是主要代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Generator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.embeddings.huggingface&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HuggingFaceEmbedding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.llms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CustomLLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CompletionResponseGen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llama_index.core.llms.callbacks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;llm_completion_callback&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cached_property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 配置日志 创建一个与当前模块同名的 logger&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 从环境变量获取 API 密钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;DEEPSEEK_API_KEY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;DEEPSEEK_API_KEY environment variable is not set&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 29&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeepSeekChat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;DeepSeek 聊天模型的封装类。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 32&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://api.deepseek.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 35&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Pydantic 配置类。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 38&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;arbitrary_types_allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="c1"&gt;# 允许模型接受任意类型的字段&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 这增加了灵活性，但可能降低类型安全性&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 在本类中，这可能用于允许使用 OpenAI 客户端等复杂类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 42&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@cached_property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;创建并缓存 OpenAI 客户端实例。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 47&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 51&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 52&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;deepseek-chat&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 53&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 54&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 55&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 56&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 57&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 使用 DeepSeek API 发送聊天请求。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 返回流式响应或完整响应内容。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 62&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 63&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 64&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 65&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 66&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 67&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 68&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 69&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 70&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 71&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 72&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 73&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 74&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 75&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Error in DeepSeek API call: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 76&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 77&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 78&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_stream_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Generator&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 79&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;处理流式响应，逐块生成内容。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 80&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 81&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 82&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 83&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 84&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeepSeekLLM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomLLM&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 85&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;DeepSeek 语言模型的自定义实现。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 86&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 87&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;deep_seek_chat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DeepSeekChat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DeepSeekChat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 88&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 89&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 90&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 91&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;返回 LLM 元数据。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 92&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LLMMetadata&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 93&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 94&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@llm_completion_callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 95&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 96&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;执行非流式完成请求。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 97&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deep_seek_chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 98&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你是一个聪明的 AI 助手&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 99&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;100&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;101&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;102&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@llm_completion_callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;103&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CompletionResponseGen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;104&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;执行流式完成请求。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;105&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deep_seek_chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;106&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你是一个聪明的 AI 助手&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;107&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;108&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;109&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response_generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;110&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;生成器函数，用于逐步生成响应内容。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;111&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;112&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deep_seek_chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stream_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;113&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;114&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response_content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;115&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;CompletionResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response_content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;116&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;117&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_generator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;118&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;119&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 设置环境变量，禁用 tokenizers 的并行处理，以避免潜在的死锁问题&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;120&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;TOKENIZERS_PARALLELISM&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;false&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;121&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;122&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;123&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;主程序函数，演示如何使用 DeepSeekLLM 进行文档查询。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;124&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 从指定目录加载文档数据&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;125&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;126&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;127&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 设置 LLM 和嵌入模型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;128&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DeepSeekLLM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;129&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embed_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HuggingFaceEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;BAAI/bge-base-zh-v1.5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;130&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;131&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 创建索引和查询引擎&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;132&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;133&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;streaming&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;134&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;135&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 执行查询&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;136&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;查询结果：&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;137&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;作者学习过的编程语言有哪些？&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;138&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;139&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 处理并输出响应&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;140&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;response_gen&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;141&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 流式输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;142&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_gen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;143&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;144&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# 确保立即输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;145&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;146&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 非流式输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;147&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;148&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;149&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; 查询完成&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;150&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;151&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;152&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你别看代码写的长，那是因为我做过重构，其实可以实现的更短。不要被篇幅吓到，其实主要执行逻辑与上一篇文章中写的没什么区别，只在自定义 DeepSeekLLM 这里有所不同，如果你把本文从头看到尾，其实其中的第一步分解拆开都有解释过，也比较简单。&lt;/p&gt;
&lt;p&gt;我们来看一下效果，测试数据仍然是上一篇文章中的文本内容，问题仍然是 ：“作者学习过的编程语言有哪些？”&lt;/p&gt;
&lt;p&gt;已关注&lt;/p&gt;
&lt;p&gt;Follow&lt;/p&gt;
&lt;p&gt;Replay Share Like&lt;/p&gt;
&lt;p&gt;Close&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;观看更多&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更多&lt;/p&gt;
&lt;p&gt;&lt;em&gt;退出全屏&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;切换到竖屏全屏**退出全屏&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;小盒子的技术分享已关注&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Share Video&lt;/p&gt;
&lt;p&gt;，时长00:09&lt;/p&gt;
&lt;p&gt;0/0&lt;/p&gt;
&lt;p&gt;00:00/00:09&lt;/p&gt;
&lt;p&gt;切换到横屏模式&lt;/p&gt;
&lt;p&gt;继续播放&lt;/p&gt;
&lt;p&gt;进度条，百分之0&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;Play&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;00:00&lt;/p&gt;
&lt;p&gt;/&lt;/p&gt;
&lt;p&gt;00:09&lt;/p&gt;
&lt;p&gt;00:09&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;倍速&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;全屏&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;倍速播放中&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;0.5倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;0.75倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;1.0倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;1.5倍&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;2.0倍&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;超清&lt;/a&gt; &lt;a class="link" href="javascript:;" &gt;流畅&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Your browser does not support video tags&lt;/p&gt;
&lt;p&gt;继续观看&lt;/p&gt;
&lt;p&gt;提速 RAG 应用：用 DeepSeek API 替换本地 Ollama 模型，LlamaIndex 实战解析&lt;/p&gt;
&lt;p&gt;观看更多&lt;/p&gt;
&lt;p&gt;转载&lt;/p&gt;
&lt;p&gt;,&lt;/p&gt;
&lt;p&gt;提速 RAG 应用：用 DeepSeek API 替换本地 Ollama 模型，LlamaIndex 实战解析&lt;/p&gt;
&lt;p&gt;小盒子的技术分享已关注&lt;/p&gt;
&lt;p&gt;Share点赞Wow&lt;/p&gt;
&lt;p&gt;Added to Top Stories&lt;a class="link" href="javascript:;" &gt;Enter comment&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="javascript:;" &gt;Video Details&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;本文我们介绍了如何通过调用国产大模型 &lt;code&gt;DeepSeek&lt;/code&gt; 的 API 来提升 &lt;code&gt;RAG&lt;/code&gt;（检索增强生成）应用的执行效率。相比使用本地 Ollama 模型，DeepSeek 的 API 不仅解决了本地计算资源不足导致的运行速度慢的问题，还保持了高质量的生成结果。DeepSeek 在成本和效果上表现出色，特别适合中文模型的应用。通过自定义 LLM 的方式，我们成功将 DeepSeek 与 &lt;code&gt;LlamaIndex&lt;/code&gt; 集成，展示了如何实现高效的数据处理和生成。本文提供的方法和示例代码为构建高性能 RAG 应用提供了一种实用的解决方案。&lt;/p&gt;</description></item></channel></rss>