现在你的签到日历会显示你每一天的签到金币收入,并且会显示该月的总签到金币收入、检定成功、失败、大成功以及大失败的次数,谁欧谁非一眼万年。

同时也给金币页面 https://2libra.com/coins 做了较大的更新,也就是金币的变动数据可视。该页面现在可以通过热力图直接看到某个月份的金币变动概况,绿涨红跌,颜色越深表示值越大。
日期格子底部小圆点表示当天,点击某一天则会对金币的历史记录做日期过滤,方便查看该日期的金币流水情况。

image

除了热力图外,也增加了收支类型的饼图,可以直观看出你的支出和收入在哪个类型下的占比。

image

一般打赏排第一的应该才是慈善家doge_flower

除此之外,新版本也做了下面的一些优化改进:

  1. Markdown 中的引用移除了引号。
  2. 修正“捣蛋鬼”徽章引起页面显示评论的类型不正确。
  3. 对签到页面的请求做了一些约束,避免快速切换月份时数据不正确。

MariaDB是一款开源的关系型数据库管理系统,由MySQL的创始人迈克尔·维德纽斯主导开发。

MariaDB在MySQL的基础上进行了大量优化和改进,保持完全兼容的同时,加入了更多存储引擎、性能优化和安全性增强。对于需要数据库支持的Web应用、服务器项目和开发环境来说,MariaDB是目前最可靠的开源数据库选择之一。

在开源关系型数据库领域,MariaDB和MySQL是双胞胎但又有区别:

对于普通Web开发来说,MariaDB在兼容性、性能和社区支持之间达到了最好的平衡。如果你之前用过MySQL,那用MariaDB几乎不需要学习成本,命令、语法、工具完全兼容,直接换过来就行。

MariaDB下载

MariaDB安装包(官网正版)下载地址:
https://pan.quark.cn/s/fe438e4d9a9d
https://pan.xunlei.com/s/VOr7rzV0Bd-6pXpNmJvH2ZXsA1?pwd=fzrm#

MariaDB安装

1) 我的是 Windows 系统,下载.msi安装包后双击运行:

2) 点击"Next"进入安装选项,会安装所有组件包括数据库服务端、客户端和开发库。安装路径可以改到D盘:

3) 设置root密码,这个就是数据库管理员密码,一定要记住。建议勾选"Enable access from remote machines for 'root' user"(如果需要在其他电脑连这台数据库的话):

4) 如果你已经安装过mysql,再安装mariaDB的时候回提示端口已经占用了,所以需要改变一下端口号即可:

5) 等待安装完成:

在开始菜单中找到安装好的 mariaDB 命令行窗口,可以输入mariadb --version查看是否安装成功,用mysql -u root -p输入密码后就能进入数据库管理界面。

MariaDB基础使用

安装完成后用 mysql -u root -p 登录数据库,输入之前设置的密码。

创建数据库:CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 建议用utf8mb4字符集,支持完整的Unicode字符(包括表情符号)。

创建用户并授权:推荐创建专用的数据库用户,不要直接用root。CREATE USER 'myuser'@'localhost' IDENTIFIED BY '密码'; 然后授权:GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost'; 最后FLUSH PRIVILEGES;让配置生效。

查看状态:用SHOW DATABASES;查看所有数据库,用SHOW TABLES;查看当前数据库中的表,用STATUS;查看MariaDB的运行状态和版本信息。

退出:输入exit或按Ctrl+D退出数据库命令行。

MariaDB常见问题

连接数据库时报错"Access denied for user 'root'@'localhost'",一般是忘记了密码或者密码输入错误。Windows下可以以管理员身份运行命令提示符,执行mysqld --skip-grant-tables跳过权限验证来重置密码。Linux下则用sudo mysqld_safe --skip-grant-tables启动后修改密码。

MariaDB服务无法启动,可以先检查3306端口是否被占用。运行netstat -ano | findstr :3306(Windows)或sudo lsof -i :3306(Linux/macOS)查看端口占用情况。如果有其他数据库占用了3306端口,修改MariaDB配置文件中的端口号即可。

总结

MariaDB作为MySQL的完全兼容替代品,是目前开源数据库市场的不二之选。安装 MariaDB 后做好安全配置和基础设置,配合备份习惯,就能稳定运行数年不变。

对于正在搭建Web项目或学习数据库的开发者和运维人员来说,MariaDB值得一试。

一、系统架构与核心拓扑

单相PFC整流+离网逆变器系统主要由PFC整流级离网逆变级组成,采用双向H桥拓扑(支持DC-AC/AC-DC双向能量流动),核心拓扑结构如下:

  • PFC整流级:采用图腾柱无桥PFC(或Boost PFC),实现交流市电到直流母线的整流,同时通过功率因数校正(PFC)使输入电流正弦化(PF>0.99),降低谐波污染(THD<5%)。
  • 离网逆变级:采用全桥逆变拓扑(如H4桥),将直流母线电压转换为单相交流电压(220V/50Hz),支持纯正弦波输出(THD<3%),满足家用或工业负载需求。

二、PFC整流级设计与MATLAB实现

PFC整流级的核心是实现高功率因数整流,采用电压电流双闭环控制(外环电压稳定、内环电流跟踪),以下是MATLAB仿真实现:

1. 主电路拓扑(Boost PFC)

Boost PFC电路由交流电源滤波电感开关管(SiC MOSFET)二极管直流母线电容组成,拓扑结构如下:

% Boost PFC主电路参数
Vin = 220;          % 交流输入电压(V)
Vdc_ref = 400;      % 直流母线参考电压(V)
L = 1e-3;           % 滤波电感(H)
C = 1e-3;           % 直流母线电容(F)
fs = 100e3;         % 开关频率(Hz)
Ts = 1/fs;          % 开关周期(s)
2. 双闭环控制策略
  • 电压外环:稳定直流母线电压,输出电流参考值(Iref)。
  • 电流内环:跟踪输入电压正弦波形,使输入电流(Iin)与输入电压(Vin)同相,实现高功率因数。

MATLAB代码实现(双闭环控制)

% 电压外环PI控制器
function duty = voltage_control(Vdc_meas, Vdc_ref)
    persistent integral_error;
    if isempty(integral_error)
        integral_error = 0;
    end
    Kp_v = 0.1;       % 电压环比例系数
    Ki_v = 0.01;      % 电压环积分系数
    error = Vdc_ref - Vdc_meas;
    integral_error = integral_error + error * Ts;
    duty = Kp_v * error + Ki_v * integral_error;
    duty = max(min(duty, 0.95), 0.05);  % 限制占空比(0.05~0.95)
end

% 电流内环PI控制器
function d = current_control(Iin_meas, Iref)
    persistent integral_error;
    if isempty(integral_error)
        integral_error = 0;
    end
    Kp_i = 0.5;       % 电流环比例系数
    Ki_i = 0.1;       % 电流环积分系数
    error = Iref - Iin_meas;
    integral_error = integral_error + error * Ts;
    d = Kp_i * error + Ki_i * integral_error;
    d = max(min(d, 0.95), 0.05);       % 限制占空比
end

% 主仿真循环
t = 0:Ts:0.1;       % 仿真时间(0.1s)
Vdc_meas = zeros(size(t));
Iin_meas = zeros(size(t));
duty = 0.5;          % 初始占空比

for i = 1:length(t)
    % 读取输入电压、电流
    Vin_t = Vin * sqrt(2) * sin(2*pi*50*t(i));  % 交流输入电压(峰值)
    Iin_meas(i) = (Vin_t / L) * integral(duty * Ts);  % 输入电流(简化计算)
    
    % 电压外环计算电流参考
    Vdc_meas(i) = Vdc_ref + 10*sin(2*pi*10*t(i));  % 模拟直流母线电压波动
    Iref = voltage_control(Vdc_meas(i), Vdc_ref);
    
    % 电流内环计算占空比
    duty = current_control(Iin_meas(i), Iref);
    
    % 更新开关管状态(PWM输出)
    % (此处省略PWM信号生成代码,可使用MATLAB的PWM模块)
end
3. 仿真结果
  • 输入电流波形:输入电流(Iin)与输入电压(Vin)同相,正弦化程度高(THD<5%)。
  • 直流母线电压:稳定在400V左右,波动小(<5%)。

三、离网逆变级设计与MATLAB实现

离网逆变级的核心是将直流母线电压转换为纯正弦波交流电压,采用电压电流双闭环控制(外环电压稳定、内环电流跟踪),以下是MATLAB仿真实现:

1. 主电路拓扑(全桥逆变)

全桥逆变电路由直流母线电容开关管(SiC MOSFET)输出滤波器(LC)负载组成,拓扑结构如下:

% 全桥逆变主电路参数
Vdc = 400;          % 直流母线电压(V)
Vout_ref = 220;     % 交流输出参考电压(V)
fout = 50;          % 输出频率(Hz)
Lf = 0.6e-3;        % 输出滤波电感(H)
Cf = 10e-6;        % 输出滤波电容(F)
fs_inv = 25e3;      % 逆变开关频率(Hz)
Ts_inv = 1/fs_inv;  % 逆变开关周期(s)
2. 双闭环控制策略
  • 电压外环:稳定交流输出电压,输出电流参考值(Ioref)。
  • 电流内环:跟踪输出电压正弦波形,使输出电流(Io)与输出电压(Vo)同相,保证负载供电质量。

MATLAB代码实现(双闭环控制)

% 电压外环PI控制器
function Io_ref = voltage_control_inv(Vout_meas, Vout_ref)
    persistent integral_error;
    if isempty(integral_error)
        integral_error = 0;
    end
    Kp_v_inv = 0.2;   % 电压环比例系数
    Ki_v_inv = 0.02;  % 电压环积分系数
    error = Vout_ref - Vout_meas;
    integral_error = integral_error + error * Ts_inv;
    Io_ref = Kp_v_inv * error + Ki_v_inv * integral_error;
    Io_ref = max(min(Io_ref, 10), -10);  % 限制电流参考(±10A)
end

% 电流内环PI控制器
function d_inv = current_control_inv(Io_meas, Io_ref)
    persistent integral_error;
    if isempty(integral_error)
        integral_error = 0;
    end
    Kp_i_inv = 1.0;   % 电流环比例系数
    Ki_i_inv = 0.2;   % 电流环积分系数
    error = Io_ref - Io_meas;
    integral_error = integral_error + error * Ts_inv;
    d_inv = Kp_i_inv * error + Ki_i_inv * integral_error;
    d_inv = max(min(d_inv, 0.95), 0.05);  % 限制占空比
end

% 主仿真循环
t_inv = 0:Ts_inv:0.1;  % 仿真时间(0.1s)
Vout_meas = zeros(size(t_inv));
Io_meas = zeros(size(t_inv));
d_inv = 0.5;            % 初始占空比

for i = 1:length(t_inv)
    % 读取输出电压、电流
    Vout_meas(i) = Vout_ref * sin(2*pi*fout*t_inv(i));  % 输出电压(参考)
    Io_meas(i) = (Vout_meas(i) / Lf) * integral(d_inv * Ts_inv);  % 输出电流(简化计算)
    
    % 电压外环计算电流参考
    Io_ref = voltage_control_inv(Vout_meas(i), Vout_ref);
    
    % 电流内环计算占空比
    d_inv = current_control_inv(Io_meas(i), Io_ref);
    
    % 更新开关管状态(SPWM输出)
    % (此处省略SPWM信号生成代码,可使用MATLAB的SPWM模块)
end
3. 仿真结果
  • 输出电压波形:输出纯正弦波(220V/50Hz),THD<3%。
  • 输出电流波形:与输出电压同相,满足阻性/感性负载需求。

参考代码 单相PFC整流+离网逆变器 www.youwenfan.com/contentsfa/65161.html

四、系统协同控制与优化

  • 模式切换:支持逆变模式(DC→AC)整流模式(AC→DC)无缝切换(切换时间<10ms),通过检测母线电压或负载需求自动切换。
  • 保护机制
  • 过流保护(DESAT检测,响应时间<2μs);
  • 过压/欠压保护(TVS+RC吸收电路);
  • 温度降载(实时监测SiC MOSFET结温,超过150℃时降频)。

五、关键器件选型(SiC MOSFET)

