躬行笔记 - 开发
type: channel
link: https://www.fuzhoupyy.work/index.php/category/kf/
items: AI时代的隐形杀手：为什么你的团队代码产出翻倍，但交付速度却在原地踏步？
items: 从零到一搭建AI智能体：hello-agents项目实战部署全记录
items: 开源协议有哪些你知道吗？
items: 这个开源监控神器，让我再也不用半夜被老板电话轰炸了！
items: 从零开始：给Typecho写个Redis缓存插件，让你的博客飞起来！

AI时代的隐形杀手：为什么你的团队代码产出翻倍，但交付速度却在原地踏步？
type: item
date: 2026-05-14T14:58:41+00:00
description: 最近在和几个朋友聊天的时候，发现一个特别有意思的现象。大家都在说AI编程工具有多牛，claude code,kiro,codex等这些工具确实让写代码变得飞快，我自己用下来感觉效率至少提升了一半以上。但是奇怪的事情来了，虽然代码写得快了，但项目交付的速度好像并没有跟着提升多少，有时候甚至感觉更慢了。刚开始我以为是个例，结果跟圈子里其他人一聊，发现这个问题还挺普遍的。后来仔细想想，问题可能出在我们一直忽略的地方——代码审查。生产力悖论：写得快了，为什么交付还是慢？我记得去年刚开始用AI编程工具的时候，那种感觉简直不要太爽。以前写一个功能模块可能要大半天，现在有了AI助手，一个上午就能搞定好几个。当时还挺得意，觉得自己的生产力简直爆表了。但是很快就发现问题了。以前我一天可能提交1-2个PR（Pull Request），现在一天能提交5-6个。看起来很厉害对吧？但是这些PR在那里排队等审查的时间越来越长，有时候一个PR要等好几天才有人看。更要命的是，当你手头有3-4个未审查的PR在那里挂着的时候，脑子里总是惦记着这些事情，没法专心做新的功能。这种感觉就像是你同时在做好几件事，结果哪件事都做不好。我们团队的情况可能还算好的，听说有些团队的PR积压更严重，一个PR从提交到合并要一个星期，这完全失去了敏捷开发的意义。审查工作的性质变了以前代码都是人写的，虽然偶尔有bug，但整体逻辑是连贯的，审查起来相对轻松。现在AI生成的代码就不一样了，表面上看起来没问题，但仔细一看总感觉哪里不对劲。我举个例子，前几天我用AI写了一个数据处理的函数，代码跑起来没问题，测试也通过了。但是我的同事在审查的时候发现，这个函数处理边界情况的方式特别复杂，用了很多防御性编程的技巧，但实际上我们的业务场景根本不需要这么复杂的处理。这就是AI代码的特点：功能上没毛病，但可能过度设计了。审查的时候你不仅要看代码对不对，还要判断这个代码有没有必要，会不会给后续维护带来负担。这比以前单纯检查bug要费脑子多了。还有一个问题是，AI生成的代码风格可能跟团队的编码规范不太一致。虽然大部分静态检查工具能发现明显的问题，但有些细节还是需要人工判断。这就导致审查的工作量实际上是增加的，不是减少的。积压的恶性循环代码审查的延迟不是简单的时间延迟，它会触发一系列连锁反应。当你的PR在队列里等待的时候，你可能已经开始下一个功能的开发了。等审查意见回来的时候，你可能已经忘记当初为什么要这么写了，需要重新熟悉代码。我们团队就遇到过这种情况。有一次我提交了一个重构的PR，因为审查队列比较长，等了一个星期才有人看。这期间我又开发了两个新功能，等审查意见回来的时候，发现新功能和重构的代码有冲突，结果花了比原来更多的时间来解决冲突。更糟糕的是，当你同时维护多个未合并的分支时，心理负担会成倍增加。每次切换分支都需要重新理解上下文，这种上下文切换的成本是非常高的。有研究表明，开发者同时处理多个任务时，光是上下文切换就会消耗20-50%的时间。解决方案不是招更多人很多团队遇到这个问题的第一反应就是招更多的高级开发者来做代码审查，或者要求大家加快审查速度。但这些方法都没有抓住问题的本质。代码审查是一个工作流程，不是一个简单的关卡。我们需要把它当作一个系统来优化，而不是单纯地增加处理能力。首先，不是所有的PR都需要同样深度的审查。比如说，修改文档、调整配置文件、简单的bug修复，这些低风险的改动完全可以走快速通道。我们可以根据改动的类型、涉及的文件、代码复杂度等因素来自动评估风险等级。我们团队现在用的方法是给PR打标签，绿色标签表示低风险可以快速审查，黄色表示中等风险需要仔细看看，红色表示高风险需要多人审查。这样审查者可以根据自己的时间安排来选择处理哪些PR。其次，审查队列的可见性很重要。如果一个人手头有15个PR等着审查，另一个人只有2个，这种不平衡需要及时发现和调整。我们现在用一个简单的看板来跟踪所有人的审查队列，这样就能及时发现瓶颈。AI工具在审查中的应用虽然AI写代码可能会增加审查的复杂性，但AI同样可以帮助我们做审查。现在有很多工具可以自动检查代码风格、安全漏洞、性能问题等机械性的内容，让人类审查者可以专注于架构设计和业务逻辑。我们团队最近在试用一些AI代码审查工具，效果还不错。这些工具可以自动标注出可能有问题的代码段，提供修改建议，甚至可以解释为什么某段代码可能有问题。当然，最终的判断还是要靠人，但这些工具确实能提高审查的效率。另外，保持PR的大小也很重要。一个包含几百行改动的PR和几个只有十几行改动的小PR，审查的难度是完全不同的。小的、专注的PR不仅更容易审查，也不容易在队列里腐烂。工具和流程的结合光有好的流程还不够，还需要合适的工具来支撑。现在市面上有一些专门的代码审查管理工具，可以把所有仓库…

