MoreRSS

site iconSunZhongWei | 孙仲维 修改

博客名「大象笔记」,全干程序员一名,曾在金山,DNSPod,腾讯云,常驻烟台。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

SunZhongWei | 孙仲维 的 RSS 预览

Windows PowerShell 中安装 NeoVim

2026-08-01 17:32:59

我发现在 Windows PowerShell 中使用 NeoVim 非常流畅,跟 linux 中没有两样,看来每台 windows 上都应该安装一份 neovim 了。所以记录一下配置。

安装

winget install Neovim.Neovim

查看版本号

> nvim --version
NVIM v0.12.4
Build type: Release
LuaJIT 2.1.1774638290

说明安装成功了。

设置别名

用 nvim 命令还是别扭,需要设置一个 vim 别名。

查看 PowerShell 的配置文件路径:

> echo $PROFILE
C:\Users\zhong\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

如果文件不存在,需要新建一个

New-Item -Path $PROFILE -ItemType File -Force

在文件里,添加别名配置

Set-Alias vim nvim

使配置生效:

. $PROFILE

为什么有的模型上下文能到 1M,而有的只有 100k

2026-08-01 09:48:57

今天在用 CodeBuddy 里的免费混元3模型时,看到它的上下文窗口只有 256k,而 DeepSeek V4 Pro 等模型已经能做到 1M。实际编程能力,体验上混元3明显要优于 DeepSeek V4 Pro。可能 deepseek 这个版本只是预览版吧。题外话,昨天 DeepSeek V4 Flash 正式版号称已经超越了 GLM 5.2 等能力。我的疑问来了,为何有的模型上下文能到 1M,而有的只有 100k?为何小上下文的模型,在实际使用中反而更好用?问了一下 AI,还是学到了不少东西。现在工作严重依赖于模型,还是有必要了解一下。

1M(百万级)和100k(十万级)的差距,本质不是“谁技术更先进”,而是“谁更愿意为这个能力买单”以及“为谁设计”的问题。

你可以把上下文窗口想象成一个会议桌——桌子越长,能同时摊开的文件越多,但找资料(推理)的速度越慢,租用场地(算力成本)也越贵。

注意力机制的“智商”差异

传统模型(如早期Llama)用的是“全注意力”机制,意思是桌上每份文件都要和所有其他文件互相“对视”一遍。上下文翻倍,计算量直接翻4倍(平方级增长)。所以100k基本是传统架构的算力天花板。而能做到1M的模型(如Gemini、DeepSeek、Qwen),通常引入了“稀疏注意力”或“线性注意力”。它们不再强制每份文件互相对视,而是先筛选出“关键联系人”进行交流。这打破了计算量的平方律,让窗口可以暴力拉长。

GPU 硬件“钱”的问题

在推理时,1M上下文需要存储的Key-Value缓存(KV Cache)极其庞大。

  • 一个100k的模型,推理时大概需要占用 20GB-40GB 显存。
  • 一个1M的模型,如果用传统方法,显存需求会飙到 200GB-400GB 以上,这远超单张显卡(H100的80GB)的承载能力。

所以,敢于开放1M的厂商,要么采用了极致的量化压缩技术,要么使用MoE(混合专家)架构(每次只激活部分参数),把显存开销硬生生压下来。这背后是巨额的工程优化成本,小厂根本烧不起。

位置编码的“外推”能力(算法“视力”)

模型在预训练时通常只看了几千到几万token的“短文章”。想让它看懂“长篇小说”,必须给它配一副“变焦眼镜”——即位置编码(RoPE等)。
如果编码设计得不好,模型看到超过训练长度的文本时,就会像近视眼看远处一样“失焦”,产生胡言乱语。能做到1M的模型,往往在算法上做了“长度外推”优化(如调整基频),让模型天生具备看长文的潜力;而做不到的模型,可能直接放弃治疗,把训练长度就定在100k。

不同使用场景的选择

  • 如果你需要总结几十页财报、检索整部小说细节,优先选1M的(如Gemini或Qwen长上下文版);
  • 如果你需要逻辑推理、数学解题、代码逻辑纠错,选那个100k但推理能力极强的模型,效果往往比硬塞1M垃圾数据要好得多。