采用国产SiC MOSFET(如BASiC基本股份的B3M系列),优势如下:

  • 高频高效:开关频率可达100kHz以上,开关损耗低(仅为IGBT的1/3);
  • 低导通损耗:导通电阻(RDS(on))小(如50A SiC MOSFET的RDS(on)约10mΩ);
  • 耐高温:结温可达200℃以上,适合户外恶劣环境。

六、总结

单相PFC整流+离网逆变器系统通过双闭环控制实现了高功率因数整流和纯正弦波逆变,采用SiC MOSFET提升了系统效率和功率密度。MATLAB仿真验证了控制策略的有效性,输出波形质量高(THD<3%),满足家用或工业负载需求。该系统可广泛应用于离网储能应急电源电动汽车V2L等场景,具有广阔的市场前景。

Navicat是一款专业的数据库管理和开发工具,支持MySQL、MariaDB、SQL Server、PostgreSQL、SQLite、Oracle等多种主流数据库。

Navicat用图形化界面替代了命令行操作,让创建表、写SQL、导入导出数据、同步数据库等操作更加直观高效。对于经常跟数据库打交道的开发者和DBA来说,Navicat是提升工作效率的利器。

在数据库管理工具领域,Navicat的主要竞争对手是DBeaver和HeidiSQ:

Navicat优势在于界面美观、操作流畅、支持十几款主流数据库、有中文版,一个软件就能管理所有类型的数据库,非常方便。

Navicat下载

Navicat安装包(官网免费版)下载地址:
https://pan.quark.cn/s/5f72872e4757
https://pan.xunlei.com/s/VOr84vdcU2m1MsnHJyrbP8gXA1?pwd=4g74#

Navicat安装

1)双击下载的navicatXXX_x64.exe安装包,点击"下一步":

2)选择安装路径,建议改到D盘:

3)继续点击"下一步"直到安装完成。安装后桌面上会生成Navicat快捷方式。

4)首次打开需要创建 Navicat ID 账号,创建的过程非常简单,创建后就可以永久使用了:

Navicat基础使用

打开Navicat后,首先需要创建数据库连接。点击左上角的"连接"按钮,选择你要连接的数据库类型(如MySQL、MariaDB等),输入主机地址(本地填localhost或127.0.0.1)、端口(MySQL/MariaDB默认3306)、用户名和密码。点击"测试连接"确认能连上,然后点"确定"保存。

浏览和操作数据:连接建立后,左侧会出现数据库列表。展开数据库可以看到表、视图、存储过程等。双击一个表,右侧会显示表中的所有数据,可以直接在表格中修改数据内容,类似操作Excel一样方便。

导入导出数据:右键表名选择"导出向导",可以把数据导出为Excel、CSV、SQL文件等格式。同理,"导入向导"可以从外部文件把数据导入到数据库表中,支持批量导入。

Navicat总结

Navicat是目前功能最全面的数据库管理工具之一,图形化操作让数据库管理变得简单高效。官网就提供免费的 Navicat,大家不用再去别的地方找了。

一个Navicat Premium就能管理所有类型的数据库,特别适合需要同时操作MySQL、PostgreSQL、SQL Server等多种数据库的开发者和运维人员。不管是日常查询数据、导入导出还是数据库迁移,Navicat都能帮你节省大量时间。

4 月 24 日,AI 圈迎来了真正的"神仙打架"——OpenAI 凌晨发布 GPT-5.5,DeepSeek 同日官宣 V4 预览版。一边是闭源智能体之王,一边是开源性价比屠夫,2026 年大模型赛场的分水岭,就此划下。

📰 导语

2026 年 4 月 24 日,注定被写进 AI 发展史。

北京时间凌晨,OpenAI 毫无预警地放出 GPT-5.5——自 GPT-4.5 以来首次从零重训的基础模型,定位"真实工作的新型智能";数小时后,DeepSeek 正式发布 V4 预览版并同步开源,1.6T 参数 + 100 万 Token 上下文,直接对标国际顶流。

这不是巧合,这是宣战。

当 AI 竞赛从"谁更聪明"进入"谁能干活"的智能体决胜阶段,两款旗舰同日亮相,意味着大模型竞争正式进入差异化时代:顶级效率、综合性能、极致性价比,三条赛道各有王者。


🐋 DeepSeek V4:开源旗舰,百万上下文普惠时代开启

发布时间:2026 年 4 月 24 日 | 开源协议:Apache 2.0 | 来源DeepSeek 官方

✨ 核心亮点

  • 🧠 1M Token 超长上下文:一次处理整部《百年孤独》,1M 上下文成为所有官方服务标配,不再区分"有没有长文本权限"
  • 推理效率暴涨:单 Token 推理 FLOPs 仅为上一代 V3.2 的 27%,KV Cache 显存占用降至 10%,算力成本断崖式下降
  • 🔓 完全开源:Apache 2.0 协议,权重 + 技术报告同步放出,企业可自由部署、二次开发
  • 🇨🇳 中文能力领跑:Chinese-SimpleQA 得分 84.4,大幅领先所有竞品

📊 双版本参数一览

参数V4-Pro(旗舰)V4-Flash(轻量)
总参数量1.6T284B
激活参数49B13B
上下文窗口1M Token1M Token
最大输出384K Token384K Token
预训练数据33T Token32T Token
推理模式思考 / 非思考(三档强度)思考 / 非思考(三档强度)
架构MoE + CSA + HCA + mHCMoE + CSA + HCA + mHC
许可证Apache 2.0Apache 2.0
💡 技术解读:CSA(压缩稀疏注意力)+ HCA(重度压缩注意力)混合注意力机制,配合 mHC(流形约束超连接),是 V4 能在 1M 上下文下保持低算力消耗的关键。FP4 + FP8 混合精度训练则是业界首次在万亿参数规模上验证可行。

💰 API 定价

模型输入(缓存命中)输入(缓存未命中)输出
V4-Flash¥0.2 / M¥1 / M¥2 / M
V4-Pro¥1 / M¥12 / M¥24 / M
换算为美元:V4-Flash 输出仅 $0.28/M,V4-Pro 输出 $3.48/M。Flash 版本几乎等于免费调用。

🦁 GPT-5.5:闭源王者,重新定义"AI 干活"

发布时间:2026 年 4 月 23 日(美东时间) | 闭源 | 来源OpenAI 官方博客

✨ 核心亮点

  • 🤖 原生智能体能力:不是"更聪明的问答机",而是"能自己干活的 AI 员工"——自主规划、调用工具、检查结果、持续执行
  • 🔬 科研里程碑:内部版本借助工具链发现了 Ramsey numbers 的新证明,并通过 Lean 验证——AI 数学研究的标志性事件
  • 💻 编码能力登顶:Terminal-Bench 2.0 得分 82.7%,远超 Opus 4.7(69.4%)和 Gemini 3.1 Pro(68.5%)
  • 📈 Intelligence Index 领跑:Artificial Analysis 智能指数得分 60,领先 Opus 4.7 和 Gemini 3.1 Pro 各 3 分

📊 双版本参数一览

参数GPT-5.5 ThinkingGPT-5.5 Pro
定位标准版,替代 GPT-5.4高精度版,可靠性再提升
上下文窗口1M Token(API)/ 400K(Codex)1M Token
核心场景编码、知识工作、工具调用高精度需求、科研辅助
面向用户Plus / Pro / Business / EnterprisePro / Business / Enterprise
💡 关键信息:GPT-5.5 是自 GPT-4.5 以来首个从零重训的基座模型——架构、预训练语料、训练目标全部更新,不是微调,是重建。

💰 API 定价

模型输入输出备注
GPT-5.5$5 / M$30 / MBatch/Flex 半价
GPT-5.5 Pro$30 / M$180 / MBatch/Flex 半价
⚠️ 相比 GPT-5.4(输入 $2.5 / 输出 $15),GPT-5.5 价格翻倍,是 GPT-5.x 系列史上最大涨幅。但 OpenAI 强调 Token 效率提升约 40%,按实际任务完成量计算,成本涨幅约 20%。

⚔️ 核心对比:谁是你的最优解?

🏆 性能基准对决

测试项目DeepSeek V4-ProGPT-5.5胜出方
GPQA Diamond(研究生级推理)90.193.6🦁 GPT-5.5
Chinese-SimpleQA(中文理解)84.4🐋 DeepSeek
LiveCodeBench(编程能力)93.5🐋 DeepSeek
Codeforces Elo(竞赛编程)32063168🐋 DeepSeek
Terminal-Bench 2.0(智能体执行)67.982.7🦁 GPT-5.5
OSWorld-Verified(电脑操作)78.7🦁 GPT-5.5
Toolathlon(工具调用)51.855.6🦁 GPT-5.5
MRCR 1M(长上下文检索)83.574.0🐋 DeepSeek
SWE-Bench Pro(代码修复)55.458.6🦁 GPT-5.5
FrontierMath Tier 4(前沿数学)~3535.4🦁 GPT-5.5
Artificial Analysis 智能指数60🦁 GPT-5.5

💸 价格对比:差距有多大?

模型输入价格输出价格相对 GPT-5.5
DeepSeek V4-Flash$0.14 / M$0.28 / M便宜 107 倍(输出)
DeepSeek V4-Pro$1.74 / M$3.48 / M便宜 8.6 倍(输出)
GPT-5.5$5 / M$30 / M基准
GPT-5.5 Pro$30 / M$180 / M6 倍(输出)
📌 一句话:DeepSeek V4-Pro 以不到 GPT-5.5 十二分之一的价格,提供了接近顶级闭源模型的综合性能;V4-Flash 更是便宜到"几乎免费"。

🎯 场景推荐:谁更适合你?

你的需求推荐模型理由
🇨🇳 中文任务 / 中文知识问答DeepSeek V4-Pro中文能力断档领先
💻 竞赛编程 / 算法推理DeepSeek V4-ProCodeforces 3206,世界级
📖 超长文档处理 / 百万上下文检索DeepSeek V4-ProMRCR 1M 领先,成本低
🤖 复杂 Agent 工作流 / 多步骤任务GPT-5.5Terminal-Bench 碾压级优势
🖥️ 电脑操作 / 跨工具自动化GPT-5.5OSWorld + Toolathlon 双冠
🔬 科研辅助 / 数学证明GPT-5.5Ramsey numbers 新证明,FrontierMath 领先
💰 成本敏感 / 高并发调用DeepSeek V4-Flash性能为 Pro 版 80-95%,价格仅 1/8
🏢 企业私有化部署DeepSeek V4完全开源,Apache 2.0 可商用

🔮 总结与展望

2026 年的 AI 赛场,不再是"谁最强"的单选题,而是"你要什么"的多选题。

GPT-5.5 的意义在于,它把 AI 从"回答问题"推到了"完成工作"的执行层。当模型能自主规划、调用工具、检查结果,AI 就不再是助手,而是协作者。这是 OpenAI 对"智能体时代"的答案——强、贵、但能干别人干不了的事。

DeepSeek V4 的意义在于,它证明了开源不等于妥协。1.6T 参数、1M 上下文、Apache 2.0 完全开源,以不到 GPT-5.5 十分之一的价格提供接近顶级的性能。这是 DeepSeek 对"AI 普惠"的答案——快、省、而且你全权掌控。

值得注意的是:GPT-5.5 的 AA-Omniscience 幻觉率高达 86%(自信地给出错误答案),远超 Opus 4.7 的 36%。在"AI 自己干活"的智能体场景中,自信的错误可能比诚实的不知道更危险。这提醒我们:更强的能力,也需要更强的验证机制。

2026 年的大模型格局,已经不是一家独大,而是各有所长。 选模型,先想清楚你要解决什么问题——这才是这场巅峰对决给我们最重要的启示。


📋 信息来源:DeepSeek 官方 API 文档OpenAI 官方博客ofox.ai 技术分析新浪财经中关村在线CSDN 基准测试对比。数据截至 2026 年 4 月 24 日,模型持续迭代中,请以官方最新信息为准。

很多人以为多Agent协同的瓶颈是算力或者模型能力,其实真正拖垮整个系统效率的,是那些看不见摸不着的沟通误解。两个看似都很聪明的Agent,可能会因为对同一个词的不同理解,在同一个问题上反复拉扯几个小时,最后产出一堆毫无价值的内容。这种情况在复杂任务中尤为常见,尤其是当任务需要多个Agent分工合作、信息传递链条较长的时候,任何一个微小的误解都会被无限放大,最终导致整个任务彻底偏离预期。我见过太多人兴冲冲地搭建了多Agent团队,结果运行不到三天就彻底放弃,不是因为Agent不够聪明,而是因为它们根本听不懂彼此在说什么。