从零到一搭建AI智能体：hello-agents项目实战部署全记录
type: item
date: 2026-04-29T15:28:30+00:00
description: 最近在GitHub上发现了一个非常不错的AI Agent项目——hello-agents，这个项目是由Datawhale团队开源的，专门用来帮助大家快速上手AI智能体的开发。作为一个天天和各种开源项目打交道的运维人员，我觉得这个项目特别适合想要入门AI Agent开发的同学。说实话，之前我对AI Agent这块了解得不多，主要还是专注在传统的运维工作上。但是最近公司开始讨论要不要引入一些AI工具来提升运维效率，我就开始关注这个领域了。正好碰到这个hello-agents项目，就想着自己动手试试看。项目地址：https://github.com/datawhalechina/hello-agents文档地址：https://hello-agents.datawhale.cc/#/README.md项目初体验首先说说这个项目给我的第一印象吧。整个项目的文档写得挺清楚的，不像有些开源项目，README写得跟天书一样。hello-agents的目录结构也比较清晰，主要包含了几个核心模块：agents/ - 智能体的核心实现examples/ - 各种示例代码docs/ - 详细的文档说明tests/ - 测试用例我记得第一次clone这个项目的时候，整个过程还是比较顺利的：git clone https://github.com/datawhalechina/hello-agents.git cd hello-agents不过说实话，刚开始看代码的时候还是有点懵的，毕竟AI Agent这个概念对我来说还比较新。但是项目里的examples目录真的帮了大忙，里面有很多现成的例子可以参考。环境搭建的那些坑按照项目文档的说明，环境搭建这块主要是Python环境的配置。我在公司的测试服务器上试了一下，系统是CentOS 7.9，Python版本是3.8.10。首先是安装依赖包：pip install -r requirements.txt这里遇到了第一个问题，有几个包的版本冲突了。特别是transformers和torch这两个包，版本要求比较严格。我花了不少时间在这上面，最后发现还是用虚拟环境比较好：python -m venv hello-agents-env source hello-agents-env/bin/activate pip install -r requirements.txt虚拟环境这个东西真的是好用，避免了很多版本冲突的问题。不过在生产环境中，我一般还是倾向于用Docker容器来部署，这样更加稳定一些。安装过程中还遇到了一个小插曲，就是网络问题。你知道的，有些Python包的下载速度特别慢，我就换了一下pip源：pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/这样速度就快多了。第一个智能体的诞生环境搭建好之后，我就迫不及待地想要跑一个简单的例子。项目里有一个基础的聊天机器人示例，我觉得这个比较适合入门。看了一下代码结构，主要是这样的：from hello_agents import Agent, LLMConfig # 配置大语言模型 llm_config = LLMConfig( model_name="gpt-3.5-turbo", api_key="your-api-key", base_url="https://api.openai.com/v1" ) # 创建智能体 agent = Agent( name="助手小王", description="一个友好的AI助手", llm_config=llm_config ) # 开始对话 response = agent.chat("你好，请介绍一下自己") print(response)不过这里有个问题，就是API Key的配置。项目文档里建议使用环境变量来存储敏感信息，这个做法我是赞同的。在生产环境中，绝对不能把API Key直接写在代码里。export OPENAI_API_KEY="your-actual-api-key" export OPENAI_BASE_URL="https://api.openai.com/v1"然后在代码中这样读取：import os llm_config = LLMConfig( model_name="gpt-3.5-turbo", api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL") )第一次运行的时候，我用的是OpenAI的API，但是你知道的，网络访问有时候不太稳定。后来我改用了国内的一些兼容OpenAI API的服务，效果还不…

