SearXNG安全部署:Docker+Nginx实现Token认证网关

要说最近这一年我复用率最高的自建服务,SearXNG 一定排前三。起初只是想在局域网里搭一个不记录搜索历史的聚合搜索页,后来发现它顺手还能当 JSON 搜索 API 用,于是就把整套服务搬到了公网。结果第二天日志里全是陌生 IP 的搜索请求,有人直接把它当免费搜索接口薅。所以在认真讲 Docker 部署 SearXNG 之前,我先把最重要的话放在开头:如果是公网部署,自定义 Token 认证网关不是可选项,是必选项。

这篇文章我会完整拆解整个落地过程,包括目录规划、容器网络设计、SearXNG 的 settings.yml 配置、Nginx 认证网关实现、令牌轮换和常见问题排查。适合已经知道 Docker 基本操作、想把 SearXNG 私有化部署并对接自动化调用的读者。纯内网使用的人也可以参考,网关那部分哪怕只做局域网保护,也能帮你挡住不少安全事故。

1. 为什么我要给 SearXNG 加一层认证网关

1.1 SearXNG 能做什么,部署前想清楚

SearXNG 是一个元搜索引擎,它本身不维护索引,而是把多个上游搜索引擎的搜索请求汇总到一起,做过去重、聚合和排序后再展示给用户。它通常以两种形态工作:一是浏览器网页界面,适合日常查资料;二是 JSON 格式的搜索接口,适合接进自己的脚本、聊天机器人、自动化流程里。

这个“既能当页面用、又能当 API 用”的双重属性,正是它适合自建的核心原因。你不需要自己写爬虫,不需要维护索引库,只需要维护一个实例,就能获得一个相对统一的搜索入口。隐私方面也比直接访问搜索引擎更好一些,搜索行为和浏览器不直接绑定,很多情况下不会留下账号级的搜索记录。

适合自建 SearXNG 的人无非三类:不想被搜索引擎跟踪的个人用户,需要一个内网统一搜索接口的开发团队,以及做自动化采集或内容分析、需要批量调用搜索结果的开发者。如果你只是偶尔搜一次,那个部署成本和维护成本确实不划算,但如果你每天都要消耗几十上百次搜索请求,自建实例的价值就体现出来了。

1.2 裸奔的 SearXNG 有多危险

我最初部署时图省事,只把容器端口映射到宿主机然后直接暴露公网,没有加任何认证。头一天还好,第二天打开 Nginx 访问日志,发现大量来自陌生 IP 的请求,路径全部是 /search?q=...,频率稳定在每分钟好几次,有些 IP 还会改变 q 参数的值,明显是拿我的实例当免费搜索代理用。

裸奔的后果不是“被用一下”那么简单。上游搜索引擎会对发出搜索请求的来源做风控,你的公网 IP 会因为在短时间内向多个搜索引擎发起大量请求而被限流,甚至被暂时封禁。一旦被封,你自己反而没法正常搜索了。更麻烦的是,第三方怎么使用你的实例你是完全不可控的,他可能用来批量采集、可能用来跑违规查询,这些行为产生的负载和风险最后都会落在你这个部署者的头上。

所以结论很直接:SearXNG 绝不能裸奔公网。无论是加 Basic Auth、加 Token 还是加其他策略,至少要保证“只有知道凭据的人才能访问”。

1.3 认证网关方案怎么选

给 SearXNG 加认证有几个层次的做法。第一是改 SearXNG 自己的源码或插件,比如在应用层拦截请求;第二是给容器内部再加一个代理进程;第三是在容器前面挂一层独立的反向代理网关。第一种侵入性太强,升级版本时容易出问题;第二种等于自己重新造了一个网关,工作量不小;第三种用 Nginx 单独做认证层,不侵入应用,SearXNG 随意升级,这是个合理的平衡点。

具体到网关内部,我又对比了几种方案。最省事的是 Nginx Basic Auth,配置一个密码文件就行,但浏览器弹窗体验一般,自动化脚本还得维护用户名密码,不适合做成 API 网关。更灵活的是用 Nginx 的 auth_request 模块,让 Nginx 在转发前先向一个认证服务发内部子请求,根据子请求返回的 2xx 还是 401 决定是否放行。这个方案既绕开了应用层,又能完全自定义认证逻辑,所以我最终选了它。

需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。

2. Docker 环境与整体架构规划

2.1 基础环境准备

我用的是一台 2 核 4G 内存的 Linux 服务器,系统为 Debian 系。SearXNG 本身占内存不算大,默认配置下大约 300~500MB,再加一个 Nginx 和一个轻量认证服务,2G 内存也能跑,但 4G 会比较从容。

Docker 方面,需要安装 Docker Engine 和 Docker Compose 插件。新版本 Docker 通常已经集成了 compose 子命令,如果还没有启用,可以参考下面的步骤:

bash复制# 确认 docker 与 compose 可用
docker --version
docker compose version

# 如果没有 compose 插件,用 apt 安装 docker-compose-plugin 即可
sudo apt install docker-compose-plugin -y

我建议统一使用 docker compose 命令而不是老的 docker-compose。compose 文件格式上,新版也不再需要写 version 字段,直接把 services 写在最外层即可,避免格式过时导致报错。

2.2 目录结构与端口规划

部署前先规划目录,避免后面配置散落一地。我习惯把整个项目放在一个独立目录里,目录结构大致如下:

text复制~/searxng/
├── docker-compose.yml
├── .env
├── searxng/
│   └── settings.yml
├── auth-gateway/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── auth_service.py
└── nginx/
    └── searxng.conf

端口规划方面,我的原则是:对外只暴露一个入口,其他服务只在 Docker 内部网络通信。具体映射如下:

服务 容器内端口 是否暴露到宿主机 说明
Nginx 网关 80 是,映射为宿主机 8080 唯一外部入口
SearXNG 8080 否 仅内部网络访问
认证服务 8081 否 仅内部网络访问,由 Nginx 调用

这样设计的好处是外部请求必须经过 Nginx,SearXNG 没有独立的公网入口,即使有人扫描端口也扫不到应用本身。认证逻辑和安全策略集中在网关层,主服务保持干净。

2.3 容器网络的设计思路