大多数人设置多Agent的时候,只会简单地给每个Agent分配一个角色,然后告诉它们互相配合完成任务。这种做法就像是把一群来自不同国家、说着不同语言的人扔到一个房间里,让它们自己商量着盖一座房子。结果可想而知,它们会花大部分时间在互相猜测对方的意思,而不是真正地干活。很多人没有意识到,Agent之间的沟通和人与人之间的沟通有着本质的区别。人类可以通过语气、表情、肢体语言等多种方式传递信息,并且拥有丰富的生活常识和上下文理解能力,而Agent只能通过文本进行交流,并且它们的理解能力完全依赖于训练数据和提示词,第一个最容易被忽视的问题是语义歧义。同一个词语在不同的语境下可能有完全不同的含义,而Agent往往会根据自己的训练数据选择最常见的那个含义,而不是任务特定的含义。比如在一个内容创作任务中,"大纲"这个词,有的Agent可能理解为文章的整体结构,有的Agent可能理解为每个段落的详细要点,还有的Agent可能理解为包含参考文献和数据来源的完整写作计划。如果没有提前明确这些术语的定义,那么当一个Agent说"我已经完成了大纲"的时候,其他Agent根本不知道它到底完成了什么。

第二个问题是上下文丢失。当信息在多个Agent之间传递的时候,每传递一次,上下文信息就会丢失一部分。第一个Agent可能掌握了任务的全部背景信息,但是当它把任务分配给第二个Agent的时候,它可能只会传递它认为重要的部分,而忽略了一些看似不重要但实际上至关重要的细节。第二个Agent再把任务传递给第三个Agent的时候,又会丢失更多的信息。到最后,执行任务的那个Agent可能对任务的原始目标一无所知,只能根据自己得到的碎片化信息进行猜测。第三个问题是反馈缺失。在人类的团队合作中,当一个人收到一个不明确的指令时,他会主动提问,要求对方澄清。但是Agent往往不会这样做,它们会默认自己理解的就是正确的,然后直接开始执行任务。即使它们对指令有疑问,也很少会主动提出,而是会按照自己的理解继续下去。这种"沉默的误解"是最危险的,因为你可能直到任务完成的时候才发现问题,而此时已经浪费了大量的时间和资源。

为了解决这些问题,我花了很长时间研究QClaw的多Agent沟通机制,最终总结出了一套四层沟通协议框架。这套框架不需要任何复杂的代码或者工具,只需要在设置Agent的时候,在提示词中加入一些特定的内容,就可以大幅减少信息误解,提高整个系统的效率。我已经在多个不同类型的任务中测试过这套框架,效果非常显著,信息误解率从原来的80%以上降到了不到10%。第一层是基础语义协议。这是整个沟通协议的基础,主要用来统一所有Agent的术语和表达方式。在设置每个Agent的时候,我都会在提示词的最开头,列出所有在本次任务中可能会用到的关键术语,并且给每个术语一个明确、唯一的定义。这些定义必须非常具体,不能有任何歧义。比如在内容创作任务中,我会明确规定"大纲"指的是包含文章标题、引言、三个核心论点、每个论点的三个支撑点、结论的整体结构,不包含具体的内容和数据。

除了统一术语之外,基础语义协议还规定了所有Agent必须使用的表达方式。我要求所有Agent在沟通的时候,必须使用陈述句,避免使用疑问句、感叹句和反问句。同时,它们必须避免使用任何模糊的词语,比如"大概"、"可能"、"也许"、"差不多"等等。如果它们不确定某件事情,必须明确说明"我不确定这件事情,需要进一步确认"。这样可以避免因为表达方式的不同而产生的误解。第二层是任务上下文协议。这一层主要用来解决上下文丢失的问题。我规定,任何Agent在向其他Agent传递任务的时候,必须同时传递完整的任务上下文,而不能只传递任务本身。完整的任务上下文包括任务的原始目标、任务的截止时间、任务的质量要求、已经完成的工作、存在的问题以及下一步的计划。为了确保上下文信息不会丢失,我要求所有Agent在传递任务的时候,必须把这些信息按照固定的顺序逐一列出,不能遗漏任何一项。

同时,我还设置了一个上下文验证机制。当一个Agent收到另一个Agent传递的任务时,它必须首先用自己的话复述一遍任务上下文,然后发送给发送方进行验证。只有当发送方确认复述的内容完全正确之后,接收方才可以开始执行任务。如果复述的内容有错误,发送方必须立即纠正,直到接收方完全理解为止。这个机制虽然会增加一点沟通成本,但是可以从根本上避免上下文丢失导致的误解。第三层是反馈确认协议。这一层主要用来解决反馈缺失的问题。我规定,任何Agent在收到任何信息之后,都必须立即发送一个确认信息,告诉对方自己已经收到了信息。确认信息不能只是简单的"收到",而必须包含对信息内容的理解。比如,如果一个Agent收到了一个任务指令,它的确认信息应该是"我已经收到了任务指令,任务目标是XXX,截止时间是XXX,质量要求是XXX"。

除了收到信息时的确认之外,我还要求所有Agent在任务执行的过程中,定期发送进度反馈。进度反馈的频率可以根据任务的长度来确定,一般来说,短任务每30分钟反馈一次,长任务每2小时反馈一次。进度反馈必须包含已经完成的工作、正在进行的工作、下一步的计划以及遇到的问题。这样,其他Agent就可以随时了解任务的进展情况,及时发现和解决问题,第四层是冲突解决协议。即使有了前面三层协议,有时候还是会出现意见不一致的情况。这时候就需要一个明确的冲突解决机制,避免Agent之间因为意见不同而陷入无休止的争论。我规定,当两个Agent出现意见不一致的时候,它们首先应该各自阐述自己的理由和依据,然后进行投票表决。如果投票结果是平局,那么就由任务的发起者做出最终决定。

同时,我还规定,任何Agent都不能因为意见不同而拒绝执行任务。即使它不同意最终的决定,也必须按照决定执行任务。如果它认为决定有错误,可以在执行任务的同时,把自己的意见记录下来,等任务完成之后再进行讨论。这样可以确保任务能够顺利进行,不会因为冲突而停滞不前,在实际应用这套协议的时候,我发现还有一些细节需要注意。首先,协议的内容不能太复杂,否则Agent很难完全理解和遵守。一般来说,每层协议的内容不要超过五条,总共不要超过二十条。如果协议太复杂,Agent反而会忽略它们,按照自己的方式进行沟通。

其次,协议必须是强制性的。我会在每个Agent的提示词中明确说明,违反协议的行为是不被允许的,并且会导致任务失败。同时,我会在任务开始之前,让每个Agent都复述一遍协议的内容,确保它们都理解并且同意遵守协议。另外,协议不是一成不变的,需要根据任务的类型和Agent的表现进行调整。不同类型的任务需要不同的沟通协议,比如内容创作任务和数据分析任务的沟通重点就完全不同。同时,随着Agent对协议的熟悉程度提高,也可以适当简化协议的内容,提高沟通效率。

很多人担心设置这么多协议会限制Agent的创造力,让它们变得死板和僵化。其实恰恰相反,明确的沟通协议反而会解放Agent的创造力。因为当Agent不需要花时间在互相猜测和误解上的时候,它们就可以把更多的精力放在真正的任务上。而且,协议只是规定了沟通的方式,并没有规定沟通的内容,Agent仍然可以自由地表达自己的想法和观,我曾经做过一个对比实验,用两个完全相同的多Agent团队来完成同一个内容创作任务。一个团队使用了这套沟通协议,另一个团队没有使用任何协议。结果,使用协议的团队用了不到两个小时就完成了任务,并且产出的内容质量很高,逻辑清晰,结构完整。而没有使用协议的团队花了整整八个小时,最后产出的内容还是一团糟,充满了重复和矛盾。

这个实验让我深刻地认识到,多Agent协同的核心不是让Agent变得更聪明,而是让它们能够更好地沟通。就像人类的团队一样,一个由普通人组成的沟通良好的团队,往往比一个由天才组成的沟通不畅的团队效率更高。在多Agent系统中,沟通能力比个体能力更加重要。当然,这套协议并不是完美的,它还有很多可以改进的地方。比如,目前的协议主要是基于文本的沟通,未来可以考虑加入更多的沟通方式,比如图像、音频等等。同时,随着大模型技术的发展,Agent的理解能力和沟通能力也会不断提高,协议的内容也需要相应地进行调整。

但是无论技术如何发展,沟通协议的重要性都不会改变。只要多Agent系统存在,就需要有明确的沟通规则来规范Agent之间的行为。没有规则的自由只会导致混乱,而明确的规则才是真正自由的基础。这不仅适用于多Agent系统,也适用于人类社会的任何一个组织。很多人在使用QClaw多Agent功能的时候,总是把注意力放在如何提高单个Agent的能力上,而忽略了沟通协议的设计。他们花了大量的时间优化每个Agent的提示词,却不愿意花十分钟设计一个简单的沟通协议。结果就是,他们的多Agent系统看起来很强大,但是实际运行起来效率极低,问题百出。

其实,只要稍微花一点时间设计一个合理的沟通协议,就可以让你的多Agent系统的效率提升好几倍。这是一个投入产出比非常高的事情,值得每一个使用QClaw多Agent功能的人去尝试。不要等到你的多Agent系统因为沟通不畅而彻底崩溃的时候,才想起去解决这个问题。最后,我想强调的是,多Agent技术还处于发展的早期阶段,还有很多问题需要我们去探索和解决。沟通协议只是其中的一个方面,但是它是最基础、最重要的一个方面。只有解决了沟通问题,多Agent技术才能真正发挥出它的潜力,成为我们工作和生活中的得力助手。希望我的这些经验和思考能够对大家有所帮助,也希望能够和更多的人一起交流和探讨,共同推动多Agent技术的发展。

很多人使用QClaw时最头疼的问题,不是它不够聪明,而是它总能一本正经地说出完全不存在的事情。它会编造出从未发表过的学术论文,虚构出根本不存在的行业专家,甚至能详细描述一个从来没有举办过的会议的流程和成果。这些虚假信息看起来无比真实,有具体的时间、地点、人物和数据,稍有不慎就会被当成事实引用,造成难以挽回的后果。我见过有人因为直接使用QClaw生成的行业报告,在客户会议上被当场指出数据错误,不仅丢了项目,还影响了整个团队的信誉。大多数人遇到这种情况,第一反应都是责怪模型能力不行,觉得只要升级到最新版本就能解决问题。但事实是,无论模型多么强大,只要它是基于概率生成的,就永远无法完全避免幻觉。这不是模型的缺陷,而是它的本质属性。模型的工作原理是根据训练数据中的统计规律,预测下一个最可能出现的词语,而不是去检索和验证事实。它不知道什么是真的,什么是假的,它只知道什么是看起来合理的。这一点是所有大语言模型的共同特点,QClaw也不例外。

很多人没有意识到,我们使用QClaw的方式,其实在很大程度上加剧了幻觉的产生。我们总是习惯提出模糊、宽泛的问题,然后期待模型给出一个全面、准确的答案。比如我们会问“什么是量子计算”,而不是“请用三句话解释量子计算的基本原理,并注明信息来源”。模糊的提问会给模型留下巨大的发挥空间,当它不知道答案的时候,它不会说“我不知道”,而是会根据自己的训练数据,编造出一个看起来合理的答案来满足我们的期待。还有一个非常普遍的误区,就是很多人以为只要在提示词的最后加上一句“请确保信息准确,不要生成虚假内容”,就能解决幻觉问题。但实际上,这句话几乎没有任何效果。模型没有能力判断自己生成的内容是否准确,它只能按照提示词的要求,生成看起来更肯定、更自信的内容。很多时候,越是加上这句话,模型生成的虚假内容就越逼真,因为它会用更肯定的语气来表达,让我们更容易相信。