当然也有使用成本上的考虑,Token 成本随上下文大小增长。例如,codebuddy 官方文档中的说明:

CodeBuddy Code 处理的上下文越大,消耗的 Token 越多。CodeBuddy Code 通过 Prompt 缓存(减少重复内容如系统提示的成本)和自动压缩(在接近上下文限制时压缩对话历史)自动优化成本。

差异大吗

90%的模型在处理超过70%窗口长度的内容时,性能都会急剧下降。
所以,1M和100k在日常使用体验上的差距,远没有数字对比那么夸张。如果你用它分析一本50万字的小说(约500k),1M模型能放进去但可能记混配角名字,100k模型放不进去但放进去了就记得很准。

未来

我觉得随着硬件成本下降、算法优化和模型架构的进步,未来大部分模型都能达到 1M 上下文的能力。

tmux 的 Windows 版本 psmux,无需 WSL Cygwin

2026-07-25 22:19:55

一直想找一个 Windows 下的 tmux 版本,因为最近都是在 Windows 下开发,调试和编译也是在 Windows 下进行的。没有 tmux 感觉很不自在。没想到居然找到一个 Windows 下的 tmux 版本 psmux,完全脱离 WSL Cygwin 或 MSYS2,直接在 Windows 下运行。不得不佩服这帮搞 Rust 的大兄弟,战斗力太强了。Github 地址:

https://github.com/psmux/psnet

psmux windows tmux

安装

在 Windows PowerShell 下执行:

winget install psmux

验证安装成功

> psmux --version
tmux 3.3.7
psmux 3.3.7 (05cc5d4 2026-07-20)

配置文件

用户根目录下的 ~/.psmux.conf。例如,在 psmux 中把默认的 Ctrl+b 前缀键改成 Ctrl+a

> cat ~/.psmux.conf
# 将前缀键改为 Ctrl+a
set -g prefix C-a

完全兼容 tmux 配置

于是我把之前 ~/.tmux.conf 文件中的部分配置复制了过来:

# 将前缀键改为 Ctrl+a
set -g prefix C-a

# 窗口编号从 1 开始
set -g base-index 1
# 面板(pane)编号也从 1 开始(可选,看你是否也需要)
set -g pane-base-index 1

# split window
bind | split-window -h
bind _ split-window -v

# pane navigation
bind -r h select-pane -L  # move left
bind -r j select-pane -D  # move down
bind -r k select-pane -U  # move up
bind -r l select-pane -R  # move right
bind > swap-pane -D       # swap current pane with the next one
bind < swap-pane -U       # swap current pane with the previous one

# maximize current pane
bind + run 'cut -c3- ~/.tmux.conf | sh -s _maximize_pane "#{session_name}" #D'

# pane resizing
bind -r H resize-pane -L 2
bind -r J resize-pane -D 2
bind -r K resize-pane -U 2
bind -r L resize-pane -R 2

# window navigation
unbind n
unbind p
bind -r C-h previous-window # select previous window
bind -r C-l next-window     # select next window
bind Tab last-window        # move to last active window

实现类似 smug / tmuxinator / tmuxp 的功能

项目文件管理,参考:

https://github.com/psmux/psmux/blob/master/docs/use-cases.md#6-reproducible-development-environments-in-one-command

例如,新建一个名为 dev-myapp.ps1 的 powershell 脚本,内容如下:

$proj = "C:\Projects\myapp"
psmux new-session  -d -s myapp -n edit  -c $proj -- nvim
psmux new-window   -t myapp   -n server -c $proj -- pwsh -c "npm run dev"
psmux new-window   -t myapp   -n build  -c $proj -- pwsh -c "cargo watch -x build"
psmux split-window -t myapp:build -v -c $proj -- pwsh -c "Get-Content .\dev.log -Wait"
psmux select-window -t myapp:edit
psmux attach -t myapp

防止退出