同一台机器上三个容器需要一个共享网络。Compose 默认会给每个项目创建一个 bridge 网络,服务之间可以通过服务名互相解析。我这里显式声明一个网络,方便后续扩展:

yaml复制networks:
  searxnet:
    driver: bridge

在这个网络里,Nginx 可以用 http://searxng-app:8080 访问 SearXNG 主服务,也可以用 http://auth:8081 访问认证服务。服务名作为主机名直接解析,不需要关心容器 IP 变化,这在容器重启后尤其省心,因为 Docker 内部的 DNS 会自动更新解析记录。

3. 编排 SearXNG 主服务

3.1 主编排文件怎么写

完整 compose 文件大概是这样的,我会在下面逐步解释关键点:

yaml复制# ~/searxng/docker-compose.yml
services:
  searxng-app:
    image: searxng/searxng:latest
    container_name: searxng-app
    restart: unless-stopped
    volumes:
      - ./searxng:/etc/searxng
    environment:
      - SEARXNG_BASE_URL=http://searxng-app:8080/
    networks:
      - searxnet
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID

  auth:
    build: ./auth-gateway
    container_name: searxng-auth
    restart: unless-stopped
    environment:
      - AUTH_TOKEN=${AUTH_TOKEN}
    networks:
      - searxnet
    cap_drop:
      - ALL

  nginx:
    image: nginx:1.27-alpine
    container_name: searxng-nginx
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./nginx/searxng.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - searxng-app
      - auth
    networks:
      - searxnet

networks:
  searxnet:
    driver: bridge

.env 文件里写令牌变量,避免直接写死在 compose 文件里:

text复制# ~/searxng/.env
AUTH_TOKEN=请替换成你的随机令牌

SearXNG 官方镜像会读取挂载目录里的配置文件。第一次启动时,如果 ./searxng 目录下还没有 settings.yml,镜像会自动生成一份默认配置。你可以先启动一次再改配置,也可以直接丢一份默认配置进去再改,两条路都行。关键是后续所有配置修改都要在宿主机这个目录里改,改完重启容器生效。

3.2 settings.yml 关键配置解读

SearXNG 的配置集中在 /etc/searxng/settings.yml 文件里。对 Docker 部署来说,有四个参数必须看:

第一是 server.secret_key。这个密钥用于会话签名和加密相关功能,不设置或设置成默认值会导致警告。生成方式很简单:

bash复制openssl rand -hex 32

把输出填进 settings.yml:

yaml复制server:
  secret_key: "生成的随机值"
  bind_address: "0.0.0.0"
  port: 8080

第二是 server.bind_address。默认配置里可能是 127.0.0.1,但在容器里这样设置会导致容器外部无法访问。Docker 部署必须改成 0.0.0.0,它表示监听容器内所有网卡接口。

第三是 server.base_url 或环境变量 SEARXNG_BASE_URL。这个值会影响搜索结果里生成的链接地址。我在 compose 里设置了 http://searxng-app:8080/,这是容器网络内部地址;如果你后面搭配了域名和 HTTPS,可以改成你的实际外部地址。

第四是 JSON API 的开关。默认网页搜索没问题,但如果要启用 /search?format=json 这种 API 形式,需要确认配置里有 json 格式:

yaml复制search:
  formats:
    - html
    - json

改完配置后重启 SearXNG 容器,API 就能返回 JSON 结果了。这一步是很多人容易漏掉的,不改的话网页搜索正常,但调用 API 会一直报格式错误。

3.3 启动与自检

一切就绪后启动服务并观察日志:

bash复制cd ~/searxng
docker compose up -d
docker compose logs -f searxng-app

日志里如果没有明显的 ERROR 或 Traceback,说明主服务启动成功。由于我设计成不对外暴露 SearXNG 端口,自检时可以在宿主机上用一次性容器或通过 Nginx 容器内部访问:

bash复制# 在 Nginx 容器里测试主服务是否可达
docker compose exec nginx sh -c "wget -qO- http://searxng-app:8080/ | head -n 20"

如果能正常返回页面内容,说明容器网络是通的。此时可以先临时放行一次请求,把 X-Search-Token 认证加上之前,先确认 SearXNG 自身能响应 JSON API 请求。这个自检步骤很关键,它可以帮你把“主服务问题”和“网关问题”隔离开,后面排错会快很多。

4. 实现自定义 Token 认证网关

4.1 方案选型:auth_request 为什么比 Basic Auth 更合适

Nginx 的 auth_request 机制并不复杂。它在处理请求时会向一个内部地址发起一个子请求,如果子请求返回 2xx,则继续处理原始请求;如果返回 401 或 403,则直接拒绝。这个子请求的地址不会直接暴露给客户端,配置了 internal 标识后只能由 Nginx 内部调用。

它比 Basic Auth 强的点在于,认证逻辑完全解耦。你想校验 Header 里的 Token、想校验多个令牌、想对接自己的用户系统,都可以通过认证服务实现,不需要动 Nginx 配置之外的业务逻辑。Basic Auth 密码文件虽然也行,但用户名密码的方式对 API 场景不友好,Token 语义更清晰,也更容易做轮换和多密钥管理。

在这里我给认证服务定义为:读取请求头 X-Search-Token,和预设的令牌做比对,一致就返回 200,否则返回 401。令牌放在自定义 Header 里而不是 URL 参数里,是因为 URL 参数很容易出现在 Nginx 访问日志、浏览器历史和各类代理缓存里,Header 方式相对干净。

4.2 轻量级认证服务实现

认证服务我用一个极简的 Flask 应用。文件结构很简单:

text复制auth-gateway/
├── Dockerfile
├── requirements.txt
└── auth_service.py

requirements.txt 只需要一行:

text复制flask>=3.0

核心代码 auth_service.py:

python复制import os
import secrets

from flask import Flask, request

app = Flask(__name__)

VALID_TOKENS = [t.strip() for t in os.environ.get("AUTH_TOKEN", "").split(",") if t.strip()]

@app.route("/auth", methods=["GET", "POST"])
def auth():
    provided = request.headers.get("X-Search-Token", "")
    if not VALID_TOKENS:
        return "auth server not configured", 500
    for token in VALID_TOKENS:
        if secrets.compare_digest(provided, token):
            return "OK", 200
    return "invalid token", 401

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8081)