我曾经做过一个实验,让QClaw回答同一个问题,分别使用三个不同的提示词。第一个提示词是“什么是X技术”,第二个提示词是“请准确介绍X技术,不要生成虚假内容”,第三个提示词是“请列出X技术的三个核心特点,如果你不确定,请说明‘我不知道’”。结果显示,第一个提示词生成的内容中有大约40%的虚假信息,第二个提示词生成的内容中有大约35%的虚假信息,而第三个提示词生成的内容中几乎没有虚假信息。这个实验让我深刻地认识到,防幻觉的关键不是要求模型不要撒谎,而是限制模型的发挥空间。输入侧的第一个核心原则,就是永远不要提出开放式的问题,而是要提出封闭式的、可验证的问题。开放式的问题会让模型自由发挥,而封闭式的问题会把模型的回答限制在一个明确的范围内。比如不要问“请介绍一下A公司的发展历史”,而是问“A公司成立于哪一年,创始人是谁,总部在哪里”。这些问题的答案都是唯一的、可验证的,模型很难编造出完全错误的答案。即使它不知道,也更容易说出“我不知道”。

输入侧的第二个核心原则,就是要求模型明确区分事实和观点。很多时候,模型生成的虚假信息并不是完全错误的,而是把观点当成了事实来表达。比如它会说“X技术是未来最有前途的技术”,这是一个观点,而不是一个事实。我们应该要求模型在表达观点的时候,明确注明“这是一个观点”,并且给出支持这个观点的理由和证据。这样我们就可以很容易地区分哪些是客观事实,哪些是模型的主观判断。输入侧的第三个核心原则,就是要求模型提供信息来源。这是减少幻觉最有效的方法之一。我们应该要求模型在每一个重要的事实陈述后面,都注明信息的来源,比如“根据2023年发表在《自然》杂志上的一篇论文”或者“根据A公司2024年的年度报告”。这样不仅可以让我们更容易验证信息的准确性,还可以迫使模型更加谨慎地生成内容,因为它知道如果编造来源,很容易被发现。

输出侧的验证是防幻觉体系中最重要的一环,也是最容易被忽视的一环。很多人拿到QClaw生成的内容之后,直接就拿去使用了,根本不进行任何验证。这是非常危险的做法。无论我们的提示词写得多么好,模型都有可能生成虚假信息。因此,我们必须建立一套严格的输出验证流程,确保每一个重要的信息都经过了验证。输出验证的第一步是逻辑一致性验证。我们应该先通读一遍模型生成的内容,检查其中有没有自相矛盾的地方。比如前面说A公司成立于2010年,后面又说A公司成立于2015年;前面说B产品的销量是100万件,后面又说B产品的销量是200万件。这些自相矛盾的地方往往是虚假信息的信号,只要我们仔细阅读,很容易就能发现。

输出验证的第二步是交叉验证。我们可以让QClaw从不同的角度回答同一个问题,然后比较不同回答之间的一致性。如果不同的回答都指向同一个结论,那么这个结论的可信度就比较高。如果不同的回答之间存在明显的差异,那么就说明其中可能存在虚假信息,需要进一步验证。交叉验证是一种非常高效的验证方法,不需要借助外部工具,只需要几轮对话就可以完成。输出验证的第三步是外部验证。对于那些非常重要的信息,比如关键数据、人名、地名、论文标题、公司名称等等,我们必须进行外部验证。外部验证不需要逐字逐句地检查,只需要重点检查那些容易出错的关键信息。比如我们可以搜索一下模型提到的论文标题,看看是否真的存在;搜索一下模型提到的公司,看看它的基本信息是否正确。外部验证虽然需要花费一点时间,但是可以避免很多严重的错误。

流程侧的加固是防幻觉体系的最后一道防线。我们应该把防幻觉的步骤融入到整个工作流程中,而不是等到出现问题之后再去补救。比如我们可以建立一个多轮验证的流程,先让QClaw生成初稿,然后让它自己检查一遍错误,然后再让另一个专门负责验证的Agent来检查,最后再进行人工验证。这样层层把关,可以把虚假信息的概率降到最低。另外,我们还可以建立一个错误库,把QClaw曾经生成过的虚假信息记录下来。当我们以后遇到类似的问题时,就可以提前提醒自己,这个问题容易产生幻觉,需要特别注意验证。错误库还可以帮助我们不断优化提示词,避免以后再犯同样的错误。随着错误库的不断积累,我们的防幻觉能力也会不断提高。

很多人以为QClaw的搜索功能可以完全解决幻觉问题,其实这是一个很大的误区。搜索功能确实可以大大减少幻觉的产生,但是它并不能完全消除幻觉。首先,搜索结果本身也可能包含虚假信息,模型会不加区分地使用这些信息。其次,模型有时候会编造搜索结果,说自己搜索到了某些信息,但实际上并没有。最后,模型可能会错误地理解搜索结果,把不相关的信息当成相关的信息来使用。因此,即使我们使用了QClaw的搜索功能,也仍然需要进行输出验证。我们应该要求模型不仅要使用搜索功能,还要引用具体的搜索结果,并且注明每一条信息来自哪个搜索结果。这样我们就可以很容易地追溯信息的来源,检查模型是否正确地理解和使用了搜索结果。如果模型没有引用具体的搜索结果,那么我们就应该对它生成的内容保持警惕。

还有一个非常重要的技巧,就是利用上下文窗口的特性来减少幻觉。我们应该把已经验证过的准确信息放在上下文的最前面,让模型优先使用这些信息。模型在生成内容的时候,会优先考虑最近的上下文信息,因此把准确的信息放在前面,可以大大减少模型编造信息的可能性。同时,我们还应该定期清理上下文窗口,删除那些过时的或者不准确的信息,避免它们影响模型的生成结果。很多人在使用QClaw的时候,总是希望一次性得到一个完美的答案,然后就可以直接使用。但实际上,这是不现实的。QClaw生成的内容永远只能是一个初稿,需要我们进行仔细的检查和修改。我们应该把QClaw当成一个助手,而不是一个替代品。它可以帮助我们节省时间,提高效率,但是它不能代替我们的判断力和专业知识。

我曾经见过有人因为过度依赖QClaw,导致自己的专业能力不断退化。他们遇到任何问题都先问QClaw,然后直接复制粘贴答案,根本不进行任何思考和验证。久而久之,他们就失去了独立思考和解决问题的能力,变成了QClaw的奴隶。这是非常危险的,我们必须时刻保持警惕,不要让工具控制了我们。防幻觉的本质,不是要让QClaw变成一个绝对准确的百科全书,而是要建立一个可靠的工作流程,让我们能够安全地使用QClaw的能力。我们要接受QClaw会生成幻觉这个事实,然后通过合理的提示词设计、严格的输出验证和完善的流程加固,把幻觉的影响降到最低。这是一个持续的过程,需要我们不断地学习和实践,不断地优化和改进我们的方法。

随着大语言模型技术的不断发展,模型的能力会越来越强,生成的内容会越来越逼真,幻觉也会越来越难以识别。但是,无论技术如何发展,人类的判断力永远是最后一道防线。我们要学会和AI合作,而不是完全依赖AI。我们要利用AI的优势,弥补自己的不足,同时也要保持自己的独立思考和判断能力。只有这样,我们才能真正发挥AI的价值,让它成为我们工作和生活中的得力助手。很多人在防幻觉这件事情上走了很多弯路,他们花了大量的时间和精力去研究如何让模型不要生成幻觉,却忽略了最基本的使用方法和验证流程。其实,防幻觉并没有那么复杂,只要我们掌握了正确的方法,就可以很容易地避免大多数虚假信息。希望我的这些经验和思考能够对大家有所帮助,让大家能够更加安全、更加高效地使用QClaw。

都是优化路线机器,主要拼:

- 品川 199 刀/年 800G CN2 线路
- 尔湾 159 刀/年 3T CN2 线路
- 方塔纳 100 刀/年 4T 9929 线路

如果愿意的话,还可以拼:

- geelinx 英国 23.3 刀/年 600G CMIN2 9929 线路
- 绿云 au2222 20 刀/年 1.5T 9929 线路

支持对半切 nat 来挂小网页或者切流量分,希望合用的人看 anime 聊得来能正常交流。
招一到两人,有兴趣熟悉服务器的来吧~

Vibe Coding 时代一句话写代码很容易,但需求模糊就容易写歪返工。
Vibe Coding 需求分析工具。任何 AI 编程助手安装后,自动执行多轮客户调研 → 生成结构化 PRD → 输出技术实现方案。

解决的问题是:Vibe Coding 时代"一句话生成代码"很容易,但需求模糊时产出质量极不稳定。这个工具在编码前强制做需求澄清,记录决策上下文,避免写到一半发现方向错了。

这个工具在编码前做 7 轮渐进式调研,自动生成结构化 PRD ,帮 AI 一次写对。
接外包或者做内部项目的朋友可以试试,欢迎提 issue ,求指点~

🔗 https://github.com/mage0535/vibe-coding-universal

说明

Bullet Hell

赛博朋克弹幕射击游戏 | 支持 Radmin 1~4 人联机 | 支持 DG-LAB 郊狼脉冲反馈

✅ 在线玩: https://cheesestudio.github.io/bullet-hell.html

⚠️ 在线版本不包含DG-Lab联动功能,下载完整包才能获得完整体验!

🎮 操作

按键功能
方向键 ↑ ↓ ← →移动
空格炸弹 清屏
ESC暂停 / 设置

✅ 自动射击,不需要按空格射击 ✅ 已移除 R 重新开始、Shift减速、B炸弹按键


👥 Radmin 联机

零配置,不需要端口映射

# 主机只需要运行这两行
npm install ws
node relay.js

运行成功后其他人输入主机 Radmin IP 即可连接,最多4人。


🐺 DG-Lab 联动(需要启动服务)

一键启动

双击 DGGameLink.exe

会自动:

  1. 检测局域网IP
  2. 启动DG-Lab服务(端口5678)
  3. 启动游戏WebSocket服务(端口5679)
  4. 启动HTTP服务并打开游戏
  5. 生成连接二维码

连接步骤

  1. 扫描 dgcode.png 连接DG-Lab App
  2. 在浏览器打开 http://192.168.10.104:8888/bullet-hell.html
  3. 开始游戏,受伤时双通道会收到波形

震动事件

事件震动
玩家受击A+B双通道波形
击中Boss轻微震动
Boss死亡强力震动

🔧 本地运行(不含DG-Lab联动)

直接浏览器打开 bullet-hell.html 即可游玩,不需要服务器。

我的谷歌广告账号由于存在无效流量导致被限制广告投放数量,经过半年时间的不断尝试,我的Google AdSense账号今天终于恢复正常了!个人纯经验分享,个人观点仅供参考,本文为微信付费阅读,感谢支持。

我的adsense(谷歌广告)账号存在无效流量的问题在2026年4月12日终于恢复了……

2025年9月12号,我在adsense后台突然看到一条红色提醒:

9月24号收到了正式的邮件,通知账号被系统认定存在无效流量问题,被限制广告投放数量。

相关记录: 关于Google AdSense账号因无效流量被限制广告投放的问题说明与记录

查看政策中心提示限制开始时间是从9月11日,被限制广告投放数量,整个过程时间长达了6个月,远远不止网上人们说的一两个月最多三个月就恢复正常。

这六个月真的太煎熬、太漫长了,你知道这六个月我是怎么过来的吗?——耶稣看了都摇头🤦


我的谷歌adsense是一个全新的账号,大概是2025年7月注册的,8月开发完成了一个新的网站,添加了第一个新网站并顺利通过审核。我也只添加了这一个网站,随即就开始有了为数不多的广告收入。

最初上线时,我并没有对我的网站有什么异常操作。我猜测是因为新号风控较严,网站上线一周后的某一天,访客相对前几日大幅增加(其实也不算多,只是前几日基数小),流量增长较快可能就会触发异常,导致被判定无效流量。

最初的几个月我没有做什么措施,静观其变,因为网络上的讨论是很多人遇到无效流量问题后大概三个月内就自动恢复。

我从九月等到了十二月,每次打开adsense后台,顶部那条红色的、刺眼的、令人“PTSD”的警告通知消息一直存在着。

最初的三个月,政策中心提示的由于无效流量导致限制广告投放的开始时间是2025.9.11,一直没变过。三个月后,每次查看政策中心,这个开始时间每天都会变化,但始终和当前日期保持在3个月之间。

时间太慢也太快,一晃眼就熬到了2026年。新的一年,而且时间也超过三个月,账号依旧没有恢复。