开源协议有哪些你知道吗？
type: item
date: 2026-04-09T14:22:15+00:00
description: 前两天有个读者在后台问我，说他们公司用了某个开源组件做项目，结果收到律师函了，说违反了开源协议。我当时心里一紧，这种事其实挺常见的，很多开发者压根不知道自己用的那些库到底有什么限制。想起来我刚开始做运维那会儿，也是一脸懵。那时候领导让我搭建个内部代码仓库，我就直接把公司代码往上一推，后来才知道有些依赖包是不能随便用的。好险没出大问题。今天就聊聊开源协议这事儿，说实话，很多人干了几年开发运维，对这个还是一知半解。特别是那个让无数SaaS公司闻风丧胆的AGPL，后面我会专门讲到。开源协议到底是啥玩意说白了，开源协议就是一套规则，告诉你这个代码你能怎么用、能怎么改、能不能商用。就像你去别人家做客，主人得跟你说清楚哪些房间能进，哪些东西能碰。很多人有个误区，觉得开源就是免费的，想咋用咋用。这想法挺危险的。开源不等于免费，也不等于没有约束。代码开源了，作者还是拥有著作权的，只是给了你一些使用权而已。我记得有次跟同事争论，他说开源代码随便用，公司项目里直接复制粘贴都行。我当时就提醒他，你看看那个项目的 LICENSE 文件写的啥。结果一看，GPL 协议，这下尴尬了。为啥要有这东西这事儿得从上世纪 80 年代说起。那时候有个叫 Richard Stallman 的程序员，他在 MIT 人工智能实验室工作。有次打印机坏了，他想修改打印机的驱动程序，结果发现厂商不给他源代码。这事儿让他很不爽，他觉得软件应该自由，用户应该有权利修改自己用的软件。于是 1983 年，他发起了 GNU 项目，1985 年成立了自由软件基金会（FSF）。这个人挺有意思，为了理想，放弃了不少东西。他创造了一个概念叫 copyleft，跟 copyright（版权）对着干。说到这儿，我得插一句。很多人分不清自由软件和开源软件。其实这俩不完全一样。自由软件强调的是用户的自由权利，开源软件更强调开发模式的开放性。不过日常交流中，大家经常混着用。主流开源协议大盘点现在主流的开源协议有好几种，每种都有自己的脾气。我一个个说。MIT 协议这个最宽松，基本上就是"你想咋用咋用，只要保留我的版权声明就行"。具体来说，你可以：随便用，商业用途也行随便改，改完可以闭源随便分发限制就一条：保留原作者的版权声明和许可声明。我见过不少项目用 MIT，比如 React、Vue.js 这些。选择 MIT 的好处是传播广，坏处是别人拿你的代码去赚钱，你也没话说。Apache 协议 2.0这个比 MIT 稍微复杂点，但也很友好。主要特点：可以商用，可以闭源修改后需要说明修改了什么保留版权声明如果有专利，自动授予专利许可这个专利授权挺重要的。有些公司开源项目，可能会涉及专利问题。Apache 2.0 就明确说了，我开源这代码，专利权也给你用了，不能以后拿专利来告你。Android 系统就是用的 Apache 2.0，这也是为什么那么多厂商能基于 Android 做自己的系统。BSD 协议这个有好几个版本：BSD 2-Clause、BSD 3-Clause 等。BSD 2-Clause 跟 MIT 差不多，就是允许自由使用，保留版权声明就行。BSD 3-Clause 多了一条：不能用原作者的名义来做广告。这条挺实用的，避免有人打着原作者的旗号搞事情。GPL 协议这个是开源协议里的刺头，也是争议最大的。GPL 的核心思想是传染性。什么意思呢？如果你用了 GPL 协议的代码，那你的项目也得开源，而且得用 GPL 协议。这就像病毒一样，会传染。所以很多商业公司对 GPL 很警惕，生怕一不小心就中招了。GPL 有两个主要版本：GPL v2 和 GPL v3。GPL v2 是最经典的，Linux 内核就是用的这个。Linus Torvalds 到现在还坚持用 v2，不用 v3。GPL v3 更严格，加了一些反 DRM 的条款。简单说就是，如果你的设备上运行的是 GPL v3 的软件，你得让用户能修改这个软件。这主要是针对 TiVo 这种设备的，TiVo 用了 Linux，但不让用户修改，所以 FSF 在 v3 里专门加了这条款。LGPL 协议这个是 GPL 的弱化版，主要给库文件用的。如果你把 LGPL 的库链接到你的程序，你的程序不需要开源。但如果你修改了这个库本身，那修改的部分得开源。很多底层库用 LGPL，比如 glibc。这样商业软件也能用，不用担心被传染。MPL 协议Mozilla Public License，Mozilla 基金会搞的。这个比较折中，文件级别的 copyleft。就是说，你修改了某个文件，这个文件得开源。但你新增的文件不需要开源。Firefox 就是用的这个协议。AGPL 3.0 - SaaS 公司的噩梦这个协议我得专门拿出来说，因为这几年太重要了，也是很多公司容易踩坑的地方。AGPL …