为何运行当前 powershell 脚本后,没有出现 server window,而是窗口一闪而过。
使用 -c(或 -Command)参数时,PowerShell 会在执行完指定的命令(这里是 ls)后立即退出。当 psmux 发现该窗口内运行的进程结束时,就会自动关闭这个窗口,所以你会看到它一闪而过。

在 pwsh 命令中加上了 -NoExit 参数:

加入 -NoExit 后,PowerShell 执行完 ls 会继续保持交互模式,窗口就不会自动留在那里而不会闪退。

示例

$projBackend = "D:\work\backend"
$projFrontend = "D:\work\frontend"

psmux new-session -d -s some_project -n backend -c $projBackend -- pwsh -NoExit -c "ls"
psmux new-window -t some_project -n frontend -c $projFrontend -- pwsh -NoExit -c "npm run dev"
psmux select-window -t some_project:backend
psmux attach -t some_project

放哪里

可以仿照 linux 的做法,放到用户目录的 bin 目录下,即在用户目录下新建一个 bin 目录,然后把这个 bin 目录加到系统环境变量 Path 中,这样就可以在任意目录下直接运行这个脚本了。或者放到 .local/bin 下,我发现一些 python 相关的程序已经在里面了。

至于脚本名,可以以 psmux_ 为前缀,例如 psmux_project1.ps1 等。

查看 session 列表

> psmux ls
0: 2 windows (created Fri Jul 24 19:48:29 2026)
1: 1 windows (created Fri Jul 24 19:56:15 2026)
some_project: 3 windows (created Fri Jul 24 19:44:00 2026)

kill session

psmux kill-session some_project
psmux kill-session 0
psmux kill-session 1

有了这个,就能完全脱离 WSL 了。

Red Hat 8 / Rocky Linux 配置国内软件源

2026-07-22 20:38:46

今天真是崩溃的一天,第一次遇到这么坑的 Linux 系统环境搭建。平时一直用 Ubuntu Server, 偶尔也用 CentOS,但是从没遇到过 Red Hat 这么折腾的情况。耗费一个下午,就部署了两台服务器,下班了,还有一个 redis 在低速下载中。。。

系统环境

# cat /etc/redhat-release
Red Hat Enterprise Linux release 8.10 (Ootpa)

查了一下这个版本号,发现 RHEL 8.10 是 2024 年 5 月发布的,属于 RHEL 8 系列的最新版本。

This system is not registered with an entitlement server

# dnf list redis --showduplicates
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Error: No matching Packages to list

报错的原因是,RHEL 系统没有注册 Red Hat 订阅,因此官方 AppStream 软件源被锁定了,无法搜索到 Redis 包。
我第一次遇到新装的系统,你不交钱就不给你用软件源的情况。那我怎么安装 gcc 和 make 。。。而且这个服务器还是内网环境,无法访问外网。。。我第一次感觉到无能为力。找运维帮忙安装 gcc,运维说可以给我开外网权限。。。

我实在无法理解,为什么要选择 Red Hat,选择了又不交钱。那用 CentOS 啊,CentOS 虽然已经变成了历史尘埃,那也比没有软件源的系统强吧。

去掉可恶的 Red Hat 订阅提示

sed -i 's/enabled=1/enabled=0/g' /etc/yum/pluginconf.d/subscription-manager.conf

查看 subscription-manager.conf 的内容,其实就是把 enabled=1 改成 enabled=0。

# cat /etc/yum/pluginconf.d/subscription-manager.conf
[main]
enabled=0

# When following option is set to 1, then all repositories defined outside redhat.repo will be disabled
# every time subscription-manager plugin is triggered by dnf or yum
disable_system_repos=0

yum 还是 dnf

没想到,几年不见,连安装工具都改名叫 dnf 了。乍一看,还以为是腾讯赞助的呢😅。

dnf 是 yum 的下一代版本,在 RHEL 8 及之后版本中,yum 命令本身已是 dnf 的一个别名。还是继续用 yum 吧,毕竟习惯了。
从版本号看,就是一样的。