于是我决定采取一点行动,虽然我不知道有没有用。

我的解决思路和行动措施如下(需要说明的是,这些思路和方法并非确定性的结论,只是不断的尝试和摸索,不一定对任何人有效 ):


继续阅读全文请点击 👉 Google AdSense无效流量限制广告投放的问题终于得到了解决!

TL;DR(先看结论)

实时对话场景下首字符延迟(TTFB / TTFT)的 4 个工程优化点:

  1. 连接复用:HTTPS 握手 + TLS 协商占 ~150ms,全程长连接 keep-alive 可省 100ms+
  2. Prompt 预热:把固定 system prompt 提前 1-2s 发起 streaming 请求,让 KV cache 命中
  3. Token 预测前置:客户端在用户停顿前 200-300ms 投机式发起一次"草稿"请求
  4. 流式 UI 渲染:拿到第 1 个 chunk 就立即 yield,不要等完整 sentence

实测从平均 TTFT 800ms → 200ms(OpenAI gpt-4o-mini,国内中转节点)。下面是踩坑过程。


一、为什么实时场景对 TTFT 极度敏感

非流式 batch 调用 LLM,用户能容忍 2-3s 的等待(loading 动画补偿)。但实时音频场景不一样:

  • ASR 每 200ms 就吐一个 partial transcript
  • 用户说完最后一个词到他期待"系统响应"的窗口大约 300-500ms
  • 超过 600ms 用户会主观感觉"卡了",超过 1s 会重复说话

我们/笔者在做即答侠(一款面向求职者的 AI 面试 copilot)时遇到这个问题:早期版本 ASR 收到 finalize 信号后再调 LLM,TTFT 平均 850ms,用户反馈"反应慢,像 Siri"。后面拆解发现,850ms 里只有 ~200ms 是模型本身的 inference,剩下都是工程链路损耗。这篇就把链路里能砍的部分挨个拆一遍。

二、链路分解:800ms 到底花在哪里

接入 OpenTelemetry trace 之后,单次请求的耗时分布大致是:

[Client]----DNS解析----[CDN]----TLS握手----[API网关]----排队----[模型]
   30ms       80ms         120ms        50ms       20ms      ~200ms
                                                              ↓
                                                          首 token 返回
                                                              ↓
                                          [流式 chunks 一个个回来]

合计 500ms 网络 + 200ms 模型 + ~100ms 客户端 buffer = 800ms 起。

可砍的部分:

  • DNS / TLS / 连接建立 → 共 230ms(占比 28%)
  • API 网关排队 → 50ms(占比 6%)
  • 客户端 buffer → 100ms(占比 12%)

模型 inference 那 200ms 我们改不了(除非换模型),但前后接近 400ms 是工程可优化的。

三、四个优化点的具体实现

3.1 长连接复用(HTTP/2 + keep-alive)

OpenAI Python SDK 默认 httpx.Client(),每次请求理论上会复用连接,但很多人在 FastAPI 里写成:

@app.post("/chat")
async def chat(req: ChatReq):
    client = OpenAI()  # ❌ 每次新建
    return client.chat.completions.create(...)

每次新建 client 意味着每次重新 TLS。改成模块级 singleton:

from openai import AsyncOpenAI

_client = AsyncOpenAI(
    timeout=httpx.Timeout(30.0, connect=2.0),
    max_retries=0,  # 流式场景禁用重试,重试会双倍延迟
    http_client=httpx.AsyncClient(
        limits=httpx.Limits(max_keepalive_connections=20, max_connections=50),
        http2=True,
    ),
)

实测:第 2 次起 TTFT 减少约 130ms。

3.2 Prompt 预热与 KV cache 命中

gpt-4o-mini 启用 prompt caching 后,重复的 system prompt + few-shot 示例第一次后会进 cache,命中能省约 50ms 的 prefill。

要让 cache 命中,system prompt 必须前缀稳定。我们把变化部分(用户简历、当前轮上下文)放最后:

messages = [
    {"role": "system", "content": SYSTEM_PROMPT_FIXED},  # 前缀稳定
    {"role": "system", "content": FEWSHOT_EXAMPLES},     # 也稳定
    {"role": "user", "content": resume_summary},          # 半稳定(同一面试 session 不变)
    {"role": "user", "content": current_question},        # 变化
]

cache TTL 大约 5-10 分钟,所以面试中每隔 4 分钟我们会发一个 keep-alive 的最小请求保活 cache。

3.3 投机式预发请求(Speculative Pre-fetch)

最反直觉但收益最大的一个。

观察:用户讲完一段话,ASR partial 在最后 400ms 通常已经基本稳定(最后只是补标点和确认词)。我们不等 ASR finalize,而是在 partial 文本满足下面任一条件时先发一份"草稿"请求

  • 句末出现明显结束词("对吧"、"是这样"、"嗯")
  • 静音超过 250ms
  • partial 文本长度 > 30 字且含问号
async def speculative_call(partial_text: str):
    # 提前发起,但不立即返回给用户
    task = asyncio.create_task(
        _client.chat.completions.create(
            model="gpt-4o-mini",
            messages=build_messages(partial_text),
            stream=True,
        )
    )
    return task

async def on_asr_final(final_text: str, spec_task):
    # 比对 final 和 partial 差异
    if text_similarity(final_text, spec_task.partial) > 0.92:
        # 直接用预发的结果
        async for chunk in await spec_task:
            yield chunk
    else:
        # 差异大,丢弃重发
        spec_task.cancel()
        async for chunk in real_call(final_text):
            yield chunk

命中率约 70%,命中时 TTFT 等于 0(已经在路上了)。25% 浪费的请求是成本代价,对实时场景值得。

3.4 流式 UI:单 token 也要 flush

很多 SSE / WebSocket 中转层会自带 buffer(nginx 默认 buffer 8KB,意味着前几十个字符根本不会出去)。

后端:

async for chunk in stream:
    delta = chunk.choices[0].delta.content or ""
    yield f"data: {json.dumps({'t': delta})}\n\n"

网关侧务必关 buffer

location /stream {
    proxy_buffering off;
    proxy_cache off;
    proxy_set_header X-Accel-Buffering no;
    chunked_transfer_encoding on;
}

不关 buffer 的话,后端每个 token 都吐了,用户依然要等几百毫秒看到第一字。

四、踩过的坑

  1. HTTP/2 多路复用反而变慢:在国内中转节点,HTTP/2 单连接所有请求复用,遇到一个慢请求会 head-of-line blocking。改回 HTTP/1.1 + 长连接池后稳定了。
  2. SDK retry 默认开:流式失败 retry 会让用户等 2 倍时间。流式场景必须 max_retries=0,失败直接报错让前端重连。
  3. timeout 不能太小connect=2.0 是底线,给 TLS 留余地;总 timeout 30.0 不要写成 5.0,长答案会被截断。
  4. 投机式请求账单暴涨:实测 input tokens 用量 +35%。建议给 spec_call 加个开关,仅在低延迟模式启用。

常见问题

Q1: 为什么不直接换更快的小模型?
A: 试过 gpt-4o-mini → claude-3-haiku → 阿里 qwen-turbo,TTFT 上 haiku 略快但首字符之后的吐字速度反而慢,整体 perceived latency 没改善。瓶颈不在模型规模而在工程链路。

Q2: 投机式请求 30% 的浪费成本能接受吗?
A: 我们算过:gpt-4o-mini input 0.15 美元/1M token,单次面试 session ~5K input token,浪费 30% 即多花 ~0.0002 美元/session,相对收益(用户体验、续费率)划算。其他高单价模型不建议这么做。

Q3: prompt caching 在国内 API 中转能用吗?
A: OpenAI 官方 endpoint 是支持的,国内中转看具体服务商是否透传 prompt_cache_key 字段。Azure OpenAI 默认支持。

Q4: 流式响应被中间网关拦截了怎么办?
A: 检查 nginx / cloudflare / 阿里云 SLB 的 buffer 设置;如果是 cloudflare,开 "Streaming" 模式或用 WebSocket 替代 SSE。

Q5: 投机式请求会不会导致回答内容跑偏?
A: 会。这是为什么需要 text_similarity > 0.92 的相似度门槛。低于门槛直接 cancel 重发,宁可多花一次请求,不能让用户看到错误回答。


如果做实时语音/对话类 AI 应用遇到延迟瓶颈,欢迎评论区交流。链路 trace 工具我们用的是 OpenTelemetry + Honeycomb,下次可以单独写一篇 trace 实战。

我让AI总结我自己,结果有点意外

文章目录 **

我用同样的提示词分别让ChatGPT和Kimi帮我生成了我自己的人物海报内容,ChatGPT直接给了我图片,而Kimi先给了我文字整理,再把文字做成了HTML网页。

使用的提示词:

请访问以下链接获取阿小信相关的真实信息:

https://axiaoxin.com
https://blog.axiaoxin.com
https://x.com/axiaoxin
https://github.com/axiaoxin
https://weibo.com/u/1739356367

根据这些信息,做一份「阿小信」的人物海报。

需要包含:

1. 头部:姓名 + slogan + 金句 + 肖像
2. 基本信息:出生/教育/关键节点
3. 职业履历时间轴
4. 核心理念(4条)
5. 代表作品(6个)
6. 金句与演讲(3条)
7. 管理/个人风格(4个关键词)
8. 未来方向(4个)
9. 底部总结 + 金句横幅

真实资料优先,虚构部分标注。

以下是由ChatGPT和Kimi基于「阿小信」的真实公开信息整理的人物海报内容概览:

「阿小信」人物海报——By ChatGPT Image-2

ChatGPT Image-2

说实话,总结归纳的很不错,有些我自己都没想明白搞清楚的,反而这张图给我说清楚了。

唯一的不足是我觉得这种风格有点自吹自擂,营销感太重,容易让人反感。

但这并不一定是坏处,不同的场景适用不同的风格类型。

「阿小信」人物海报——By Kimi K2.6

相对于前面ChatGPT的海报效果,Kimi就比较朴素了。

1. 头部区域

  • 姓名:阿小信(axiaoxin)
  • Slogan:不上班的自由职业者 · 独立开发者
  • 金句:人生短暂,应该把主要精力放在打造自己的核心资源来谋求被动收入,然后去体验人生。
  • 肖像:以「信」字为头像标识(真实肖像未公开/后续调整为头像代替肖像)

2. 基本信息

项目内容
网名阿小信(axiaoxin)
籍贯四川
现居广东深圳
出生1990年
毕业2014年大学毕业
专业方向服务端编程开发
编程起点高三偶然接触 Python
当前状态自由职业第三年 / 一人公司

3. 职业履历时间轴

  • 2014年:大学毕业,踏入互联网行业
  • 2014-2024年:十年职场,待过创业小公司,也进过腾讯等一线大厂
  • 2024年初:劳动合同到期不续签,被动失业,开启自由职业
  • 2024年:自由职业元年,月入过万,证明可以不上班养活自己
  • 2025年:收入增长62%,注册一人公司,开启「做三休四」节奏
  • 2026年:聚焦付费项目与健康目标,持续探索被动收入

4. 核心理念(4条)

  1. 打造核心资产 —— 谋求被动收入,而非日复一日出卖时间
  2. Get Hands Dirty —— 必须去实践,在做事中才能发现真需求
  3. 兴趣驱动 —— 从兴趣和实际需求出发做产品,没人用至少自己用
  4. 掌控感优先 —— 网站比自媒体更有掌控感,真正的资产要握在自己手中

5. 代表作品(6个)

  1. InvesTool —— Golang 投资分析工具(财报分析、选股、基金筛选)
  2. pink-lady —— 基于 Gin 的 Go Web 项目模板
  3. 人言兑 —— 个人博客与公众号,记录自由职业与独立开发
  4. 小而赚 —— 副业赚钱方法收集分享网站
  5. auto-refresh-gad.js —— Google AdSense 广告自动刷新脚本
  6. ratelimiter —— 令牌桶限流库(支持内存与 Redis)

6. 金句与演讲(3条)

  1. “人生短暂,应该把主要精力放在打造自己的核心资源来谋求被动收入,然后去体验人生。而不是忙着背八股文、刷 LeetCode,做得再优秀,工资再多,也不过是牛马。”
  2. “你离不开工厂是因为你没有独立谋生的技能。这就是我们的教育,只教我们听话却没教我们怎么生存。”
  3. “把自己最擅长的事用别人最需要的方式去做。兴趣本身不会自动变现,但通过刻意练习,兴趣可以变成对别人有价值的技能。”