这个开源监控神器，让我再也不用半夜被老板电话轰炸了！
type: item
date: 2025-08-11T14:26:49+00:00
description: 周末用python flask框架通过“氛围编程”方式做了一个网站监控（项目地址：https://github.com/dearisyou/Websit_survival_monitoring/tree/master），有粉丝盆友指出Uptime Kuma更好用。今天去学习了下。果然还得多交流，这么好的项目不多点人知道太可惜了！！！再次感谢粉丝盆友分享！什么是Uptime KumaUptime Kuma项目地址：https://github.com/louislam/uptime-kumaUptime Kuma是一个开源的自托管监控工具，界面非常漂亮，功能也很全面。最重要的是，它真的很简单！不像那些传统的监控系统，需要写一堆配置文件，Uptime Kuma基本上点点鼠标就能搞定。这个项目在GitHub上已经有7.3万多个star了，说明还是很受欢迎的。而且更新也很频繁，作者很活跃，基本上有问题提issue都会得到回复。安装部署过程安装Uptime Kuma有好几种方式，我比较推荐用Docker，因为真的很方便。docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1就这一条命令，然后等个几分钟下载镜像，基本就搞定了。当然，如果你的服务器在国内，可能下载会比较慢，建议先配置一下Docker的镜像加速。[root@webtest ~]# docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1 Unable to find image 'louislam/uptime-kuma:1' locally 1: Pulling from louislam/uptime-kuma b338562f40a7: Pull complete 874bf4d93720: Pull complete b16337721583: Pull complete 7d955db85b85: Pull complete 2c706596bd17: Pull complete 88a5c59ed14f: Pull complete 5a1d0a896c33: Pull complete e68c2f25b946: Pull complete 2e6c90f010d6: Pull complete ff15b10fabb8: Pull complete 4f4fb700ef54: Pull complete d2a400cc8adb: Pull complete Digest: sha256:431fee3be822b04861cf0e35daf4beef6b7cb37391c5f26c3ad6e12ce280fe18 Status: Downloaded newer image for louislam/uptime-kuma:1 3cce7589efe3ec5b82b34737943163830583425f63268b2ef51b9a1434908348 [root@webtest ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3cce7589efe3 louislam/uptime-kuma:1 "/usr/bin/dumb-init …" 42 seconds ago Up 41 seconds (health: starting) 0.0.0.0:3001->3001/tcp, :::3001->3001/tcp uptime-kuma [root@webtest ~]# 如果不想用Docker，也可以直接用npm安装：npm install pm2 -g git clone https://github.com/louislam/uptime-kuma.git cd uptime-kuma npm run setup pm2 start server/server.js --name uptime-kuma不过说实话，我还是推荐Docker方式，因为依赖管理会简单很多。部署完成后，打开浏览器访问 http://你的服务器IP:3001 就能看到界面了。第一次访问会让你创建管理员账号，这个过程很简单，按提示操作就行。界面和功能体验十分钟demo体验地址：https://demo.kuma.pet/start-demo打开Uptime Kum…

