欢迎 :)
我是 Cassius0924,目前在字节跳动担任 Golang 开发工程师。
我热衷于开源项目和新技术的探索。
在这里我分享关于编程、技术趋势和开发经验的思考。
为什么需要"订阅 Release"这件事如果你维护过任何"部署在服务器上、却由第三方持续迭代"的软件,一定经历过这种时刻: 某个插件修了一个你踩过的 bug,但你不知道,还在用老版本 你手动执行升级,结果升级把本地补丁冲掉了,功能悄悄变坏 你配了 GitHub Watch 邮件通知,但邮件太多,根本没在看 我自己的服务器上就有三个这样的"受害者": 目标 性质 痛点 sz-metro-api(深圳地铁 API 服务) 我自己的仓库 每次 push 都要手动 SSH 上去 pull + 编译 + 重启 Hermes Agent(AI Agent 框架) 别人的仓库(NousResearch) 有新版本不知道;而且本地有 patch,更新会重置 hermes-feishu-streaming-card(飞书卡片插件) 别人的仓库(baileyh8) 三天连发 4 个小版本,全靠手动检查 三者的共同问题:版本更新如何被自动感知、并自动执行后续动作(部署/升级/通知)? 本文不打算只讲某一个具体方案,而是给出一个可复用的方法论:GitHub Release 自动化更新有三条路径,各自适用什么场景、有什么坑,以及如何用一张决策树快速选型。三个真实案例(上面的三个目标)就是这套方法论的实践样本。 核心决策框架:三个维度任何"订阅 Release"的需求,都可以用三个维度来定位: 维度 1:仓库是不是自己的?——决定技术上能不能用 WebhookGitHub 的 Webhook 只有仓库管理员(owner 或 admin 权限的协作者)能配置。这是最硬性的约束: 自己的仓库 → 原生 Webhook 可用(实时、官方、零轮询成本) 别人的仓库 → GitHub 不会给你推事件,只能轮询(Argus / Cron) 维度 2:更新敏感度?——决定要不要全自动执行 敏感(改代码、重启核心服务、有本地 patch 要维护)→ 需要可控的自动执行 + 失败可见 不敏感(换个包、重启个 sidecar)→ 简单自动即可,甚至只需要通知 维度 3:实时性要求?——决定轮询周期和架构复杂度 秒级响应(CI/CD 链路)→ Webhook 分钟级(第三方工具的版本跟进)→ Argus(30 分钟轮询) 小时/天级(低优先级的检查)→ Cron 决策树 flowchart TD Q1{"仓库是自己的?"} Q1 -- "是" --> A["方案一:原生 Webhook实时推送 release/push 事件配 HMAC 签名验证"] Q1 -- "否" --> Q2{"更新敏感(需补丁恢复、失败可见)或需要统一看板?"} Q2 -- "是" --> B["方案二:Argus 轮询 + Webhook 转发监控器查 GitHub API发现新版主动发 Webhook 给你"] Q2 -- "否" --> C["方案三:Cron 定期检查脚本 curl 对比版本有变化才动作,无变化静默"] 一句话版本:自己的仓库用 Webhook,别人的仓库里"重要"的用 Argus、“无所谓"的用 Cron。 ...
你家的小爱同学,能不能拥有一个真正属于你自己的 AI 大脑? 这篇文章完整记录了我把 小爱音箱 Play 增强版 接入自部署 Agent(Hermes)的全过程:架构设计、组件拆解、短信验证码登录、多轮会话修复,还有一箩筐踩坑实录。 一、为什么要折腾小爱音箱的"小爱同学"很聪明,但它的聪明是小米云端的黑盒:你无法让它调用你的记忆、控制你的服务器、理解你的专属上下文。而我的服务器上跑着 Hermes——一个自部署的开源 AI Agent,带着持久记忆、工具调用、Home Assistant 控制、飞书接入等完整能力。 目标很简单: 音箱麦克风 → Hermes(我的大脑)→ 音箱扬声器 让小爱音箱成为 Hermes 的"耳朵"和"嘴",客厅里喊一声,动用的却是自己的 Agent。 二、硬件前提:为什么走"云端链路"我的音箱是 小爱音箱 Play 增强版(型号 L05C)。调研后发现两个硬件事实: 没有 3.5mm AUX IN(背面只有一个 DC 电源口),无法用线缆把外部音频送进去; 蓝牙 A2DP 延迟 100-200ms,作为语音助手外放体验不佳。 所以不能走"本地音频接入"路线(麦克风采集 → 本地 STT → Agent → 本地 TTS → 线缆/蓝牙播放),而是走云端链路:小爱音箱本身的拾音和发声都是小米云能力,我们只需要在云端"拦截"它识别出来的文字,把回答"塞回"它的嘴巴。 三、架构总览整条链路共 4 个组件,全部跑在自己的服务器上: flowchart LR U[你说话:小爱同学,xxx] --> S[小爱音箱 Play 增强版 L05C] S -->|音频| MC[小米云 STT 识别] MC -->|识别文本| XG[xiaogpt监听对话流 + 拦截] XG -->|POST /v1/chat/completions| OS[openai-shim:8790OpenAI 兼容翻译层] OS -->|POST /v1/voice| VB[hermes-voice-bridge:8788] VB -->|hermes chat -q -Q --continue| H[Hermes Agentvoice profile 独立人格] H -->|回复文本| VB VB --> OS OS --> XG XG -->|小米云 TTS 原声| MC2[小米云 TTS] MC2 -->|音频| S 组件 角色 类比 xiaogpt 登录小米云,监听音箱对话流,拦截问题;把回答通过小米云 TTS 播报 耳朵 + 嘴 openai-shim 把 xiaogpt 以为在调的 OpenAI API 翻译成 voice-bridge 的协议(约 80 行) 翻译 hermes-voice-bridge 调用 hermes chat -q -Q --continue <会话ID> 跑真正的 Agent 接线员 Hermes voice profile 独立配置的 Agent 实例:语音人设、精简参数、共享记忆 大脑 四、组件逐个拆解4.1 xiaogpt:耳朵与嘴xiaogpt 是社区项目,核心能力是通过小米云的私有协议(mina 实时事件流)监听音箱的对话记录(MiService 等库是对这套协议的封装)——注意,它不碰音频,拿的是云端识别好的文字,所以不需要 root、不需要改固件。 ...
如何写好一个 Skill?如果只从表面看,Skill 很容易被理解成更长的 Prompt。但实际用下来,Skill 更像是给 Agent 装上的一块可复用能力模块:它告诉模型什么时候应该使用这套方法、具体该怎么执行、执行到什么程度算完成、如何验证自己做对了。 因此,写好一个 Skill 的关键,不是把经验全部塞进 SKILL.md,而是把一类任务中真正会影响结果的知识,组织成 Agent 能稳定调用的工作流。 暂时无法在飞书文档外展示此内容 Skill 是什么?在 Agent Skill 的定义里,一个 Skill 本质上是一个文件夹,里面至少包含一个 SKILL.md 文件。SKILL.md 里会写元信息,比如 name 和 description,也会写具体的执行说明。除此之外,一个 Skill 还可以附带脚本、参考文档、资产文件等资源。 一个典型结构大概是这样: my-skill/ ├── SKILL.md ├── scripts/ ├── references/ └── assets/ 这个结构背后有一个很重要的设计思想:渐进式披露。 Agent 启动时不会把所有 Skill 的完整内容都塞进上下文,而是只读取每个 Skill 的 name 和 description。当用户任务匹配某个 Skill 的描述时,Agent 才会加载完整的 SKILL.md。如果执行过程中还需要更详细的资料,再按需读取 references/、assets/ 或执行 scripts/ 里的内容。 所以,Skill 的第一个门槛不是“内容写得多不多”,而是:它能不能在正确的时机被触发。 description 是触发器,不是简介Skill 写得再好,触发不了等于零。Agent 只在启动时加载每个 Skill 的 name 和 description,靠这两行决定"要不要把这个 Skill 拉进来"。描述写不好,Skill 永远不会被调用。 很多人会把 description 写成 Skill 简介: ...
我把尘封已久的 树莓派4B 拿出来,准备重新使用它,结果发现我进不去系统了 😇。。。 因为忘记了 Hostname 和 Password,手上没有 TTL 转串口线,也没有 micro HDMI 转 HDMI 线,无法通过串口或者显示器进行恢复。 不过正好有一个 micro SD 卡读卡器,可以通过重新烧录系统镜像的方式来恢复树莓派的使用。 准备工作 一台 macOS/Linux/Windows 电脑,我这里以 macOS 为例 一个 micro SD 卡读卡器 如果你是 MacBook,你多半没有 USB-A 接口,需要一个 USB-A(母)转 USB-C(公)的转接头。 步骤下载树莓派官方烧录工具前往树莓派官网下载官方烧录工具 Raspberry Pi Imager,根据你的操作系统选择对应的版本进行下载和安装。这里不赘述。 将 SD 卡插入读卡器并连接电脑这很简单吧,怎么插就不展示了。 烧录配置打开安装好的 Raspberry Pi Imager。 选择设备型号 选择你的树莓派的型号,我是树莓派 4B,我这里选择 Raspberry Pi 4。 不知道树莓派型号的话,看看你的板子,找找板上的小字,一般会有型号标识。 选择操作系统 选择你想要烧录的操作系统,一般都选择 Raspberry Pi OS (64-bit)。 选择存储设备 选择你插入的 SD 卡读卡器对应的存储设备。如果你的电脑只插了 配置 Hostname ...