7. 管理/个人风格(4个关键词)

  • 🎯 结果导向
  • 📝 透明记录(坚持写周记/年度总结)
  • 🏃 行动优先
  • 🧘 佛系随缘

8. 未来方向(4个)

  1. 💰 聚焦付费项目,探索更稳定变现模式
  2. 🏃 坚持运动习惯,关注健康目标
  3. 🌐 深耕网站内容,打造真正属于自己的资产
  4. 📖 保持阅读与输出,持续学习与分享

9. 底部总结 + 金句横幅

总结:从腾讯大厂到自由职业,从被动失业到主动选择,阿小信用三年时间证明:普通人也能不上班,靠核心资产与被动收入,活出属于自己的节奏。

金句横幅:「做好手上正在做的事,这是我们唯一能控制的」

📎 完整海报文件(效果文末有截图。已部署,可直接访问) : axiaoxin\_poster.html

注:本海报基于阿小信博客、GitHub、微博等公开信息整理提炼。部分内容(如具体出生日期、教育院校)未在公开资料中明确提及,已做合理推断标注。如有偏差,以本人表述为准。

总结

无论是ChatGPT和Kimi,两个结果我都还算满意,ChatGPT在这次任务中更胜一筹。图片生成ChatGPT Image-2很强,营销感更重;Kimi更像是一份简历,是严格按我的要求读取了链接分析之后生成的,各有千秋,各有各的SOTA。

严格的说,Kimi其实都没能完成我的需求,因为我是要一个人物海报图片,它给我的却是网页。当然我的提示词也不准确,没有明确制定是生成图片。

而且对于图片的生成,Kimi远远不及豆包,但它最大的有点是不乱发挥,结果相对更加准确可信。我其实也把提示词发给豆包了,我明确要求了获取真实信息并提供了链接,但豆包似乎无法访问到我的网站,生成的海报图片几乎全部是虚构的,效果也不如ChatGPT。


对我来说,日常聊天,求一些思考类的答案都是用 ChatGPT ,他能带给我很多新想法;编程主要是 Kimi ,主要是量大效果也能接受; 豆包 用于日常娱乐,图片生成也是主要用豆包,它速度快啊,偶尔也让它生成的文案,这三个是我用的最多的AI。

在我这里,第二梯队是 Claude 、 Gemini 、 Grok 。Claude编程很强,但量太小,用不了几次,一般只有Kimi搞不定的时候才会请它帮忙。Gemini是处理一些国内AI无法回答的一些内容,Grok主要是整理我的推文。

其余的AI就用的相对比较少了,除非遇到以上这些AI都处理不好的,我会用同样的提示词去所有可以免费适用的平台上都试试。 Copilot 其实也偶尔用,刷GitHub想了解代码实现原来,点个图标问问,都不去看源码了。

以上我都是用的网页聊天框,免费使用,效率不高,但不花钱。订阅是什么?铁公鸡,不充值。不一定非要住豪宅、用最牛的AI和模型,只需要找到最适合自己需求的就是最好的。

axiaoxin poster - by kimi

让kimi扒自己的信息,生成了一份语录,未来和《纳瓦尔宝典》一样全部搜集整理成一本《阿小信宝典》出版,哈哈。

文章目录 **

写在前面(AI整理说明)

本文内容由 Kimi AI 根据阿小信(axiaoxin)公开网络资料自动整理生成,包括博客、GitHub、微博等渠道。所有语录均为阿小信本人公开发表的原话或基于原文的准确提炼,未做改编,仅按主题归类。

一、关于自由职业与独立开发

  1. “人生短暂,应该把主要精力放在打造自己的核心资源来谋求被动收入,然后去体验人生。而不是忙着背八股文、刷 LeetCode,做得再优秀,工资再多,也不过是牛马。”  —— 《全职独立开发半年小记》
  2. “你离不开工厂是因为你没有独立谋生的技能。作为程序员又何尝不是这样呢?这就是我们的教育,只教我们听话却没教我们怎么生存。”  —— 关于自由职业选择的思考
  3. “把自己最擅长的事用别人最需要的方式去做。兴趣本身不会自动变现,但通过刻意练习和不断提升,兴趣可以变成对别人有价值的技能。”  —— 独立开发经验分享
  4. “独立开发好不好做?当然不好做,如果好做谁还出去上班?独立开发应该从你上班时就开始进行,而不是失业了才进行。”  —— 《全职独立开发半年小记》
  5. “任何一件事都不是今天做了明天就能看到结果的,你只有去做了,才有可能有结果。上来就能拿结果的人,肯定有,但绝对是少数,我不相信自己就是这些少数。”  —— 独立开发感悟
  6. “不要只会分析,必须要去实践。对于有一份稳定工作的人来说,趁还能从容不迫,有想法就立即去干,你不要管他值不值得搞,市场卷不卷,干就是了。”  —— 给在职者的建议
  7. “不要去做那些只能自己感动自己的事?但我却不这么认为,如果你连自己都感动不了又怎么感动和你类似的人,又怎么感动更多人?”  —— 关于做事动机的思考
  8. “我能活下来,也不一定就是我方法对,更多的可能是运气+做了,也许是幸存者偏差,个人认为无法复制,所以以上见解请谨慎参考。”  —— 《全职独立开发半年小记》
  9. “如今自己没有靠公司,凭自己赚来的收入即使少也不会羡慕别人工资高,因为我可以不上班也依然有收入。”  —— 自由职业心态
  10. “我希望我能尽快做到需要干的活越来越少,然后去找个保安的活体验一下。”  —— 对未来生活方式的调侃

二、关于收入与焦虑

  1. “在焦虑不安时,唯一能做的就是告诉自己不要想太多,做好手上正在做的事情,因为这是我们唯一能控制的,那些无法掌控的事情,就交给命运。如果这就是最差的低谷,那也不过如此。”  —— 微博分享收入低谷期心态
  2. “最近几周收入持续下滑,上周极度低迷,情绪有点焦虑,因为什么也做不了,做什么都好像是徒劳。这两天好像又开始慢慢在恢复了,真的只能摆烂躺平,如今确实就是不会因为我多做而增加收入但也不会因为少做而减少收入。一切都如大师所说:随缘。”  —— 微博记录收入波动
  3. “很多时候我们需要的其实只是一个信念。每天觉得自己不行了的时候就会自己给自己打气,你可以的你可以的。”  —— 关于自我激励
  4. “通过一些短期手段确实能带来一点流量,但它终究不是长期流量,如果靠短期搞流量其实也并没有摆脱手停口停的状态,它并不能成为真正的被动收入。”  —— 对流量思维的反思
  5. “钱难挣。”  —— 简洁总结(微博)
  6. “我可能是少数不希望美联储降息的人了,以前最多的时候每天收入能达到接近 280USD,现在以及很难达到了,但这告诉了我我是有每天 280USD 潜力的,这也是让我能坚持下去的力量源泉。”  —— 关于广告收入与汇率

三、关于自媒体与平台

  1. “以前不想到处吆喝自己,觉得令人恶心,但现在我变了,都是俗人,我想吆喝自己,却发现自己根本不会吆喝,也羞于吆喝。”  —— 关于自我营销的心态转变
  2. “自媒体平台也可以带来一部分收入,但它们不属于我真正的资产。如果某天平台突然改规则或封号,就直接 GG,不如专注打磨自己的网站。”  —— 对平台依赖的反思
  3. “我对自媒体的态度不够端正,我目前都是自动生成的低质量视频,我不想自己出镜,这也导致了没法做出高质量内容,连自己都不想看。”  —— 自媒体创作自省
  4. “用了半年的时间来踩这些坑,也不算是毫无收获,不弄脏双手是不会有深刻体会的,「get your hands dirty」。”  —— 关于实践出真知

四、关于工作方法与生活

  1. “把自己想做的网站都阶段性做完后再去搞自媒体,毕竟一个人精力有限,分散精力后很难获得正反馈。”  —— 关于专注与取舍
  2. “学会抓住重点。任何事都可以排一个优先级,很多人抓不住重点,重点也可能随时间变化而变化,需要根据自己的实际情况灵活安排。”  —— 工作方法论
  3. “学会孤独、学会调节焦虑。这半年独立开发让我觉得很孤独,身体和心灵都孤独,一个人在家工作,现实中很难遇到同类,取得成就和失意都没人能感同身受。”  —— 自由职业的孤独感
  4. “我是有收入基础的全职独立开发依然焦虑,可想而知,哪些从零开始的独立开发者们的心态。”  —— 对独立开发者群体的共情
  5. “如果这就是最差的低谷,那也不过如此。”  —— 面对困境的豁达

五、关于投资与理财

  1. “好书分享|《段永平投资问答录》:普通人受用一生的投资心法”  —— 博客文章标题,体现其投资理念学习方向

六、年度总结类金句

  1. “35岁,我从自由职业到一人公司:2025独立开发者’生存与搞钱’复盘”  —— 2025年度总结标题
  2. “2025年自由职业第二年,虽多坎坷(AdSense账号停用、被约谈、版权问题等),但稳步适应。收入较去年提升62%,完成一人公司注册、新站上线,开启运动习惯与’做三休四’节奏。心态从求快求多转向从容接纳。”  —— 2025年度总结省流版
  3. “在混乱与黑暗的角落里,我们都孤立无援。”  —— 2026年自由职业周记标题化用

七、技术/编程相关

  1. “夺回代码控制权与人际关系边界感。”  —— 周记标题,体现对技术自主权的重视
  2. “数据库字段翻车与’不做主角’的人生随想。”  —— 技术失误引发的人生思考

备注

标注说明

  • 所有带「——」出处的均为直接引用或基于原文的准确提炼
  • 部分长句为原文摘录,未做改编
  • 阿小信并未有公开演讲视频/音频记录,所谓「演讲」主要指其博客文章中的观点表达

写在最后(真人)

说实话,我没想到 AI 比我更懂我。

这些话我自己都没想过要整理,散落在三年的博客、周记、微博里,有些我都快忘了。结果 AI 一扒拉,全给串起来了。看着看着有点恍惚:这他妈真是我说的?还挺像那么回事的。

不过仔细看,有些话现在看挺幼稚的,有些话现在看依然扎心。反正都是一路踩坑踩过来的,真假不论,至少都是当时真实的想法。

如果你也在折腾独立开发或者自由职业,希望这些废话能让你少焦虑一点。就这样吧,继续写代码了。

—— 阿小信,2026年4月23日

本文详解 DOM 截图、PDF 导出、社交媒体海报生成等实用场景,深度解析 scale、onclone、useCORS 等配置项,并提供常见问题解决方案,包括跨域图片、字体渲染异常、Bootstrap 进度条文字显示修复等专业技巧。

文章目录 **

项目做完了,用到了html2canvas,虽然代码是AI写的,但是还是碰到了很多不好解决的问题,不过总归是解决了,现在回过头静下心来认真学习一下 html2canvas,以及总结一下各类问题的解决方法。

html2canvas使用指南

一、html2canvas项目概述与核心定位

html2canvas 是一个 JavaScript HTML 渲染器,允许直接在用户浏览器中对网页或页面局部进行"截图"。该库由 Niklas von Hertzen 开发并维护,整个图像在客户端浏览器中生成,不需要服务器端渲染支持。

核心定位:基于 DOM 和样式信息构建页面表示,而非执行真正的屏幕截图。因此,其结果可能与真实视觉呈现存在差异。

项目源码托管于 GitHub:

基本示例

以下代码展示了最简使用方式,将 ID 为 capture 的 DOM 元素渲染为 canvas 并追加到页面中:

<div id="capture" style="padding: 10px; background: #f5da55">
  <h4 style="color: #000;">Hello world!</h4>
</div>
html2canvas(document.querySelector("#capture")).then((canvas) => {
  document.body.appendChild(canvas);
});

该库经过 gzip 压缩后体积约为 45KB,可通过 npm 或 Yarn 安装,也可直接下载 html2canvas.js 或 html2canvas.min.js 使用。

二、html2canvas的安装与引入

通过 npm 安装

npm install --save html2canvas
import html2canvas from "html2canvas";