# yum --version
4.7.0
  Installed: dnf-0:4.7.0-20.el8.noarch at Mon 17 Mar 2025 10:27:30 AM GMT
  Installed: rpm-0:4.14.3-31.el8.x86_64 at Mon 17 Mar 2025 10:23:29 AM GMT

# dnf --version
4.7.0
  Installed: dnf-0:4.7.0-20.el8.noarch at Mon 17 Mar 2025 10:27:30 AM GMT
  Installed: rpm-0:4.14.3-31.el8.x86_64 at Mon 17 Mar 2025 10:23:29 AM GMT

dnf 是什么的缩写?

  • dnf 是 Dandified YUM(常写作 Dandified Yum,意思是”升级版/更优雅的 YUM”)的缩写。
  • YUM = Yellowdog Updater, Modified,是早期 RHEL/CentOS 的包管理器

配置国内软件源

折腾了半天,始终没有搞定国内软件源的配置。Red Hat 8 的资料太少了,我也没法 Google,靠 CSDN 上的一众垃圾文章,折腾了半天阿里云、清华源也没搞定。最后,还是靠 WorkBuddy AI 分析出来了,最终用腾讯云的 Rocky Linux 软件源镜像搞定。WorkBuddy 有一点比较强,就是会主动去调用网络请求,验证连接的可用性,帮你排查问题。这是比纯网页版的 AI 强的地方。

之前靠阿里云 CentOS 源配置失败的原因是,CentOS 8 最后一个版本停在 8.5.2111,而 RHEL 8.10 比它新很多,用 CentOS vault 源会缺 8.6–8.10 的包。更合适的做法是改用 Rocky Linux 8.10 的源——Rocky 与 RHEL 8.10 二进制同源、版本号完全对齐,国内镜像也都同步了。

# 用腾讯云 Rocky 8.10 源覆盖原仓库文件
sudo tee /etc/yum.repos.d/Rocky-Base.repo > /dev/null <<'EOF'
[baseos]
name=Rocky-8.10 - BaseOS - tencent
baseurl=https://mirrors.cloud.tencent.com/rocky/8.10/BaseOS/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.cloud.tencent.com/rocky/RPM-GPG-KEY-rockyofficial

[appstream]
name=Rocky-8.10 - AppStream - tencent
baseurl=https://mirrors.cloud.tencent.com/rocky/8.10/AppStream/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.cloud.tencent.com/rocky/RPM-GPG-KEY-rockyofficial

[extras]
name=Rocky-8.10 - Extras - tencent
baseurl=https://mirrors.cloud.tencent.com/rocky/8.10/extras/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.cloud.tencent.com/rocky/RPM-GPG-KEY-rockyofficial
EOF

# 重建缓存并验证
sudo dnf clean all
sudo dnf makecache
sudo dnf repolist

dnf install 测试

# dnf install gcc
# gcc --version
gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28)

dnf 安装 nginx

# dnf module list nginx

会发现只有 1.14 到 1.24 的版本,1.30 还没有。要安装 1.30 的话,需要从官方源安装。

# 1. 导入官方 GPG key
sudo rpm --import https://nginx.org/keys/nginx_signing.key

# 2. 添加官方仓库(用你刚配的腾讯云做基础依赖源即可)
sudo tee /etc/yum.repos.d/nginx.repo > /dev/null <<'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=https://nginx.org/packages/centos/8/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF

# 3. 安装
sudo dnf clean all
sudo dnf install nginx

验证

# nginx -V
nginx version: nginx/1.30.4

启用 systemd

sudo systemctl enable --now nginx
sudo systemctl status nginx
curl -I http://localhost

会看到 200 的响应。

HTTP/1.1 200 OK
Server: nginx/1.30.4
Date: Wed, 22 Jul 2026 07:15:47 GMT
Content-Type: text/html
Content-Length: 896
Last-Modified: Wed, 15 Jul 2026 18:05:52 GMT
Connection: keep-alive
ETag: "6a57cc00-380"
Accept-Ranges: bytes

安装 redis

https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/linux/#from-the-official-red-hatrocky-rpm-feeds