systemd 是现代 Linux 发行版中广泛使用的初始化系统和服务管理器。它不仅提供了强大的功能来管理系统服务,还允许用户轻松地配置和管理自启动服务。 systemd 中的字母 d 表示 (daemon)守护进程,相信学过操作系统的同学都知道守护进程是指在后台运行的进程。 systemd 作为守护进程管理器,负责启动、停止和管理系统中的各种服务。 systemd 操作命令systemctlsystemctl(system control)是 systemd 的主要命令行工具。它用于检查和控制 systemd 系统和服务管理器的状态。 刷新配置文件 sudo systemctl daemon-reload 但你修改了服务的配置文件后,需要运行这个命令来让 systemd 重新加载配置文件。否则 systemd 不会识别你的更改。 启动服务 sudo systemctl start <service_name> 这个命令用于启动指定的服务,当系统重启后,服务不会自动启动。 停止服务 sudo systemctl stop <service_name> 这个命令用于停止指定的服务。 重启服务 sudo systemctl restart <service_name> 这个命令用于重启指定的服务。 重新加载服务配置 sudo systemctl reload <service_name> 这个命令用于重新加载指定服务的配置,而不停止服务。它与 restart 的区别在于,reload 不会中断服务的运行,适用于支持热加载配置的服务。 查看服务状态 sudo systemctl status <service_name> 这个命令用于查看指定服务的当前状态。 启用服务自启动 sudo systemctl enable <service_name> 这个命令用于使指定的服务在系统启动时自动启动。但是不会立即启动服务,如果想立即启动服务,可以加上 --now 选项: sudo systemctl enable --now <service_name> ...
使用 Certbot 申请泛域名证书并不复杂,几个月前搞过,但是又忘了,今天重新搞了一遍,记录一下步骤。 前提条件 你需要有一个域名,并且可以管理该域名的 DNS 记录。 一般我们都是在 阿里云 或 火山引擎 等云服务商购买的域名,这里以阿里云为例。 申请泛域名证书安装 Certbot先用 certbot --version 检查是否已经安装 Certbot,如果没有安装,下面一句话安装一下: sudo apt install certbot -y 申请证书泛域名证书需要通过 DNS-01 验证域名所有权,使用以下命令申请,注意将 *.cassdev.com 和 cassdev.com 替换为你的域名,example@google.com 替换为你的邮箱。 笔记 什么是 DNS-01 验证? ...
大家在使用 LLM 生成内容时,不知道有没有注意到 LLM 的一些可配置参数,比如 Temperature 和 Top-p,是否关注过这些参数的作用? 无论是在 OpenAI 的 API 文档、Google 的 AI Studio、以及各种的 AI 平台,你都能看到它的身影。 什么是 Temperature 和 Top-p?在与 LLM 聊天时,大家可能已经注意到,有的 Agent 十分有创造力,有的 Agent 又十分严谨。这其中除了 Prompt 的影响外,还有一个重要的因素就是 LLM 的采样参数,包括 Temperature 和 Top-p。 提示 TL;DR ...