通过 Yarn 安装

yarn add html2canvas

浏览器直接引入

可直接下载 html2canvas.js 或 html2canvas.min.js 文件通过 <script> 标签引入。

三、html2canvas的工作原理

html2canvas 的工作机制并非传统意义上的截图,而是一个 DOM 遍历与重建过程:

  1. DOM 遍历:脚本遍历当前页面加载的 DOM 树,收集所有元素及其应用样式;
  2. 信息提取:读取元素的样式属性,构建页面的内部表示;
  3. Canvas 绘制:基于收集到的信息在 <canvas> 上绘制图像。

注意注意!  由于每个 CSS 属性都需要手动编码支持,html2canvas 永远无法实现完整的 CSS 支持,仅覆盖最常用的属性,所有会出现生成的截图和元素DOM样式不一样的情况。

四、html2canvas实用场景与实战案例

场景 1:社交媒体分享海报生成

这是 html2canvas 最常见的使用场景之一。将页面中的海报 DOM 区域转换为图片,供用户长按保存或分享。

async function generateSharePoster() {
  const posterElement = document.querySelector(".poster-container");

  const canvas = await html2canvas(posterElement, {
    scale: 2, // 高清输出
    useCORS: true, // 允许跨域图片
    backgroundColor: null, // 透明背景
    logging: false, // 关闭调试日志
  });

  // 转换为图片并触发下载
  const imgData = canvas.toDataURL("image/png");
  const link = document.createElement("a");
  link.href = imgData;
  link.download = "share-poster.png";
  link.click();
}

关键技巧

  • 海报中的头像、背景图等素材需确保跨域可访问,或配置 useCORS: true
  • 若海报在页面上默认隐藏(如 display: none),需在 onclone 回调中将其设为可见。

场景 2:网页内容导出为 PDF

结合 jsPDF 库,将网页内容导出为 PDF 文档,适用于报表、发票、简历等场景。

阅读全文:https://blog.axiaoxin.com/post/html2canvas-guide/

本文详解客户端 HTML 转 PDF 的完整配置、分页控制、Worker API 链式调用,以及跨域图片、Canvas 尺寸限制、CVE-2026-22787 XSS 漏洞修复等常见问题解决方案。

文章目录 **

你可能会问,鼠标右键就能打印网页,为什么还要自己编程实现这种需求。我的回答是:第一,做网页永远优先考虑移动端的体验;第二,需要精细化控制生成的PDF内容,而非打印整个HTML网页。

一、html2pdf.js 项目简介

html2pdf.js 是一个纯客户端 JavaScript 库,可将任意网页或 DOM 元素转换为可打印的 PDF 文档。它基于 html2canvas(负责将 HTML 渲染为 Canvas 图像)和 jsPDF(负责将图像封装为 PDF)构建,整个转换过程完全在浏览器中完成,无需服务器参与

核心定位:html2pdf.js 本质上是一个"截图式"PDF 生成器——它将 HTML 内容先渲染为位图图像,再将图像嵌入 PDF。这意味着输出的是栅格图像而非矢量文本,文字不可选中和搜索,且文件体积较大。

html2pdf.js 基本用法示例

最简单的用法只需一行代码:

var element = document.getElementById("element-to-print");
html2pdf(element);

这将生成 #element-to-print 的 PDF 并自动触发下载。

二、html2pdf.js 安装与引入

CDN 引入(推荐)

使用 cdnjs 锁定特定版本,确保稳定性:

<script
  src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
  integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
  crossorigin="anonymous"
  referrerpolicy="no-referrer"
></script>

直接下载

下载 dist/html2pdf.bundle.min.js 到项目目录:

<script src="html2pdf.bundle.min.js"></script>

npm 安装

npm install --save html2pdf.js
注意:包名必须包含 .js 后缀,即 html2pdf.js 而非 html2pdf

Bower 安装

bower install --save html2pdf.js

浏览器控制台临时使用

若无法修改页面源码,可在浏览器控制台中动态注入:

function addScript(url) {
  var script = document.createElement("script");
  script.type = "application/javascript";
  script.src = url;
  document.head.appendChild(script);
}
addScript(
  "https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js",
);

// 注入后即可使用
html2pdf(document.body);

非打包版本依赖顺序

若使用未打包的 dist/html2pdf.min.js,必须按顺序引入依赖,否则 jsPDF 内置的 html2canvas 会覆盖独立版本:

<script src="jspdf.min.js"></script>
<script src="html2canvas.min.js"></script>
<script src="html2pdf.min.js"></script>

三、html2pdf.js 使用场景与实战案例

github仓库:https://github.com/eKoopmans/html2pdf.js

场景 1:发票与报表导出

这是 html2pdf.js 最典型的应用场景。将页面中的发票或报表区域导出为 PDF。

function exportInvoice() {
  const element = document.getElementById("invoice");

  const opt = {
    margin: [10, 10, 10, 10], // 上下左右边距 10mm
    filename: `invoice-${Date.now()}.pdf`,
    image: { type: "jpeg", quality: 0.98 },
    html2canvas: {
      scale: 2, // 高清输出
      useCORS: true, // 允许跨域图片
      scrollX: 0,
      scrollY: 0,
    },
    jsPDF: {
      unit: "mm",
      format: "a4",
      orientation: "portrait",
    },
    pagebreak: {
      mode: ["css", "legacy"], // 尊重 CSS 分页规则
      avoid: ".no-break", // 避免在这些元素内分页
    },
  };

  html2pdf().set(opt).from(element).save();
}

场景 2:简历 / 证书 / 合同一键下载

利用 onclone 在导出前隐藏编辑按钮、添加水印等。

function exportResume() {
  const element = document.querySelector(".resume-container");

  const opt = {
    margin: 0,
    filename: "my-resume.pdf",
    image: { type: "jpeg", quality: 0.95 },
    html2canvas: { scale: 2 },
    jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
  };

  // 使用 Worker API 在生成后添加水印
  html2pdf()
    .set(opt)
    .from(element)
    .toPdf()
    .get("pdf")
    .then(function (pdf) {
      // 在每页添加页眉/页脚
      const totalPages = pdf.internal.getNumberOfPages();
      for (let i = 1; i <= totalPages; i++) {
        pdf.setPage(i);
        pdf.setFontSize(10);
        pdf.text("Confidential - Page " + i + " of " + totalPages, 10, 287);
      }
    })
    .save();
}

场景 3:长文档分页导出(避免内容截断)

继续阅读全文:https://blog.axiaoxin.com/post/html2pdf-guide/

Ollama 怎么装?命令怎么用?模型怎么选?一文吃透Ollama全知识点,含安装步骤、常用命令速查、模型导入与生态集成,解决本地大模型部署所有基础问题,新手零门槛上手。

文章目录 **

想本地部署大模型,却被复杂配置、命令操作劝退?这篇Ollama全实操指南,从基础定义、多系统安装,到模型运行、量化自定义,手把手教你上手,新手也能快速实现本地大模型自由。

第一次听说 Ollama 是2025年,那时候其实没太在意,觉得大概又是某种跑模型用的轮子。但实际用了一段时间之后发现,这玩意儿确实值得写一篇笔记。

Ollama 使用教程 - 本地部署大模型

Ollama 是什么

Ollama 是一个开源跨平台的大模型工具,专为在本地环境运行大型语言模型设计。它的核心价值在于降低在本地部署大模型的门槛。你不必像早期那样手动配置环境、处理依赖、写一堆 Python 代码,一个命令就能把模型拉下来跑起来。Ollama 有点像Docker的感觉。

Ollama 的支持范围很广,主流的开源模型基本都覆盖了,包括 Llama 系列、DeepSeek-R1、Qwen 系列、Gemma 等等,Ollama官方的模型库在 https://ollama.com/library 

如何安装 Ollama

Ollama 支持 macOS、Windows、Linux,操作方法各有不同。对机器的配置也有一定的要求,详情可参考: 本地部署AI大模型需要多高的电脑配置,你的电脑能跑大模型吗?

macOS 安装 Ollama

我自己用的是 Mac,安装有两种方式:

Homebrew(推荐):

brew install ollama

直接下载 DMG:

去 Ollama官网下载地址 下载 ollama.dmg,把应用拖到 Applications 文件夹里。

系统要求是 macOS Sonoma (v14) 或更新版本,支持 Apple M 系列和 x86(仅 CPU)。

Windows 安装 Ollama

Windows 用户去官网下载 Ollama MSI 安装包或者直接运行 OllamaSetup.exe,一路下一步。安装完成后,Ollama 后台自动运行,ollama 命令就可以在 cmd、PowerShell 或任何终端里用了。系统要求 Windows 10 22H2 或更新版本。

关于 GPU:如果用的是 NVIDIA 显卡,安装 452.39 或更新版本的驱动;如果用的是 AMD Radeon,需要对应驱动。

Linux 安装 Ollama

Linux 可以用Ollama官方提供的一键安装脚本:

curl -fsSL https://ollama.com/install.sh | sh

如果是 Linux 系统作为服务运行,建议用 systemd 来管理。脚本会自动帮你配置好 service 文件,启动服务用 systemctl start ollama,查看状态用 systemctl status ollama

Docker 安装 Ollama

Docker 部署也很方便。CPU 版本直接用:

docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

如果有 NVIDIA GPU,需要先安装 NVIDIA Container Toolkit,然后带 --gpus=all 参数启动。AMD GPU 则用 rocm 标签。

Quick Start:从零开始跑第一个模型

假设你已经装好了 Ollama。先开个终端手动启动ollama:

ollama serve

再开一个终端执行:

ollama run qwen3.5:4b

输入命令之后,Ollama 会去自动下载模型文件。

下载完成后,会自动跳进交互式对话界面。屏幕上会出现类似下面的东西:

ollama run qwen3.5:4b

>>> Send a message (/? for help)

在这里打字回车,模型就会开始回复。按 Ctrl+D 或者单独一行打 /bye 可以退出。比如:

>>> 你好
Thinking...
嗯,用户发来“你好”,看起来是个简单的打招呼。可能刚打开对话框,想测试一下我的反应,或者只是随便说句话。作为助
手,我应该保持友好热情,但也不要太冗长。

用户用“你好”开头,属于非常基础的问候,没有具体需求。这时候直接追问问题可能显得机械。我应该先回应问候,再给出
一些可切入的选项,比如帮助、聊天、任务等,让用户选择下一步方向。

不过,用户可能只是单纯想聊聊天,不一定有明确目标。所以除了提供帮助的选项,也可以提一句“随便聊聊也可以”。另外
,用表情符号能让语气更友好,比如笑脸或帮助手势。

最后,保持简短,避免信息过载。如果用户真有需求,他们会继续提问;如果只是打招呼,这样回应也合适。
...done thinking.

你好!有什么我可以帮你的吗?无论是解答问题、处理任务,还是随便聊聊,我都在这里哦~ 😊

>>> Send a message (/? for help)

Ollama 常用命令

装了 Ollama 之后,最重要的就是掌握几种常用的命令了。

ollama serve

手动启动 Ollama API 服务。Ollama 默认服务端口是 11434,服务启动后就可以用 API 来生成回复了。

ollama pull

拉取(下载)模型到本地:

ollama pull llama3.2

pull 命令会从官方仓库下载模型文件,模型文件通常有几个 GB 到几十 GB 不等。如果下载速度慢,可以配置代理或者换国内镜像地址。还有一个小技巧:下载卡住的时候 Ctrl+C 取消,再跑一次 pull 命令,有时能恢复。

ollama run

运行模型,如果是第一次用,会自动先 pull:

ollama run gemma3

ollama run 启动后会进入交互式聊天模式,你可以直接敲问题和模型对话。也可以加上参数来调整模型行为,比如 --temperature 0.7

ollama list

查看本地已经下载了哪些模型。

ollama ps

查看当前哪些模型加载在内存里,以及它们用了什么处理器(GPU 还是 CPU)。

ollama create

通过 Modelfile 创建自定义模型,比如导入手动下载的 GGUF 文件。

「创建模型」这个说法容易让人误解,以为是在训练一个全新的模型。其实 Ollama 的 create 做的事情是:基于现有模型生成一个「变体」,变体可以改、可以量化、可以合并 LoRA 适配器,但底层骨架还是原来的模型。