From the official Red Hat/Rocky RPM Feeds

果然,这两个系统被归为一类了。

/etc/yum.repos.d/redis.repo

[Redis]
name=Redis
baseurl=http://packages.redis.io/rpm/rhel8
enabled=1
gpgcheck=1

安装:

curl -fsSL https://packages.redis.io/gpg > /tmp/redis.key
sudo rpm --import /tmp/redis.key
sudo yum install epel-release
sudo yum install redis-stack-server

不要漏掉 sudo yum install epel-release,否则会报错缺少依赖。

# redis-server --version
Redis server v=7.4.6 sha=4b03ddfd:0 malloc=jemalloc-5.3.0 bits=64 build=8a9aacd8a7e90802

启用 systemd

sudo systemctl enable redis-stack-server
sudo systemctl start redis-stack-server

配置文件

# cat /etc/redis-stack.conf
port 6379
daemonize no
loadmodule /opt/redis-stack/lib/rediscompat.so
loadmodule /opt/redis-stack/lib/redisearch.so
loadmodule /opt/redis-stack/lib/redistimeseries.so
loadmodule /opt/redis-stack/lib/rejson.so
loadmodule /opt/redis-stack/lib/redisbloom.so
loadmodule /opt/redis-stack/lib/redisgears.so v8-plugin-path /opt/redis-stack/lib/libredisgears_v8_plugin.so

设置密码:

echo "requirepass xxxx" | sudo tee -a /etc/redis-stack.conf
sudo systemctl restart redis-stack-server.service

RuoYi 中开启 mybatis debug 日志

2026-07-22 20:07:19

昨天遇到一个 mybatis SQL bug,排查了半天才定位到问题。同时使用了三个不同模型,都没有很好地定位到问题。但,这不完全是 AI 的问题,还是旧系统的表设计太随意了。

报错信息

Error querying database. Cause: java.sql.SQLDataException: ORA-01861: 文字与格式字符串不匹配

但是我肉眼看了半天也没看出有什么问题,无非就是 date 类型转字符串的问题。AI 也一段乱改,也没有修复。而这个 SQL 100 多行,看起来头痛。所以想能不能打开 debug 日志,看看具体传递给 mybatis xml 中的参数值是什么类型,以及什么值。

启用 debug 日志

<configuration>
    ...
    <!-- MyBatis SQL 日志 -->
    <logger name="cn.xxx.business.stats.mapper" level="DEBUG" />
    <logger name="org.mybatis" level="DEBUG" />
    <logger name="java.sql" level="DEBUG" />
    <logger name="java.sql.PreparedStatement" level="DEBUG" />
</configuration>

启用之后,就能看到 SQL 对应的参数值,即,每个 ? 对应的实际值和类型。以 RuoYi 的用户表为例:

19:37:07.588 [debug,137] - ==>  Preparing: update sys_user SET login_ip = ?, login_date = ?, update_time = sysdate where user_id = ?
19:37:07.598 [debug,137] - ==> Parameters: 127.0.0.1(String), 2026-07-17 16:37:06.979(Timestamp), 1(Long)

也确实靠这个方法定位到了问题,原来是联表查询时,关联了两个表,但是两个表的日期字段使用不同的字段类型,一个是日期类型,另一个是字符串类型。所以,参数类型转换的方法应该是不同的,而 AI 想当然的以为是同类型的。。。看来以后在让 AI 排查 SQL 问题时,一定要把建表语句提前给到 AI。

开发环境与生产环境启用不同的日志级别

开发环境配置文件 application-dev.yml

# 日志配置(开发环境:开启 MyBatis SQL 调试日志)
logging:
  level:
    cn.xxx: debug
    org.mybatis: debug

生产环境配置文件 application-prod.yml

# 日志配置(生产环境:关闭 SQL 日志,仅保留 info 及以上)
logging:
  level:
    cn.xxx: info
    org.mybatis: warn

公共配置文件 application.yml

# 日志配置
logging:
  level:
    cn.xxx: info
    org.springframework: warn

日志记录的目录也做区分