从零开始：给Typecho写个Redis缓存插件，让你的博客飞起来！
type: item
date: 2025-08-05T13:37:00+00:00
description: 最近有朋友问我，博客访问速度慢怎么办？我第一反应就是：上缓存啊！作为一个天天跟服务器打交道的人，我深知缓存对网站性能的重要性。今天就来分享一下我最近给自己的Typecho博客写的一个Redis缓存插件的经验。说实话，网上关于Typecho插件开发的资料真的不多，特别是涉及到Redis这种高级功能的。我也是摸着石头过河，踩了不少坑才搞出来这个插件。不过好在最终效果还不错，页面加载速度提升了好几倍呢。为什么选择Redis做缓存可能有人会问，为什么不用文件缓存或者Memcached？我来说说我的想法。文件缓存确实简单，但是在高并发情况下，磁盘IO会成为瓶颈。而且清理缓存也比较麻烦，你得去遍历文件夹删除文件。Memcached虽然也不错，但是Redis功能更强大啊！不仅可以做缓存，还能做消息队列、计数器什么的。而且Redis的数据结构更丰富，扩展性更好。最重要的是，Redis现在基本上是标配了，大部分VPS都能很方便地安装。我自己的服务器上早就跑着Redis，用来做各种缓存和数据存储。插件的整体设计思路在开始写代码之前，我先理了理思路。一个缓存插件需要做什么事情呢？无非就是这几个步骤：用户访问页面时，先检查Redis里有没有缓存的内容；如果有，直接返回；如果没有，正常生成页面，然后把内容存到Redis里。当内容更新时，要记得清除相关缓存。听起来很简单对吧？但是实际写起来，细节问题可多了。比如什么时候该缓存，什么时候不该缓存？缓存的键怎么设计？过期时间怎么设置？这些都需要仔细考虑。核心代码解析让我来详细说说这个插件的实现。Redis连接初始化public static function initRedis() { if (self::$redis !== null) { return self::$redis; } $options = Helper::options(); $config = $options->plugin('RedisCache'); // 如果禁用缓存，直接返回 if (isset($config->enableCache) && $config->enableCache == '0') { return null; }这里我做了个单例模式，避免重复连接Redis。还加了个开关，可以随时禁用缓存功能。这个设计在调试的时候特别有用。连接Redis的时候，我加了很多错误处理：try { // 检查Redis扩展是否加载 if (!extension_loaded('redis')) { throw new Exception('PHP Redis扩展未安装'); } // 尝试连接Redis $redis = new Redis(); $connected = $redis->connect($config->host, $config->port, 2); // 2秒超时 if (!$connected) { throw new Exception('无法连接到Redis服务器'); }这里设置了2秒的连接超时，避免Redis服务器出问题时网站卡死。我之前就遇到过Redis服务器挂了，结果整个网站都访问不了的情况。缓存读取逻辑public static function beforeRender($archive) { // 管理员登录时不使用缓存 if (Typecho_Widget::widget('Widget_User')->hasLogin()) { return; } // 初始化Redis $redis = self::initRedis(); if (!$redis) { return; } // 获取当前请求的唯一标识 $requestUri = $_SERVER['REQUEST_URI']; $cacheKey = self::$prefix . 'page:' . md5($requestUri); // 尝试从缓存获取内容 $cachedContent = $redis->get($cacheKey); if ($cachedContent !== false) { // 缓存命中，输出内容并结束执行 echo $cachedContent; exit; } // 缓存未命中，开始输出缓冲 ob_start(); }这段代码是整个插件的核心。我用REQUEST_URI的MD5值作为缓存键，这样可以确保每个页面都有唯一的缓存标识。注意这里有个细节：管理员登录时不使用缓存。这是因为管理员看到的页面可能包含一些特殊内容，比如编辑链接什么的，这些不应该被缓存。缓存写入逻辑public static function afterRender() { // 管理员登录时不缓存 if (Typecho_…