两个细节值得说道。第一,secrets.compare_digest 是恒定时间比较,比直接 == 更不容易被时序攻击试探出令牌。这个服务虽然长度很短,但既然要暴露公网,防小人不防君子。第二,允许通过逗号分隔配置多个令牌,这样后续给不同的人或不同场景发不同令牌时,不需要改代码,只需要改环境变量然后重启容器。

Dockerfile 可以写成:

dockerfile复制FROM python:3.12-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY auth_service.py .
RUN adduser -D -u 10001 authuser
USER authuser
EXPOSE 8081
CMD ["python", "auth_service.py"]

加一个非 root 用户运行是个好习惯,认证服务本身不该有 root 权限,减少被攻击后的影响面。

4.3 Nginx 网关配置

Nginx 的配置文件 nginx/searxng.conf 是核心,完整内容如下:

nginx复制limit_req_zone $binary_remote_addr zone=searxng_limit:10m rate=5r/s;

server {
    listen 80;
    server_name _;

    location = /auth {
        internal;
        proxy_pass http://auth:8081/auth;
        proxy_set_header X-Search-Token $http_x_search_token;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location / {
        limit_req zone=searxng_limit burst=10 nodelay;
        auth_request /auth;
        auth_request_set $auth_status $upstream_status;

        proxy_pass http://searxng-app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Search-Token "";
    }
}

逐行解释重点。location = /auth 是精确匹配的认证端点,internal 表示该地址只能被 Nginx 内部访问,外部 HTTP 请求这个路径会被拒绝。认证子请求里把客户端传入的 X-Search-Token 头原样转发给认证服务。

主请求到了 location / 时,auth_request /auth 会先发内部子请求。注意我在 location = /auth 里用了 $http_x_search_token,它会自动读取原始客户端请求的 X-Search-Token 头,不需要在主 location 里手动塞,这个变量是 Nginx 内置的。

proxy_set_header X-Search-Token "" 这一行很容易被忽略,它的作用是清空传给 SearXNG 的认证头,避免 Token 被透传到上游服务。SearXNG 不认识这个头,留着没用,清掉更安全。

外加了 limit_req 限流,基于客户端 IP 限制为每秒 5 次、突发 10 次。这个限流不严格,主要是防止恶意脚本把实例流量打满。如果你要批量调用自己的 API,可以调高到 10r/s 或者按自己的实际阈值配置。

配置完成后,重新加载 Nginx 并测试:

bash复制docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload

不带 Token 请求时应该被拒绝:

bash复制curl -i 'http://localhost:8080/search?q=docker&format=json'
# 预期返回 401

带正确 Token 请求时应该正常返回:

bash复制curl -H "X-Search-Token: 你的令牌" \
  'http://localhost:8080/search?q=docker&format=json'
# 预期返回 200 和 JSON 数据

到这一步,一个可用的公网搜索网关就算搭起来了。日常网页搜索同样通过这个入口,浏览器客户端需要在请求里带上自定义 Header 才能访问。所以如果你的主要使用场景是浏览器页面,建议配合一个浏览器扩展或者临时用 curl 验证。

4.4 免代码方案:Nginx map 直通令牌校验

如果你不想引入 Python 服务,只想在 Nginx 层做一个最简单的一对一令牌校验,可以用 map 指令完成。它的原理是把请求头里的令牌映射成判断结果,在 server 块里拦截失败的请求。

nginx复制map $http_x_search_token $token_valid {
    default 0;
    "你的随机令牌" 1;
}

server {
    listen 80;

    location / {
        if ($token_valid = 0) {
            return 401;
        }
        proxy_pass http://searxng-app:8080;
    }
}

这个方案代码量为零,部署最快,但缺点也同样明显:令牌变更要改 Nginx 配置,多个令牌要写多行映射,不具备混淆保护,也无法做超时判定之类的复杂逻辑。它最适合临时应急,或者一个团队里只有少数几个固定调用方。如果是长期服务,我还是推荐用带认证服务的 auth_request 方案,扩展性好太多。

5. 安全加固与日常运维

5.1 令牌管理与轮换

Token 生成最简单的方式是随机长串,长度建议至少 32 字节:

bash复制openssl rand -hex 32

令牌放在 compose 文件同目录的 .env 里,用 ${AUTH_TOKEN} 引用,避免把秘密写进代码仓库或 Dockerfile。轮换令牌时,直接修改 .env 文件里的值,然后重启认证服务即可:

bash复制docker compose up -d auth

认证服务支持逗号分隔多令牌,这给了我很顺滑的轮换思路:先在 AUTH_TOKEN 里追加新令牌,重启认证服务;等所有客户端都切换到新令牌后,再删掉旧令牌,重启一次。整个过程不需要中断服务,客户端也不会出现空白期。

还有一条经验:绝对不要让 Token 直接出现在访问日志里。Nginx 默认不会记录请求头,但在调试阶段,很多人会自定义 access log 格式把 headers 打进去,这就把令牌暴露了。如果确实要审计调用方,建议记录令牌的哈希而不是明文。

5.2 最小化容器权限

容器权限上,我建议在 compose 里加 cap_drop: - ALL,再按需添加必要能力。SearXNG 镜像需要写配置缓存,所以加 CHOWN、SETGID、SETUID 三个能力够用;认证服务直接全部能力丢弃。这样即使某个容器被攻破,攻击者能利用的内核能力也很有限。

认证服务的 Dockerfile 里已经加了非 root 用户,Nginx 官方镜像默认以 root 启动 master 进程,worker 进程则是 nginx 用户,这个还好。SearXNG 镜像内部也做了降权。整体来说,这三个容器没有特殊的高危权限需求,最小化之后可以明显降低风险面。

5.3 限流与日志监控

Nginx 的 limit_req 是比较粗糙的限流,但很有效,能把扫描器的流量挡掉大半。我上面配置的是 5r/s,实际部署时建议根据团队规模调一下。如果主要给脚本调用,可以提高到 20r/s;如果只给自己用,5r/s 已经足够。

日志监控方面,Nginx 的 access log 能看出访问来源和路径。我习惯每天瞄一眼:

bash复制docker compose logs --tail=200 nginx | grep 401

如果大量 401,说明有人在穷举令牌或者扫接口。此时可以结合 fail2ban 之类工具做进一步封禁,也可以直接在网关层加 IP 黑名单。对普通个人部署来说,401 观察足够,毕竟令牌是长随机串,穷举成功的概率低到可以忽略。

5.4 升级与备份

SearXNG 和 Nginx 都是官方镜像,升级流程很常规:

bash复制docker compose pull
docker compose up -d

升级前最好先把 searxng/settings.yml 备份一份。SearXNG 升级偶尔会调整默认配置项,如果新版本认为某参数不合法,容器会起不来。此时直接看 docker compose logs searxng-app,按报错提示改配置即可。认证服务文件也要备份,好在它很小,打包存一个目录就行。

我自己的习惯是每周做一次配置目录的 tar 打包,保留最近三份:

bash复制cd ~/searxng && tar czf backup_$(date +%F).tar.gz searxng auth-gateway nginx .env

这个粒度对个人和小团队足够,没必要上正式备份系统。

6. 常见问题与排查实录

6.1 SearXNG 容器一直重启

最多的问题是 SeaxrNG 容器启动后反复退出。常见原因集中在 settings.yml 上:YAML 缩进错误、secret_key 没设置、改挂载目录后权限不对。排查顺序建议如下:

bash复制docker compose logs --tail=50 searxng-app

如果是配置文件报错,日志会直接指出是哪一行。如果是权限问题,通常提示 cannot open /etc/searxng/settings.yml。这时检查宿主机目录权限,确保容器内用户能读写:

bash复制chown -R 1000:1000 ~/searxng/searxng

不同镜像版本使用的 UID 不同,可以用 docker run --rm -v ~/searxng/searxng:/tmp searxng/searxng ls -ln /tmp 反过来看容器内用户权限,匹配 UID 即可。

6.2 认证一直返回 401

Nginx 和认证服务都启动正常,但带 Token 请求依然 401,这个我踩过几次。首先看认证服务日志,确认请求有没有到达它那里:

bash复制docker compose logs auth

如果没有新的日志输出,问题大概率出在 Nginx 没有把 Token 头传给认证服务。检查 Nginx 配置里 proxy_set_header X-Search-Token $http_x_search_token 是否写对了,是不是放在了错误的 location 里。还有一个容易忽略的点:如果用了 HTTPS 且外层还有一层反代,请求头的下划线可能被过滤。Nginx 默认会忽略请求头里带下划线的字段,X-Search-Token 其实是安全的,但如果你改成了 X_Search_Token,就会中招。可以用 underscores_in_headers on; 打开,或者干脆坚持连字符命名。

6.3 搜索接口频繁报限流错

SearXNG 本身不产生搜索内容,它依赖上游搜索引擎。如果你频繁调用,上游会判断为异常请求,返回 429 或直接弹出验证码。SearXNG 的日志里常出现类似“429 Too Many Requests”的记录。

解决办法有几个方向。一是降低自己的调用频率,对批量任务加退避重试;二是在 settings.yml 里调整 outgoing 参数,比如池大小、超时时间:

yaml复制outgoing:
  pool_size: 20
  request_timeout: 5.0

三是为不同搜索任务配置不同的引擎组合,减少对单一引擎的依赖。引擎配置在 settings.yml 的 engines 模块里,可以按需启用或停用。SearXNG 本身就是聚合,多个引擎分担压力是最有效的做法。

6.4 内存占用持续走高

SearXNG 容器默认内存占用可能在 300~500MB,长时间运行后会缓慢升高。如果机器内存紧张,可以在 compose 里给容器加资源限制:

yaml复制deploy:
  resources:
    limits:
      memory: 768M

不过在无 Swarm 模式下的 compose 里,deploy 资源配置不直接生效,更稳妥的方式是用 mem_limit 字段:

yaml复制services:
  searxng-app:
    mem_limit: 768m

设置后再观察日志,如果频繁触发 OOM,就需要调高限制或减少同时启动的上游引擎数量。

6.5 快速问题速查表

现象 可能原因 快速处理
容器反复重启 settings.yml 语法或权限问题 看日志,逐行排查配置,检查目录 UID
网页能打开但 API 返回格式错误 search.formats 未包含 json settings.yml 中启用 json 后重启
带 Token 仍 401 Header 名不一致或未转发 确认 X-Search-Token 且 Nginx 已转发
请求被上游 429 调用频率太高 降低频率、调 outgoing、切换引擎
没有认证也放行了 Nginx 配置未重载 nginx -s reload 后重新验证
令牌忘了存在哪 环境变量管理混乱 统一写入 .env,避免散落各处

写在最后

我在这套方案上踩过不少坑,最后沉淀下来一条最重要的经验:网关要和主服务解耦。一开始我也试过直接在 SearXNG 容器里塞认证逻辑,升级镜像时改动的文件全被覆盖,花了一晚上重新整理。后来改成独立的 Nginx 加认证服务后,SearXNG 升级只是 docker compose pull && docker compose up -d 两条命令的事,认证层完全不受影响。

另外一个小建议是,先把 SearXNG 部署到内网,用一个月熟悉它的配置行为和 API 返回格式,再对外开放。内网阶段可以省掉认证,但公网阶段一定要补上。等到你真正依赖这个搜索服务的时候,就会发现加这一层认证网关花掉的两小时,和被人薅一天带来的清理成本相比,实在算不了什么。

这套架构后续还有很多可扩展空间:认证服务可以对接新的 Token 体系、可以做调用量按日统计、可以在网关层接入更细粒度的权限控制。希望这篇实战经验能帮你少走一些弯路,把精力留给真正想解决的问题。

内容推荐

Qt5 MSVC2017中文乱码全解:源码编码、编译器与输出链路统一
Qt5 · MSVC2017 · 中文乱码
字符编码是软件工程的基础问题,尤其在跨平台开发中,源文件保存、编译器解释、输出解码三者若不一致,就会出现中文乱码。Qt5 是常见的跨平台 C++ 框架,在 Windows 上配合 MSVC2017 编译时,qDebug 日志输出中文常出现“涓枃”或“锟斤拷”等典型乱码,其根因在于 UTF-8 源文件被系统代码页误读,或窄字符串字面量按 GBK 生成,与 Qt 的 UTF-8 解码预期不符。理清 MSVC 的 /utf-8 编译参数、Windows 控制台代码页、Qt 消息处理器等环节,便能构建统一的编码链路。本文将从编码原理出发,给出修复乱码的完整方案,适用于 Qt5 + MSVC 工程实践。
从BERT到预训练范式:中文NLP分类任务实战与踩坑指南
自然语言处理 · BERT · 预训练模型
自然语言处理(NLP)的目标是让机器理解人类语言,而基于Transformer的预训练模型已成为当前主流技术路线。在BERT出现之前,每个NLP任务都需要独立训练专用模型,严重依赖大规模标注数据;通过海量无标注文本预训练,再针对具体任务微调,能够显著降低数据门槛。注意力机制、位置编码、掩码语言模型等概念,构成了理解现代NLP模型的底层逻辑。从中文文本分类到信息抽取、语义匹配,预训练模型在搜索、推荐、客服等场景中展现了强大的泛化能力。本文以中文情感分析为切入场景,涵盖环境配置、数据处理、微调训练、结果评估全流程,并总结了实战中的经典陷阱与优化策略,帮助开发者快速掌握这套通用方法论,从容应对后续更复杂的模型与应用。
物业管理系统源码实战:从数据库设计到权限控制与二次开发
物业管理系统源码 · Spring Boot · 数据库设计
在软件工程实践与毕业设计场景中,管理系统源码的二次开发是常见需求。理解其数据库设计原理与权限控制机制,是保障系统可维护性与安全性的关键。以物业管理系统为例,其核心围绕“人、房、钱、事”展开,涵盖业主管理、工单流转、费用计算等模块。通过拆解Spring Boot等主流技术栈下的源码结构,掌握数据表关系、状态机约束、金额精度处理等工程要点,能有效提升从代码阅读到功能扩展的实战能力。从典型源码包出发,梳理环境搭建、常见排错与改造方向,帮助开发者快速将课程设计级项目落地为可运行的小型系统,并在此基础上完成分页搜索、消息通知、数据库备份等进阶优化。
PyTorch动态图深度解析:从工作原理到工程部署的实践指南
动态图 · PyTorch · 计算图
计算图是深度学习框架的核心抽象,静态图与动态图的取舍直接影响开发者效率与模型性能。PyTorch以动态图机制实现了边执行边建图,使张量运算与Python控制流无缝融合,带来了即时调试和灵活建模的巨大优势。其背后的autograd系统自动记录反向传播路径,支持梯度累积、梯度裁剪与混合精度等工程实践。在图像分类等典型任务中,可结合Dataset/DataLoader、迁移学习、torch.compile和ONNX导出完成从研究到部署的全流程。文章围绕动态图原理、autograd机制、工程实现与性能优化展开,帮助开发者理解灵活性的代价以及静态化妥协方案。
五款主流冷干机30天实测:压力露点、能耗与选型指南
冷干机 · 冷冻式干燥机 · 压缩空气系统
压缩空气是工业生产的常见动力源,但空压机排出的压缩空气温度高、湿度大,夹带大量液态水和饱和水蒸气,若不处理,极易腐蚀管路、损坏气动元件。冷冻式干燥机(简称冷干机)利用制冷系统将压缩空气降温至3-10℃,使水蒸气凝结并排出,从而保证后端设备获得干燥空气。在实际选型中,不能只看样本参数,因为实验室数据与车间工况常有差距。在相同工况下对五款主流冷干机连续30多天实测,从压力露点稳定性、能耗、压降、故障率、维护成本五个维度进行评分对比,并结合用气负荷、环境温度、后端敏感度给出选型建议。无论你是初次接触冷干机,还是正在对比品牌,这份测评都能帮你建立自己的判断标准。
提示词工程实战:掌握AI对话的精准表达艺术
提示词工程 · 大语言模型 · Prompt技巧
大语言模型的出现让普通人也能借助AI完成写作、编程、数据分析等复杂任务,但模型本身并不具备真正的意图理解能力,它只是基于用户输入进行概率预测与文本生成。同样的模型,输入模糊的指令与结构清晰的指令,输出质量可能天差地别。提示词工程正是解决这一问题的关键技能,它研究如何通过角色设定、背景信息、任务指令、输出格式与负面约束等要素,向模型高效传达真实需求,从而显著提升结果的质量与稳定性。这项技术不需要编程基础,适用于日常办公、内容创作、代码调试、数据解读等广泛场景,还能有效降低模型幻觉带来的信息失真风险。掌握提示词工程,意味着你不仅能更好地驾驭AI工具,也能倒逼自己更清晰地思考问题与表达需求,在人工智能时代获得实实在在的生产力提升。本文从模型原理出发,系统拆解好提示词的五个基本要素与多种实用策略,助你快速上手这一核心技能。
AGV核心零部件全拆解:从感知、决策到驱动与供电系统详解
AGV · 核心零部件 · 激光雷达
自动导引运输车(AGV)已成为智能仓储与柔性制造的关键设备,其稳定运行依赖感知、决策、执行与供电四大系统的紧密协作。从基础技术原理来看,激光雷达与二维码传感器解决定位导航问题,PLC与工控机构成运算决策核心,伺服电机与减速机负责精确运动执行,磷酸铁锂电池与BMS保障能量供给。理解这些组件的选型逻辑与工程匹配,有助于快速落地AGV项目。在室内仓储、重载搬运、复杂环境导航等典型场景中,不同传感器、控制器和驱动方案的组合各有优劣。本文结合工程实践,系统拆解AGV核心零部件的功能原理、选型要点与常见故障排查方法,为相关技术人员提供一套可参考的落地思路。
边剥虾边聊中医:AI辅助的短视频内容起号方法论
AI内容创作 · 短视频运营 · 中医科普
内容创作正在从正襟危坐的专家模式,转向生活场景里的松弛表达。知识科普类内容天然带有信任门槛,如果直接用术语输出,很难留住泛用户;而借助生活场景制造代入感,把专业内容“降燥”,能有效降低观看门槛。AI在这个过程中扮演着关键的角色:它既能帮创作者快速梳理人设、场景和选题,也能将专业话术转译为口语化口播稿,甚至辅助标题测试和热点借势。当短视频平台越来越看重垂直度与互动率,这种“生活流+垂直知识”的组合就成了可行的起号路径。本文以一个人边剥小龙虾边讲中医技能的真实玩法为例,完整拆解从账号定位、AI拆选题、低成本拍摄到发布复盘的整套执行链路,适合想做个人IP的内容创作者和传统文化传播者参考。
Camera扫描与关联:从相机取流到业务数据绑定的完整实现
条码识别 · CameraX · ZXing
条码识别是移动端应用中最常见也最容易被低估的能力之一。很多人以为扫码只是调一个SDK,但真正决定体验的,是相机取流、解码格式、曝光对焦、识别区域裁剪等一系列底层细节。在此基础上,扫码结果还需要与业务对象完成映射绑定,并处理重复扫码、离线补偿、异常追查等工程问题。这类能力广泛用于库存盘点、资产绑定、物料追溯、工单核验等场景,本质上是在搭建一条从物理世界到业务系统的映射管道。本文以CameraX和ZXing的整合实践为例,完整拆解了从相机预览、条码识别到关联落库的每一步实现,并总结了反光、脏污、竖屏一维码等现实场景下的兜底策略,帮助开发者避开扫码项目最常见的坑。
AI智能体运营工程师:普通人入局AI的最佳切口与实操指南
AI智能体运营 · 大模型 · 普通人入局
随着大模型能力持续进化,如何将其落地到具体业务场景成为焦点。AI智能体作为连接技术与业务的新兴载体,正在催生“AI智能体运营工程师”这一复合岗位。其核心原理是通过需求翻译、流程编排与提示词设计,让大模型在真实业务流程中稳定产出可衡量的价值。相比算法调参,运营侧更注重逻辑清晰、业务敏感与数据思维,门槛较低但作用关键。从客服助手到内容选题,从销售跟进到数据分析,该角色正渗透各行业。本文梳理了普通人的四步入局路径、核心技能地图与避坑实录,为想借助AI实现职业跃迁的读者提供务实参考。
Docker环境安装避坑指南:从系统检查到镜像加速全解析
Docker安装 · Docker Engine · 镜像加速
容器技术已成为现代软件交付的基础设施,而Docker作为最流行的容器引擎,其安装环境的正确性直接影响后续的开发与运维效率。安装前需理清docker engine、客户端、containerd等组件差异,并选择合适的安装路线:Linux服务器推荐原生Docker Engine,桌面端则用Docker Desktop。实际操作中,系统版本、CPU架构、内核版本、旧环境清理、软件源配置都是高频故障点,稍有不慎就会导致apt源404或服务启动失败。通过配置官方apt源或公共镜像加速服务,可有效解决拉取镜像慢和超时问题。本文还涵盖用户权限分组、镜像加速配置、常见故障排查清单等工程实践,帮助你在Ubuntu、Debian、Windows、macOS等平台快速搭建一个真正可用的容器环境,并确保重启后服务自动拉起、团队协作无需共享root密码。
企业微信外部群自动化运营实战:从架构到落地指南
企业微信 · 外部群 · 自动化运营
企业微信外部群的运营效率,往往卡在入群欢迎、定时推送、客户打标等大量可被规则描述的重复操作上。自动化不是简单堆砌API,而是基于回调事件驱动、access_token统一管理、消息频控保护等原理,构建稳定可靠的数据链路与任务链路。这套方法不仅能将人工成本从每天数小时降至分钟级,还可显著减少漏发、错发、误踢等事故,广泛应用于私域运营、SCRM选型、社群秩序维护、客户分层等场景。本文以真实落地实践,梳理外部群自动化的核心场景、整体架构与关键实现,并完整复盘接入企业微信接口时的典型问题与避坑经验。
PyTorch手把手实现Seq2Seq机器翻译:编码器-解码器与注意力机制全解析
Seq2Seq · 编码器-解码器 · 注意力机制
自然语言处理中,序列到序列模型解决了输入输出不等长的问题,是机器翻译、文本摘要、对话生成等任务的基础。其核心思想是先由编码器将源序列压缩成语义向量,再由解码器逐词生成目标序列。注意力机制的引入让解码器在每一步动态聚焦源序列的相关部分,显著缓解长句信息丢失。循环神经网络(RNN)与GRU在捕捉时序依赖时依然高效,配合嵌入层、padding掩码和Teacher forcing策略,即可训练一个最小可用的翻译模型。本文基于PyTorch从零搭建带Bahdanau注意力的双向GRU编码器-解码器,完整展示数据准备、词表构建、掩码处理、训练循环与贪心解码推理,并针对训练崩溃、重复词输出等工程问题给出排错指南。即使是初学者,也能通过这份代码理解注意力机制与Seq2Seq架构的完整工作流程,为后续切换到Transformer打下扎实基础。
Jupyter Notebook工程化工作流:释放发散创新,告别代码混乱
Jupyter Notebook · Python · 数据分析
在数据分析和算法实验场景中,交互式编程环境已成为探索数据的核心工具。Jupyter Notebook基于内核与单元格架构,让代码执行保持非线性,这种机制大幅缩短了“想法→验证”的链路,是发散创新的理想载体。然而,自由散漫的使用方式也易导致状态混乱、可复现性差。从工程实践角度看,通过项目骨架管理、环境隔离、随机种子固定以及papermill自动化执行等手段,能在保留Notebook探索优势的同时,建立可维护、可回放的工作流。本文从这些通用技术概念切入,系统阐述如何在Python数据项目中构建兼具灵活性与纪律性的Notebook开发流程,让每一次实验都有迹可循、可自动运行。
BERT预训练模型原理详解与文本分类微调实战
BERT · 预训练模型 · 微调
自然语言处理中,从词向量到预训练语言模型的演进深刻改变了文本分析的方式。传统Word2Vec等静态词向量难以处理一词多义,而基于Transformer架构的BERT通过掩码语言模型实现真正双向编码,能动态建模上下文语义。这种通用语言表示经过微调,可高效适配文本分类、问答、信息抽取等下游任务,极大降低了特征工程成本。本文从BERT的架构设计、预训练任务原理出发,结合中文文本分类实操,解析微调中的学习率、序列长度等关键参数选择,并分享显存优化与上线部署经验,帮助开发者从原理到工程全面掌握预训练模型的应用路径。
OpenClaw实战:AI助手Skills集成与京东云一键部署指南
OpenClaw · Clawdbot · Skills
智能体(AI Agent)正从单一对话工具演进为可执行复杂任务的数字生产力。其核心机制是通过技能扩展(Skills)让大模型理解意图并调用外部工具,实现信息检索、任务调度等真实操作;而容器化与云主机则解决了本地运行的稳定性与资源瓶颈。本文从基础概念出发,拆解 OpenClaw 的配置结构、Skill 标准写法,并结合京东云主机与 Docker 完成一键部署,覆盖安全组、systemd 托底、域名代理等工程实践,帮助开发者快速搭起 7x24 小时可用的 AI 助手服务。
SearXNG安全部署:Docker+Nginx实现Token认证网关
SearXNG · Docker · Nginx
自建搜索服务是隐私保护和自动化检索的常见选择,元搜索引擎因聚合多源结果而备受欢迎。在容器化部署中,Docker Compose简化了服务编排,但直接暴露公网会带来被滥用和IP封禁的风险。通过Nginx反向代理与auth_request模块构建独立认证层,可以实现基于Token的访问控制,同时保持主服务干净、可升级。这种架构适用于个人隐私搜索、内网统一查询入口以及自动化脚本批量调用。本文以SearXNG为例,拆解目录规划、容器网络、settings.yml配置、认证服务实现及令牌轮换等落地细节,帮助读者在公网场景下快速搭建安全、可控的搜索网关。
Linux下rpm方式安装MinIO:从环境准备到生产级部署
MinIO · rpm安装 · Linux对象存储
对象存储是构建云原生应用和数据分析底座的重要基础设施,MinIO凭借S3兼容接口和轻量部署特性,成为自建对象存储的热门选择。在Linux服务器上部署MinIO时,安装方式直接关系到后续的服务托管、升级维护和故障管理。rpm包不仅将二进制文件、systemd服务文件及运行用户一次性安装到位,还能通过标准化的rpm命令实现版本升级和配置保留,非常适合生产环境长期维护。文章从对象存储的基本概念出发,介绍rpm包相较于二进制包、docker镜像等部署方式的优势,并完整演示环境检查、rpm安装、环境变量配置、systemd启动与健康检查的流程,同时针对数据目录权限、SELinux、端口冲突、控制台访问等常见问题给出排查思路,帮助运维人员快速落地一套可用的MinIO服务。
蓝队告警疲劳实战解法:构建秒级自动化响应处置体系
安全运营 · 告警疲劳 · SOAR
在安全运营(SecOps)中,告警洪流与告警疲劳是蓝队面临的核心挑战,大量误报不仅消耗人力,更可能导致真实威胁被淹没。自动化响应作为安全编排与自动化响应(SOAR)的关键技术,其原理在于将告警接入、研判、编排、执行、复盘等重复性环节标准化,通过剧本引擎和策略配置,将处置链路从分钟级缩短至秒级。这项技术不仅显著提升了威胁响应速度,更让安全团队从繁琐的告警筛选与人工封禁中解放出来,专注于高价值的安全分析与事件狩猎。在实际工程应用中,无论是应对SSH暴力破解、WebShell上传还是钓鱼邮件投递,自动化响应系统都能结合信任分级、置信度评分与熔断机制,在保障业务连续性的前提下,实现精准的封禁、隔离与取证。本文从基础概念讲起,深入剖析了这套秒级响应体系从零到一的落地全过程,为备受告警疲劳困扰的蓝队工程师提供了一条可参考的自动化防御路径。
YOLO商品识别模型训练与RK3588部署实战
YOLO · 无人售货柜 · RK3588
目标检测是计算机视觉的核心任务之一,旨在从图像中定位并分类多个物体。YOLO系列将检测视为回归问题,通过单次前向推理同时输出类别与边界框,兼顾速度与精度,成为边缘端实时识别的首选。在边缘计算场景中,RK3588等带NPU的SoC可高效运行YOLO模型,结合RKNN工具链完成量化与部署,大幅降低功耗与延迟。这一组合广泛应用于无人售货柜、智能安防等需要本地实时处理的领域。针对无人售货柜的商品识别需求,需要完成从数据采集、标注、训练到模型转换的完整流程,并解决实际场景中的反光、遮挡等问题。本文以YOLOv5s为例,详细介绍在RK平台上的环境搭建、数据准备、训练调优与RKNN转换部署,帮助开发者快速落地边缘端检测应用。
已经到底了哦
精选内容
热门内容
最新内容
GCDloss:解决YOLO26小目标检测梯度湮灭的损失函数
在目标检测任务中,损失函数不仅衡量预测误差,更直接决定了模型参数的梯度分配。针对小目标检测,普遍存在模型“学不动”的问题:大目标主导梯度,小目标因特征弱、正样本少而梯度湮灭。YOLO26作为主流检测框架,虽具备多尺度检测头,仍难逃这一难题。GCDloss是一种梯度可控的解耦损失,通过尺度感知采样、梯度响应调制和定位辅助项,有效补偿小目标的梯度反馈,让模型在不改动网络结构的前提下提升小目标召回。该损失函数在遥感密集小目标、工业质检等场景中可显著提升mAP与小目标AP,尤其适合小目标占比高的数据集。从原理、代码到超参配置,详解如何在YOLO26上落地GCDloss,实现小目标涨点。
SpringBoot+Vue校园跑腿平台:订单状态机与权限控制实战解析
平台型业务系统是通过角色分工和数据流转连接供需双方的典型应用,其核心在于订单状态管理与权限控制。以SpringBoot为后端框架,结合Vue前端构建的前后端分离架构,可以高效实现此类系统:MyBatis-Plus简化数据持久化,JWT保障接口安全,订单状态机确保业务流转有序。这类技术组合不仅适用于校园跑腿平台,也广泛用于同城配送、任务接单等场景。本文即以校园跑腿平台为例,拆解从数据库设计、角色权限到部署交付的完整闭环,帮助开发者掌握真实项目中的工程实践与避坑要点。
行为预测系统落地实战:从特征工程到AI原生应用部署
用户行为分析是智能运营与推荐系统的核心,而精准行为预测则是将数据洞察转化为业务决策的关键。行为预测并非简单打标签,而是基于可观测行为序列,在特定时间窗口内计算目标事件的发生概率。其技术价值在于驱动精细化运营,如购买意向预测、流失预警等场景。实现过程中,特征工程与模型选型决定效果上限,而数据泄漏、离线在线特征不一致等问题常导致模型失效。AI原生应用强调模型作为决策主体,通过预测-决策-行动-反馈闭环持续迭代。本文从实操角度,完整梳理了行为预测系统的数据接入、特征构造、模型训练、上线部署与监控排查全流程,为构建可靠系统提供工程实践参考。
自写PLC的PID功能块:SCL实现与博图/STEP7跨平台移植
PID控制是工业过程控制中最基础也最常用的算法,工程上通常直接调用PLC自带的功能块。然而在涉及多品牌或新旧平台共存的场景下,官方PID块的“黑盒”特性往往造成调试不便与维护成本上升。从位置式PID原理出发,理解比例、积分、微分三项的物理意义与工程约束,是设计可靠控制功能块的起点。SCL作为PLC高级编程语言,能够实现算法逻辑的标准化封装,便于在TIA博图和STEP7等不同环境中复用。本文结合积分饱和抑制、不完全微分、手自动无扰切换等工程细节,介绍一个自编PID功能块的完整设计过程,并给出跨平台移植时的差异处理与调试经验。对需要掌握PID内核或统一多平台控制程序的工程师,提供了一条可落地的实践路径。
从零手写C# OPC UA服务器:无依赖纯代码实现全解析
OPC UA是工业设备互联的核心通讯协议,但很多项目在离线内网、精简运行时或严格安全审计环境下,无法直接使用官方SDK或成熟开源库。面对这类场景,理解协议底层原理并掌握纯代码实现能力,成为解决部署依赖和版本兼容问题的关键。本文从OPC UA二进制协议帧结构讲起,剖析SecureChannel、Session、Subscription三条核心链路,以及地址空间模型的设计逻辑,并完整展示如何在C#中从零实现一个无第三方依赖的服务端。协议编解码、请求分发、节点浏览读写、互操作性测试等工程细节均逐一拆解,同时记录真实踩坑经验,帮助读者在纯内网或定制化环境中快速打通设备联网通路。无论你希望深入理解OPC UA内部机制,还是需要无依赖方案的落地参考,本文都提供了一条可复用的实践路径。
CANN模型优化:omg-model-optimizer通道剪枝实战指南
模型压缩是深度学习推理部署中的核心环节,尤其当训练环境与目标设备差异显著时,模型冗余会导致时延超标和内存紧张。结构化剪枝作为一种高效的模型优化手段,通过删除对任务贡献很小的卷积核通道,在不改变硬件算子结构的前提下减少计算量与访存开销,较稀疏化更适用于通用推理设备。通道级剪枝依托BN层缩放因子或权重大小判断重要性,同时需联动调整后续层的输入维度与残差结构。基于CANN生态的omg-model-optimizer工具在模型转换阶段实现感知硬件的剪枝优化,配合算子融合和内存排布,可显著提升端侧推理性能。本文深入介绍其剪枝原理、配置参数与实操避坑方法,适合部署工程师参考。
给Agent接入Skill层:从会聊天到会干活的完整实现指南
大语言模型(LLM)虽能生成自然语言,却无法直接执行外部操作,这一局限在构建AI Agent时尤为明显。要让Agent真正“动手”,需要引入工具调用(Function Calling)机制:模型负责决策,代码负责执行。本文从Agent的Skill层设计入手,讲解工具调用协议的原理,展示如何通过接口规范、注册表与主循环改造,将计算器、天气查询等能力安全地集成进Agent系统。同时讨论参数Schema编写、异常处理、上下文管理等工程实践,并梳理模型不调工具、参数解析失败、死循环等常见问题。无论你是刚跑通基础对话流程,还是准备向工程化方向迭代,这套技能接入方案都能帮你少走弯路,让Agent从“纸上谈兵”升级为“真刀真枪”干活的智能体。
双界面元素还原实战:从设计标注到代码落地的关键路径
从前端开发中的设计还原说起。界面还原作为一项基础且关键的实现能力,要求开发者不能只是简单取色和填写样式,而要深入理解设计稿背后的分层逻辑与混合规则,包括颜色、阴影、圆角、字体渲染以及交互状态的联动。凭借参数表、状态机等工程化手段,开发者能够在复杂UI场景中精确复现视觉层级,同时保证动效平滑与代码可维护。在涉及终端类应用的双界面等复杂交互场景时,还原难度进一步提升:卡片常包含多层半透明叠加、动态文本和切换状态,需要同时借助CSS多重背景、Canvas手动绘制或混合模式等实现方式,才能稳定还原渐变、模糊、多层阴影与状态切换等细节。真正的高质量还原,最终依赖一套从标注拆解、量化参数到逐项走查的关键路径,将设计意图变成稳定、可维护的界面实现。
Flutter鸿蒙化Native崩溃栈还原:native_stack_traces适配实践
在客户端开发中,Native崩溃日志的符号化是定位线上问题的关键能力。当程序发生SIGSEGV等异常时,系统会输出包含内存地址和模块路径的原始栈帧,但缺少函数名和行号,排查效率极低。借助ELF文件中的DWARF调试信息,工具链可以将地址还原为可读的调用栈,这正是native_stack_traces库的核心机制。然而在Flutter鸿蒙化的场景下,AOT编译产物、符号剥离策略以及崩溃日志格式的差异,导致现有工具无法直接使用。本文深入分析native_stack_traces的工作原理,并给出针对鸿蒙日志格式、加载基址换算和DWARF版本兼容性的三个关键适配方案,帮助开发者在鸿蒙设备上实现高还原率的Native崩溃栈分析,顺畅融入排障审计流程。
CANN模型优化实战:omg-model-optimizer剪枝技术原理与排坑
模型部署到AI芯片时,体积、算力与带宽成为硬约束,压缩优化往往比改网络结构更稳妥。剪枝是一种删除冗余通道、核或层的模型压缩技术,其中结构化剪枝与硬件矩阵计算单元更契合。在CANN生态中,omg-model-optimizer承担模型瘦身与结构优化:先做敏感度分析确定各层安全剪枝比例,再剪枝、微调、导出ONNX,最后完成算子融合与布局优化。通道剪枝能显著降低计算量,配合INT8量化可进一步压缩模型,适合边缘推理、板卡部署等场景。实际操作中需注意通道对齐、算子融合失败及精度崩溃等坑,掌握这些能让模型减重更稳。
已经到底了哦