补充一句,Ollama 的命令结构和 Docker 挺像的,用习惯 Docker 的人会感觉熟悉。

更多 Ollama 命令详细用法请参考文章: Ollama 命令详解(含参数完整说明)

Ollama 模型导入与自定义

Ollama 官方模型库的模型已经够多了,但有时候需要导入自己的模型文件(比如从 Hugging Face 下载的 Safetensors 格式或 GGUF 格式的模型)。

导入 GGUF 模型

写一个 Modelfile 文件:

FROM /path/to/your/model.gguf

然后在同目录下执行:

ollama create my-model -f Modelfile

这样就可以通过 ollama run my-model 来用了。

导入 LoRA 适配器

如果你有微调好的 LoRA 适配器(Safetensors 格式),Modelfile 写法如下:

FROM llama3.2
ADAPTER /path/to/adapter/directory

然后执行 ollama create my-model 构建模型。

量化

量化这是一个化整为零的过程,会把模型里那些高精度的数据,比如32位浮点数(FP32),转换成更精简的4位或8位整数(INT4/INT8)来进行运算。

打个比方:你可以把浮点数(FP32)想象成一份详尽到小数的财务报表,而整数(INT8/INT4)相当于四舍五入到个位。虽然损失了些许“精度”,但数据量大大降低,计算速度也快得多了。对大模型来说,这点精度的牺牲通常不影响回答质量,但省下的资源是实实在在的。

Ollama 还支持直接把 FP16/FP32 模型量化成 GGUF 格式:

ollama create --quantize q4_K_M mymodel -f Modelfile

相关阅读推荐:

Ollama 环境变量配置

Ollama 支持很多环境变量来定制化行为,几个常用的:

OLLAMA_MODELS: 模型文件的存放目录,默认是 ~/.ollama/models。如果 C 盘满了或者想放到外置硬盘,改这个变量就行。

OLLAMA_HOST: 服务监听的地址和端口,默认 127.0.0.1:11434。如果想允许局域网其他设备访问,改成 0.0.0.0:11434

OLLAMA_CONTEXT_LENGTH: 上下文长度,默认根据显存动态决定(VRAM < 24G 时 4k,24-48G 时 32k,≥48G 时 256k),但可以手动改。

OLLAMA_NUM_GPU: 指定使用的 GPU 数量。

Ollama 的核心能力

Streaming(流式输出)

Ollama 的一大特点是支持流式输出。就是模型生成回复的时候一字一字地往外蹦,而不是等全部生成完了再一次性返回。

REST API 默认开启了 streaming,SDK 里需要显式设置 stream=True

流式输出里还能处理多字段:常规内容就是 content 字段,思考过程在 thinking 字段,工具调用则在 tool_calls 字段里。

Thinking(思考链)

一些 Thinking-capable 模型(比如 DeepSeek-R1、Qwen3)会在 thinking 字段输出推理过程,然后最终的答案在 content 字段里。这个能力可以帮助你理解模型是怎么一步步推理出答案的,也可以用来调试模型的逻辑是不是跑偏了。

使用的时候可以控制是否启用 thinking:

ollama run deepseek-r1 --think "What's the capital of France?"
ollama run deepseek-r1 --hidethinking "Summarize this article"

在命令行交互模式下,也可以用 /set think 或 /set nothink 来临时开关。

Structured Outputs(结构化输出)

结构化输出是 Ollama 一个很实用的能力。通过提供 JSON schema,可以强制模型按照固定格式返回 JSON,而不是自由发挥。

from ollama import chat
from pydantic import BaseModel

class Pet(BaseModel):
    name: str
    animal: str
    age: int

response = chat(
    model='gpt-oss',
    messages=[{'role': 'user', 'content': '我有两只猫...'}],
    format=Pet.model_json_schema(),
)
pet = Pet.model_validate_json(response.message.content)

建议把 temperature 设低(比如 0),输出会更稳定。Ollama 的云端版本暂时不支持结构化输出,只有本地模型能用。

Vision(视觉/多模态)

Vision 模型可以接受图片输入,模型会描述图片内容、识别物体、回答关于图片的问题。

ollama run gemma3 ./image.png "what's in this image?"

API 调用时把图片转成 base64 编码后传给 images 数组。SDK 支持文件路径、URL 或 raw bytes,REST API 只接受 base64。

相关阅读: CURD 程序员,该如何理解 AI 大模型中的多模态(Multimodal)?

Embeddings(嵌入向量)

Embeddings 模型把文本转成向量(浮点数数组,长度一般 384-1024 维度),可以用来做语义搜索、RAG 等。

ollama run embeddinggemma "Hello world"

或者通过 API 批量生成:

curl -X POST http://localhost:11434/api/embed \
  -H "Content-Type: application/json" \
  -d '{"model": "embeddinggemma", "input": ["sentence1", "sentence2"]}'

相关阅读: 从零上手 Ollama 嵌入向量:语义搜索 + RAG 落地代码教程

Tool calling(工具调用)

Ollama 支持函数调用,模型可以根据用户问题决定调用哪个工具,然后把工具返回的结果纳入回复中。可以单次调用一个工具,也支持并行调用多个工具。

相关阅读: Ollama 工具调用(Tool Calling)学习笔记

Web search

Ollama 还提供 Web Search API。可以用来让模型获取最新信息、减少幻觉。前提是注册 Ollama 账号并创建 API Key,然后用 ollama.web_search("your query") 调用。

Ollama 的生态集成

Ollama 的一大优势就是和众多 AI 工具和 IDE 的集成做得很好。官方文档列出了很多:

OpenClaw 可以把 Ollama 变成个人 AI 助理,跨 WhatsApp、Telegram、Slack、Discord 等平台,自带 web search 能力。推荐至少 64k 上下文长度。启动命令:ollama launch openclaw

Hermes Agent 是 Nous Research 做的 AI Agent,支持自动技能创建、跨会话记忆,内置 70 多种技能。

Claude Code(Anthropic 的智能编程工具)可以通过 Ollama 的 Anthropic兼容 API 来用开源模型。

Codex(OpenAI):用 --oss 标志就能对接。OpenCode 也是类似的终端 AI 编程助手。

VS Code 可以直接在 Copilot Chat 的模型选择器里用 Ollama 的模型。

详情阅读: Ollama 生态集成实操指南:Claude Code、Codex、Hermes Agent、OpenClaw、VS Code等工具集成实操(附命令)

阅读原文:https://blog.axiaoxin.com/post/ollama-guide/

AI 多模态学习笔记:多模态大模型排名、原理与应用

AI大模型中的多模态是什么意思,它又是如何工作的?从应用层程序员视角拆解多模态大模型的工作原理,涵盖主流模型排名、实际应用场景与行业思考,帮你真正理解 AI 多模态技术。

文章目录 **

搞应用开发的 CURD 程序员,该如何理解多模态 AI 大模型?

最近打算认真写一点关于 AI 方面的学习笔记。本文是我整理的多模态相关的内容,不是那种底层原理——太底层的我也看不懂——只是用自己的理解记录一下大概的实现原理。如果你也是写业务代码的,对 AI 有点兴趣但又不想啃论文,这篇文章应该适合你。

理解 AI 大模型中的多模态(Multimodal)

先解决最基础的问题:多模态是什么意思

多模态的英文是 Multimodal,这个词拆开来看,multi 是「多」,modal 是「模态」。模态这个词听起来有点学术,其实就是「信息的载体形式」。

我们人类认识世界本来就不是只靠一种感官的。你看到一个苹果,是红色的、圆的,这是视觉信息;你咬一口,咔嚓一声,这是听觉信息;尝到甜味,这是味觉信息;摸起来光滑,这是触觉信息。你的大脑把这些不同渠道的信息整合在一起,才形成了对「苹果」这个概念的完整理解。

AI 里的多模态,就是让模型也能干同样的事——同时处理和理解多种不同类型的信息。这些信息类型主要包括:

  • 文本(Text):文章、对话、代码
  • 图像(Image):照片、图表、截图
  • 音频(Audio):语音、音乐、环境声
  • 视频(Video):动态画面,本质上是图像+音频+时间轴
  • 其他:比如 3D 模型、传感器数据、甚至脑电波信号

所以 多模态学习(Multimodal Learning)  就是研究怎么让 AI 同时处理、关联、融合这些不同模态的数据。而 多模态大模型(Multimodal Large Language Model,简称 MLLM) ,就是在大语言模型的基础上,给它装上了「眼睛」和「耳朵」,让它不再只能读文字,还能看图、听声音、看视频。

多模态大模型是怎么工作的

我一开始以为多模态模型就是分别训练一个视觉模型和一个语言模型,然后硬拼在一起。后来看了一些技术资料才知道,事情没那么简单,但也没那么复杂。

目前主流的多模态大模型架构,基本思路是这样的:

第一步:通过视觉编码器对图片或视频进行编码

视觉编码器(Vision Encoder)负责把图像或视频帧转换成模型能理解的向量表示。最常用的就是 ViT(Vision Transformer) ,把图片切成一个个小 patch,每个 patch 变成一个 token。一张 224x224 的图片大概能拆成几百个 token。

大语言模型(比如 GPT、Claude、Llama)本质上是处理文本的。大家都知道,现在token中文命名叫做「词元」了,你输入一串 token(可以粗暴理解为「词」),它输出一串 token。它不认识图,不认识声音,只认识数字向量。

所以多模态要做的第一件事,就是把非文本的东西转换成文本模型能消化的形式。

用一个类比来理解这个过程。

假设你有一个只会说中文的翻译官(大语言模型),现在来了一个只会说法语的客户(一张图片)。你们之间需要一个翻译流程,向量编码就是将法语翻译成某种中间语言。

图片本身是一堆像素(RGB 值),模型看不懂像素。所以需要「视觉编码器」把图片转换成一组向量。

可以粗暴理解为:把图片「翻译」成了一组数字。这个过程类似你做图片压缩。一张 1024x1024 的图有几百万个像素,视觉编码器把它压缩成几百个「特征向量」。

每个向量代表图片里的某个局部信息,比如「左上角有个红色的圆形物体」「中间偏右有文字」。

第二步:通过投影层对齐

视觉编码器输出的向量维度和语言模型的 token 维度通常不一样,就像那个中间语言可能是某种方言,翻译官还是听不懂,需要一个中间层来做对齐。

「投影层 / 连接器(Projection Layer / Connector)」就是这个用来做中间转换的对齐层。这个对齐层的设计很关键,它决定了视觉信息能不能被语言模型「看懂」。常见的做法有简单的线性投影,也有更复杂的 Q-Former 结构(比如 BLIP-2 用的那种)。你可以把它想象成一个适配器(Adapter),把视觉向量映射到语言模型的 token 空间里。

这个对齐层是怎么训练出来的?简单说就是:给模型看大量的「图片+文字描述」对,让它学会「这张图」和「这段文字」在向量空间里应该离得近。

训练完成后,模型就建立了一种「跨模态的对应关系」—— 它知道一张猫的图片和「一只猫」这句话,在内部表示中是同一个东西。

第三步:推理

这就是模型的「大脑」了,负责接收对齐后的视觉 token 和文本 token,进行统一的推理和生成。可以是 GPT 系列、Llama 系列、Qwen 系列等等。

继续阅读全文:https://blog.axiaoxin.com/post/what-is-multimodal/

这两天觉得 gpt 5.5 太强了都,解决了一个我多年解决不了的问题。同时,我也尝试用 opus 4.7 ,没能解决。

唯一的缺点就是,消耗得太快了。

感觉至少需要几个号来轮流用。

具体案例:V2EX Safe Reading Helper 5.3.0,一个油猴脚本,核心逻辑不复杂,但 530 行里能清楚看到几层叠加痕迹:

  • topic 来源从 API 一路加到 /recent、节点页、ID 逐个扫描——每次拿不到帖子就加一个 fallback ,互相没有合并
  • 两套 refill 防重入并存(isRefilling flag + refillPromise 互斥)
  • legacy key 迁移代码永远留在运行时,跑一次之后就是死代码
  • hasGMStorage() 每次读写都检测,而不是初始化时确定一次
  • 对一个翻页脚本加了完整的白屏 watchdog + 自动刷新恢复机制

模型每次报错就往上堆,不回头清理,不合并逻辑。

有没有系统性的解法?还是说这就是现阶段 agentic coding 的固有缺陷?