如果想让 LLM 输出 JSON 格式的内容,大家第一反应会是什么?可能大多数人和我一样,直接在提示词中写上"请输出 JSON 格式的内容,格式为 { “key”: “value” }"。但其实,这种方式并不是最优的。 从之前我们也了解到了,LLM 的输出是一个概率性的文本补全器。单纯依靠提示词工程来控制 LLM 的输出格式并不可靠。用自然语言去描述一个复杂的 JSON 结构本就不易,再加上当提示词很长时,LLM 的注意力可能会分散,这些因素都容易导致它输出不符合预期的格式,甚至根本不输出 JSON。 具体来说,这种方式可能会遇到以下三个主要问题: 混入无关文本:模型可能在 JSON 对象前后添加对话式的"口水话",如"好的,这是您要的 JSON:…",这给后续的程序化解析带来了困难。 结构性错误:生成的 JSON 可能存在语法错误,例如缺少逗号、括号不匹配或引号使用不当,导致解析失败。 内容幻觉:模型可能"幻觉"出指令中未要求的字段,或遗漏必要的字段,破坏了数据模式的一致性。 让 LLM 生成符合预期的 JSON 格式内容的最佳实践是使用 response_format 参数,在程序算法的层面上去干预 LLM 的输出格式。这个参数允许我们让 LLM 进行结构化内容输出,确保 LLM 生成的内容符合预期的结构和语法。 Response Format 参数response_format 参数在绝大多数现代 LLM API 中都可用,允许开发者指定模型输出的格式。 DeepSeek API Response Format OpenAI API Response Format DouBao API Response Format 通过这个参数,我们可以明确要求 LLM 生成特定格式的内容,如 JSON 对象、纯文本或符合 JSON Schema 的数据结构。 response_format 参数支持以下三个模式: ...