这个则需要把 logback.xml 改为 logback-spring.xml。
因为 logback-spring.xml 是 Spring Boot 的“一等公民”。Spring Boot 在启动时会主动找到它,插入自身的上下文信息,这使得日志配置能感知并利用 Spring 的强大功能。而 logback.xml 则是一个独立的 Logback 配置文件。

logback-spring.xml 中的日志路径配置

<springProperty name="log.path" source="logging.path" defaultValue="/home/ruoyi/logs" />

application-dev.yml 中的日志路径配置

logging:
  path: logs

application-prod.yml 中的日志路径配置

logging:
  path: /var/logs/xxx/

AI Skill

遇到类似的 “ORA-01861: 文字与格式字符串不匹配” 时,让 AI 去指定的目录或文件中查看对应的表结构。

但是这个方案,还是浪费时间,应该在写代码时,就让 AI 去对应的建表语句文件参考对应表的结构。

补充

今天外出溜达时,看了一下 mybatis 教程,发现还有比 debug 更低的日志级别:

MyBatis日志的最低级别是TRACE,在这个日志级别下,MyBatis会输出执行SQL过程中的详细信息,这个级别特别适合在开发时使用。

mybatis 最常用的是以下五个级别(按输出信息量从多到少排序):

  • TRACE:最详细级别,输出 SQL 参数绑定、结果集映射等调试信息。
  • DEBUG:输出 MyBatis 核心日志(如执行的 SQL 语句)。想看到 SQL 语句至少应设为该级别。
  • INFO:输出应用运行过程中的一般信息。
  • WARN:输出潜在问题或警告信息。
  • ERROR:仅输出错误和异常信息。

需要注意的是,需要把 cn.xxx 的日志级别调成 trace,调 org.mybatis 并没有用。

Linux 服务器上将 Excel 文件转换为 PDF,docker 镜像 gotenberg

2026-07-19 11:20:32

想在一个现有的 golang 后台服务里增加一个 Excel 文件转换 PDF 的功能。但是调研了一圈,并没有很好的 golang 或者 Python 开源免费的实现,基本都需要依赖 LibreOffice 命令工具。于是找到一个 docker 的集成镜像 gotenberg

A Docker-based API built for PDF conversion

可以将 HTML 网页,markodwn,office 文档(word,excel等)转换成 PDF。看起来非常强大,而且省去了自己折腾部署的痛苦。测试了一下非常靠谱,之前用 golang 开发了一套人事系统,因为没有找到合适的 golang 库就没有实现人事简历 PDF 版功能,现在看来完全可以使用这个 docker 方案来搞定。

docker 镜像

https://hub.docker.com/r/gotenberg/gotenberg

镜像大小 431.1 MB。

分为不同的版本,例如:Chromium-Only,LibreOffice-Only 等删除了部分依赖,能有效减少镜像的体积。可以通过不同的 tag 来区分。

不同版本的取舍

  • 内置 Chromium 的镜像支持: HTML, URL, Markdown to PDF via Headless Chromium
  • 内置 LibreOffice 的镜像支持:Office documents to PDF via LibreOffice (100+ formats)

如果只是 excel 到 pdf 的转换,只需要 LibreOffice-Only 版本即可。

Drops Chromium and its dependencies. ~38% smaller than the full image.

可以执行命令安装:

docker pull gotenberg/gotenberg:8-libreoffice

如果是国内,无法直接直接访问 docker hub,可以通过:

docker pull m.daocloud.io/docker.io/gotenberg/gotenberg:8-libreoffice

测试

没想到在 Windows 上安装了 Docker Desktop 客户端之后,也能在 powershell 中使用 docker 命令行。命令行就方便多了,哪个 docker desktop 的 GUI 用起来很不顺手。

转换接口,提供原始文件,就能返回 pdf 格式的文件。

curl \
--request POST http://localhost:3000/forms/libreoffice/convert \
--form files=@/path/to/docx \
-o my.pdf

参考:https://gotenberg.dev/docs/convert-with-libreoffice/convert-to-pdf

docker 实例内存占用

90M 左右。