<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>白发川的BLOG</title>
  
  <subtitle>思绪飘过，偶尔在这里停留</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://www.baifachuan.com/"/>
  <updated>2025-09-29T02:45:04.479Z</updated>
  <id>http://www.baifachuan.com/</id>
  
  <author>
    <name>惊帆的BLOG</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>如何为智能应用构建长期记忆</title>
    <link href="http://www.baifachuan.com/posts/40c5dd8c.html"/>
    <id>http://www.baifachuan.com/posts/40c5dd8c.html</id>
    <published>2025-09-29T02:42:12.000Z</published>
    <updated>2025-09-29T02:45:04.479Z</updated>
    
    <content type="html"><![CDATA[<p>之前我写的一篇文章中我与AI深度Pair这个把月提到大模型目前在工程视角下的问题，比如没有记忆能力，导致脱离上下文的历史记录窗口，智商就回到一个固定时间点的快照。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">比如，在一个长达一小时的视频中：第 5 分钟，一个「穿蓝色衬衫的男人」出现；第 25 分钟，一个「戴着眼镜的男人」发表了讲话。对于人类观察者而言，很容易判断他们是同一个人。</span><br></pre></td></tr></table></figure><p>但对于 AI 系统，这却是一个难题，因为AI自身是没有对自己已经处理过的知识信息进行提取处理的流程，因此AI是一个健忘的，没有记忆的人，它的记忆力始终停留在模型厂商训练它所使用的知识的阶段，无法进阶到使用者投喂给它的知识的这个阶段。</p><p>这个问题非常影响实际的质量，如果不解决这个问题，会导致智能应用最佳效果是上线那一刻，随着使用的时间增加，质量会越来越差，除非投入更庞大的维护成本，前几天字节出了M3 agent,刚好解决的是类似的问题：</p><p><img src="/posts/40c5dd8c/22220.webp" alt="image"></p><p>M3 Agent的解决思路是由一个多模态大语言模型（MLLM）和一个外部的长期记忆模块构成。其运作通过两个并行且持续进行的过程实现：记忆化 (Memorization) 和 控制 (Control) 。</p><p>简单来说其实就是把知识提取出来后，从原始的数据中进行分层，再由控制器提供检索，而提取后又基于实体信息构建了一个知识网，而这和我正在尝试在外围构建一个服务,作为另一种类型的知识库，用于大模型自身的记忆模块的思路几乎一致。只不过M3 Agent目前是在多模态下做的尝试，而我则想构建一个更加通用的体系可以适应于不同的数据集，在大模型的问答之上，构建一个知识流转模块。</p><p>从长远来说，这个能力我认为大概率会被模型厂商给实现掉，因为如果模型在落地过程中如果不能解决记忆的问题，那么意味着每一个智能应用随着时间的推移，越使用维护成本会越高，这不太符合发展的规律。</p><p>在我看来，大模型自身的记忆能力和外围知识库的关系，并不是单纯的存储类型不同。</p><p>当我们在做存储系统,比如存算分离的情况下，通常会有一个本地缓存,将远端的数据缓存在本地,当查询的时候直接从本地读取则会大大提升加载速度。</p><p><img src="/posts/40c5dd8c/22221.webp" alt="image"></p><p>这种情况下要求本地缓存和远端数据具有严格的一致性，然而在大模型和知识库之间，并非这样的关系，如果完全依靠大模型自身的能力去问答，显然会缺失很多内部的数据知识，导致答案质量不符合预期，如果外挂知识库,随着时间的推移，知识库会越来越庞大，必然导致检索时间增强，上下文内容过大,出现无法回答问题的情况。</p><p>为了解决这个问题,会做很多额外操作，比如知识的压缩,历史记录的对齐，甚至做部分微调来降低知识库的内容。</p><p>可以发现知识库的企业自有知识和大模型自身的知识之间并不是完全的一一对等，那么什么应该放在知识库，什么应该去微调，什么又是大模型的记忆数据,就必须定义的清清楚楚。</p><p>我认为从用户输入问题开始到模型产生输出，在这个过程中涉及到的数据的流转，经历的是知识的加工过程，而非数据存储的转移，应该遵循漏斗筛选的去转移知识。</p><p>也就是需要在用户的聊天记录+外挂知识库+大模型自身内置的知识中间形成一个流转和淘汰的机制。</p><p>而要形成这个机制，首先就需要对知识进行分层,比如最简单的知识分层：</p><ul><li>用于存储历史记录的知识库。</li><li>用于存储领域知识的知识库。</li><li>用于存储问答所需要的知识的知识库。</li><li>用于存储微调数据的知识库。</li></ul><p>在我开始设计UniverAI这个平台UniverAI一款企业AI员工协作平台的知识库的时候，我就首先考虑了领域知识和业务的数据知识。</p><p><img src="/posts/40c5dd8c/22222.png" alt="image"></p><p>在这多层知识库之间还需要有一个流动机制，当知识从上层流转至下层后，还需要进行清理,类似层级缓存的淘汰机制一样。</p><p><img src="/posts/40c5dd8c/22223.webp" alt="image"></p><p>一个合理的，健康的，质量还不错的智能应用，应该是左边的这样一个知识分布，但是随着使用时间的增加，知识的变多，逐步会变成后边这种头头重脚轻的结构，导致大量的信息堆砌在了前端，需要借助庞大的上下文才能获得较好的质量，甚至都不能获得较好的质量，这时候整个应用会出现使用上体验变慢，知识维护麻烦，问答幻觉非常严重，问答质量非常差的情况。</p><p>因此需要一直机制，将起从后边拉回左边的结构，回归一个高质量应用的本体，而这个拉回的动作，实际上就是让上层知识流动到下层进行固化，因为固化了，自然就记忆到了下层，从而能解决大模型本身的记忆的问题。</p><p>而这个记忆管理模块，就需要负责全方位观察用户的输入和产生问题的答案的信息来源，从中整理出整个数据流向，因为这个模块不单要做知识的流转，还需要做知识的加工淘汰，就像人的记忆一样，有些错误的无用的信息，是没有必要保留的，需要适当的遗忘。</p><p>关于这个记忆模块的设计，后面的篇章再逐步展开。。。</p>]]></content>
    
    <summary type="html">
    
      如何为智能应用构建长期记忆
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>AI未来会淘汰什么样的人？</title>
    <link href="http://www.baifachuan.com/posts/56577ef6.html"/>
    <id>http://www.baifachuan.com/posts/56577ef6.html</id>
    <published>2025-09-29T02:40:35.000Z</published>
    <updated>2025-09-29T02:45:04.476Z</updated>
    
    <content type="html"><![CDATA[<p>AI未来会淘汰什么样的人？可能淘汰这个词不太准确，不过AI必然会形成一个新的工作方式，这个工作方式是AI和人形成一种更加默契的分工，从而出现一种新的协作形态。</p><p>几乎行业对AI的需求从来没有发生过较大的变化，那就是降低门槛，让能力不强的人，也可以干要求很高的事。</p><p>对于这种认知，我是不太同意的，我认为当AI能独立干很多事的时候，人的作用几乎会浓缩成一个：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">处理异常情况。</span><br></pre></td></tr></table></figure><p>这就像很多流水线的机器旁边总有一个人，在出现意外的时候立马去处理，这个人能力弱吗？错了，反而这个人的能力要求很高，因为只有能力很高的人，才能在极短的时间内去找到问题，解决问题。</p><p>而很多事AI能独立干了之后，其实就没有一般人的事儿了，也就是未来的淘汰链路和形态会是：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">10个10块成本，共计100成本的团队，在引入AI后，会变成AI干掉所有的事，然后重新招聘一个80块成本的人过来充当异常处理者。</span><br></pre></td></tr></table></figure><p>而要降低异常处理者的成本，那就是只有一个：培训。</p><p>将所有异常流程由非标转标，然后形成完备的培训机制，否则这个异常处理的成本会变的很高，虽然可能99%的时间都用不上，但是却无法淘汰，就像消防设施，可能用不到，但是谁敢说不需要，或者用廉价的呢。</p>]]></content>
    
    <summary type="html">
    
      AI未来会淘汰什么样的人？
    
    </summary>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/categories/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/tags/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>UniverAI一款企业AI员工协作平台</title>
    <link href="http://www.baifachuan.com/posts/231799d3.html"/>
    <id>http://www.baifachuan.com/posts/231799d3.html</id>
    <published>2025-09-29T02:35:10.000Z</published>
    <updated>2025-09-29T02:45:04.477Z</updated>
    
    <content type="html"><![CDATA[<p>在过去很长的一段时间中，我在不同的角色中反复横跳，本质上是为了在：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">发现问题-&gt;设计出方案-&gt;抽象成产品功能-&gt;卖出去。</span><br></pre></td></tr></table></figure><p>在这样的一个链路下反复打磨，UniverAI这款产品越来越完善，生命力也越来越强，目前也已经在商业环境下运作良好，然而几乎没有时间来仔细回顾建造它的过程。</p><p><img src="/posts/231799d3/1110.png" alt="image"></p><p>产品的打磨本身就是一件非常痛苦的事，除了设计本身外，还需要考虑市场端，如何卖，卖给谁。<br>虽然目前经过反复打磨和市场的验证，产品本身已经具备完整的商业能力，但是一个不能和行业深度绑定的产品，是很难直接对齐到业务价值，只是用什么样的形式去绑定。</p><p>未来我的主要精力会在于如何将这样的一个能力，和行业属性进行深入的绑定，我并不介意平台自身丧失部分通用性的能力，去换取具有更深价值的行业贴合度。</p><p><img src="/posts/231799d3/1111.webp" alt="image"></p><p>在今天来说，AI开发平台本身，已经不具有稀缺性，无论是私有化的，还是公有云的，无论是开源免费的，还是闭源收费的。<br>然而我相信即便如此，每一款产品依旧有它不一样的地方，这取决于设计者，取决于它的创造者对它赋予的期待，就像每一个小孩初步一看，都有手有脚有五官，但是细致去接触会发现一个个体都有完全不可替代的部分。</p><p>产品依旧如此，对于AI平台，在GPT刚出来的时候，我便直观的认为当AI的能力足够强的时候，它会作为一种基础设施存在，如同曾经的IAAS，而围绕IAAS的云化诞生出了一系列的PAAS服务。<br>同样当AI能力成为基础设施的IAAS层后，必然需要一种AI 的PAAS的形态去支撑企业新的应用形态的开发，到今天这个方向已经有无数的产品崭露头角，无论是Dify还是Coze本质上都在这个区间进行对齐。</p><p><img src="/posts/231799d3/1112.png" alt="image"></p><p>就算是全民都在进行AI的基建，从产品设计的角度来说，也会有非常多的问题是尚未被标准化的，这也是我多次将大量的代码删了又写，写了又改，改了又删，只为找到一个走的通的逻辑，讲的明白的思路。<br>我曾经写过一篇文章：AI Agent骗局，AI Agent是个筐，什么都往里装 在这个概念被用烂的时间点，要自洽一个思路并不容易。</p><p>从整个产品设计的角度来说，AI落地核心其实就是：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">大模型+知识+工作场景。</span><br></pre></td></tr></table></figure><p>不外乎是怎么样落地，用什么样的形式落地，比如大模型+知识，可以是Rag，也可以是微调，而大模型和知识结合后所展现出来的理解力，再配合周边的系统集成，打包都叫智能体。<br>要拟人化的话，智能体会更加容易被类比成人。</p><p><img src="/posts/231799d3/1113.png" alt="image"></p><p>那么接下来的问题就是如何让人工作起来，在这个问题上，UniverAI 重新实现了概念的映射，具体的逻辑为：</p><ul><li>模型：刚毕业的学生，不同的参数的学生，具有不同的能力，类似于博士生，本科生，专科生。</li><li>智能体：将企业的知识和知识与模型进行了融合，约等于一个入职后且经过培训，可直接上岗的员工。</li><li>工作空间：员工可工作的岗位。</li></ul><p>UniverAI提供以上整体端到端的构建能力的支撑，而这里的核心重点便是工作空间，我曾在仔细思考，所谓的A2A，multi agent system，到底有何意义，这种通信到底解决什么问题，从技术角度来说，单纯的通信不解决任何问题。<br>而无论A2A，还是MAS，本质上都是在为一堆智能体创造一个可以开展工作的环境，因此UniverAI抽象出了工作空间这个重要的模块，不同于其他产品的概念，UniverAI的工作空间，核心是基于业务场景下的工作空间，并非单纯的群聊，或者一堆能力的堆砌。</p><p><img src="/posts/231799d3/1114.png" alt="image"></p><p>在工作空间内，有非常多的场景，每一个场景都基于行业特有的属性进行细节打磨，而场景下如何开展工作，已经被清晰的定义，这就意味着每一个场景都是直接对齐到业务的。</p><p><img src="/posts/231799d3/1115.png" alt="image"></p><p>在工作空间内，是一群智能体的工作场合，是一堆员工上岗的工作岗位，是最终在业务角度对齐的交付产物。<br>而在这样的一个场景下，不同能力的智能体，可以协同完成该场景下的业务，且智能体可以自由搭配和组合。</p><p><img src="/posts/231799d3/1116.png" alt="image"></p><p>也就是在UniverAI下，通过工作空间的属性，和行业形成了深度的绑定，UniverAI不单纯是一个开发平台，而是一款智能体的协作平台，在UniverAI下，可完成智能体的构建外，还可以完成多个智能体在行业场景下的协作办公。</p>]]></content>
    
    <summary type="html">
    
      UniverAI一款企业AI员工协作平台
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>我与AI深度Pair这个把月</title>
    <link href="http://www.baifachuan.com/posts/329323a9.html"/>
    <id>http://www.baifachuan.com/posts/329323a9.html</id>
    <published>2025-09-29T02:32:51.000Z</published>
    <updated>2025-09-29T02:46:58.084Z</updated>
    
    <content type="html"><![CDATA[<p>时间差不多1个半月左右，一个半月我花掉了3.5万人民币token的费用。而这，还是使用当前市面上最便宜的，性价比最高的模型，如果使用claude，估计会翻一倍，达到恐怖的7万人民币。而这，领域还仅仅只限制在 “编程” 领域这一个范围内，类似使用loveable这种东西并不在统计范畴内。</p><p>所谓的深度pair，就是完成不用顾及成本，只需要关心效果，但凡有需要就提问，就和身边真正做了一个人一样，甚至还能抽空聊聊天，把沟通的频率加快到无时无刻不在发生，让对话保持和自己思路同步的节奏，不用瞻前顾后，替AI考虑。这成本似乎看起来不可思议，似乎看起来不可想象，但是实实在在就是花了这么多。</p><p>整个结果下来给我一个非常深的感受，那就是当一个项目在1万行代码以下，当一个项目不需要上下文，彻底从头开始，大模型能带来非常好的效果和体验，而这两个条件的约束下，我仅仅称之为入门的教程，使用if else也能达到类似的效果，不外乎穷举逻辑复杂些。</p><p>其次一个肤浅的认知，但凡月花费token在5千以下的，都没资格说自己体验过把AI作为生产力工具。</p><p>当一个项目的代码量在一万行以上的时候，哪怕这个项目是完全使用大模型从0开始编写的，它依旧会表现的一脸懵圈。而这，让我觉得大模型的记忆能力，一定需要发生质的改变，才能彻底解决一个核心问题，那就是：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">大模型根本必须区分哪些东西是它自己写的，而这种区分不能是上下文记录的方式。</span><br></pre></td></tr></table></figure><p>这就相当于一个人能一眼看出签名是不是自己写的，这本书是不是自己写的，只需要看目录就能知道自己书写的内容是什么，而这种记忆是大模型不具备的，这种能力的缺失会导致大模型完全无法驾驭大型项目，单纯依靠rag或者微调，都是死胡同。<br>目前我正在设想一种架构，用新的工程能力去解决两个问题：</p><ul><li>如何让大模型能一眼识别这是自己的产出，从而快速会议起整个项目的上下文，避免大幅度的无用代码的重播。</li><li>如何让大模型的接口标注化，避免出错的时候才知道能力缺失，比如一个大模型是否支持图像输入，只有调用接口的时候报错才知道，否则需要人提前基于模型名字手工去登记约束，而这，明显是在开历史倒车。</li></ul><p>在Agent流行的今天，到底花多少token，并不取决于用户本身的问题输入，因为Agent会基于自己的逻辑进行反复的思考和推理，多次与大模型交互，可能用户的问题只输入了一个字，但是发生了1000次和大模型的交互，而这1000次则是由Agent自由发挥出来的。<br>这也是我消耗token量最大的原因，当然，这并没有什么问题，因为我自己做Agent的时候也是这样干的，甚是Agent能自我思考，还会成为销售的卖点之一。</p><p>在这个月的时间内，我和AI进行深度Pair的代码工程量大概在20万行代码左右，刚好最近我又重新思考大数据的一些内容，而Hadoop 3.3.1整个工程已然超三百万行代码，我尝试过使用和他进行深度Pair，然后放弃了，因为如果使用Hadoop的话，一周可能3.5万就花光了。</p><p>我所选择的项目大约20万行代码，且将我所不擅长的领域，前端开发丢给AI去搞，虽然不擅长，但是如果AI写出来，我依旧能读的懂，能修改，这样AI则能完全弥补我所面对的能力不足的短板问题。</p><p>在这个过程中我无时无刻不面临一个非常痛苦的问题，就是如何让大模型知道，这个事它已经干过，这是一个非常痛苦的事情。当然很多搞过Agent的人会嗤之以鼻说这不就是Agent自己的历史记录，或者自己做一个历史记录，再结合大模型自身的Cache能力，能保留历史记录的同时，还能降低Token的成本。</p><p>这当然是一种缓和的方案，但是这种固化，低效，且不具备任何智能的硬记忆丝毫不能让大模型有进化的能力，它永远像一个5岁的小孩，我告诉他1+1等于2，它知道了，我告诉它3+4等于7，它也知道了，但是当他知道3+4等于7的时候，它已经忘掉了1+1等于2。</p><p>对我来说，我不是获得了智能，我是获得了智能在某一个时间的快照。</p><p>这个快照永远是在那个时间点，无论我如何优化处理，它的智商永远无法跃级。</p><p>回到最痛苦的这个问题上来说，我希望大模型是在一个连续的思路下我和进行Pair，而不是在背诵文章一样在一个连续的文本复制列表的场景下和我进行Pair。</p><p>这就导致每次我和它沟通的时候表达出：基于上一个修改的思路进行调整的时候，它都无法准确的定义出上一个修改的思路到底是什么，哪怕曾经我告诉过它这个思路是什么。我需要继续附带无数的样例和源码去解释这个思路是什么，不厌其烦的讲述。</p><p>而这，好歹还能通过连篇的废话去反复告诉他，能解决，紧接着一个更麻烦的问题是，目前并没有特别好的读代码的功能，大模型具备语言层面的能力，但是并不具备非常友好的代码阅读能力。</p><p>而这个阅读能力的构建，除了考验大模型自身之外，也考验工具本身的查找能力，比如我尝试让AI帮我解决一个问题：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">将整个工程中使用到await axios.post和await fetch发起网络请求的地方都添加一个header xxx&#x3D;xx。</span><br></pre></td></tr></table></figure><p>在这个任务下，修改非常简单，难的是需要找到使用的地方，我将循环次数设置到1000次以上去，在20多万行代码中，AI狂奔了大半小时，成功找到且改掉的地方也寥寥无几。</p><p>在反复多次调整话术，甚至指导它定向去找之后，效果依旧没有达到预期。最后只能自己上手去修改，而在这多次来回，几个小时的AI自由发挥之后，几千块的Token没了。</p><p>中间本质的问题在于，如果要完成这样一个相对简单的问题来说，AI首先需要做到：找到位置-&gt;修改代码，而找到位置AI自身无法直接实现，需要借助工具，而不同工具呈现出来的查找能力是不一样的，特别是在海量的工程内进行搜索，虽然这只是IDE的一个control+shift+F的功能而已。</p><p>其次受限于窗口大小的限制，就算找到了也不见的能直接发送所有片段，只能逐个修改，对于无法的代码片段，会直接导致上下文不足从而引起质量不符合预期的问题，且在这样的问题下，由Agent发挥一堆之后，不但质量不行，成本还非常高。在这个过程中我无数次遇到出现上下文超长，直接导致Agent宕掉，既没有获得结果，费用也消耗了情况。</p><p>我认为一个Agent未来在商业逻辑上应该发生调整，那就是Agent的收费不应该和LLM绑定，应该完全解耦合。</p><p>因为目前所有Agent在收费上有一个很反常的逻辑就是无论Agent是否成功解决问题，甚至可能中途直接挂掉了，但是依旧需要付费，因为消费了Token，而Token是LLM收的，LLM只要使用就会产生Token，那么购买Agent到底是购买的什么？这是所有做Agent的场景下需要仔细思考的商业逻辑。</p><p>AI对于存量知识的理解，是难以跨越的门槛，这个门槛随着项目复杂度的上升，会出现指数级的增长，当我和AI进行深度Pair的时候，最严重的时候是下发一个修改命令后，吃顿饭回来，AI还在那读代码呢。。费用到是水涨船高。</p><p>当然因为我用的是成品面向C端的AI工具，对于企业来说，或者有企业服务经验的人会直观认识到说，这种情况下，典型应该构建企业内部代码知识库，提升代码效果，将企业知识，包括代码，文档，构建于一个知识库内，提升AI对存量知识的理解能力，加速查找过程，可以深度解决上下文的问题。</p><p>关于这个，下次有空再说了，我只能说，算了，算了，我在Hadoop这300万以上的代码工程中，尝试搞了这套，差点没累死。。</p>]]></content>
    
    <summary type="html">
    
      我与AI深度Pair这个把月
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>看三年，想一年，认认真真干十年</title>
    <link href="http://www.baifachuan.com/posts/4534a22d.html"/>
    <id>http://www.baifachuan.com/posts/4534a22d.html</id>
    <published>2025-09-29T02:30:26.000Z</published>
    <updated>2025-09-29T02:45:04.481Z</updated>
    
    <content type="html"><![CDATA[<p>对比开始，想清楚更重要<br>当一件事处于设计阶段的时候，任何的投入，都是收益，因为不可行的结论，也是很有价值的输出，而事情一旦步入到实施，那么每一天的投入，都是成本，需要考虑复杂的投入产出比。<br>因此想做一些事的时候，最好是先旁观三年看清局势，想一年如何开展，然后踏踏实实，认认真真的做十年。十年并不长，一个人从大学毕业后，需要工作五十年才能达到退休年龄，十年又很长，足够在一个方向上做出纵深的东西。<br>AI看起来好像来了<br>两三年前OpenAI的ChatGPT刚出来的时候，我正在关注superblocks这家初创企业，superblocks以一种极简的方式去创造数据商品，每一个商品就是一个小的blocks，一个数据领域的微服务架构的实现载体，把data mesh的很具像化的落地了，尤其是商品的整套交易体系。<br>然后GPT以一种超乎寻常的热度问世了，用尬聊的方式满足了全世界人和AI顺畅聊天的好奇心，满足了大众的猎奇心理。而此时的模型也仅仅只具备聊天的能力，在通用聊天下表现出非常强的拟人性，给人了一种耳目一新的感受，软件由GUI进入到CUI的风气也由此展开。</p><p>似乎真正的强人工智能到来了。</p><p>一切AI辅助都是伪生意</p><p>我认为，在B端市场下，一切以团队/组织 作为单位的AI辅助，都是伪生意，当一家企业引入一个所谓的AI辅助后，在管理者的视角下，如何度量这个东西产生了价值 ？非常简单，以前10个人的活现在5个人能不能做？如果能，那么被裁掉的那5个人是谁？对于甲方自己来说，如果要推行AI辅助，而又不进行减员，那么这个AI辅助引入就是纯成本，如果要减员，就需要确认AI辅助的引入是真的产生了价值，这本身就是利益矛盾。</p><p>老板问：AI有用吗？<br>员工异口同声回答：有用！<br>老板说：好，明天团队人员减半！</p><p>那么能不能先裁员呢？当然不能，因为这个板很大，并不是谁都敢拍。</p><p>所以AI辅助对于企业来说，永远只会是个额外新增成本，不会带来任何收益，这个AI能力没有关系，单纯利益而已。</p><p>甚至，AI辅助在B端市场是个双输的局面。</p><p>AI应用的平台生意</p><p>在我第一次使用GPT的时候，除了尬聊的场景外，如果考虑到工程落地，GPT给我的第一个感受并不是一个应用，而更像是数据库。距离应用之间还缺点什么东西，其次当企业在落地的时候，还面临内部信息需要让大模型感知到，才能准确的回答，这些都是问题。</p><p>而紧接着在后续的科技大爆发中，基于工程解决方案的RAG和基于模型本身的微调，都尝试提供一个适配接口，让大模型可以工作在一些特定的场景下，具备更好的灵活性。</p><p>然而这还不够，即便有RAG和微调，大模型距离变成应用，依旧有着遥远的距离，如同数据库一般，要做成应用，上层还需要一系列的工程框架，而首先需要的是一个应用平台。随着大模型的发展趋势，越来越多的企业采纳AI只是时间问题，虽然中间会面临非常多的问题，但是并不影响整个趋势的发展。</p><p>因此企业需要一个平台来构建和开发AI应用，似乎是一个必然的选项，但是AI应用又和业务应用不太一样，AI应用极度依赖数据本身，而在企业中数据通常又归属于数据平台中，且数据集不一定是为AI而准备。</p><p>然而一个AI应用开发平台必然需要打通企业的数据平台，且具备直接消费和加工数据的能力，基于这样的一个基础上，需要提供一套支持AI应用开发的体系，于是我尝试在数据平台的作为基础之上，引入模型管理的概念，且通过应用模板的概念来串通数据，模型和使用场景，而业务模板作为牵引，可直接对齐到业务，会更容易绕过企业IT带来的限制，基于这个思路，初步实现了整套原型验证。</p><p>得到一位大V的支持，进行了部分市场的验证，与此同时在北京和另一位老友进行了沟通，他也认可同样的方向，且进行着同样的事情，所不同的是在实施上，我沿着上层的应用往下走，而他更倾向于从下往上做，在寒冷的大冬天在北京的花园里探讨过数次关于整个平台的价值。</p><p>然而就算平台的价值非常被认可，在我看来，平台依旧不是一门好生意，因为目前来说平台无论怎么发展，平台的受众始终是企业IT，而企业IT是成本中心，而非利润中心，且非常重要的一点是IT并不控制预算，也不控制需求。和IT部门的合作更像是0.8*0.8=0.64，两两合作，两败俱伤。</p><p>也就是平台的价值始终需要间接的以工具的方式加入到企业中，平台本身并不直接创造业务价值，基于平台上做出的东西才是具有业务价值的东西。</p><p>要做的不是AI平台，而是用AI去做平台</p><p>AI平台本质上还是一门效能工具的生意，只是创造对象从应用系统变成了AI应用而已，并没有打破本身的业务模式，所以必然会面临平台同样的困境。</p><p>在AI平台的经验上，我认为似乎我们把AI想的太简单了，AI本身应该作为生产力对待，而非固化的工具箱。即便现在的AI达不到对应的要求，那么未来也必然只是个时间问题，要知道全球从能源到芯片全方位开展军备竞赛搞AI，不可能只是单纯的做一个聊天更好的模型。</p><p>AI成为生产力工具只是时间问题，大势所趋。</p><p>所以重新思考会平台本身，我认为平台价值并没有问题，但是平台的工作方式和受众应该发生变化，传统平台为什么需要面向IT，而不能直接面向终端使用者？</p><p>因为终端使用者不具备需求的实现能力，而IT团队为了实现需求需要一个趁手的工作台，而平台就是这个工作台，工作台加速了IT团队实现需求的效率。平台生意是给IT部门售卖了一个锤子，IT部门用这个锤子去敲出一朵花，这朵花如果不好看，不能满足需求，平台自然也会受到负面评价。</p><p>所以未来平台型的生意应该以终端需求方能接受的工作方式直接交付产物，这个产物是平台动态产生的，而能动态产生的核心引擎就是AI作为生产力。</p><p>这样会对企业的IT带来影响吗？并不会，因为这样的平台同样需要人来运维和服务，也就是平台从曾经定位成工具，由人去操作，变成了自动生产的引擎，由人来维护，带来的直接价值就是降低了交付周期，加快了业务流转，提升了企业的利润。</p><p>让参与方都能获利。</p><p>所谓的行业到底是什么</p><p>自古以来，对我来说行业知识，领域知识，一直是蒙着一层面纱，似乎总是隔着一层什么，很多时候总以缺乏行业知识聊以自慰，那么到底什么是 行业知识？什么是行业经验？</p><p>在这些年的逐步摸索和学习过程中，我认为所谓的行业知识，本质上是需要理解此行业下的商业运作模式和企业经营理念，而这套东西得融入到日常的工作中，才能有深刻的体会，其他途径是无法获取的。</p><p>对于很多不接触市场的产研团队来说，似乎销售就是靠吃吃喝喝，发发红包，就能拿下单子，照着吃吃喝喝去聊单子，基本上路子就跑偏了。</p><p>而一个AI平台要 发挥真正的价值，必然也得融入到一个聚焦的行业中，而要融入到一个行业中，自然也得贴合此行业的商业模式和企业经营理念。</p><p>任何一个通用到任何行业都能用的平台，平台本身几乎没有任何价值。</p><p>想了什么，做什么</p><p>在AI这个时代，围绕AI而出来的产品生意无数，但是生意模式上并没有发生本质的变化，依旧是用曾经的商业模式，在AI中重新做了一遍。</p><p>然而我认为，在一个平台上给不同行业做应用，提供一个通用的平台，显然不如给一个行业做平台，实现一个行业平台。</p><p>然而本质的区别还在于，这个平台是自工作的。</p>]]></content>
    
    <summary type="html">
    
      看三年，想一年，认认真真干十年
    
    </summary>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/categories/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/tags/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>一篇讲MCP理解误区的文章带来了最大的误区</title>
    <link href="http://www.baifachuan.com/posts/692e752b.html"/>
    <id>http://www.baifachuan.com/posts/692e752b.html</id>
    <published>2025-09-29T02:29:20.000Z</published>
    <updated>2025-09-29T02:45:04.477Z</updated>
    
    <content type="html"><![CDATA[<p>今天看到一篇传播的比较广的内容，内容主题是《关于 MCP 的几个理解误区》。</p><p>当我仔细看完后，发现他带来了MCP最大的误区。</p><p>原文：</p><p>误区 1：MCP 协议需要大模型支持</p><p>结论：MCP 协议不需要大模型支持，哪怕你使用古老的 gpt-2 作为问答模型，依然可以使用 MCP 协议补充上下文信息。</p><p>一叶扁舟，公众号：代码麻辣烫<br>关于 MCP 的几个理解误区</p><p>我的看法：</p><p>MCP的M是Model的意思，类比一下，智能汽车的车机系统只能在汽车上用吗？当然不是，比如车机可能采用Android的系统去构建，而手机也能使用Android去构建自己的系统，但是会有人把手机的系统叫做“车机”吗？</p><p>所以“汽车车载的车机可以不在汽车上使用”这句话错吗？其实也没有错，kernel可以到处移植，虽然都是同一个kernel，但是使用场景，实现的系统的目的，甚至提供的功能完全不一样，几乎没有任何类比，所以这个回答虽然没有错，但是有意义吗？毫无意义。。</p><p>原文：</p><p>误区 2：只有支持 Function Calling 的模型才支持 MCP 协议<br>聊 MCP 协议，必须要理解 Function Calling 机制。<br>结论：不支持 Function Calling 的模型，依然可以使用 MCP 协议补充上下文信息。<br>一叶扁舟，公众号：代码麻辣烫<br>关于 MCP 的几个理解误区</p><p>我的看法：</p><p>在MCP的官方定义中<a href="https://modelcontextprotocol.io/introduction的开头就直接清晰且直接的说了：" target="_blank" rel="noopener">https://modelcontextprotocol.io/introduction的开头就直接清晰且直接的说了：</a></p><p>MCP is an open protocol that standardizes how applications provide context to LLMs.</p><p>什么意思？意思是MCP重点规范的就是模型上下文的使用方式，而这个使用方式就是Tools Call，然后这里直接抛弃这个前提，这和自媒体常用的“抛开事实不谈”几乎异曲同工之妙，都不提Tools Call，那还提啥MCP。且如果通过手写Prompt来解决问题，这就是MCP的话，那在外面挂个程序，提前通过传统方式处理一下音视频文件转成文本丢给gpt的chat模型，那么意味着gpt的chat模型也应该叫多模态模型。</p><p>原文：</p><p>误区 3：大模型原生支持 MCP 协议<br>结论：大模型原生支持 MCP 协议，这种说法是不专业的。大模型现阶段不可能原生支持 MCP。<br>一叶扁舟，公众号：代码麻辣烫<br>关于 MCP 的几个理解误区</p><p>我的看法：</p><p>大模型当然原生支持MCP协议，且非常专业，注意重点“协议”，这就像说Nginx支持HTTP协议，这个说法很专业，没有问题，且一般不会有人听到“Nginx支持HTTP协议”这句话的时候，会理解成Nginx要提供一堆带HTTP服务的应用程序。。</p><p>通常来说大模型在对MCP协议的支持上，重点的地方有2个：</p><p>一个是传输协议的支持，这个目前的模型来说，基本上都是复用了Tool Call的用法，然后通过外部的framework，例如langgraph这种框架提供面向应用程序的接口。</p><p>另一个是内容协议的支持，目前内容协议这部分，不同模型的实现几乎完全不一样，这一部分内容是需要继演进的。</p><p>所以这样一篇讲MCP误区的文章，带来了最大的误区。。</p><p>而另一方面，每一个概念必然有其边界和作用域，如果一个东西被泛化成什么都可以用它解释，那么这个东西大概率什么价值都没有。</p>]]></content>
    
    <summary type="html">
    
      一篇讲MCP理解误区的文章带来了最大的误区
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>想用AI降本？想多了，那是骗人的</title>
    <link href="http://www.baifachuan.com/posts/ebb1ea3b.html"/>
    <id>http://www.baifachuan.com/posts/ebb1ea3b.html</id>
    <published>2025-09-29T02:28:11.000Z</published>
    <updated>2025-09-29T02:45:04.479Z</updated>
    
    <content type="html"><![CDATA[<p>曾经有部电影叫《我不是药神》，故事很简单，徐峥扮演的男主角程勇，一个犯着“穷病”的中年离异男，处于父病妻离且要带走儿子的漩涡中。</p><p>一天，一位患有白血病的不速之客意外到访，打破了他的平凡人生，他从一个交不起房租、倒卖卖印度神油的商贩，一跃成为印度仿制药“格列宁”的独家代理商。收获巨额利润的他，生活剧烈变化，被病患们冠以“药神”的称号。</p><p>这个故事和AI有什么关系？格列宁，是经过了高额的研发成本，然后投放到市场，因此站在道德上格列宁卖高价，有一点不顾死活的感觉，但是站在商业上来看，如果格列宁卖低价，那么收入必然无法支撑高额的研发费用。</p><p>最终的结果就是再也不会有企业愿意投入研究，去研发新药。</p><p>AI也如同格列宁一样，投入了高额的研发成本，经历了漫长的研发周期，而相比格列宁还不如的地方是，格列宁一旦研发突破后，运维成本会大大降低，而AI在取得突破后，持续提供服务依旧需要非常高额的成本，就算是仿制，也无法做到格列宁仿制品那样的低廉。</p><p>所以如果站在成本的角度上来看，AI能带来的成本降低几乎微乎其微，甚至不但不会降低，还会造成更高的成本投入，有无数宣传AI的会使用马斯克裁员来证明AI带来的成本的降低。</p><p>但是仔细看马斯克的裁员过程会发现，其实和AI一毛钱关系都没有，马斯克是使用996,007的工作方式，营造危机感，让1个人干2个人活，但是工资并不会增加，其次X本身就非常冗余，没有新的赛道突破在成熟期本身就不需要这么多人。</p><p>也就是无论有没有AI，马斯克裁掉90%的员工，系统依旧可以运转，至于会不会崩，就听天由命了。还有不少人会拿着智能客服举例子，请仔细阅读这篇：《给我转人工，转人工」，AI客服把人逼疯》 <a href="https://mp.weixin.qq.com/s/_l93N0iWmktp2NDVscmgIg?poc_token=HM7T82ejDz2Rew-tWU6_BB6jvHYUsU4MoJTBRA5W" target="_blank" rel="noopener">https://mp.weixin.qq.com/s/_l93N0iWmktp2NDVscmgIg?poc_token=HM7T82ejDz2Rew-tWU6_BB6jvHYUsU4MoJTBRA5W</a></p><p>那么AI的价值在哪里？AI的价值在与增效，降本只是附属品，而增效的价值体现在曾经无法做到的事，可以使用AI做到了，仅此而以。</p><p>那么围绕曾经无法做到，而现在可以做到这一点来构建出来的业务，自然可以获得增效，而降本是怎么体现的呢？是这样体现的：</p><p>曾经想做的事，在没有AI的时候如果要去做，会投入大量的成本，甚至都无法取得令人满意的效果，这时候的成本就非常高。</p><p>现在有了AI，可以做到曾经无法做到的事，并且成本比曾经预期的低很多，这难道不是降本吗？</p><p>只有正确认识AI的价值，以及AI擅长的地方，才能更好的使用它，否则不过是盲目操盘，引入一场灾难而已。</p><p>所以企业在考虑AI的时候，如果是指望使用AI大幅度降低现有业务的成本，可能效果不会那么的符合预期。站在商业的角度，业务要发展，企业要做的更好，可以开源节流，而AI的价值是在与开源，而并非是节流。</p>]]></content>
    
    <summary type="html">
    
      想用AI降本？想多了，那是骗人的
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>反人类的AI应用构建方式：工作流编排器</title>
    <link href="http://www.baifachuan.com/posts/e7b4526b.html"/>
    <id>http://www.baifachuan.com/posts/e7b4526b.html</id>
    <published>2025-09-29T02:19:06.000Z</published>
    <updated>2025-09-29T02:45:04.478Z</updated>
    
    <content type="html"><![CDATA[<p>使用工作流的方式去编排整个流程，完成业务的构建，这种方式并不稀奇，在很多领域存在特别多，比如影视传媒，在影视上采用工作流的方式去完成整个视频的加工 处理，甚至可能会编写部分脚本，这是非常常见的现象 。</p><p>这是因为这样的行业下的很多流程，大体可以被标准化，原因也在于输入和输出是标准化的，再配合多年以来的行为习惯和模式，以这样的方式来完成业务，基本上已经成了共识，这样的工作方式也确实可以提高效率，完全满足需求。</p><p>但是，当把这样的工作流的方式迁移到软件研发领域后，问题就会暴露的非常明显，在曾经的低代码时代，就有不少解决方案，尝试通过拖拉拽和流程编排处理的方式，构造一个可用的软件，到了AI时代，这样的工作方式更加盛行，仿佛已经是标配。</p><p><img src="/posts/e7b4526b/640.webp" alt="image"></p><p>基于任何一个做生成似AI的平台，都有一个通过拖拉拽配置工作流，快速构建AI应用的引擎，然而这个东西真有那么好吗？</p><p>首先，从价值上来说，肯定有价值，只是价值多少的问题，对于工作流这样的引擎来说，投入产出比并不见的有多大。</p><p>站在目标群体的视角来看：</p><p>工作流引擎的目标群体始终是飘忽不定的，真正的终端用户，消费者，用的是应用的SAAS成品，根本不会去搞拖拉拽，给自己造一个应用。</p><p>真正具备研发实力的研发人员，面对各种各样的需求，从可控性和可交付性的角度来说，更愿意自己首先逻辑去开发一个AI 应用，以确保最终能够被交付。</p><p>如此看起来，似乎只有不会写代码，但是又要去做AI应用的群体，才会是这个东西的受众，那么这个群体，是否会承担企业开发生产级AI应用的职责？</p><p>当然不会。</p><p>所以会发现这样的软件，受众一直不明确，可能只有那种想快速demo一把的群体，或者尝鲜的群体，想自己体验一把动手能力的群体，会尝试使用一下这样的东西。</p><p>站在开发工作量的角度来看：</p><ul><li>开发一款功能完备的工作流引擎，工作量非常大，除了穷举出所有所需要的节点之外，还需要考虑到不同节点之间的组合方式，从而让整个流程可以运行起来，无论是从前端体验较好的界面，还是整体工作流的执行，都会有非常大的工作量。</li><li>用超大的研发工作量，服务于一个不那么确定的群体，用不那么清晰的流程去解决一个不知道可能是什么的问题。</li></ul><p>这样的方案，相对来说，真的糟糕透了。。</p><p>所以我向来非常反对这种拖拉拽进行编排的解决方案，因为它真的不会有想象中那么好，但是在AI领域下，类似comfyui就好评非常好，原因在于，comfyui所面对，或者目标群体本身就是影音范围，对于这个群体来说，工作流本身就是曾经的工作方式，只是平迁了而已。</p><p><img src="/posts/e7b4526b/641.webp" alt="image"></p><p>但是抛开这个领域外，在研发领域，工作流真正的问题会在于，它既改变了曾经研发群体的工作方式，又没有办法完全保证所有场景都支持，这就导致整个方案会面临非常大的缺陷和不确定的未知风险，一旦出现这种风险，几乎没有解决方案。</p>]]></content>
    
    <summary type="html">
    
      反人类的AI应用构建方式：工作流编排器
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>要去甲方，外包狗都不做？不存在，没有甲方</title>
    <link href="http://www.baifachuan.com/posts/a5ed2ff0.html"/>
    <id>http://www.baifachuan.com/posts/a5ed2ff0.html</id>
    <published>2025-09-29T02:17:47.000Z</published>
    <updated>2025-09-29T02:45:04.481Z</updated>
    
    <content type="html"><![CDATA[<p>在B端生意内，有一句广为流传的话，叫外包狗都不做。这样的说法缘起的原因在于B端生意，特别是国内，有非常多的约束，比如：</p><ul><li>条件苛刻，企业找外包，是因为想降低成本，用比自己还便宜的人去做自己做不出来的东西，接锅的外包团队基本就是火坑。</li><li>汇款周期慢，三年五年甚至十年才回款，是国内企业的主流。</li><li>非专业的过度干涉，按照对方自己的认知，会干扰到实施过程中的任何一个环节，对实施方的自主性带来很多约束。</li><li>极度不信任，外包属于廉价劳动力，长工，甚至短工，所以需要不断的监视，监控。</li></ul><p>等等非常多的约束，导致B端的生意成为了一个非常难做的市场，甚至连李开复都喊出了“坚决做ToC，不做赔钱的ToB”的感悟，那么对于普通的B端供应商来说，日子自然不会更好过。</p><p>但是，反过来思考一下，什么是外包？</p><p>什么又不是外包？</p><p>有没有真正的甲方？</p><p>A企业给B企业做项目，这时候A企业对于B企业来说，就是外包，而B企业对A企业来说就是甲方。</p><p>但是B企业同样也有他的客户C，对C来说，B企业就是C的外包，而C企业就是B的甲方。</p><p>所以外包狗都不做，要去做甲方，到底什么才是甲方？</p><p>在国内的B端圈子，基本上形成了一个恶心循环，曾经我见到某企业对他的外包团队说：</p><p>“你就是我花钱买来的一条狗”，并且这种认知在国内算是所有甲方的主流认知，不过这家企业公开的说出来了而以。</p><p>那时以为这就是这家企业自身的问题，后面发现，因为他的甲方就是这样对他的，所以他自然也会这样对待他的外包，在这样的一条链路下，形成了一连串的鄙视链。</p><p>无论在哪一个环节，都免不了会成为某家企业的外包，同时也可能是某一家企业的甲方，具有双重身份。</p><p>所以外包？</p><p>不存在，大家都是外包，甲方？</p><p>也不存在，所有企业都是甲方。</p><p>那么对于选择来说，真的是不进外包，进甲方吗？</p><p>非也。</p><p>在这个链路下，过得正常的节点，不是甲方也不是外包，而是话语权最大的那个，所以优先选择，应该选择话语权最大的那个节点，无论他是外包身份居多，还是甲方身份居多。</p><p>只要话语权足够大，外包又何妨？</p><p>是不是甲方又有何所谓？</p><p>至始至终可以按照自己的既定节奏走，而不受对方影响，这个才是最重要的。</p>]]></content>
    
    <summary type="html">
    
      要去甲方，外包狗都不做？不存在，没有甲方
    
    </summary>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/categories/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/tags/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>AI 原生应用？现在造不出来，不存在</title>
    <link href="http://www.baifachuan.com/posts/6faa6aa.html"/>
    <id>http://www.baifachuan.com/posts/6faa6aa.html</id>
    <published>2025-09-29T02:16:39.000Z</published>
    <updated>2025-09-29T02:45:04.475Z</updated>
    
    <content type="html"><![CDATA[<p>自从大语言模型发展起来之后，围绕LLM的生态也在快速发展，许许多多的开发者自底向上，或者自顶向下构建各种和LLM相关的场景。但是AI应用并非LLM而诞生，在LLM之前，AI落地的应用已经不计其数，只是所采纳的技术各有差别。</p><p>在LLM起来后，又一个新的概念被提起来，那便是：AI原生应用。</p><p>然而很遗憾，到今天，不存在任何AI原生的应用，如果有谁提到自己能做AI原生应用，那一定是在忽悠，只是从能力储备的角度，储备AI原生应用可能需要的能力平台，是有可能做到的。</p><p>即便如今火热的Rag，Agent等实现，依旧和AI原生扯不上一丁点关系，只能说应用中使用到AI的能力。</p><p>要清晰认识到这一点，那么就必须知道什么叫：原生。</p><p>在AI原生之前，还有一个已经非常成熟的概念，叫：云原生应用。</p><p>而云原生中的核心是：云，而不是应用，那么同样，AI原生应用的重点也一定是AI而非应用，所以但凡从应用视角去讲述采纳了何种对话技术，搜索技术，做了何种模型优化手段，都是不合适的。</p><p>在云原生应用中，因为云提供了便利的基础设施，云所具有的弹性能力，以及内部预设的各种基础设施服务，可以让应用开发者完全只关心真正的业务逻辑，而无需关注组件依赖以及整体的运维。</p><p>典型的实施方式就是基于k8s的技术下，应用开发者只需要写自己业务的代码打包成docker images后，发布就可以使用，至于依赖的数据库，缓存等组建，已经被提前打包好，直接使用即可。</p><p>这便意味着云原生通过部署+预设穷举的方式，把应用开发者的环境隔离出去。提供了底层的基础设施，所以云原生的核心能力在于从运维入手，剥离开发者的运维工作。</p><p>那么在AI的概念下，如果需要实现AI原生，那么意味着AI一定是基础设施，什么样的AI才能成为基础设施？云原生已经解决了运维的问题，那么AI必须得解决生成的问题。</p><p>这就是意味着AI原生必须达到替换掉写代码的群体，才能称之为AI原生，在云原生下，每一个应用不再需要有专业的运维人员，统一收口到云上就行了，所有的运维都去云平台，让开发者开开用云平台。</p><p>同样对于AI原生来说，不在需要开发者，所有写代码的人都去AI原生的平台，做平台，让业务开发者来用AI原生平台。</p><p>但是到今天，AI能力依旧达不到完备的基础设施的程度，要完全取代程序员，直接生成软件，更是有一段距离。</p><p>所以在AI尚且不能成为基础设施之前，就谈不上AI原生应用，所以凡是宣称已经做了AI原生应用的，大概率都是在忽悠。。</p>]]></content>
    
    <summary type="html">
    
      AI 原生应用？现在造不出来，不存在
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>AI Agent骗局，AI Agent是个筐，什么都往里装</title>
    <link href="http://www.baifachuan.com/posts/2f6cad1f.html"/>
    <id>http://www.baifachuan.com/posts/2f6cad1f.html</id>
    <published>2025-09-29T02:14:42.000Z</published>
    <updated>2025-09-29T02:45:04.472Z</updated>
    
    <content type="html"><![CDATA[<p>我对AI Agent这个东西非常反感，反感到听到这个词就觉得是在搞诈骗，因为AI Agent 100个人有100个解释，更加解释不清楚AI Agent和AI APP是个啥区别。</p><p>甚至搞不清楚自己在做AI Agent，还是AI APP，如果认为自己是在做AI Agent，那么AI APP似乎就只是个前端页面？</p><p>如果业务视角交付的是AI APP，那么又何必大炒AI Agent这个概念，谁家在讲OA系统的故事时候会到处给人说自己在MySQL创建了一张表？</p><p>扯来扯去似乎就变成了，AI APP就等于AI Agent，造AI 应用，就是在造AI Agent，事实上的工作内容真的是这样吗？</p><p>对于实施方来说，重要的是脚踏实地的稳步前进，如果要炒概念讲业务故事，就老老实实的说应用，如果是搞技术的，就老老实实的去做Agent，非要把两者混为一谈。</p><p>似乎交付业务价值就是在交付AI Agent，但是又没法圆AI APP这个概念，于是就干脆把两者等价算了，这，是忽悠的表现。</p><p>在不少大忽悠的大V的热点文章中，把AI Agent称为大模型落地的最后一公里，且非常深奥的抽象出了Agent的落地架构：</p><p>包含规划（Planning）、记忆（Memory）、工具（Tools）、执行（Action）四大要素。</p><p>然后还费劲的做了非常高深的解释：</p><ul><li>第一，感知环境。首先，AI Agent需要收集环境信息，可以使用传感器或从各种来源收集数据。</li><li>第二，处理输入数据。AI Agent处理前序环节收集到的知识，这可能包括组织数据、创建知识库，或创建AI Agent可以理解和使用的内部表征。</li><li>第三，决策制定。AI Agent使用逻辑或统计分析等推理技术，基于知识库和目标做出明智的决策，可能涉及应用预先设定的规则或机器学习算法。</li><li>第四，规划和执行行动。AI Agent制定实现目标的计划或一系列步骤，可能包括创建分步策略、优化资源分配或考虑各种限制和优先事项。根据计划，AI Agent执行所有步骤以实现预期目标。AI Agent还可以从环境中接收反馈或新信息，用于调整未来的行动或更新知识库。</li><li>第五，学习和改进。在采取行动后，AI Agent可以从经验中进行学习，通过反馈循环提高AI Agent的性能，并适应新的情况和环境。</li></ul><p>这架构有问题吗？没问题，但是这哪是最后一公里，这简直就是二万五千里长征刚起步，遵义会议都还没开。。</p><p>甚至曾经的各种XX中台在这个要素下都得叫声哥。。。</p><p>在理解什么是AI Agent，以及什么是AI APP的时候，首先需要罗列清楚一个基于大模型去实现的应用，到底是一个什么样的工作流，而那些讲不清AI Agent的人，大概率就是做了个hello world就开始上路。。</p><p>如果是做一个demo，基于大模型的应用基本上会有如下流程：</p><ul><li>用户上传个PDF</li><li>后端接收PDF并且做文本提取等操作</li><li>基于预设prompt与大模型对话</li><li>适当引入些COT类似的机制，多次思考再反复与大模型对话，确保答案符合预期，这个过程可能会用到一些工具。</li><li>后端组装结果，返回结果给页面</li></ul><p>在这个过程中，大部分忽悠师傅把 2, 3, 4 全部囊括在Agent的范畴内，且做出了一些所谓的平台只需要在页面点点点就能完成：上传文件，与大模型对话 这件事。</p><p>然后就衍生出了前面的一系列Agent的高深定义，看起来似乎很合理？</p><p>那么如果是做一个生产应用呢？它会有如下流程：</p><ol><li>用户往数据平台上传1万个PDF，每个PDF 1个GB。</li><li>采用 Spark或者Ray框架，对每个PDF进行加工处理，必要的时候会做向量化。</li><li>在元数据管理系统构建整套非结构化数据的信息。</li><li>做一个前端页面支持用户对话，对话的时候可以选择文件（而非直接上传）。</li><li>后端接收用户输入的请求。</li><li>基于预设prompt与大模型对话。</li><li>适当引入些COT类似的机制，多次思考再反复与大模型对话，确保答案符合预期，这个过程可能会用到一些工具。</li><li>后端组装结果，返回结果给页面。</li></ol><p>那么在这个环节中，Agent应该包含哪几步呢？包含1和2吗？如果不包含，那么典型和那一套深奥的范围对不上，如果包含，那岂不是整个数据平台都可以叫Agent了？</p><p>反而这样一拆解之后，其实结论已经非常清晰了，Agent仅仅只是构建大模型应用中的一个非常细微的步骤，甚至都不是必须的步骤，比如：</p><p>微调了一个垂直模型，直接问答就能得到完全符合预期的答案。</p><p>那么基于这个模型只需要做一个非常简单的前后端，什么所谓的AI Agent框架都可以不要，这算不算AI APP？和Agent有关系吗？</p><p>所以从概念上来说，开发一个 XXX助手的描述  明显 是合理的，而 开发一个XXX Agent 明显是个骗子。</p><p>这就像 开发一个小程序的官网 听起来 就很合理，但是 开发一个小程序官网的数据库表结构 听起来是不是很不对劲？</p><p>因为小程序官网才是真正的业务，而创建数据库的表，只是完成这个官网开发中间的一个步骤，甚至都不是必须的，万一是个静态页面呢？</p><p>AI Agent不是筐，不能什么都往里装，老老实实去搞 AI 应用，不要造一些稀奇古怪的概念，反而让人很难理解。</p>]]></content>
    
    <summary type="html">
    
      AI Agent骗局，AI Agent是个筐，什么都往里装
    
    </summary>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/categories/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
    
      <category term="智能应用" scheme="http://www.baifachuan.com/tags/%E6%99%BA%E8%83%BD%E5%BA%94%E7%94%A8/"/>
    
  </entry>
  
  <entry>
    <title>ToB想当然之从项目沉淀产品</title>
    <link href="http://www.baifachuan.com/posts/3f49becf.html"/>
    <id>http://www.baifachuan.com/posts/3f49becf.html</id>
    <published>2025-01-18T14:35:58.000Z</published>
    <updated>2025-01-18T14:37:00.635Z</updated>
    
    <content type="html"><![CDATA[<p>有时，乙方在给甲方做项目的时候，想着期望通过这一个项目，抽象出一个更加通用的方案，然后将可以复用的代码，整理成一个成型的产品，再直接带到下一个客户那，作为下一个项目交付的起点，不断往复，从而形成独特的竞争力和拉低成本的手段。</p><p>甚至部分甲方会主动说，把这个实现可以带到其他客户那里，因为这是个行业痛点，作为和乙方进行价格谈判的一个筹码，而一旦乙方信了这个方向，便会陷入未来的辉煌想象中，从而觉得当前的一切都是值得投资的，于是开启了地狱般的交付模式。</p><p>实际上，这是一个非常不现实且想当然的做法，首先做项目和产品本身是冲突的，且不说项目解决的是眼前的苟且，产品是为了诗和远方，单纯从运作上来说，如果这样做能成功，那么世界上最伟大的产品一定出自外包公司，然而事实上并不是。</p><h2 id="国内的乙方的定位只是廉价劳动力"><a href="#国内的乙方的定位只是廉价劳动力" class="headerlink" title="国内的乙方的定位只是廉价劳动力"></a>国内的乙方的定位只是廉价劳动力</h2><p>鉴于国内特殊的环境下，所有的乙方都是廉价劳动力，以相对的角度来说，国外的企业在寻求乙方的时候，更多是能力上的补充，这也凸显出差异化的地方，就是国外的大厂无论是AWS还是Google，都会有比较聚焦的地方，也正以为如此，世界上才会有DataBricks这类优秀的企业的生存空间，而微软又和DataBricks形成了非常紧密的生态，其次AWS的Markplace中也有不少优秀的内容。</p><p>所以国外企业寻求乙方的时候更多时候是站在能力补充，这时候对乙方会相对友好，且更容易做成功事，乙方可以有更多的灵活性去做出更好的设计，其次更容易形成长期的合作，从而实现共赢的局面，甲方更多是Manager的角色。</p><p>而国内不一样，国内找乙方原因很简单：需要廉价劳动力，且甲方一定有一个所谓的“技术负责人”，写了张PPT自以为出了个解决方案，但是重点是他已经在内心敲定这个东西需要几天实现，工作量拍死了，基本上已完全框死了乙方的设计范围，乙方的定位就是：“甲方已经想清楚了这个事的复杂度，怎么做，都在脑子里面过了一遍，技术负责人也选好了，就差几个廉价劳动力。”，因此无论乙方如何定位自己，在甲方的眼里都是廉价劳动力，也正因为如此，国内的乙方是不可能结束到有意义的业务的，基本上都是些甲方不想做的，边角料和体力活，在这样的业务背景下，乙方就不可能获得所谓的行业知识。</p><p>因为如果这样业务很重要，技术要求很高，甲方为什么不自己招个团队？既要又要还要是国内所有企业的共识，这也是国内永远不会有DataBricks这类企业出现的原因之一。</p><h2 id="项目是做加法"><a href="#项目是做加法" class="headerlink" title="项目是做加法"></a>项目是做加法</h2><p>再从项目本身上来说，项目之所以会存在，本质上就是因为没有一个标品能直接开箱就满足要求，所以需要设立一个项目，完全贴合这个业务场景本身，这就意味着从项目去抽象通用功能本身就是一个不现实的做法，如果这个业务场景可以抽象出一个通用的产品，为什么不直接找个类似产品公司来二开？</p><p>所以始终要明白，项目的投入是成本，项目被认可的一个重要的因素是可以随着业务变动而快速响应，需要完全贴合业务，否则这个项目基本上就中道崩卒。</p><h2 id="产品关注的是杠杆"><a href="#产品关注的是杠杆" class="headerlink" title="产品关注的是杠杆"></a>产品关注的是杠杆</h2><p>从产品的角度来说，产品关注的是如何同一个相对通用的解决方案去撬动多方业务，从而实现一个比较好的杠杆，这就意味着产品一定是重设计，再实现，而项目优先是实现，其次才是设计，站在项目受众的角度来说，这个东西其他公司能不能用不重要，自己能用的舒服才重要，所以产品是无法先投入实现，再来抽象的，因为已经投入实现后，问题已经被具像化，没法再抽象了。</p><h2 id="项目交付和产品规划是互斥的"><a href="#项目交付和产品规划是互斥的" class="headerlink" title="项目交付和产品规划是互斥的"></a>项目交付和产品规划是互斥的</h2><p>如果一个项目组既承担了产品研发，又在负责一个具体的项目的交付，那么基本上团队很容易就人格分裂了，如果一个 需求这个业务场景下就是需要，但是产品完全完全不需要，怎么办？代码怎么管理？两套分之如何处理？从项目管理来说，管理一个项目的交付和管理一个产品的迭代是完全不一样的，因为两者衡量价值点是不一样的。</p><ul><li>对于项目来说，衡量的点在于这个东西甲方是否喜欢？只要满足这一点，后面的预算就有着落，项目就能交付，其他不重要。</li><li>对于产品来说，衡量的点在于这个东西是否通用？能否卖给其他公司？如果不能，即便这个客户给再多的钱，也不一定会投入去做，因为这涉及到资源协调的问题，伺候好这一个客户，虽然一次性的赚到了100万，但是可能没有精力去支持那10个10万的通用客户，孰轻孰重很清晰就能看到。</li></ul><p>更何况乙方做到了完全满足甲方业务要求，还能做出抽象这件事，大概率这个项目投入的成本会比预估超出一倍，因为抽象产品这个事的成本一定只能乙方自己承担，如果甲方以此作为条件，统一乙方去做，从而砍低了价格，那么亏的可能更多。</p><h2 id="合规是很难定义的"><a href="#合规是很难定义的" class="headerlink" title="合规是很难定义的"></a>合规是很难定义的</h2><p>站在甲方来说，甲方投钱找了乙方给自己的业务做了一个定制化的业务的项目，乙方抽象成了一个产品再卖给其他客户，如何定义这个版权的问题？如果一开始甲方就完全同意了这个事，那么意味着乙方大概率亏的很惨，如果不同意，那么基本上法律上就走不通。</p><h2 id="项目和产品的运做模式不一样"><a href="#项目和产品的运做模式不一样" class="headerlink" title="项目和产品的运做模式不一样"></a>项目和产品的运做模式不一样</h2><p>项目在企业运作上，本质上是人力资源的管理，站在更高层次来看，一家服务型的外包公司的来源本质上是“人”，也就是只要“人”卖出去，在项目上，那么就有利润，至于这个人实际上做的是什么，其实不重要，因为赢利点不是事，而是人，只需要操控好人员资源协调即可，这也是很多外包公司在项目结束后就直接裁员或者只发底薪的原因之一。所以项目的经营本质上是围绕人建立一个良好的流动机制，让人可以刚好被使用满。</p><p>而产品的赢利点在物上，而非人，无论是谁，本质上对外透出都是物，这意味着需要围绕物建立一个循环机制，来辅助这个事可以持续运做，那么各种研发机制，解决方案机制就会持续被建立，且这个建立和人没有关系，哪怕人全走了，这个东西依旧可以持续卖。</p><p>在运做模式上，这两者有着本质的区别，做项目的企业也会关注新技术，本质上是为了人更好卖，而做产品的企业同样关注新技术，本质上是让物解决大范围的问题，让物变的更好卖。</p><p>所以无论从哪个角度来看，想从项目沉淀产品，都是一件非常不可行的事，那么是不是说产品就没有办法做出来呢？其实并不是。</p><p>做产品一定是先投资再实施见收益，而项目一定是先实施直接见收益，所以产品的生存风险一定高，项目的风险相对低，但是低风险和高收益的事不可能同时存在，企业之所以想从项目沉淀产品，就是因为已经有项目了，有了资金来源了，再去做产品，感觉风险就低了，再以此来博得高收益，实际上却忽视了交付风险和管理风险。</p><h2 id="受众不同"><a href="#受众不同" class="headerlink" title="受众不同"></a>受众不同</h2><p>产品和项目的受众其实是不一样的，站在乙方的角度，如果做项目，可以做到来者不拒，因为没有什么人是招聘不来的，如果不行，那就加工资，而产品不一样，做不了的就是做不了，无论如何改变也做不了，所以产品需要有取舍，恰巧难点就在于取舍，如何控制好权衡好这个取舍点在哪，就已经成功了一半，这决定了这个物应该怎么卖，卖给谁。</p><h2 id="要有敬畏心"><a href="#要有敬畏心" class="headerlink" title="要有敬畏心"></a>要有敬畏心</h2><p>做产品的仪式感会远远大于项目，对于项目来说，做砸了再来一个就行了，至少有个首付款，而对产品来说，砸了，就啥也没了，所以产品不是一堆代码的堆砌，而是一套销售和运营模式的改变，想从人力资源运做为主的项目中提取出一个产品实现低风险高回报的事，是非常想当然的。</p>]]></content>
    
    <summary type="html">
    
      从项目沉淀产品
    
    </summary>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/categories/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/tags/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>使用openai sdk出现和httpx不兼容的问题</title>
    <link href="http://www.baifachuan.com/posts/f976e256.html"/>
    <id>http://www.baifachuan.com/posts/f976e256.html</id>
    <published>2025-01-16T02:35:51.000Z</published>
    <updated>2025-01-16T02:36:50.536Z</updated>
    
    <content type="html"><![CDATA[<p>这几天我使用python的openai sdk的时候，碰到一个问题：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">self.client &#x3D; OpenAI(base_url&#x3D;self.endpoint, api_key&#x3D;api_key)</span><br><span class="line">  File &quot;&#x2F;app&#x2F;.venv&#x2F;lib&#x2F;python3.10&#x2F;site-packages&#x2F;openai&#x2F;_client.py&quot;, line 123, in __init__</span><br><span class="line">    super().__init__(</span><br><span class="line">  File &quot;&#x2F;app&#x2F;.venv&#x2F;lib&#x2F;python3.10&#x2F;site-packages&#x2F;openai&#x2F;_base_client.py&quot;, line 857, in __init__</span><br><span class="line">    self._client &#x3D; http_client or SyncHttpxClientWrapper(</span><br><span class="line">  File &quot;&#x2F;app&#x2F;.venv&#x2F;lib&#x2F;python3.10&#x2F;site-packages&#x2F;openai&#x2F;_base_client.py&quot;, line 755, in __init__</span><br><span class="line">    super().__init__(**kwargs)</span><br><span class="line">TypeError: Client.__init__() got an unexpected keyword argument &#39;proxies&#39;</span><br></pre></td></tr></table></figure><p>从描述的问题来看，是sdk接口不兼容，然后跟了一把后发现是因为openai的sdk使用了httpx，openai传递了proxies参数来构造httpx的client，但是httpx里面却没有定义这个参数。</p><p>又仔细去看了下版本发布，原因是因为在httpx的0.28的版本开始，httpx移除了proxies参数来构造httpx的client，但是由于openai的版本管理是采用兼容高版本自动升级的方式，所以同样的openai的版本安装出来的httpx版本会不一样。</p><p>这就导致出现了不兼容的问题，既然知道了原因，解决方式就简单多了，降级httpx到0.28以下的版本就行了。</p>]]></content>
    
    <summary type="html">
    
      Client.__init__() got an unexpected keyword argument  proxies
    
    </summary>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>mlflow将模型数据保存至外部存储</title>
    <link href="http://www.baifachuan.com/posts/83bf6fbe.html"/>
    <id>http://www.baifachuan.com/posts/83bf6fbe.html</id>
    <published>2024-10-10T04:20:04.000Z</published>
    <updated>2024-10-10T04:26:15.714Z</updated>
    
    <content type="html"><![CDATA[<p>安装：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mlflow</span><br><span class="line">boto3</span><br></pre></td></tr></table></figure><p>设置环境变量：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">AWS_ACCESS_KEY_ID&#x3D;admin</span><br><span class="line">AWS_SECRET_ACCESS_KEY&#x3D;admin</span><br><span class="line">MLFLOW_S3_ENDPOINT_URL&#x3D;http:&#x2F;&#x2F;192.168.5.108:9000</span><br><span class="line">MLFLOW_TRACKING_URI&#x3D;http:&#x2F;&#x2F;192.168.5.108:15000&#x2F;</span><br></pre></td></tr></table></figure><p>例子：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">import mlflow.sklearn</span><br><span class="line">from sklearn.datasets import load_iris</span><br><span class="line">from sklearn.model_selection import train_test_split</span><br><span class="line">from sklearn.linear_model import LogisticRegression</span><br><span class="line"></span><br><span class="line"># 加载数据集</span><br><span class="line">iris &#x3D; load_iris()</span><br><span class="line">X, y &#x3D; iris.data, iris.target</span><br><span class="line"></span><br><span class="line"># 划分训练集和测试集</span><br><span class="line">X_train, X_test, y_train, y_test &#x3D; train_test_split(X, y, test_size&#x3D;0.2, random_state&#x3D;42)</span><br><span class="line"></span><br><span class="line"># 训练模型</span><br><span class="line">lr &#x3D; LogisticRegression(max_iter&#x3D;1000)</span><br><span class="line">lr.fit(X_train, y_train)</span><br><span class="line"></span><br><span class="line"># 设置 MLflow 运行</span><br><span class="line">with mlflow.start_run():</span><br><span class="line">    # 记录模型参数和性能指标（这里省略了具体指标的计算）</span><br><span class="line">    mlflow.log_param(&quot;solver&quot;, lr.solver)</span><br><span class="line">    mlflow.log_param(&quot;max_iter&quot;, lr.max_iter)</span><br><span class="line"></span><br><span class="line">    # 保存模型到 MinIO</span><br><span class="line">    mlflow.sklearn.log_model(sk_model&#x3D;lr, artifact_path&#x3D;&quot;iris_model&quot;, registered_model_name&#x3D;&quot;iris-logistic-regression&quot;)</span><br></pre></td></tr></table></figure><p>便可以在minio和mlflow里面看到对应的数据。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">import mlflow.sklearn</span><br><span class="line">from sklearn.datasets import load_iris</span><br><span class="line">from sklearn.model_selection import train_test_split</span><br><span class="line"></span><br><span class="line">iris &#x3D; load_iris()</span><br><span class="line">X, y &#x3D; iris.data, iris.target</span><br><span class="line"></span><br><span class="line">X_train, X_test, y_train, y_test &#x3D; train_test_split(X, y, test_size&#x3D;0.2, random_state&#x3D;42)</span><br><span class="line"></span><br><span class="line">loaded_model &#x3D; mlflow.pyfunc.load_model(&quot;runs:&#x2F;&lt;run-id&gt;&#x2F;iris_model&quot;)</span><br><span class="line"></span><br><span class="line">predictions &#x3D; loaded_model.predict(X_test)</span><br><span class="line">print(predictions)</span><br></pre></td></tr></table></figure><p>在mlflow里面找到对应的run id，进行加载即可使用对应的模型。</p>]]></content>
    
    <summary type="html">
    
      mlflow将模型保存到minio
    
    </summary>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>Elasticsearch python dsl关于对双下划线的处理</title>
    <link href="http://www.baifachuan.com/posts/72781e6a.html"/>
    <id>http://www.baifachuan.com/posts/72781e6a.html</id>
    <published>2024-09-09T12:59:47.000Z</published>
    <updated>2024-09-09T13:05:57.477Z</updated>
    
    <content type="html"><![CDATA[<p>在es中，如果字段名中包含双下划线，es是被允许的，但是我最近在使用py的es dsl库的时候，发现一个问题，就是字段名称如果带有双下划线。例如 <code>a__b__c</code> 就查询不出东西，经过debug发现在es的dsl内部经过转换后，最终生成的es的查询语法中，<code>a__b__c</code>已经变成了<code>a.b.c</code>，这意味着字段名称和es的已经不匹配了，自然就查询不出东西。</p><p>经过分析，以及相关的搜索：</p><p><a href="https://stackoverflow.com/questions/51050043/multiple-underscores-in-elasticsearch-field-name-with-python-elasticsearch-dsl" target="_blank" rel="noopener">https://stackoverflow.com/questions/51050043/multiple-underscores-in-elasticsearch-field-name-with-python-elasticsearch-dsl</a></p><p>可以得知，es的py dsl在设计的时候，借鉴的是Django ORM 设计思路，而在Django ORM 的设计中，__ 也就是双下划线， 代表着嵌套查询，因为是嵌套查询了，那么<code>a__b__c</code> 自然也就会被翻译成 <code>a.b.c</code> 了。</p><p><a href="https://github.com/elastic/elasticsearch-dsl-py/issues/28" target="_blank" rel="noopener">https://github.com/elastic/elasticsearch-dsl-py/issues/28</a></p><p>在这个issue中，社区进行了大量讨论，不过都对要做转换这个结论是认可的。</p><p>具体到代码的位置：</p><p><a href="https://github.com/elastic/elasticsearch-dsl-py/blob/605d7570e2a1501f319fdc74ad8d8270a5e61ea2/elasticsearch_dsl/utils.py#L222-L223" target="_blank" rel="noopener">https://github.com/elastic/elasticsearch-dsl-py/blob/605d7570e2a1501f319fdc74ad8d8270a5e61ea2/elasticsearch_dsl/utils.py#L222-L223</a></p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">def __init__(self, _expand__to_dot&#x3D;EXPAND__TO_DOT, **params):</span><br><span class="line">        self._params &#x3D; &#123;&#125;</span><br><span class="line">        for pname, pvalue in iteritems(params):</span><br><span class="line">            if &#39;__&#39; in pname and _expand__to_dot:</span><br><span class="line">                pname &#x3D; pname.replace(&#39;__&#39;, &#39;.&#39;)</span><br><span class="line">            self._setattr(pname, pvalue)</span><br></pre></td></tr></table></figure><p>可以看到这里做了转换。</p><p>不过作为es的dsl库，约束不像es向下对齐，而是向上对齐，也是少见了。</p>]]></content>
    
    <summary type="html">
    
      elasticsearch python dsl 字段包含双下划线
    
    </summary>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/categories/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/tags/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>使用shell脚本实现命令失败后重试</title>
    <link href="http://www.baifachuan.com/posts/f397c129.html"/>
    <id>http://www.baifachuan.com/posts/f397c129.html</id>
    <published>2024-08-27T01:01:28.000Z</published>
    <updated>2024-08-27T01:02:23.575Z</updated>
    
    <content type="html"><![CDATA[<p>典型场景：程序的实现需要调用第三方的API，但是我们并不能保证第三方API一直好用，也不能保证网络一直畅通，所以在调用第三方API时需要加上错误重试。</p><p>通用场景：程序的运行不符合预期，我们知道再次调用大概率可以使之符合预期，这时就需要重试。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">#!&#x2F;bin&#x2F;bash</span><br><span class="line"></span><br><span class="line"># 最大重试次数</span><br><span class="line">MAX_RETRY&#x3D;5</span><br><span class="line"></span><br><span class="line"># 当前重试次数</span><br><span class="line">retry_count&#x3D;0</span><br><span class="line"></span><br><span class="line"># 循环执行命令，如果失败则重试</span><br><span class="line">while true; do</span><br><span class="line">    # 执行命令，可以将具体的命令替换为你需要执行的命令</span><br><span class="line">    command_result&#x3D;$(some_command)</span><br><span class="line"></span><br><span class="line">    # 检查命令的返回值，如果成功则退出循环</span><br><span class="line">    if [ $? -eq 0 ]; then</span><br><span class="line">        echo &quot;Command succeeded!&quot;</span><br><span class="line">        break</span><br><span class="line">    fi</span><br><span class="line"></span><br><span class="line">    # 命令执行失败，检查是否已达到最大重试次数</span><br><span class="line">    if [ $retry_count -ge $MAX_RETRY ]; then</span><br><span class="line">        echo &quot;Command failed after $MAX_RETRY attempts.&quot;</span><br><span class="line">        exit 1</span><br><span class="line">    fi</span><br><span class="line"></span><br><span class="line">    # 命令执行失败，增加重试次数，等待一段时间后再次执行命令</span><br><span class="line">    echo &quot;Command failed, retrying in 10 seconds...&quot;</span><br><span class="line">    retry_count&#x3D;$((retry_count+1))</span><br><span class="line">    sleep 10</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p>在上面的脚本中，MAX_RETRY定义了最大重试次数，retry_count用于记录当前重试次数，while循环用于不断执行命令，if语句用于检查命令的返回值，如果成功则退出循环，否则增加重试次数，并等待一段时间后再次执行命令，直到命令成功或达到最大重试次数。如果命令在最大重试次数内执行失败，则脚本退出并返回错误码1。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;典型场景：程序的实现需要调用第三方的API，但是我们并不能保证第三方API一直好用，也不能保证网络一直畅通，所以在调用第三方API时需要加上错误重试。&lt;/p&gt;
&lt;p&gt;通用场景：程序的运行不符合预期，我们知道再次调用大概率可以使之符合预期，这时就需要重试。&lt;/p&gt;
&lt;figu
      
    
    </summary>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/categories/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/tags/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>为什么那么多企业都要考虑All in AI</title>
    <link href="http://www.baifachuan.com/posts/b75c04f.html"/>
    <id>http://www.baifachuan.com/posts/b75c04f.html</id>
    <published>2024-08-19T04:08:44.000Z</published>
    <updated>2024-08-19T04:11:36.303Z</updated>
    
    <content type="html"><![CDATA[<ul><li>23年4月张一鸣在会上表示，字节无法错过AGI（通用人工智能），它可以解决字节跳动的第二曲线增长困境。</li><li>24年2月，魅族宣布战略调整，将停止传统智能手机新项目的开发，全力投入“明日设备”（AI For New Generations）。</li><li>24年2月，OPPO称，AI手机将成为继功能机、智能手机之后的第三阶段。</li><li>23年华为正式启动全面智能化新战略。</li><li>腾讯要让混元大模型成为腾讯业务的“倍增器”。</li><li>阿里确立以“AI驱动”为战略重心。</li><li>周鸿祎在第21届中国企业领袖年会向在座的企业家强调要建立「AI信仰」，否则将被使用AI的同行淘汰。</li><li>百度和360也都喊出了“All in AI”的口号。</li></ul><p>几乎所有的企业，都开始考虑All in ai这一件事，这不单纯是炒作或者跟风，也不是在竞争某个新技术，比如出现过的区块链，元宇宙，VR/AR，都是非常优秀的技术，但是并没有企业喊出All in 区块链，唯一一家喊出all in 元宇宙，还改名的企业，现在已经全面拥抱大模型了。</p><p>这些企业之所以喊出All in AI的口号，虽然在动作上少有差异，但是如同张一鸣所说，一语点中了本质，因为 All in AI可以解决企业第二曲线增长困境，就是这么简单。</p><p><img src="/posts/b75c04f/image.png" alt></p><p>如果组织和企业能在第一曲线到达巅峰之前，找到带领企业二次腾飞的第二曲线，并且第二曲线必须在第一曲线达到顶点前开始增长，弥补第二曲线投入初期的资源消耗，那么企业永续增长愿景就能实现。</p><p>实现永续增长无非两种方式：</p><ul><li>一是产品实际需求相对稳定，但公司具有持续的提价能力，比如奢侈品公司。</li><li>二是不断通过二次增长实现迭代增长，不断推出新产品和服务实现持续增长，比如苹果公司不断进行新产品迭代更新。</li></ul><p>要形成持续的提价能力对许多行业和公司来说是比较困难的，大部分行业和企业要实现永续增长愿景一般都要依赖第二增长曲线。</p><p>我们再看看历史上为第二曲线做了选择的那些企业：</p><ul><li>阿里是第二曲线做的最好的公司之一，最早是B2B，然后是淘宝，蚂蚁金服，阿里云到钉钉等，正是不断地找到这些第二增长曲线，阿里才能成长为今天市值万亿的巨无霸企业。</li><li>苹果的第一曲线是iMac，之后做的iPod、iPhone、iPad都是它的第二增长曲线。</li><li>亚马逊也是如此，第一曲线是网上书店，之后做电商，做云计算，做物流，始终保持增长，才成为全球市值最高的企业。</li></ul><p>可见，企业要保持基业长青，永续经营，就要不断突破增长拐点，跨越非连续性，找到第二增长曲线。</p><p>再看上面的第二曲线，虽然不同企业的选择不同，比如阿里做淘宝，苹果做iPhone，看起来业务不太一样，但是背后实质性都是因为生产力的变化，移动互联网的出现，移动互联网才是第二增长的源动力。</p><p>同样在今天，几乎所有企业都认为GenAI是源动力，可以成为带领企业走出第二增长曲线的动力，于是围绕这个动力，不同企业扩展了自己的第二曲线业务，比如360做安全Agent，字节用抖音做防守，剪映博AI。</p>]]></content>
    
    <summary type="html">
    
      为什么那么多企业都要考虑All in AI
    
    </summary>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>mlserver1.4.0开始对dataframe的序列化逻辑调整</title>
    <link href="http://www.baifachuan.com/posts/7804e1a3.html"/>
    <id>http://www.baifachuan.com/posts/7804e1a3.html</id>
    <published>2024-08-05T14:10:22.000Z</published>
    <updated>2024-08-05T14:13:00.050Z</updated>
    
    <content type="html"><![CDATA[<p>最近对于本地私有化模型，我是通过seldon这一套进行封装，也就是最终是通过mlserver启动模型服务，然后外部调用mlserver的接口去做模型的推理。</p><p>其中调用mlserver的推理服务的时候，涉及到传输参数的序列化，序列化的大概样例如下：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">tools: list &#x3D; None</span><br><span class="line">df&#x3D;pd.Series(</span><br><span class="line">            [</span><br><span class="line">                tools</span><br><span class="line">            ],</span><br><span class="line">            index&#x3D;[     </span><br><span class="line">                &quot;tools&quot;</span><br><span class="line">            ],</span><br><span class="line">        )</span><br><span class="line">PandasCodec.encode_request(df, use_bytes&#x3D;False)</span><br></pre></td></tr></table></figure><p>其中PandasCodec是mlserver提供的序列化类，此代码在mlserver 1.4.0以前的版本下都没有问题，在1.4.0开始，包括1.4.0会出现如下问题：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">TypeError: ufunc &#39;isnan&#39; not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule &#39;&#39;safe&#39;&#39;</span><br></pre></td></tr></table></figure><p>跟踪代码会发现：</p><p><a href="https://github.com/SeldonIO/MLServer/blob/release/1.4.0/mlserver/codecs/numpy.py#L108" target="_blank" rel="noopener">https://github.com/SeldonIO/MLServer/blob/release/1.4.0/mlserver/codecs/numpy.py#L108</a></p><p>对于dataframe中的每列，会做是否非空的检查：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">def _to_response_output(series: pd.Series, use_bytes: bool &#x3D; True) -&gt; ResponseOutput:</span><br><span class="line">    datatype &#x3D; to_datatype(series.dtype)</span><br><span class="line">    data &#x3D; series.tolist()</span><br><span class="line"></span><br><span class="line">    # Replace NaN with null</span><br><span class="line">    has_nan &#x3D; series.isnull().any()</span><br><span class="line">    if has_nan:</span><br><span class="line">        data &#x3D; list(map(convert_nan, data))</span><br></pre></td></tr></table></figure><p>convert_nan的逻辑为：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">def convert_nan(val):</span><br><span class="line">    if np.isnan(val):</span><br><span class="line">        return None</span><br><span class="line"></span><br><span class="line">    return val</span><br></pre></td></tr></table></figure><p>由于np的isnan是：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">The &#96;isnan&#96; method doesn&#39;t work on Numpy arrays with non-numeric</span><br></pre></td></tr></table></figure><p>这就意味着，对于前面的tools来说，是non-numeric类型，通过调试也可以看到，tools的类型为BYTES，这就导致np.isnan(val)直接抛出异常。</p><p>由于mlserver.1.4.0之前并无此校验，因此之前的代码不会有问题。</p><p><a href="https://github.com/SeldonIO/MLServer/issues/1873" target="_blank" rel="noopener">https://github.com/SeldonIO/MLServer/issues/1873</a> 首先给他们挂了个issue描述了一下问题。</p><p>通过追踪mlserver的commit记录，可以看到：</p><p><a href="https://github.com/SeldonIO/MLServer/commit/be8bab5938b478d68f3ac0da895a5e8af73c2586" target="_blank" rel="noopener">https://github.com/SeldonIO/MLServer/commit/be8bab5938b478d68f3ac0da895a5e8af73c2586</a></p><p>在这个commit后，新增了这段逻辑，按照seldon自己的描述：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">The NaN (Not a Number) value is used in Numpy and other scientific libraries to</span><br><span class="line">describe an invalid or missing value (e.g. a division by zero).</span><br><span class="line">In some scenarios, it may be desirable to let your models receive and &#x2F; or</span><br><span class="line">output NaN values (e.g. these can be useful sometimes with GBTs, like XGBoost</span><br><span class="line">models).</span><br><span class="line">This is why MLServer supports encoding NaN values on your request &#x2F; response</span><br><span class="line">payloads under some conditions.</span><br><span class="line"></span><br><span class="line">In order to send &#x2F; receive NaN values, you must ensure that:</span><br><span class="line"></span><br><span class="line">- You are using the &#96;REST&#96; interface.</span><br><span class="line">- The input &#x2F; output entry containing NaN values uses either the &#96;FP16&#96;, &#96;FP32&#96;</span><br><span class="line">  or &#96;FP64&#96; datatypes.</span><br><span class="line">- You are either using the [Pandas codec](#pandas-dataframe) or the [Numpy</span><br><span class="line">  codec](#numpy-array).</span><br><span class="line"></span><br><span class="line">Assuming those conditions are satisfied, any &#96;null&#96; value within your tensor</span><br><span class="line">payload will be converted to NaN.</span><br><span class="line"></span><br><span class="line">For example, if you take the following Numpy array:</span><br></pre></td></tr></table></figure><p>归纳下来就是因为如果对于非基本类型接收None，那么在某些模型下，比如XGBoost就会出现模型输出NAN，为了解决这个问题，就不再支持None。</p><p>搞清楚了原因，那么解决方案就明确了，按照官方的描述，既然他们是这样认定，基本上后续的版本中也不会做额外的处理。</p><p>于是我自己重写了一个codecs，重写了PandasCodec的方法：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">from typing import List</span><br><span class="line"></span><br><span class="line">import numpy as np</span><br><span class="line">import pandas as pd</span><br><span class="line">from mlserver.codecs import PandasCodec</span><br><span class="line">from mlserver.codecs.numpy import to_datatype</span><br><span class="line">from mlserver.codecs.pandas import _process_bytes</span><br><span class="line">from mlserver.codecs.utils import inject_batch_dimension</span><br><span class="line">from mlserver.types import InferenceRequest, Parameters, RequestInput, ResponseOutput, Datatype</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def convert_nan(val):</span><br><span class="line">    try:</span><br><span class="line">        if np.isnan(val):</span><br><span class="line">            return None</span><br><span class="line">    except Exception:</span><br><span class="line">        return val</span><br><span class="line">    return val</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def _to_response_output(series: pd.Series, use_bytes: bool &#x3D; True) -&gt; ResponseOutput:</span><br><span class="line">    datatype &#x3D; to_datatype(series.dtype)</span><br><span class="line">    data &#x3D; series.tolist()</span><br><span class="line"></span><br><span class="line">    # Replace NaN with null</span><br><span class="line">    has_nan &#x3D; series.isnull().any()</span><br><span class="line">    if has_nan:</span><br><span class="line">        data &#x3D; list(map(convert_nan, data))</span><br><span class="line"></span><br><span class="line">    content_type &#x3D; None</span><br><span class="line">    if datatype &#x3D;&#x3D; Datatype.BYTES:</span><br><span class="line">        data, content_type &#x3D; _process_bytes(data, use_bytes)</span><br><span class="line"></span><br><span class="line">    shape &#x3D; inject_batch_dimension(list(series.shape))</span><br><span class="line">    parameters &#x3D; None</span><br><span class="line">    if content_type:</span><br><span class="line">        parameters &#x3D; Parameters(content_type&#x3D;content_type)</span><br><span class="line"></span><br><span class="line">    return ResponseOutput(</span><br><span class="line">        name&#x3D;series.name,</span><br><span class="line">        shape&#x3D;shape,</span><br><span class="line">        data&#x3D;data,</span><br><span class="line">        datatype&#x3D;datatype,</span><br><span class="line">        parameters&#x3D;parameters,</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class SeldonPandasCodec(PandasCodec):</span><br><span class="line">    @classmethod</span><br><span class="line">    def encode_outputs(</span><br><span class="line">            cls, payload: pd.DataFrame, use_bytes: bool &#x3D; True</span><br><span class="line">    ) -&gt; List[ResponseOutput]:</span><br><span class="line">        return [</span><br><span class="line">            _to_response_output(payload[col], use_bytes&#x3D;use_bytes) for col in payload</span><br><span class="line">        ]</span><br><span class="line"></span><br><span class="line">    @classmethod</span><br><span class="line">    def encode_request(</span><br><span class="line">            cls, payload: pd.DataFrame, use_bytes: bool &#x3D; True, **kwargs</span><br><span class="line">    ) -&gt; InferenceRequest:</span><br><span class="line">        outputs &#x3D; cls.encode_outputs(payload, use_bytes&#x3D;use_bytes)</span><br><span class="line"></span><br><span class="line">        return InferenceRequest(</span><br><span class="line">            parameters&#x3D;Parameters(content_type&#x3D;cls.ContentType),</span><br><span class="line">            inputs&#x3D;[</span><br><span class="line">                RequestInput(</span><br><span class="line">                    name&#x3D;output.name,</span><br><span class="line">                    datatype&#x3D;output.datatype,</span><br><span class="line">                    shape&#x3D;output.shape,</span><br><span class="line">                    data&#x3D;output.data,</span><br><span class="line">                    parameters&#x3D;output.parameters,</span><br><span class="line">                )</span><br><span class="line">                for output in outputs</span><br><span class="line">            ],</span><br><span class="line">        )</span><br></pre></td></tr></table></figure><p>针对isnan的判断做了额外的处理，使用的时候由<code>PandasCodec.encode_request</code>换成<code>SeldonPandasCodec.encode_request</code>即可。</p>]]></content>
    
    <summary type="html">
    
      TypeError ufunc isnan not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule safe
    
    </summary>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>Docker container中的nvidia-smi无法看到其他地方启动的程序</title>
    <link href="http://www.baifachuan.com/posts/4f6920d7.html"/>
    <id>http://www.baifachuan.com/posts/4f6920d7.html</id>
    <published>2024-06-16T14:12:58.000Z</published>
    <updated>2024-06-16T14:13:45.960Z</updated>
    
    <content type="html"><![CDATA[<p>当在docker container里面运行GPU程序的时候，通过 <code>nvidia-smi</code> 只能看到当前 container里面启动的程序，而无法看到所有运行在GPU上的程序，这个原因是因为<code>nvidia-smi</code> 这个命令通过扫描持有驱动的PID来查找对应的程序。</p><p>因此在docker container里面默认看不到别的地方启动的PID，而实际上实际上看不到才是正统的，因为docker container存在的价值就是进行多租户隔离，自己管自己，如果能看到别人，还能干掉别人的，实际上已经破坏多租户的概念了，这个隔离意义就不大，单纯就是个程序环境隔离了。</p><p>如果非要看到，也可以通过<code>-pid=host</code> 把物理机器的透过去，就能看到，不过这样来说，基本上就破坏了隔离性。</p><p><a href="https://stackoverflow.com/questions/63654885/is-it-correct-that-nvidia-smi-on-docker-does-not-show-processes" target="_blank" rel="noopener">https://stackoverflow.com/questions/63654885/is-it-correct-that-nvidia-smi-on-docker-does-not-show-processes</a></p><p><a href="https://github.com/NVIDIA/nvidia-docker/issues/179" target="_blank" rel="noopener">https://github.com/NVIDIA/nvidia-docker/issues/179</a></p>]]></content>
    
    <summary type="html">
    
      Docker container中的nvidia-smi无法看到其他地方启动的程序
    
    </summary>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="机器学习" scheme="http://www.baifachuan.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>如何调试卡住的python程序</title>
    <link href="http://www.baifachuan.com/posts/5153e8c3.html"/>
    <id>http://www.baifachuan.com/posts/5153e8c3.html</id>
    <published>2024-06-13T07:33:30.000Z</published>
    <updated>2024-06-20T03:30:12.047Z</updated>
    
    <content type="html"><![CDATA[<p>调试Python程序卡住，寻找原因</p><p>最近大模型平台有个知识库模块，在对文件进行加工处理，进行embedding，逻辑上不复杂，基本就是对文本切片，再embedding，然后落库milvus。</p><p>不过出现一个现象就是当文本过大的时候，knowledge based的处理过程会卡住，且knowledge based停止服务，请求处于等待状态，由于knowledge based的整个服务是部署在docker中的，于是查看卡在哪儿，只能在docker中进行了。</p><p>首先通过：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">docker exec  --privileged -it ID &#x2F;bin&#x2F;bash</span><br></pre></td></tr></table></figure><p>进入容器，查找到PID：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">root@815eb533c377:&#x2F;app# ps -eaf</span><br><span class="line">UID          PID    PPID  C STIME TTY          TIME CMD</span><br><span class="line">root           1       0  0 Jun12 ?        00:00:00 &#x2F;bin&#x2F;sh -c cd &#x2F;app&#x2F;src &amp;&amp; poetry run alembic upgrade head &amp;&amp; poetry run python main.py</span><br><span class="line">root          57       1  0 Jun12 ?        00:05:24 &#x2F;root&#x2F;.cache&#x2F;pypoetry&#x2F;virtualenvs&#x2F;knowledge-base-9TtSrW0h-py3.10&#x2F;bin&#x2F;python main.py</span><br></pre></td></tr></table></figure><p>可以看到PID分别为1和57，这里需要调试的是PID 57。</p><p>通过：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">gdb python 57</span><br></pre></td></tr></table></figure><p>进行调试，碰到一个问题：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Attaching to process 57 </span><br><span class="line">ptrace: Operation not permitted</span><br></pre></td></tr></table></figure><p>看了下<code>docker container</code>里面的<code>/proc/sys/kernel/yama/ptrace_scope</code> 的值是<code>1</code>，1 意味着无法attach，对于docker来说，这个值是跟随宿主机走的，也就是需要修改宿主机的值，也就是执行：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">echo 0 &gt; &#x2F;proc&#x2F;sys&#x2F;kernel&#x2F;yama&#x2F;ptrace_scope</span><br></pre></td></tr></table></figure><p>对于默认是zsh的会碰到错误：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">zsh: permission denied: &#x2F;proc&#x2F;sys&#x2F;kernel&#x2F;yama&#x2F;ptrace_scope</span><br></pre></td></tr></table></figure><p>这时候可以通过切换bash解决：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">sudo bash -c &quot;echo 0 &gt; &#x2F;proc&#x2F;sys&#x2F;kernel&#x2F;yama&#x2F;ptrace_scope&quot;</span><br></pre></td></tr></table></figure><p>再通过：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">gdb python 57</span><br></pre></td></tr></table></figure><p>进行调试，通过<code>py-list</code>查看当前执行的位置：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">(gdb) py-list</span><br><span class="line"> 700            self._checkReadable()</span><br><span class="line"> 701            if self._timeout_occurred:</span><br><span class="line"> 702                raise OSError(&quot;cannot read from timed out object&quot;)</span><br><span class="line"> 703            while True:</span><br><span class="line"> 704                try:</span><br><span class="line">&gt;705                    return self._sock.recv_into(b)</span><br><span class="line"> 706                except timeout:</span><br><span class="line"> 707                    self._timeout_occurred &#x3D; True</span><br><span class="line"> 708                    raise</span><br><span class="line"> 709                except error as e:</span><br><span class="line"> 710                    if e.errno in _blocking_errnos:</span><br></pre></td></tr></table></figure><p>gdb -p pid<br>pid是指进程号，可以通过 ps -ef 查看，请替换为实际的进程号</p><p>步骤5: 打印信息<br>py-bt<br>解释: 查看当前进程的堆栈信息，这是很重要的命令，一般可以看到进程运行到哪里发生死锁或者卡住，进而可以回到python源码确定最终问题在哪里</p><p>py-list<br>解释: 显示python进程当前代码运行到哪里</p><p>py-locals<br>解释: 显示当前进程中的局部变量</p><p>py-print<br>解释: 打印python变量的值<br>用法: 例如<br>py-print self</p><p>info threads<br>thread <thread-num><br>解释: 先查看线程信息，然后切换线程想要查看的线程</thread-num></p><p>thread apply all py-list:<br>解释:查看所有进程执行位置</p><p> 通过<code>py-bt</code>查看当前堆栈，除了使用gdb之外，还可以使用其他调试工具，比如<code>pystack</code>：</p> <figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">pip install pystack-debugger</span><br></pre></td></tr></table></figure><p> 执行<code>pystack 57</code>能看到：</p> <figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"> root@815eb533c377:&#x2F;app# pystack 57</span><br><span class="line">Dumping Threads....</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  File &quot;&#x2F;usr&#x2F;local&#x2F;lib&#x2F;python3.10&#x2F;threading.py&quot;, line 973, in _bootstrap</span><br><span class="line">    self._bootstrap_inner()</span><br><span class="line">  File &quot;&#x2F;usr&#x2F;local&#x2F;lib&#x2F;python3.10&#x2F;threading.py&quot;, line 1016, in _bootstrap_inner</span><br><span class="line">    self.run()</span><br><span class="line">  File &quot;&#x2F;root&#x2F;.cache&#x2F;pypoetry&#x2F;virtualenvs&#x2F;knowledge-base-9TtSrW0h-py3.10&#x2F;lib&#x2F;python3.10&#x2F;site-packages&#x2F;anyio&#x2F;_backends&#x2F;_asyncio.py&quot;, line 797, in run</span><br><span class="line">    item &#x3D; self.queue.get()</span><br><span class="line">  File &quot;&#x2F;usr&#x2F;local&#x2F;lib&#x2F;python3.10&#x2F;queue.py&quot;, line 171, in get</span><br><span class="line">    self.not_empty.wait()</span><br><span class="line">  File &quot;&#x2F;usr&#x2F;local&#x2F;lib&#x2F;python3.10&#x2F;threading.py&quot;, line 320, in wait</span><br><span class="line">    waiter.acquire()</span><br><span class="line"></span><br><span class="line">---------------</span><br></pre></td></tr></table></figure><p>也可以使用<code>hypno</code>:</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">pip install hypno</span><br></pre></td></tr></table></figure><p>这个库的目标不太一样，主要是为了 inject 到正在运行的 Python 进程里跑一些代码，所以灵活性比单纯打 callstack 更高，比如可以打印一些变量之类。我们用如下的命令来实现获取 callstack 的功能：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">hypno &quot;import traceback; traceback.print_stack()&quot;</span><br></pre></td></tr></table></figure><p>还有<code>py-spy</code>：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">pip install py-spy</span><br></pre></td></tr></table></figure><p>获取 callstack 可以使用 dump 命令：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">py-spy dump --pid 54</span><br></pre></td></tr></table></figure><p>此外py-spy还支持top形式的实时 profiling，以及生成火焰图（操作稍麻烦）的功能，除了程序 hang 之外也能做性能优化的监控工具。不过易用性上来说比下面要介绍的austin还是稍微差了些。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">conda install -c conda-forge austin-tui</span><br></pre></td></tr></table></figure><p>这是一个集大成的 profiling 工具，不光可以看 callstack，还可以看对应的采样（近似理解为程序时间开销）百分占比信息，以及实时更新的 flamegraph 等，竟然还是跨平台的！官网上还有更多高级特性，感觉已经可以跟 JDK 里的 visualvm 来媲美了。</p><p>python 调试工具 pyrasite 可以附加到python进程中，在这个进程中打开一个python 命令行。然后再这个里面执行代码。</p><p>pyrasite-shell PID</p><p>（对于这种挂死问题通常是由于多进程和多线程混用导致的，多线程中如果有锁，在使用fork创建多进程的过程中fork出来的进程是单线程执行的，只会复制内存中的对象当前的信息，如果有一把锁被别的线程获取到，我们当前fork出的进程中的这个线程中，这把锁的状态只会是锁定状态，会导致子进程中再使用这个锁的时候会导致死锁。）</p><p>对于python进程中的挂死问题，我们需要对于进程中所有的线程查看它们卡死在了哪一个位置。</p><p>当我们进入这个进程中后，我们可以执行下面的代码查看进程的帧栈。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">import sys</span><br><span class="line">for threadid,stack in sys._current_frames().items():</span><br><span class="line">    print(threadid,stack)</span><br></pre></td></tr></table></figure><p>对于内存泄漏问题，我们可以使用objgraph来查看内存泄漏，对于python  代码造成的内存泄漏，我们可以很容易得看出来泄漏对象的引用关系。<br>但是对于C代码造成的泄漏，我们通常只能看出对象类型来，并不能看出泄漏对象的引用关系。所以还要一步一步排除。</p><p>py-spy 也可以看出进程是否挂死</p><p>使用方法 pip install py-spy</p><p>py-spy dump -p $pid</p><p>py-spy dump -l -p $pid</p>]]></content>
    
    <summary type="html">
    
      python卡住，python程序卡住
    
    </summary>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/categories/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="编程基础" scheme="http://www.baifachuan.com/tags/%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
</feed>