1. 语法高亮简介语法高亮是指在IDE或编辑器中,对文本进行分词,即将文本拆解为 Token(标记),每个 Token 都有对应的名称(作用域)进行标记。再配合主题样式规则,对不同名称的 Token 的进行主题化,以提高代码的可读性。 程序员离不开语法高亮,就像作家离不开标点符号一样。(你可以代入一下使用 txt 文本编辑器写代码的场景) 语法高亮由两个部分组成: 分词(Tokenization):将文本拆解为一系列 Token。 主题化(Theming):对 Token 进行样式渲染,如字体颜色、背景色、加粗等。 我们以 JSON 的语法为例,简单介绍一下语法高亮的过程。 首先分词引擎会对 JSON 文本进行分词,下图是将 JSON 文本进行分词后的结果,其中每个矩形所包括的文本都是一个 Token,每个 Token 都有一个作用域名称,例如 null 对应的是 constant.language.json 作用域。 然后主题化引擎会根据 Token 的作用域名称,对 Token 进行样式渲染,例如将 constant.language.json 作用域映射为蓝色不加粗字体。那么 null 就会被渲染为蓝色不加粗字体。 2. 分词的实现方式目前主流的分词实现方式大致有有以下三种: 基于正则表达式的分词:Textmate 基于词法分析的分词:Highlight.js 基于语法树的分词:Tree-sitter (如果有其他,欢迎补充) 本文只讨论 Textmate 的语法高亮规则编写。 Textmate 原是 MacOS 下的一款文本编辑器,其语法高亮规则是基于正则表达式的,但由于其规则简单易懂,且支持多种语言,因此被广泛应用于各种编辑器和IDE中,如 VSCode、Sublime Text 等。JetBrains 的 IDE 也集成了 Textmate Bundle 插件,可以直接导入 Textmate 的语法高亮规则。 ...
适合已经熟悉 Vim 基础操作,希望提高编辑技能的开发者的实用技巧集合