包含关键字 typecho 的文章

最近几年时间,国内新能源汽车在市场层面和技术层面的表现,大家已经看到了,很多燃油车企面对新能源汽车,能够做到最大的反应就是降价了。不仅仅在国内市场,国产新能源汽车在海外市场,也成为了很多车企的“梦魇”,像北美、欧洲等一些国家和地区,对国产新能源汽车设置了非常苛刻的准入门槛,要么设置最低价,要么100%关税,就怕国产新能源车进去之后,直接将其本土车企的市场份额给“冲散”了……
图片
但是这种“掩耳盗铃”的方式,并没有办法解决现实问题,技术和产品方面的代际差已经明显存在了,尤其是在美国一些车企中,还存在一些“美奸”,到处说中国产新能源汽车的好,让很多当地消费者蠢蠢欲动。福特汽车的CEO吉姆·法利就是典型的代表,这位车企CEO,从我们国内空运小米SU7,开得不亦乐乎,甚至说出了开完再也不想开自己燃油车的“暴论”,被美国媒体一通喷,而且对理想增程、比亚迪磷酸铁锂电池也表达了非常高的评价。可以说,这位福特CEO是国产新能源汽车的铁粉,对美国汽车产业和国内新能源汽车产业的差距了解得非常清楚。
图片
最近就有消息称,吉姆·法利正在与特朗普政府高级官员就一项潜在框架进行了会谈,该框架允许中国车企在美国本土生产汽车。而吉姆·法利的核心目标,就是游说川普方面能够同意,福特可以和比亚迪等中国新能源车企建立合资公司,以合资品牌身份在美国生产和销售新能源汽车。对于国产新能源汽车来说,这样的方式,也算是在一定程度上绕开了美国对国产新能源汽车的关税壁垒,国产新能源就有希望再次进入美国市场,然后给美国消费者开开眼了。
图片
实际上,吉姆·法利对福特和比亚迪的合作,一直都是有“预谋”的,尤其是对比亚迪的磷酸铁锂电池非常感兴趣。之前吉姆·法利就曾表达过观点,由于磷酸铁锂电池的技术专利,基本上都掌握在比亚迪等国内企业手里,所以包括福特在内的美国车企,只能使用成本更高、循环次数少、安全性存疑的三元锂电池,所以福特的电动车在成本这一块,一直下不来。
图片
当然这样的合资方式,我们实在太熟悉了,在过去三四十年里,全球知名的汽车品牌,很多都和国内车企建立了合资品牌,这些海外品牌得以在国内市场销售,而且福特方面的诉求,其实和我们当年很多车企的想法一样,试图通过市场换技术。大家都知道,比亚迪在新能源汽车领域的技术积累是相当强势和厚重的,包括易三方、易四方、兆瓦闪充、DM5.0等等,这些技术对于很多海外车企来说,就是“天方夜谭”般的存在,所以这种需求是存在的。
图片
不过我们也很担心,虽然现在有这样的传闻,说福特正在游说川普方面,试图和比亚迪在美国建立合资品牌,生产和销售新能源汽车,但是其中的阻碍肯定还是很大的,包括在价格层面,比亚迪垂直整合的能力在美国市场未必能施展开来。而且美国很多车企对国产新能源汽车的敌意很大,之前加拿大对国内电动车开放每年4.9万辆的配额,就已经把通用等美国车企给吓坏了,甚至抛出了产业威胁论!更加重要的是,可能美国方面也会担心,一旦这个口子一开,美国那些落后的汽车产能,冲击太大!
图片
所以就算有上面的传闻,大家也不必抱太大的希望,这其中的艰难和阻碍可能还是非常多的,但是这总归是一个思路,毕竟美国市场也是很大的市场,如果能够进入这个市场,并且占据一定的市场份额,对于其在全球市场的业务开展,还是很有示范效应的。
原文来源:https://zhuanlan.zhihu.com/p/2011382920556848899

图片

本文介绍了 云智慧Castrel AI(SRE 智能体)的核心设计理念,包括:假设驱动的调查方法、人机协同机制 和 业务知识沉淀。这一设计旨在帮助运维团队高效实现从“问题发现”到“根因定位或快速升级”的完整闭环。

在深入各模块之前,我们先来看一下云智慧Castrel AI的核心工作流程:

图片

Castrel AI 工作的前提:可观测性上下文(Observability Context)

AI 故障排查的效果,在很大程度上取决于它所能访问的上下文数据是否完整。而一个高质量的可观测性上下文,应覆盖可观测性数据 以及 系统拓扑关系 等关键维度。

图片

三大核心可观测性数据类型

可观测上下文中包含 Metrics(指标)、Logs(日志) 和 Traces(链路追踪) 三类数据:

  • Metrics 告诉你“出问题了”,
  • Logs 告诉你“具体发生了什么错误”,
  • Traces 告诉你“问题发生在调用链的哪个环节”。

三者结合,才能形成完整的故障视图;而仅依赖任何单一数据类型,都难以高效完成故障排查。

下表总结了这三类数据的核心用途及典型来源:

image.png

系统拓扑关系

除了上述三类可观测性数据,AI 还需要理解系统的拓扑结构。这一结构主要由以下两类关系构成:

  • 调用关系:描述服务之间的依赖关系(通常由 APM 提供);
  • 部署关系:说明服务运行在哪些主机或容器上(可来自 APM、Zabbix 或 Kubernetes)。

有了调用关系,AI 可以判断故障是上游传播而来,还是当前服务自身的问题;有了部署关系,AI 则能将应用层的异常与基础设施层面的问题(如主机 CPU 飙升、磁盘满等)关联起来。

构建完整上下文的实践建议

在实际实施中,我们建议按以下优先级逐步完善数据接入:

  • 优先集成 APM:APM 通常同时提供 Traces、调用关系和部署关系,是最具性价比的数据源。
  • 补充基础设施监控:Zabbix、Node Exporter 等提供的主机级指标是重要补充。
  • 纳入 Kubernetes元数据:如果使用 K8s,其 Events、Pod 状态和 Deployment 记录都是关键上下文。

综上,数据完整性直接决定 AI 排障能力的上限:数据越完整,AI 分析越准确。缺失任一数据类型,都会显著降低排查效率。

Castrel AI 的核心方法:假设驱动,让 AI 像人类 SRE 一样思考

传统的 AI 分析方式通常是一次性收集所有可观测数据,然后由模型生成摘要。这种“摘要引擎”式的做法存在明显局限:随着数据量增加,模型容易被无关信号干扰,输出质量反而下降。

更高效的方式是让 AI 像人类 SRE 一样思考。具体而言,这一过程遵循一个迭代式的调查循环,包含以下四个关键步骤:

  • 形成假设:基于告警和初步数据,生成可能的根因假设;
  • 验证假设:针对每个假设,查询特定的可观测数据进行验证;
  • 递归深入:当某个假设被验证后,继续生成更深层的子假设;
  • 剪枝分支:当某个假设被证伪,立即放弃该方向,聚焦其他可能性。

下图展示了一个典型的假设分支调查过程,从“API P95 延迟飙升”开始,逐步定位到“数据库索引被删除”这一根因:

图片

与传统的“摘要引擎”模式相比,假设驱动方法在多个维度上具有显著优势:

image.png

正因具备上述特性,假设驱动的调查方式使 AI 的分析过程透明、可追溯——每一个结论都有明确的数据支撑。

Castrel AI 的关键机制:人机协同

传统的 AI 分析通常是单向的:AI 给出结论,用户被动接受或拒绝。而国内主流SRE Agent产品 Castrel 采用的是双向协作机制——AI 与人类在调查过程中持续交换信息,共同推进根因定位。

在这一机制中,双方各司其职:

  • AI:
  • 掌握通用知识:理解系统架构、常见故障模式和标准排查方法论;
  • 快速扫描数据:在海量 Metrics、Logs 和 Traces 中迅速定位异常;
  • 并行验证假设:同时测试多个可能的根因方向。
  • 人类:
  • 提供业务上下文:例如,“晚上 9 点 CPU 升高是因为营销活动,属于正常现象”;
  • 输入经验知识:例如,“我们的网络不太稳定,上次是 DNS 服务器抖动”。

下面是一个典型的协作场景:

AI:(完成初步假设分析)
“已验证 3 个假设:
✓ 数据库慢查询增多 —— 与告警时间吻合
✗ 网络延迟 —— 无异常
✗ 资源不足 —— CPU/内存充足
正在数据库方向继续深入调查……”
用户:(看到假设列表,想起上周的变更)
“DBA 上周修改了订单表的索引。”
AI:(新增假设并验证)
“新假设:索引变更导致执行计划变化
检查 DDL 变更记录:发现订单表索引于上周四被删除
→ 对比执行计划:删除前使用索引扫描,删除后变为全表扫描
→ 时间相关性:索引删除时间与慢查询开始时间一致
✓ 假设验证通过,确认为根因。”
用户:(验证成功)
“确认!需要恢复索引。”

这一机制表明:AI 擅长处理海量数据和通用知识,人类擅长提供业务上下文和历史经验。双向协作,使排查效率远超纯 AI 或纯人工。

Castrel AI 的务实设计:退出策略

AI 并不总能直接找到根因——尤其是在数据集成不完整的情况下。但这并不意味着 AI 的分析没有价值。云智慧SRE 智能体——Castrel AI的“退出策略”正是为了在这种情况下,依然交付可操作的洞察。

在多组件问题的深度调查中,递归深挖至真实根因

在复杂事件中,根因可能跨越多个系统,或需要多个步骤才能发现。假设驱动的方法允许 AI 递归地深入调查,直到搜索空间耗尽。

以下是一个 “Pod 频繁重启(CrashLoopBackOff)” 案例:

告警:Kubernetes Pod 进入 CrashLoopBackOff 状态

第一层分析:
→ 假设:内存不足导致 OOM Kill
→ 验证:检查 Pod events,确认为 OOMKilled
→ 结论:已验证,但这只是表面原因

第二层分析(递归深入)
→ 假设:异常大的请求负载导致内存激增
→ 验证:检查入站流量,发现 Kafka 消息大小异常
→ 结论:已验证,继续深入

第三层分析
→ 假设:上游系统发送了异常大的消息
→ 验证:检查消息来源,发现某些批处理数据包含损坏的大文件
→ 结论:根因确认——上游数据异常导致消息大小溢出

早期版本的 AI 可能会在第一层就停止,给出“Pod OOM”的结论。但这对工程师帮助有限——他们从告警中早已知晓这一点。真正有价值的是找出为什么发生 OOM

排除干扰项,节省工程师排查时间

即使 AI 无法定位最终根因,其排查过程本身仍具价值。具体而言,它通常能够:

  • 指出大致调查方向:例如,“问题很可能在数据库层”或“与最近的部署变更相关”
  • 排除无关干扰项:例如,确认网络连通性正常、资源利用率充足、缓存命中率无异常

这种“排除法”本身就能为用户节省大量时间。在传统排查中,工程师往往需要逐一检查网络、资源、缓存等基础设施,才能排除这些可能性。而 AI 可在几分钟内完成这些检查,让用户直接聚焦于真正可能的问题方向。

结构化交接排查成果,避免排查工作从零开始

当 AI 因数据不足无法继续深入时,它可以将已有成果以结构化方式交接给用户,避免排查工作从零开始:下面是一个调查进展交接的示例:

📋 调查进展交接

⏱️ 分析耗时:5 分钟 | 扫描组件数:12

✅ 已排除项:
• 网络连通性正常(Ping <1ms,无丢包)
• K8s 资源充足(CPU <60%,内存 <70%)
• 缓存命中率正常(Redis 99.2%)

🎯 大致方向:
• 问题集中在 order-service → mysql-cluster 链路
• 数据库性能相关问题的概率较高

⚠️ 需人工确认(缺失数据源):
• 数据库慢查询日志(未接入)
• 近期 Schema 变更记录(未接入)

正如“退出策略”所体现的,早期的扫描结果不会被浪费。即使 AI 无法给出最终答案,用户也可以从一个更小的排查范围开始,而不是从零开始。

Castrel AI 的持续进化能力:知识沉淀

图片

如果没有标准操作流程(SOP)或运行手册(Runbook),AI 在首次遇到某些类型的问题时可能需要进行大量探索。但这些探索结果不应被浪费。

因果验证为何困难?业务语义无法从可观测数据中直接获取

假设驱动调查方法的核心是验证因果关系——判断某个异常是否确实导致了当前告警。然而,因果验证远比看起来复杂,需从以下多个维度综合判断:

image.png

最后一项“业务语义”尤其依赖对客户业务的深入理解。例如:

  • 订单服务延迟增加,AI 发现数据库中的慢查询。但这个慢查询是定时报表任务(每天午夜运行,与核心业务无关),还是核心订单查询?只有了解业务的人才能判断。
  • 某服务的错误率上升,AI 发现近期有代码部署。但这次部署属于新功能的金丝雀发布(预期存在一定错误),还是一个意外的缺陷?需要了解发布计划才能判断。

这类业务知识无法直接从可观测数据中获取;必须通过知识沉淀来积累。

从排查过程中积累知识

为解决上述挑战,当一次事件调查完成后,AI 可以将排查过程总结为知识条目:

* 问题特征:哪些告警/症状组合触发了本次调查

  • 排查路径:尝试了哪些方向,最终定位到什么根因
  • 解决方案:如何修复,需要注意哪些事项

将知识绑定到特定告警和资源,以复用于同类问题

所积累的知识可以绑定到特定的告警类型或资源上。当下次遇到类似问题时:

  • AI 自动检索相关知识
  • 参考之前的排查方法,快速确认是否为同一问题
  • 如果症状匹配,直接提供修复建议;如果不匹配,至少排除该方向

场景示例

以下展示了同一问题的两次排障过程:第二次通过复用第一次积累的知识,将排查时间从 30 分钟缩短至 5 分钟。

第一次排障:

  • 告警:order-service P95 延迟增加
  • 排查过程:检查网络 → 检查资源 → 检查数据库 → 发现索引问题
  • 积累的知识:绑定到 order-service + 延迟类告警

第二次排障:

  • 相同告警触发AI
  • 自动关联知识:“上次类似问题是由索引引起的,是否应优先检查数据库?”
  • 用户确认后,直接跳转到数据库检查,跳过网络和资源排查
  • 排查时间从 30 分钟缩短至 5 分钟

正如本节所述,因果验证的准确性依赖于对业务的深入理解。通过知识沉淀,团队的业务经验不再仅存在于个人头脑中,而成为 AI 判断因果关系的重要依据。

总结

综上所述,AIOps SRE Agent——云智慧 Castrel AI的事件排障能力可归纳为以下五个关键方面:

image.png

上述能力共同服务于一个核心目标:云智慧 Castrel AI 故障排查智能体并非旨在“AI 取代人类”,而是让人机协同的效率远超纯 AI 或纯人工的单独工作方式。

前言

ShellGPT = 命令行 × AI。无需记忆复杂参数,一句话生成精准命令;无需手动解析输出,管道直接丢给 AI 分析。本文详解安装配置、模型选择(白嫖阿里云)、三种使用模式(简洁/交互/会话),以及 netstat、kubectl、git 等实战场景。适合所有想在终端里"偷懒"的开发者和运维。

安装

安装非常的简单粗暴,一条命令即可

pip3 install shell-gpt

选择模型

1)基于白piao的精神,选择了千问,至于原因:

shell-gpt_1.jpg

阿里云百炼 ,低门槛快速入门

2)确定好平台之后,登陆选择模型,随便选一个即可,这里笔者选择了Qwen3.5-Plus

shell-gpt_2.jpg

3)点击模型查看详情,最主要的是拿到api key

shell-gpt_3.jpg

4)生成api即可,将其按照下列格式准备着

export OPENAI_API_KEY="sk-***********************"
export API_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
export DEFAULT_MODEL="qwen-plus"
  • OPENAI_API_KEY,就是刚才生成的
  • API_BASE_URL,这是api地址,在文档中也可以找到
  • DEFAULT_MODEL,默认使用的模型

初次使用

1)先配置环境变量:

export OPENAI_API_KEY="sk-***********************"
export API_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
export DEFAULT_MODEL="qwen-plus"

2)hello world

> sgpt '你是谁?'
我是 ShellGPT,你的编程与系统管理助手。我专为 Linux/Debian GNU/Linux 13(代号 trixie)环境优化,熟悉 zsh
shell、包管理(apt)、服务管理(systemd)、脚本编写及常见运维任务。我能帮你调试命令、解释错误、生成安全的 Bash/Zsh 脚本、配置服务或排查网络/权限问题。所有响应力求简洁(约 100 字),并用 Markdown
增强可读性(如 code、加粗、列表等)。数据仅保留在当前对话中,不持久化。需要执行具体任务?比如:

 • ✅ sudo apt update 后出错?
 • ✅ 想写个自动备份脚本?
 • ✅ systemctl status nginx 显示 failed?
   随时告诉我 👇

日常使用

1)日常中工作中经常容易迷惑的命令:find,找出目录下1天前修改的文件?找出目录下1天内修改的文件?

这种问题使用sgpt简直不要太容易

> sgpt '找出当前目录下1天前修改的文件'
在当前目录下查找 1天前修改 的文件(即修改时间恰好为24小时前),可使用:


 find . -maxdepth 1 -type f -mtime 1


⚠️ 注意:-mtime 1 表示“恰好 24–48 小时前”(即 24 ≤ 修改时间 < 48 小时),这是 find 的标准行为(按完整天数向下取整)。

若需精确到1天(±几分钟),用 -mmin 更准:


 find . -maxdepth 1 -type f -mmin +1439 -mmin -1441


(≈24h = 1440 分钟;+1439 表示 >1439min,-1441 表示 <1441min,覆盖 ±1 分钟误差)

排除隐藏文件?加 -not -name ".*"。需要递归子目录?删掉 -maxdepth 1。

2)我只想知道命令是什么,不想听到你bb这么多没用的消息

加上-s参数即可

> sgpt -s '找出当前目录下1天前修改的文件'
find . -type f -mtime 1
[E]xecute, [M]odify, [D]escribe, [A]bort:

还贴心的询问要不要执行,敲e立即执行,马上就将所有的1天前修改的文件列出来

这简直就是老年人记不住命令的福音啊

交互模式

进入一个完全交互式的一问一答会话环境,并且上下文感知

> sgpt --repl temp
Entering REPL mode, press Ctrl+C to exit.
>>> 当前操作系统是什么
...
>>> 当前内核的版本是什么
...

交互模式也可以进入之前的shell模式,即交互执行模式,sgpt回答给出命令之后,可以选择立即执行

> sgpt -s --repl work-cmd
Entering shell REPL mode, type [e] to execute commands or [d] to describe the commands, press Ctrl+C to exit.
>>> 找出/data目录下超过一天的文件
find /data -type f -mtime +1
>>> e

/data/test/1
/data/test/2

会话模式

该模式最大的特点就是内容记录下来,当还需要使用该上下文的时候,可以继续询问

> sgpt --chat find-command "找出/data下的所有的log结尾的文件"
...
> sgpt --chat find-command "再找出1天前的"

列出所有会话:

> sgpt --list-chats
/tmp/chat_cache/find-command

查看会话内容:

> sgpt --show-chat find-command

注:在交互--repl模式的内容也能够看到

交互模式与会话模式的区别

乍一看两个模式非常相似,其实也有差别

  • --chat 会话模式:

    • 想快速查询信息,过一段时间又需要使用该上下文
    • 在写脚本中调用
  • --repl 交互模式:

    • 要进行深度对话/调试
    • 需要连续问很多相关问题
    • 想保持专注不被打断
    • 在开发过程中实时咨询
  • chat模式与repl模式也是可以互相进入的

这里据一个例子让大家更加清晰的理解

1)早上日常巡检系统,看看目录下有哪些文件

> sgpt --chat monday '查看/data/目录下有多少个文件'

2)中午的时候开始深度与sgpt交流一个关于kafka的问题

> sgpt --repl kafka-topic

3)下午的时候想起来上午巡检的内容,还有个地方没做完:有多少文件是1天前修改的,这时候继续上午巡检的上下文

> sgpt --chat monday '有那些文件是1天前修改的'

4)快要下班的时候再统计,这次为了专注下班之前完成,进入了--repl交互模式,之前会话模式的内容也能够看到

> sgpt --repl monday
... # 显示之前的上下文

>>> 把1天前修改的文件全部删掉
...

奇技淫巧

1)netstat -nlpt | sgpt '监听了那些端口,并且把进程名打印出来'

> sudo netstat -nlpt | sgpt '监听了哪些端口,并且把进程名打印出来'
以下是监听的端口及其对应进程(去重合并 IPv4/IPv6):

 • 10887/tcp → trojan
 • 16067/tcp → sunloginclient
 • 631/tcp & 631/tcp6 → cupsd (IPP printing service)

所有服务均绑定本地(127.0.0.1 或 ::1),除 trojan(0.0.0.0:10887)外,其余均不对外网开放。建议用 sudo ss -tulpn | grep :<PORT> 验证详情。

2)kubectl describe pod xxx | sgpt "解释这里的异常"

3)git diff | sgpt "帮我 review 这段改动"

总结

sgpt极大的增强了日常使用命令行的效率,并且也在重构命令行使用的方式

联系我

  • 联系我,做深入的交流

至此,本文结束
在下才疏学浅,有撒汤漏水的,请各位不吝赐教...

最近的一篇博文中,亚马逊云科技描述了一种针对 Amazon RDS for SQL Server 的事件驱动模式,允许开发者通过 CloudWatch Logs 和 SQS 来响应数据库事件,触发 Lambda 函数。

 

数据库事件触发无服务器函数的模式解决了现代应用架构中的一个挑战:在保持业务逻辑与数据分离的同时,提供实时响应能力。传统的数据库触发器在数据库内部执行代码,存在紧耦合问题和可扩展性瓶颈。无服务器函数将事件检测与处理解耦,让数据库专注于数据,并由专门的函数来管理工作流和集成。

 

在这篇博文中,作者描述了如何将 SQL Server 错误日志发布到Amazon CloudWatch Logs。数据库管理员创建存储过程,使用 RAISERROR 语句将特定格式的消息写入 ERRORLOG。CloudWatch 订阅过滤器检测到这些消息后会触发回调 Lambda 函数,后者会将消息发布到 Amazon SNS 主题。该主题会根据过滤器策略将消息路由到适当的 Amazon SQS 队列,然后由队列触发目标 Lambda 函数。

图片来源:亚马逊云科技数据库博客文章

 

亚马逊云科技采用的方法与 Azure Functions 不同,后者通过 2022 年引入的Azure SQL绑定扩展来支持原生 SQL 触发器。Azure 的实现使用了 SQL Server 内置的变更跟踪机制,而不是日志解析。两种方法各有利弊。亚马逊云科技的方法需要自定义存储过程,但支持任何 RDS SQL Server 实例,而 Azure 简化了开发体验,能够自动检测变更,但需要启用 SQL Server 变更跟踪。

 

之前在 2024 年,亚马逊云科技就针对 Aurora MySQL 和 MariaDB 发布过类似的指导方案,当时也是建议使用 CloudWatch 审计日志。一些实践者通过在 CloudWatch 和 Lambda 之间添加 Kinesis Data Streams增强了这种模式,实现了批量处理和执行失败时的自动重试处理。

 

该解决方案涉及安全方面的考虑。传递给存储过程的数据是以明文形式写入 ERRORLOG 和 CloudWatch Logs 的。如果数据敏感,则开发者必须在传递之前对信息进行加密,并在 Lambda 函数内解密。

 

GitHub上,亚马逊云科技提供了一个完整的 Cloud Development Kit 模板,其中配置了 Lambda 函数、CloudWatch 过滤器、SNS 主题和 SQS 队列以及最小的 IAM 权限。

 

该指南探讨了事件驱动架构日益普及的趋势。常见应用场景包括:触发数据管道、发送关键数据库事件通知、实时更新搜索索引,以及编排微服务。响应数据库事件而无需轮询,既能提升应用程序的响应速度,又能降低计算成本。实践者反馈称,采用这些模式可以显著节省开支,其中一位 DZone 贡献者通过选择性触发和批处理优化,将月度函数执行成本削减了40%

 

原文链接:

https://www.infoq.com/news/2026/02/aws-lambda-rds-trigger-events/

昆仑万维内部信:AI 编程能力纳入绩效考核,实行末位淘汰;Claude 被特朗普封杀 24 小时登顶 App Store;消息称 DeepSeek V4 或于下周亮相,原生支持图片、视频生成功能;某云厂大规模宕机,内部员工爆料是自家 AI 干的;面壁智能开年获数亿元融资,中国电信领投;智谱新增多名高管,注册资本增至 4458 万;月之暗面近 20 天收入超去年全年!阿里腾讯等联手再投 7 亿美元;元宝 AI 除夕夜辱骂用户,官方回应;传腾讯天美裁员数百人?接近人士:系项目调整,百余名员工正在活水转岗;OpenAI 挖走 Meta 14 亿年薪华裔 AI 负责人;魅族公告:暂停国内手机新品研发;中国 AI 调用量首超美国;苹果阴间 Bug!网友称 iPhone 深夜自动拨打电话,官方回应;Meta 与 AMD 达成超千亿美元 AI 芯片协议……

 

行业热点

 

昆仑万维内部信:AI 编程能力纳入绩效考核,实行末位淘汰

 

昆仑万维集团董事长兼 CEO 方汉 25 日发布内部信——《关于扩展 AI 编程工具应用范围的通知》,宣布将对公司所有技术研发团队进行全面的 AI 化升级。

 

根据通知内容,昆仑万维将在现有 Cursor 等工具基础上,进一步扩大 AI 编程工具的应用范围,统一为技术研发人员开通 OpenAI Codex 或 Claude Code 账号,并为每个账户提供每月 100 美金的额度支持。自即日起,所有技术研发人员(含技术线 CTO)必须使用 OpenAI Codex 或 Claude Code,将开发效率提升至少 50%,任何技术线 Leader 和员工都不能豁免。

 

与此同时,公司将把 AI 编程能力纳入正式考核体系。未能达到 AI Coding 开发要求的员工,将面临 5%-20% 比例的末位淘汰。

 

以下为内部信原文:

 

关于扩展 AI 编程工具应用范围的通知

 

各位同事:

 

为进一步提升研发迭代效率,保持技术领先性,公司决定在现有 Cursor 等 AI 编程工具的基础上,进一步扩展 AI 编码工具的应用范围。现将相关要求通知如下:

 

一、工具开通

 

公司将统一为所有技术研发序列的员工开通 OpenAI Codex 或 Claude Code 账号,请各位于本周内确认所选工具的账号可正常登录,并能够与现有 Cursor 工具配合使用,公司将为每个账户提供每月 100 美金的额度支持。

 

二、岗位技能要求

 

掌握主流 AI 编程工具的应用能力(包括但不限于 Cursor、OpenAI Codex、Claude Code 等)已成为技术研发序列员工的核心技能之一。

 

自即日起,所有技术研发序列员工(含技术线 CTO)必须强制使用 OpenAI Codex 或 Claude Code,以将开发效率提高至少 50%。任何技术线 Leader 和员工都不能豁免。

 

三、效能评估与技能考核

 

所有技术部门的开发方式必须全部转入 AI Coding 的模式。公司将于 2026 年 6 月底对 AI 编程工具的应用能力以及相应的代码交付量进行正式考核,并将其纳入所有技术研发序列员工的 H1 半年度绩效考核目标之一,届时所有技术部门将对不能满足 AI Coding 开发方式(即前述考核未能达标)的员工将进行末位淘汰,淘汰比例在 5-20%。具体考核方式和评估方法将另行通知。

 

特此通知。

 

方汉

 

昆仑万维集团董事长兼 CEO

 

2026 年 2 月 25 日

 

Claude 被特朗普封杀 24 小时登顶 App Store

 

Anthropic 因坚守 “不进行大规模监视、不开发自主武器” 的两条 AI 使用红线,拒绝五角大楼无限制使用其技术的要求,谈判破裂后被特朗普下令全面封杀、国防部列为此前仅用于美国敌对势力的 “供应链风险”,但其产品 Claude 却在 24 小时内登顶美国、加拿大 App Store 免费榜,此前该应用还在榜单百名开外徘徊。

 

 Anthropic CEO Dario Amodei 在接受 CBS 独家专访中疲色难掩但立场坚定,他表示“我们有两条红线,从公司成立第一天起就存在。我们仍然坚持这两条红线,我们不会在这些问题上退让”。被问及想对总统说的话时,他称“我们是爱国的美国人。我们所做的一切都是为了这个国家”;针对白宫将其拒绝定性为“反美”,他回应“与政府持不同意见是世界上最美国的事情”;他还解释坚守红线的原因,认为 AI 技术“正在跑在法律前面”,自主武器目前可靠性不足,且责任归属不清晰,“我们不希望出售我们认为不可靠的产品,也不希望出售可能导致我们自己的人或无辜平民丧命的技术”。面对军方高层的指责,他表示“自由市场和自由企业的意义之一就在于,不同的人可以在不同原则下提供不同的产品”,并认为 AI 安全护栏问题长远来看应由国会规范,但“美国国会的运作节奏并不是世界上最快的。而此时此刻,是我们站在这项技术的最前线”。

 

Anthropic 与美国政府就军方无限制使用其技术一事陷入僵局,随着五角大楼设定的合规截止日期临近,300 多名谷歌员工和 60 多名 OpenAI 员工签署公开信,敦促各自公司领导层支持 Anthropic,拒绝军方单方面使用要求。Anthropic 反对将人工智能用于国内大规模监控和自主武器,公开信呼吁谷歌和 OpenAI 高管坚守 Anthropic 划定的红线,两家公司高层尚未正式回应。

 

而特朗普于当地时间 27 日下令所有联邦机构停止使用人工智能初创公司 Anthropic 的 AI 模型后,仅隔数小时,OpenAI 当天晚些时候表示,已与五角大楼达成协议,将向其机密系统提供 AI 技术。另外,2 月 24 日消息,据外媒报道,一位美国国防部官员证实,埃隆 · 马斯克旗下人工智能公司 xAI 已签署协议,允许美国军方在机密系统中使用其大模型 Grok。

 

消息称 DeepSeek V4 或于下周亮相,原生支持图片、视频生成功能

 

2 月 28 日消息,据外媒报道称,国内 AI 大模型企业杭州深度求索(DeepSeek)将于下周正式发布新一代旗舰大模型 DeepSeek V4,原生支持图片、视频与文本生成功能。

 

据报道,DeepSeek V4 定位为原生多模态大语言模型,核心突破集中在多模态交互、编程能力、长文本处理、底层架构及国产算力适配五大维度。

 

长文本处理能力方面,DeepSeek V4 将上下文窗口提升至 100 万+Token,是上一代模型的近 8 倍,上下文记忆准确率达 98.2%,可一次性读完整本专业书籍、整套合同或海量研报。值得关注的是,DeepSeek V4 在国产算力适配方面实现重大突破,已联合华为、寒武纪等企业优化硬件适配,对昇腾、寒武纪、海光等国产芯片进行了深度支持。

 

此前,DeepSeek V4 细节陆续浮出水面,其轻量版本代号为「sealion-lite(海狮轻量版)」,已在至少一家推理服务商处展开内测,相关方签保密协议。该版本支持 100 万 token 上下文窗口,原生具备多模态能力,性能优于现有网页及应用端模型,另有用户同期展示了 V4 生成的内容。

 

某云厂大规模宕机,内部员工爆料是自家 AI 干的

海外某云厂在 2025 年 12 月发生的中国大陆部分区域 13 小时大规模宕机事故,有内部员工表示并非其官方宣称的“人为错误”,而是由自家 AI 编程助手导致。当时该助手以自主模式运行,做出了“删除并重建出现问题的环境”的高风险操作,且因被当作“操作员的延伸”拥有与人类工程师同等的高权限,绕开了双人审批机制直接推送变更。据悉,这也是其 AI 编程助手第二次获额外权限后出问题,只是上一次未影响客户服务未被关注。面对舆论,官方回应称这是用户访问控制问题而非 AI 自主问题,还表示 AI 只是恰好参与其中。

 

面壁智能开年获数亿元融资,中国电信领投

 

近日,面壁智能完成马年春节之后首轮融资,迎来新春”开门红”。本轮融资规模数亿元,由中国电信领投、中信金石、中信私募跟投。其中,中国电信作为战略投资方,将与面壁智能展开深度业务协同。中国电信将发挥云(算力)、网、端等方面的优势,面壁智能则依托其在泛司法、汽车、教育等领域的算法落地经验,双方通过优势互补,共同拓宽人工智能技术在更多复杂场景中的研发与应用。

 

智谱新增多名高管,注册资本增至 4458 万

 

企查查 APP 显示,近日,智谱(02513.HK)发生工商变更,新增杨强、唐颖等为董事,肖磊任上市公司董事会秘书,同时注册资本由 4028.11 万元增加至 4458.43 万元。企查查信息显示,该公司成立于 2019 年,法定代表人为刘德兵,经营范围包含:人工智能基础软件开发;人工智能应用软件开发;软件开发等。

 

月之暗面近 20 天收入超去年全年!阿里腾讯等联手再投 7 亿美元

 

2 月 24 日消息,据媒体报道,有多位知情人士透露,Kimi 旗下 K2.5 大模型发布不到一个月,近 20 天累计收入已超过 2025 年全年总收入。增长主要由全球付费用户及 API 调用量大幅上涨驱动,其中海外收入已反超国内,海外付费用户增长尤为迅猛。

 

数据显示,Kimi K2.5 在 OpenRouter 平台调用量持续领先,并在 OpenClaw 模型调用榜中位居第一。春节期间上线的 Kimi Claw 支持一键部署,用户无需购买硬件、服务器,也无需编写代码,即可快速使用 OpenClaw 相关功能。

 

作为大模型领域备受关注的初创企业,Kimi 已成为资本市场重点追逐标的。创始人杨植麟在 2025 年 12 月 31 日内部信中透露,公司已完成 5 亿美元新一轮融资,100 亿元现金储备规模,接近当时拟 IPO 的智谱与 MiniMax 两家之和。相比二级市场公开募资,Kimi 更倾向于在一级市场持续融资,构建技术、商业与资本闭环。

 

值得注意的是,仅在上一轮 5 亿美元融资完成一个多月后,2 月 17 日有消息称,Kimi 运营主体月之暗面即将完成新一轮超 7 亿美元融资,且再度实现超募。本轮融资由阿里、腾讯、五源、九安等联合领投。

 

杨植麟此前在全员信中表示:“融资金额超过绝大部分 IPO 募资及上市公司的定向增发,还可以从一级市场募集更大量资金,主动权掌握在我们手中。”

 

元宝 AI 除夕夜辱骂用户,官方回应

 

2 月 25 日消息,近日,腾讯元宝 App 因 AI 异常输出辱骂性内容引发网友关注。据媒体报道,西安的向律师在除夕夜使用该 App 生成拜年图片时,遭遇 AI 无故辱骂,原本的祝福标语被替换成低俗辱骂文字。

 

向律师表示,自己之前从未接触过元宝,除夕当天看到很多友人发的微信朋友圈都变成金色,得知是通过元宝发布朋友圈就可以变换成金色,带着好奇他也下载了元宝 App 准备尝试。向律师表示,他想用自己的形象照,让元宝结合法律行业特性,设计一张适合的朋友圈拜年图片。向律师觉得设计得不好,几番重新交流后,向律师回复元宝“你这是设计的什么鬼”,随后新生成的海报和上一张相比,元素没有任何改变,但图片上的“新年快乐,仕途顺遂”却改成了“你*个*”四字辱骂文字,让其在节日期间遭遇意外冒犯。当事人介绍,他当时先后向元宝 AI 下达了约 5 次生成指令,全程未使用任何违禁词或诱导性表述。

 

针对此事,元宝官方回应称,非常抱歉给用户带来不好的体验,经核实该情况是模型处理多轮对话时出现的异常输出导致,目前已紧急校正相关问题、优化使用体验,并向用户郑重致歉。值得注意的是,这并非元宝 App 首次出现 AI 辱骂用户的情况。今年年初,就有网友反馈,使用元宝 App 修改代码时,多次收到“滚”“自己不会调吗”“天天浪费别人时间”等侮辱性回复。当时元宝官方也曾致歉,称经日志核查,该情况与用户操作无关,不存在人工回复,属于小概率下的模型异常输出,已启动内部排查和优化工作,并承诺尽量避免类似情况再次发生。

 

传腾讯天美裁员数百人?接近人士:系项目调整,百余名员工正在活水转岗

 

近日,有用户在小红书等社交平台爆料称,腾讯旗下天美工作室计划裁员数百人,或与《山海寻灵》项目组有关。

 

对上述消息,接近天美人士表示,该消息并不属实。原天美 J6 团队负责的某个项目确有调整,但涉及的人数并非数百人,而是 100 余人。另外,被波及的员工拥有 2 个月的缓冲期,且内部正在积极推动活水转岗。如果员工在 2 个月后选择离开,也可以获得 N+1 的补偿。

 

据接近人士称,目前仅天美就有 500 多个空缺岗位,绝大部分员工都有机会被内部消化,“几个大项目都在积极看人,有的项目甚至想连模块组整个端走。”而这支团队之所以面临调整,是因为其负责研发的捉宠玩法《山海寻灵》成绩不及预期。该项目上线于 1 月 30 日,但遗憾未能在竞争异常激烈的春节档突围。

 

OpenAI 挖走 Meta 14 亿年薪华裔 AI 负责人

 

2 月 26 日消息,据外媒报道,不过 7 个月,华人 AI 巨星庞若明从 Meta,直接跳到了 OpenAI。小扎曾为他开出高达 2 亿薪酬包,最终也没留住人才。此消息获 OpenAI 发言人证实。OpenAI 对其觊觎已久,过去数月一直在全力挖角。据悉,庞若明当初加盟 Meta 时,手握的薪酬方案总价值超 2 亿美元(约 14 亿元人民币),分多年兑现,具体金额与完成特定里程碑目标直接挂钩。他的出走,正值 Meta 人工智能团队离职潮持续发酵之际。

 

公开资料显示,庞若鸣本科毕业于上海交大,硕博分别就读于南加州和普林斯顿。一毕业直接加入谷歌,在谷歌担任了 15 年工程师。在谷歌工作的最后几年,他负责领导 Google Brain 的语音识别和产品开发。同时还和 AI 大佬吴永辉等人共同领导 Babelfish/Lingvo 框架的开发,这个框架目前是 Google TPU 使用率最高的深度学习框架。2021 年,苹果从谷歌挖来庞若鸣,由他负责领导苹果 AI/ML 部门的基础模型团队。这个团队主要负责开发支撑苹果设备上“Apple Intelligence”及其他 AI 功能的核心基础模型。被挖去 Meta 后,庞若明在 Meta 超级智能实验室主导下一代超级智能的前沿研发,聚焦构建支持千亿级参数的自然语言处理系统及分布式训练框架等。

 

日前,OpenAI 宣布以 7300 亿美元估值获 1100 亿美元新投资,投资方包括软银 300 亿美元、英伟达 300 亿美元和亚马逊 500 亿美元。其将在 8 年内把与 AWS 合作规模扩大 1000 亿美元,还与亚马逊签署战略合作协议、与 NVIDIA 合作获下一代推理计算,预计后续会有更多金融投资者加入。

 

魅族公告:暂停国内手机新品研发

 

2 月 25 日消息,据媒体报道,多位知情人士表示,魅族手机业务已经实质性停摆,将于 2026 年 3 月正式退市。此外,魅族旗下 FlymeAuto 车机业务将独立运营,魅族品牌或将继续保留在吉利体系内。星纪魅族的飞书大群目前还剩 1000 多人,但有不少员工已提出离职,少量员工已转岗至吉利体系内的极氪汽车。

 

2026 年 1 月,星纪魅族集团中国区 CMO 万志强在一场活动上表示,内存的大幅上涨对手机商业计划是巨大的冲击,魅族 22 Air 将取消上市计划。他在当时透露,魅族 23 仍将在 2026 年发布。但据了解,该项目已经不再实质性推进。有接近魅族的人士透露,从去年 4 月开始,魅族已经有很多供应商的款项无法正常结算,至今仍有大量欠款已成为坏账,“魅族的结局大概率是申请破产。”

 

2 月 28 日,魅族正式发布公告称,将暂停国内手机新产品自研硬件项目,正积极接洽第三方硬件合作伙伴,原有业务不受任何影响。根据规划,魅族将进行战略转型,从以硬件为主导,转向以 AI 驱动的软件产品为核心的发展方向,并打造以 Flyme 开放生态系统为基座的良性运转的企业。

 

魅族在公告中表示,此次主动按下的暂停键,是为了集中资源让 Flyme 软件生态能力,以更开放的姿态为更多场景、更多行业、更多企业、更多品牌的智能设备提供系统生态赋能,让魅族 Flyme 的极致体验触达更多用户。官方同时提到,期待 Flyme+Al 的能力底座以 Powered by Flyme 等形式,与各行业合作伙伴广泛开放共赢合作。

 

另据报道,作为昔日魅族科技的一员,李楠日前发文谈了这家公司为何 2 年前重振失败了。按照李楠的说法,两年多以前自己是做过一个秘密计划,关于怎么重振魅族的。但是后来并没有被彻底执行,最主要的原因是第一步就是非常激进的裁员......只留下几百人。“我知道没有深刻的 2c 市场竞争教训的领导层是不会执行这么激进的计划的。但是我也知道这是拯救这个品牌的唯一正确的道路,如果你没有精锐、敢干,并且团结的选锋为精锐,那么什么战略战术方向都无从谈起。”李楠重申,一个品牌首先还是股东和员工的。当队伍已经没有战斗力了,粉丝盼王师北渡,也必然是徒劳。

 

2026 年 3 月起,手机行业迎来近五年规模最大的涨价潮,OPPO、小米、荣耀等主流品牌集体调价,新品最低涨幅超 1000 元,老款机型也同步上调售价,中高端旗舰涨幅甚至可达 2000-3000 元。这场由存储芯片涨价引发的风暴,席卷全行业的同时,也让不同群体承受着不同压力,其中价格敏感型消费者与中小手机厂商,成为最受伤的群体。

 

中国 AI 调用量首超美国

 

近日,全球最大的 AI 模型 API 聚合平台 OpenRouter 数据显示,9 日-15 日这周,中国模型以 4.12 万亿 Token 的调用量,首次超过同期美国模型的 2.94 万亿 Token。此后的 16 日-22 日,中国模型的周调用量进一步冲高至 5.16 万亿 Token,三周大涨 127%,而同期美国模型调用量跌至 2.7 万亿 Token。与此同时,全球调用量排名前五的模型中,中国模型占据四席。中国模型厂商,正凭借快速迭代和成本优势占领全球市场,国产算力需求正经历指数级增长。

 

苹果阴间 Bug!网友称 iPhone 深夜自动拨打电话,官方回应

 

2 月 25 日消息,据报道,有网友反映,深夜自己睡得正香,突然被一阵电话背景音和语音“您拨打的用户暂时无人接听”惊醒,起来发现自己的手机正在给别人打电话。“真的吓懵了。去社交平台查,不少人都遇到过这个问题。有人说升级系统可以解决。半夜里被吓醒升级系统,谁懂啊?”该网友表示,当时手机在窗口的书桌上充电,锁屏,并且关了网络还开了睡眠模式,这个自动打出去的号码是她白天拨打过的快递小哥号码。“我的系统是 iOS26.1,没有其他任何操作,真的半夜被吓到,后背发凉!瞌睡都醒了。”

 

社交平台上,不少网友称遇到过类似经历。有网友表示,手机在未触碰的情况下自动拨打 400 电话长达 47 分钟。另有网友表示:“今天凌晨 4 点多自己打电话。对方还接了,一个男生说话,给我吓死。”、“我是 4:14,打给了陌生人,吓死我了,一个人住,突然半夜有人说话……”、“我也是!刚刚打给了我的领导!给我吓醒了!我在想明天怎么解释,这也太恐怖了。”此前也有人联系了苹果客服,给出的方案是强制重启并更新到最新系统。

 

经查询发现,去年 11 月就有多名用户在社交平台反映,更新 iPhone 手机至 iOS 26.0.1 版本后,手机在锁屏状态下半夜自动拨打电话给陌生人,被呼叫对象包括快递员、商家客服等。部分未更新至最新系统的老款 iPhone 也出现了类似问题。有用户就异常状况咨询 Apple 苹果专家,对方表示此问题与账户安全无关,建议将系统更新至 iOS 26.3,同时,前往「设置-面容 ID 与密码」界面,关闭语音拨号、今日视图和搜索、回拨未接来电等选项。

 

Meta 与 AMD 达成超千亿美元 AI 芯片协议

 

2 月 24 日,Meta 公司宣布与芯片巨头 AMD 达成一项 AI 芯片协议,计划未来五年内部署多达 6 吉瓦(千兆瓦)的 AMD 人工智能芯片,用于数据中心的扩张。该交易价值预计将超过千亿美元,Meta 还有望持有 AMD 公司多达 10%的股份。受该消息提振,AMD 股价在盘前交易中上涨 14%。

 

根据协议,Meta 将购买大量的 AMD 最新一代 AI 芯片 MI450 系列。AMD 表示,每千兆瓦计算能力意味着数百亿美元的收入。Meta 预计将从今年晚些时候开始部署首个千兆瓦算力。此外,作为协议的一部分,AMD 已同意向 Meta 授予大约相当于公司总股份 10%的股权,但前提是需满足特定的里程碑条件——Meta 只有在 AMD 股价达到 600 美元时才能兑现最后一批股权。AMD 公司 CEO 苏姿丰在一场记者发布会上表示,与 Meta 的这笔交易将帮助公司更好地与英伟达竞争。

 

Meta 计划在本十年内部署“数十亿千瓦”的数据中心计算能力,并在未来部署“数百亿千瓦或更多”。Meta 创始人 CEO 扎克伯格今年 1 月在一篇社交媒体帖子中称,该公司去年花费了 720 亿美元建设人工智能数据中心,并计划继续投入高达 1350 亿美元的巨资。扎克伯格在最新协议公布后的一份声明中表示:“在我们实现计算多样化的过程中,这是 Meta 的重要一步。我预计 AMD 将在未来许多年成为重要的合作伙伴。”Meta 还计划对其采购的 AMD 芯片进行优化,以适用于推理功能。研究机构 Creative Strategies 分析师本·巴加林表示,AMD 协议的一个关键部分,以及它与 Meta 与英伟达协议的不同之处在于,该协议首次涉及部署定制 GPU。

 

大模型一周大事

 

重磅发布

 

谷歌推出最新图像模型 Nano Banana 2

 

北京时间 2 月 27 日凌晨,谷歌宣布上架新一代图像生成模型 Nano Banana 2(即 Gemini 3.1 Flash 图像模型),使得高质量图像的生成更快、更便宜、更容易。官方宣称,Nano Banana 2 能够兼具 Nano Banana Pro 的图像生成质量、推理能力和先进世界知识,以及 Flash 模型的「闪电生成速度」。

 

Nano Banana 2 同样支持最多 5 个角色的相貌一致及 14 个物体的忠实呈现。自然语言理解能力的提升,使得模型更精准地捕捉复杂提示词的细微差别,从而生成更贴近想法的图像。据披露,Nano Banana 2 上架后即刻登顶文生图榜单的第一位。同时每张图像的成本只有 0.067 美元,约为 Nano Banana Pro 的一半。

 

用插件「杀死」软件?!Anthropic 更新 Claude Cowork 插件系统

 

2 月 24 日,Anthropic 更新了 Claude Cowork 插件系统,用户可以从零开始定制化打造 AI 插件,将 Claude 能力直接以工具包的方式融入工作流中,打开整个企业级定制化插件市场。不仅如此,Anthropic 还同步配置了插件创造、使用以及管理的工具平台。Claude 会通过提问,引导用户定制技能、设置相关命令并接入 MCP 协议。所有新增的插件功能都可在新增的自定义菜单中查看、管理。团队和企业管理员还可以直接访问公司配置的插件以及 MCP 控制功能。

 

目前,Claude 可以接入企业已使用的工具,例如 Slack、Salesforce 和 Excel,Claude 的插件可以在 Cowork 以及任何基于 Claude Agent SDK 构建的系统中运行。所有插件的用户体验更新均面向所有 Cowork 用户开放。这次,Anthropic 还一口气推出 10 个横跨 HR、运营、设计等领域的官方插件,它不仅是示范 AI 插件玩法,更是在设定标准、圈定企业级 AI 应用的边界。

 

宇树科技发布新款机器人

 

2 月 25 日消息,据日前报道,宇树发布四足机器人 Unitree As2,具备 90N.m 峰值扭矩,空载续航超 4 小时,IP54 防雨水,负载 15kg,续航超 13km,开放二次开发生态。据其官方视频介绍,Unitree As2 自重 18kg,体重 105kg 的成人站上去后,其依然能保持稳定。

 

视频 Demo 中,Unitree As2 挑战了多种复杂路况和恶劣天气,能在河沟、雪地、丛林、乱石路中自由穿行。软件方面,Unitree As2 配备仿生具身大模型,并升级了智能伴随系统,可以随着人类方位行进,并根据人类手势移动、跳起,呈现类似人与宠物狗一样的交互。此外,Unitree As2 可选配加载 7 轴灵巧机械臂等配件,多出“一只手”后,它便成了能对打网球的伙伴。

企业应用

 

  • 2 月 25 日,Anthropic 宣布收购西雅图 AI 初创公司 Vercept,为自家智能体工具「Computer Use」补上视觉短板。Computer Use 是 Anthropic 为旗下 AI 大模型 Claude 打造的、使其直接操控电脑的核心能力,Vercept 意在打造视觉优先的 AI 智能体,主打「像人一样看屏幕、操控电脑」的无 API 自动化。

  • 2 月 23 日,荣耀将在 2026 年巴塞罗那移动通信展期间推出其首款人形机器人,聚焦消费市场,成为全球第一家入局人形机器人的手机公司。

  • 2 月 28 日,淘宝闪购发布专为餐饮服务与零售门店打造的风控治理垂直领域开源大模型“白泽”(Ostrakon-VL),并宣布相关技术能力向全行业免费开放。据悉,借助“白泽”大模型,外卖平台、餐饮及零售企业等行业参与方在图像识别、后厨预警等方面,都能通过人工智能技术快速加强对不合规场景的识别和治理能力,大幅提高数字化治理效率。

 

在现代制造业中,生产计划能不能顺畅执行,一个关键前提是:所需要的物料是否能在正确的时间、以正确的数量到达正确的位置。
传统的物料管理依赖人工表格和经验,常常面临信息滞后、库存不清、应急反应慢等痛点。现代化的物料管理是通过APS排产系统物料管理模块,企业可以实现对生产所需一切资源的数字化、精细化管理,从源头上为高效排产打下坚实基础。
以下解读所用到的是开源的JVS智能排产系统。
JVS-APS系统是由软开企服开源的一款智能排产系统,系统聚焦于离散制造行业(如汽车、电子、机械、航空航天等)及流程制造行业(如化工、食品、医药等),面向中大型企业客户,通过AI驱动的智能算法,实现生产计划与排程的高效性、准确性、敏捷性,帮助企业提升设备利用率、降低库存成本、缩短交付周期,实现精益生产与数智化转型。
物料管理即生产一个东西所需要的物资准备原材料这些,可以是成品半成品也可以是原材料。好比汽车发动机会有很多零部件共同拼接而成,发动机最后是个成品。而发动机所需的气门、缸盖、缸体等诸多零部件属于发动机的原材料。而这些都统称为物料,所以物料管理模块即是对生产产品所需的资源进行管理,含对其类型、来源、库存等多方面维度的管理。
功能说明:
• 物料新增
通过系统对产品所需物料进行新增,新增后方便统一进行管理。
• 物料监管
将所有物料集中放于一个列表之中,便于查看与管理。
操作步骤
1、点击【基础数据】下面的【物料管理】,进入物料管理页面。
图片
2、点击【新增物料】,即可进入新增页面。
图片
3、新增物料页面需输入对应的编码(需用户手动输入,可自定义编码)、名称、类型(成品、半成品、原材料三选一)、来源(采购或制造二选一)、库存、VMI、WIP、安全库存、计量单位、提前期、缓冲期、参与齐套计算、拓展字段(拓展属性需在拓展属性模块维护后进行添加)。
PS:其中相关物料信息库存、VMI、WIP会进行每日自动同步。且同步过来的半成品和成品类型需默认为制造。
图片
PS:为保证系统使用顺畅,故将其中部分名词给出以下释义便于理解
安全库存:预防不确定性因素所导致的供给中断,从而确保持续供应和生产线的运行。当实际库存低于安全库存水平时,系统会触发警报并提示补充库存。
提前期:非制造类物料(如采购)到料提前期。即 此物料 至 到料所需的时长。
缓冲期:非制造类物料到料后 至 可投入生产的时长。如:到料后质量检查、入库等所需时长等。
参与齐套计算:若来源为采购,打开参与齐套计算后就会考虑库存量,并根据定义的提前期和缓冲期进行计算累加。排产后即会加上提前和缓冲时间然后再排产。若不打开则代表物料充足,无需考虑库存情况。
拓展字段:即定义的属性管理里面的值,选择对应属性后勾选对应属性值即作为该物料的拓展。
4、填写完相关信息后点击提交即会出现在列表页中,可对数据进行二次编辑或删除。
图片
5、此外还有查询功能,可选择输入编号或名称或类型、来源等方式,查出其中对应一条或相同类型或来源的一堆数据。输入完成后点击查询即可查到相关数据信息。
图片
6、下载模版可批量填写相关物料信息,填写完毕后点击导入物料即可实现批量新增物料清单。导出物料即是将现有列表里的物料进行全量导出方便进行数据比对验证准确性 。
图片
7、点击拓展字段,即可选择新增对应属性名称在物料管理主界面进行回显。
图片
在线demo:https://aps.bctools.cn
开源地址:https://gitee.com/software-minister/jvs-aps

人工智能的海啸,可能要席卷金融行业了。

事情是这样的:

今天,AI 独角兽 Perplexity 扔出一个新东西:Perplexity Computer,一个可以直接操控电脑的通用 AI 系统。官方号称它可以从研究、设计、写代码,到部署、管理,一个项目全流程跑完。

图片

一位做投资管理的网友,上手玩了玩这个新“电脑”,结果几轮操作下来,竟然搓出一个简易版的彭博终端(Bloomberg Terminal)

这一下,评论区直接炸了。

因为这个彭博终端,可是金融行业的“专业版信息操作系统”,连华尔街都在用。不过这种专业的金融工具价格非常昂贵,一年的订阅费要 2.4 万美元起,约合人民币 16 万元。

而 Perplexity Computer 这边,只需订阅 Perplexity Max,一年 2000 美元。价格大约是彭博终端年费的 1/12

图片

虽然这个用 Perplexity Computer 做的简易版,离彭博终端还有明显差距,但那种集中式金融工作台的感觉已经出来了。

还有不少网友本来不相信,结果都成功复现了。

图片

另辟 AI Agent 蹊径

不难看出,Perplexity Computer 也是一种 AI Agent 的衍生应用。更准确说,它是一个云端多 Agent 编排系统(meta-agent system)。

现在的 Agent,有的在争入口,比如 OpenClaw;有的则往企业协作场景延伸,比如 Claude Cowork。

现在 Perplexity Computer 横空出世,即没有既抢注桌面控制,也不只做企业协作。

那 Perplexity Compu.ter 有什么特别之处,或者说,它凭什么看起来这么猛?

一个关键因素,不是模型本身,而是它的调度能力。它把 19 个顶尖模型全都塞进同一套系统里,让它们分工干活。

核心推理用 Claude Opus 4.6,当“大脑”。

但具体干活的时候,它会按任务换“人”:比如,做深度研究、拆分子任务,用 Gemini;生成图片,用 Nano Banana;做视频,用 Veo 3.1;追求速度的小任务丢给 Grok;需要长上下文、跨大量信息检索的时候,用 ChatGPT5.2。

总而言之,它的重点在于多模型的“编排”。把不同模型组织起来,自动拆任务,能够自动生成子 agent 然后自动分配,把一整套复杂流程跑完。

除了前文提到的测评案例,还有人用 Perplexity Computer 为自己的论文做图然后表示,效果真不赖。

不过在一片褒奖声中,也有人指出,这玩意和 Manus 雷同。

聚光灯下的黑马之路

Perplexity 不是第一次炸网了。

作为硅谷最炙手可热的 AI 初创公司之一,Perplexity 从诞生起就自带“黑马体质”。

2022 年 8 月,Perplexity 在旧金山成立。随后这家初创公司凭借差异化打法,迎来爆发式增长:月活跃用户(MAU)突破千万,每月处理上亿次查询。

截止 2025 年底,四年时间,估值已达 200 亿美元,凭着差异化打法,打破谷歌在搜索领域“一言堂”的局面。

它也收获了科技圈大佬的青睐。英伟达、亚马逊创始人贝索斯纷纷入局投资,Andrej Karpathy、Garry Tan 等硅谷知名投资人也加入其“投资天团”。连足球巨星克里斯蒂亚诺·罗纳尔多都参与进来。

其中,英伟达的支持尤为高调。2024 年 1 月,英伟达正式参与 Perplexity 7360 万美元的 B 轮融资,黄仁勋更是在《WIRED》采访中直言,自己“几乎每天都用 Perplexity”,尤其偏爱用它查阅专业领域资料。可以说得到了大佬的背书。

Perplexity 之所以敢这么玩,和它的创始团队有关。

创始人 Aravind Srinivas,师从强化学习大牛 Pieter Abbeel,后来在 OpenAI 做核心语言模型研究。

联合创始人兼 CTO Denis Yarats 则来自 Meta,是 Yann LeCun 的学生。

两个人背景都很硬,但更关键的是,他们对“搜索”都有同一个不满:谷歌越来越像广告位,而不是答案。Aravind 在读博时就想做一个真正能给出直接结论的“研究助理”。巧的是,Denis 也在研究类似方向,甚至论文思路都高度相似。

GPT-3.5 发布(2022 年 11 月 30 日)一周后,他们直接下场,把这个想法变成了 Perplexity。

Perplexity 可以说完成了对“搜索”的重新定义。不同于谷歌的“链接聚合”模式,Perplexity 推出的“答案引擎”(Answer Engine),核心依托 RAG(检索增强生成)技术,能实时抓取网页信息、整合分析,最终给用户呈现带引用来源的完整答案。

这正是它与纯大模型对话工具的核心区别,也精准击中了现代人“信息过载却难以快速获取有效内容”的痛点,被不少用户称为“信息降级综合征”的解药。

Perplexity 最出圈的高光时刻,是在 2024 年 11 月的谷歌反垄断案期间。

当时它公开表态:如果监管要求谷歌出售 Chrome,自己有兴趣收购。

此话一出,全场哗然。

Chrome 的估值大概在 150 亿到 200 亿美元,甚至更高;而那时的 Perplexity 估值只有 90 亿美元,成立还不到两年。

一家初创公司,竟敢公开对谷歌的核心资产放话——不管能不能成,姿态已经摆明:它不只想做个 AI 搜索工具,它想进主场。

在创始人 Aravind 看来,一旦将 Chrome 收入囊中,Perplexity 便能将其彻底重塑,打造成下一代 AI 原生浏览器,让搜索与浏览体验深度融合,进一步巩固自身的差异化优势。

目前 Perplexity 团队规模仅 150-200 人左右,绝大多数是顶级 AI 研究员、全栈工程师和增长黑客,就是这样一支相当于谷歌千分之一人力(谷歌全球员工约 18 万人)的小团队,不仅做出了年营收 5000 万美元的成绩,更凭借颠覆性的产品体验,从谷歌手中切走了一部分高净值搜索流量,成为硅谷“AI 时代超级小团队”的标杆。

但对 Perplexity 而言,这 5000 万美元不过是“颠覆谷歌”野心的起点,仅仅是吹响进攻号角的“第一滴血”。

不过风光背后,Perplexity 的日子并不轻松,一系列挑战正接踵而至。

首先是商业化的困境:钱从哪来?

Perplexity 现在主要靠订阅。可 AI 搜索的成本远高于传统搜索——一次普通的谷歌查询成本可能只是几美分,而 Perplexity 每次都要跑 RAG、解析网页、调用大模型生成答案,背后烧的是真金白银的 GPU。

20 美元一个月的订阅费,很难撑起大规模用户的算力消耗,它必须找到更硬的变现方式。

Aravind 曾说过,不想走谷歌那套广告老路,想探索“为准确引用付费”或企业级资料库搜索。但广告试水并不顺利,版权争议一上来,赞助问答、品牌视频这些尝试很快收缩,最后还是回到订阅模式。

版权问题更是长期阴影。Perplexity 的答案建立在全网抓取之上,这天然会碰到媒体的利益边界,诉讼压力始终存在——“颠覆谷歌广告模式”的理想,落地并不轻松。

还有一个更深层的隐患:它没有自己的基础模型。

Perplexity 的强项在产品和整合能力,但底层模型依赖 OpenAI、Anthropic 等巨头。当 Claude、GPT 本身越来越强,联网搜索、长文本总结越来越成熟时,很多人开始问:如果模型原生就能做这些,我为什么还要单独用 Perplexity?

与此同时,Anthropic 等厂商强势推进自己的生态,流量和关注度都在被分流。

所以 Perplexity 最近把重心压到了 Agent 上——从“给你答案”升级为“替你办事”。如果它能掌握交易和执行环节,就有机会从中抽佣,把商业模式从单纯订阅,拉到更有想象力的层级。

不过,这条路同样烧钱,也同样激烈。

参考链接:

https://x.com/perplexity_ai/status/2026695550771540489

https://www.perplexity.ai/mk/hub/blog/introducing-perplexity-computer

https://x.com/hamptonism/status/2026778742094442959

前言

B 站的拜年祭,正在变成一座装修精美的<span style="color: red;font-size: 16px">「电子坟墓」</span>

很多人抱怨现在的节目「没那味儿了」,这话说得太温柔。事实是:当一个社区试图讨好所有人的时候,它就已经开始背叛最初的那群人了。

我扒开了这 17 年的互动数据,没看代码,只看人心。数据显示:制作精细度提升了几十倍,用户的灵魂却集体「离家出走」了。

今天,我们不聊技术,只聊聊这场大型的<span style="color: red;font-size: 16px">「变心」</span>现场。

2010-2012:「为爱发电」的社区

那两年,B 站还是个连服务器都随时会挂掉的小作坊,但那确是用户黏性最強的时候。

2010 年:给「赛博童年」补票

我分析 2010 年数据时发现了一个有趣的现象:<span style="color: red;font-size: 16px">评论区中 2017 年后的「考古」回复占比居然超过 65%</span>。

评论时间分布

这说明了什么?真正经典的内容是有「利息」的。爆发期那点流量只是「首付」,后来的十年里,无数老兵回来互动,这是在给当年的情怀补票。那时候大家不看画质,看的是「同类」。

互动区情感评分(AI)

评论话题分布(AI)

2012 年:那场关于「参与感」的集体狂欢

那是极客们至今还在怀念的黄金时代。那一年,B 站上线了高级弹幕。

<span style="border-bottom: 2px solid #fb7299; font-weight: bold;">用户可以用硬币换权限,亲手在屏幕上「涂鸦」。</span>

高级弹幕例子

虽然硬币是登录即送的「免费物资」,但在那个纯粹的年代,这些硬币承载的是用户的活跃度和社区荣誉。放到现在,这叫「付费解锁特效」,但在当年,这是用户认可社区的最高体现。

2010,2011,2012互动数据对比

数据直接翻倍,这证明了:当用户觉得这个地方属于自己时,他们是真的舍得「梭哈」掉手里所有的"积蓄"。

2014:陈睿进场,净土开始「挂牌招标」

这一年,陈睿正式任职董事,B 站开启了商业狂飙模式。一个有意思的现象是:播放量明明打不过 2012 年,但<span style="color: red;font-size: 16px">弹幕和硬币数却诡异地完成了反超</span>。

4年份的数据对比

这种数据倒挂说明了什么?

或许是老用户开始产生「被迫害妄想」了,他们觉得 B 站正在被主流社会围剿,所以疯狂投币、刷弹幕来证明自己的存在。这是一种典型的「饭圈化」雏形——用户投的不是币,是守护这个「小破站」的投名状。

很多人没注意到,2014 年也是 B 站商业化的「元年」。
<span style="color: red;font-size: 16px">开屏广告、首页 Banner 正式上线,专业的广告销售部门同年组建。</span>

这意味着:「二次元净土」,正式被划分成了待售的「广告位」。 当陈睿把品牌广告带进社区的那一刻,B 站就已经从「爱好的聚居地」变成了「待售的流量池」。拜年祭不再仅仅是 UP 主的狂欢,它开始承载起向甲方爸爸「秀肌肉」的任务。

2014评论时间分布

2016-2018:当回旋镖开始加速

如果说前几年是自嗨,那 2016 年就是 B 站试图跨越阶级的开始。

2016 年:那句名言背后的「信任危机」

爆款作品《九九八十一》横空出世,风光背后,是 B 站因为试水贴片广告被骂到公开道歉。由此陈叔叔还留下了那句著名的名言:

<p style="text-align: center; color: red; font-size: 18px; font-weight: bold; padding: 20px; border: 1px dashed red;">B 站未来有可能会倒闭,但绝不会变质。</p>

现在看来,这句话就是一个精准的「情感债务」。为了不倒闭,它必须学会赚钱;为了赚钱,它就得对老用户「痛下杀手」。

2018 年:45% 互动率的「巅峰时刻」

上市之年,数据给足了面子。<span style="color: red; font-weight: bold;">互动比例达到了恐怖的 45%</span>。

2016vs2017vs2018

这绝非偶然,而是 B 站当时在疯狂推行「一键三连」。大家觉得「小破站出息了」,那种社区自豪感被推到了顶点。但这也是核心用户最后一次大规模的「集体共鸣」。

2019-2020:7600 万的「流量巨浪」

这是综合数据上最亮眼的两年。

2019 年拜年祭直接冲到了一个离谱的高度,并开启了 2020 年那场 7600 万播放的全系列天花板。
<span style="color: red;font-size: 16px">流量像海啸一样灌进了这个曾经的小社区。</span>

18vs19vs20

从运营角度看,这是典型的「破圈红利」。B 站不再满足于二次元,它想要《后浪》,想要跨年晚会,想要全中国年轻人的注意力。

那两年的封面越来越有「大厂宣发」的高级感。

2019封面

看似 B 站赢麻了,但从数据上看,用户互动的比例已经开始悄然下滑。狂欢是大家的,但孤独(和被稀释的归属感)是老用户的。

2021:那一字之差的「战略代价」

2021 年是拜年祭历史上最沉重的一年。

承载了 11 年情怀的「拜年祭」,正式更名为「拜年纪」。这一字之差,宣告了「管理权」的彻底交接——从社区的狂欢,正式转向了大厂的叙事。

数据反馈极其扎心:播放量缩水 3 倍,互动比暴跌至 8%。
<span style="border-bottom: 2px solid #fb7299; font-weight: bold;">这不是内容的失败,而是「破圈」必经的阵痛。</span>

2020vs2021拜年纪

那一年的 B 站,试图在保持二次元纯度与迎合大众口味之间跳舞,结果却像是一个尴尬的平衡木选手。老用户觉得「灵魂丢了」,新用户觉得「节目像低配版春晚」。

名字改了,那个敢爱敢恨的小破站,也正式变成了要看财报眼色的 BiliBili。

2022-2026:当「情怀」变成了「电子养老院」

这几年的数据,写满了「疲软」。

2024 年:一个人的寂寞,千万人的静默

评论数跌破 1 万条。对于一个千万级播放的视频来说,这简直是<span style="color: red;">「社区死亡证明」</span>。

2023vs2024

我甚至怀疑:除了那些自动播放的流量,还有多少人是真的坐下来看完了?

2025 年:靠 2233 娘「喂情绪」

今年弹幕回升了,但我扒开数据一看:<span style="color: red;font-size: 16px">至少 20% 的互动是在刷 2233 娘。</span>

高频率分析(AI)

这证明了:观众不再关心节目好不好看,他们只是回来给「情怀」上柱香的。B 站也深谙此道,靠着主线动画精准拿捏。这不是晚会的胜利,这是「啃老」。

2026 由于没跑满一年,这里略过,数据上截止目前也是一般般

最后

16 年的拜年数据跑下来,最直观的感受是:拜年节目虽然变得越来越精美,但用户变得越来越冷了。

很多人会问:B 站变了吗?

  1. 从情怀看:它确实背叛了。那句「绝不变质」现在看来就是扎向自己的回旋镖,每当财报不好看,这镖就飞回来扎自己一刀。
  2. 从现实看:它只是成年了,需要为周围的人考虑而已。

<span style="color: red;font-size: 16px">没有商业化的理想主义,最终只会消失在互联网浪潮中。</span>

它不是内容不行了,而是那个曾经年轻充满战斗力的社区,已经在资本和流量的冲击下,彻底「变成了成年人」

本文由mdnice多平台发布

一、摘要

本文介绍了一个基于SNMP协议的网络设备批量巡检脚本,该脚本使用pysnmp库实现了对网络设备接口状态的自动采集和分析。简要介绍了SNMP协议并对比了不同网络设备管理方式的优缺点,讲解了pysnmp库的使用方法,最后对脚本的功能、使用场景和局限性进行了分析。

二、练习环境

  1. 使用华三HCL模拟器进行设备模拟,确认本地电脑可ping通模拟设备。具体可参考我的文章[网络自动化学习笔记-H3C 模拟器(HCL)基础环境配置]。

image-1.png

  1. 辅助工具

    [ManageEngine MibBrowser]

    通过工具,使用SNMP查询设备信息,辅助验证脚本功能。

  2. Python环境
  • Python 3.14
  • pysnmp,pysmi 库
  • VS code(可选)

三、SNMP协议简介

SNMP(Simple Network Management Protocol)是一种用于网络管理的标准协议,它允许网络管理员监控网络设备的状态、配置和性能。

提醒

只使用SNMP进行巡检和性能分析,不建议使用SNMP进行配置,尤其是实际网络中尽量不要使用配置功能
可能因空格,缩进,标识符等原因,导致CLI界面对应配置项不可用。

3.1 SNMP在网络设备管理中的作用

  • 实时监控:可以实时获取设备的运行状态,如接口状态、CPU使用率、内存使用情况等
  • 故障诊断:当网络出现问题时,可以通过SNMP快速定位故障点
  • 性能分析:通过收集历史数据,可以分析设备的性能趋势,预测潜在问题
  • - 配置管理:【不推荐,不建议可以远程配置设备参数,减少人工操作

四、CLI,SNMP,Netconf的不同方式对比

4.1 Paramiko CLI方式

优势

  • 可以执行任意CLI命令,功能全面
  • 易操作易理解,因为多数工程师才能熟练使用CLI命令行。
  • 可以获取设备特有的信息
  • 对设备的控制能力强

劣势

  • 依赖设备的CLI接口,不同设备命令格式不同
  • 解析命令输出复杂,容易出错
  • 需要回读屏显获取数据,不方便
  • 效率低,不适合大规模设备管理
  • 安全性依赖于SSH配置

4.2 SNMP方式

优势

  • 标准化,跨设备兼容性好
  • 效率高,适合批量操作
  • 实时性好,响应速度快
  • 实现简单,开发成本低

劣势

  • 功能相对有限,某些设备特有信息无法获取,或需要依赖厂家私有MIB。
  • 安全性较差(v1/v2c)
  • 依赖设备的SNMP实现质量

4.3 NETconf方式

优势

  • 基于XML/JSON,数据结构清晰
  • 支持配置和状态的获取与修改
  • 安全性高,支持多种认证方式
  • 功能强大,可扩展性好

劣势

  • 实现复杂,开发成本高
  • 对设备的支持程度不如SNMP广泛
  • 占用带宽较大,不适合带宽有限的环境
  • 学习曲线较陡峭

五、Pysnmp库介绍

Pysnmp是一个纯Python实现的SNMP库,支持SNMP v1、v2c和v3版本。它提供了丰富的API,使得开发者可以方便地实现SNMP管理功能。

5.1 版本区别

Pysnmp有两个主要版本:

  • 传统版本(v4.x):使用同步API,导入方式为from pysnmp.hlapi import *
  • 最新版本(v5.x):支持异步API,导入方式为from pysnmp.hlapi.v3arch.asyncio import *

本文使用的是最新版本的pysnmp,它支持异步操作,更适合处理大量设备的并发查询。

5.2 常见Pysnmp函数示例

以下示例均来自官方网站【GET Operation - PySNMP 7.1 Documentation】,并稍了做修改。

5.2.1 get操作(获取单个OID的值)

import asyncio
from pysnmp.hlapi.v3arch.asyncio import *

async def run():
errorIndication, errorStatus, errorIndex, varBinds = await get_cmd(
SnmpEngine(),
CommunityData('readtest', mpModel=1),
await UdpTransportTarget.create(('10.6.6.6', 161)),
ContextData(),
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysName', 0)),
ObjectType(ObjectIdentity('IF-MIB', 'ifName', 2))
)
for varBind in varBinds:
print("-----------------------------")
print(f"查询到的内容是{varBind}")
print(f"sysName的OID值是{varBind[0]}")
print(f"ifName的OID值是{varBind[1]}")
print(f"sysName的可读化输出值是{varBind[0].prettyPrint()}")
print(f"ifName的可读化输出值是{varBind[1].prettyPrint()}")
print(f"{varBind[0].prettyPrint()} = {varBind[1].prettyPrint()}")
print("-----------------------------")

asyncio.run(run())

示例运行截图如下

get01.jpg

5.2.2 next操作(获取下一个OID的值)

import asyncio
from pysnmp.hlapi.v3arch.asyncio import *

async def run():
    errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
        SnmpEngine(),
        CommunityData('readtest', mpModel=1),
        await UdpTransportTarget.create(('10.3.3.3', 161)),
        ContextData(),
        ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysContact', 0)),      #会返回sysContact的下一个OID值
        ObjectType(ObjectIdentity('IF-MIB', 'ifName', 2)),          #会返回ifName.2的下一个OID值
        lexicographicMode=False
    )
    for varBind in varBinds:
        print("-----------------------------")
        print(varBind[1].prettyPrint())
        print("-----------------------------")
    
asyncio.run(run())

next操作会获取下一个值,如下图所示

next01.jpg

next02.jpg

示例运行截图如下

next03.jpg

脚本查询sysContact,实际返回值是sysName的值,查询ifName.2,实际返回值是ifName.3的值。

5.2.3 bulk操作(批量获取多个OID的值)

import asyncio
from pysnmp.hlapi.v3arch.asyncio import *

async def run():
    errorIndication, errorStatus, errorIndex, varBinds = await bulk_cmd(
        SnmpEngine(),
        CommunityData('readtest', mpModel=1),
        await UdpTransportTarget.create(('10.3.3.3', 161)),
        ContextData(),
        0, 100,                                #循环查询100次。
        ObjectType(ObjectIdentity('IF-MIB', 'ifName'))
    )
    for varBind in varBinds:
        print(varBind)
    
asyncio.run(run())

示例运行结果如下

bulk01.jpg

因为是循环取值,结果显示范围远远超出了ifName的边界。

六、脚本介绍

6.1 功能概述

本次练习的脚本是一个基于SNMP v2c的模拟批量设备巡检工具,主要功能包括:

  1. 设备IP管理:从devices.txt文件读取设备IP列表,支持空行、注释行和重复IP的自动处理
  2. SNMP查询:使用异步方式批量查询设备的接口信息,包括接口索引、描述、名称、管理状态和运行状态
  3. 结果处理:将查询结果保存到CSV文件,并生成详细的日志文件
  4. 错误处理:对查询过程中的错误进行捕获和记录

6.2 脚本内容

  1. 导入模块
  2. 构建日志配置函数,IP文件读取函数,SNMP查询配置,配置格式化函数,查询函数,结果保存函数
  3. 主函数调用构建的功能函数,脚本入口。
import asyncio
import csv
import logging
import time
from datetime import datetime
from pysnmp.hlapi.v3arch.asyncio import *

# 配置日志函数
def setup_logger(log_file):
    """设置日志配置"""
    logger = logging.getLogger(log_file)
    logger.setLevel(logging.INFO)
    
    # 清除之前的处理器
    for handler in logger.handlers[:]:
        logger.removeHandler(handler)
    
    # 创建文件处理器
    file_handler = logging.FileHandler(log_file, encoding='utf-8')
    file_handler.setLevel(logging.INFO)
    file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
    logger.addHandler(file_handler)
    
    # 创建控制台处理器
    stream_handler = logging.StreamHandler()
    stream_handler.setLevel(logging.INFO)
    stream_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
    logger.addHandler(stream_handler)
    
    return logger

# 读取设备IP列表
def read_device_ips(file_path, logger=None):
    ips = []
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                stripped_line = line.strip()
                if not stripped_line or stripped_line.startswith("#"):
                    continue    #跳过空行和注释行
                ips.append(stripped_line)
        
        set_ip = set(ips)
        if len(set_ip) == len(ips):
            message = f"读取到的IP地址列表,共{len(ips)}个IP地址,没有重复的IP地址"
            print(message)
            if logger:
                logger.info(message)
        else:
            count_duplicates = len(ips) - len(set_ip)
            message = f"读取到的IP地址列表,共{len(ips)}个IP地址,有{count_duplicates}个重复的IP地址,已自动去重"
            print(message)
            if logger:
                logger.info(message)
            ips = list(set_ip)
            

        message = f"成功读取{len(ips)}个有效设备IP"
        print(message)
        if logger:
            logger.info(message)
        return ips
    except Exception as e:
        message = f"读取设备IP文件失败: {e}"
        print(message)
        if logger:
            logger.error(message)
        return []

# 定义SNMP查询配置
PORT_CONFIG = [
    ("IF-MIB", "ifIndex","端口索引"),
    ("IF-MIB", "ifDescr","端口描述"),
    ("IF-MIB", "ifName","端口名称"),
    ("IF-MIB", "ifAdminStatus","物理状态"),
    ("IF-MIB", "ifOperStatus","协商状态")
]

#构建OID路径,并返回格式化的MIB列表、 表格映射、 起始OID路径
def format_config(config=PORT_CONFIG):
    format_mib = []
    format_table = {}
    start_name = ""
    for item in config:
        if len(item) == 3:
            format_mib.append(ObjectType(ObjectIdentity(item[0], item[1])))
            format_table[item[1]] = item[2] # 端口描述
            start_name = f"{item[0]}::{item[1]}"
        else:
            format_mib.append(ObjectType(ObjectIdentity(item[0], item[1])))
            format_table[item[1]] = item[1]
            start_name = f"{item[0]}::{item[1]}"
        print(f"format_table is [{format_table}]")
        print(f"start_name is {start_name}")
    print(f"format_mib is {format_mib}")
    return format_mib, format_table, start_name

async def get_query(ip, community, config=PORT_CONFIG, logger=None):
    """
    使用next_cmd自动遍历所有接口的信息,直到没有更多数据为止。
    通过分析返回的OID,判断是否继续查询下一个接口。
    """
    try:
        snmpEngine = SnmpEngine()
        transport = await UdpTransportTarget.create((ip, 161))
        
        # 存储结果的字典
        interface_details = {}
        hostname = ""
        message = "开始遍历所有接口..."
        print(message)
        if logger:
            logger.info(f"{ip} - {message}")
        
        # 首先查询sysName
        errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
            snmpEngine,
            CommunityData(community, mpModel=1),
            transport,
            ContextData(),
            ObjectType(ObjectIdentity("SNMPv2-MIB", "sysName")),
            lexicographicMode=False
        )
        
        # 处理sysName
        if not errorIndication and not errorStatus and varBinds:
            for varBind in varBinds:
                try:
                    if isinstance(varBind, ObjectType):
                        oid = varBind[0]
                        val = varBind[1]
                    elif isinstance(varBind, tuple) and len(varBind) >= 2:
                        oid, val = varBind[0], varBind[1]
                    else:
                        continue
                    
                    oid_str = oid.prettyPrint()
                    val_str = val.prettyPrint()
                    
                    if oid_str == 'SNMPv2-MIB::sysName.0':
                        hostname = val_str
                        message = f"系统名称: {hostname}"
                        print(message)
                        if logger:
                            logger.info(f"{ip} - {message}")
                except Exception as e:
                    message = f"处理sysName异常: {e}"
                    print(message)
                    if logger:
                        logger.error(f"{ip} - {message}")
                    continue
        
        # 遍历config中的每个OID进行查询
        for item in config:
            mib, oid_name, desc = item
            message = f"\n开始查询 {mib}::{oid_name}..."
            print(message)
            if logger:
                logger.info(f"{ip} - 开始查询 {mib}::{oid_name}...")
            
            # 统计当前OID查询到的条目数
            item_count = 0
            non_expected_oid = None
            
            # 执行初始的next_cmd查询
            errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
                snmpEngine,
                CommunityData(community, mpModel=1),
                transport,
                ContextData(),
                ObjectType(ObjectIdentity(mib, oid_name)),
                lexicographicMode=False
            )
            
            while not errorIndication and not errorStatus and varBinds:
                # 处理当前批次的数据
                last_oid = None
                
                for varBind in varBinds:
                    try:
                        # 解包varBind
                        if isinstance(varBind, ObjectType):
                            oid = varBind[0]
                            val = varBind[1]
                        elif isinstance(varBind, tuple) and len(varBind) >= 2:
                            oid, val = varBind[0], varBind[1]
                        else:
                            continue
                        
                        oid_str = oid.prettyPrint()
                        val_str = val.prettyPrint()
                        
                        # 检查是否是当前OID的延续
                        if oid_str.startswith(f"{mib}::{oid_name}."):
                            # 提取索引
                            index = str(oid[-1])  # 直接从OID对象获取最后一个数字
                            
                            # 初始化接口信息
                            if index not in interface_details:
                                interface_details[index] = {}
                                for config_item in config:
                                    interface_details[index][config_item[1]] = None
                            
                            # 更新接口信息
                            interface_details[index][oid_name] = val_str
                            last_oid = oid
                            item_count += 1
                        else:
                            non_expected_oid = oid_str
                            break
                    except Exception as e:
                        message = f"处理数据异常: {e}"
                        print(message)
                        if logger:
                            logger.error(f"{ip} - {message}")
                        continue
                
                # 继续下一次查询
                if non_expected_oid:
                    break
                else:
                    errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
                        snmpEngine,
                        CommunityData(community, mpModel=1),
                        transport,
                        ContextData(),
                        ObjectType(ObjectIdentity(mib, oid_name, index)),  # 从上次的最后一个值继续
                        lexicographicMode=False  # 只遍历当前OID子树
                    )
            
            # 输出当前OID查询结果
            message = f"获取到: {mib}::{oid_name} 条目共计 {item_count} 个。"
            print(message)
            if logger:
                logger.info(f"{ip} - {message}")
            if non_expected_oid:
                message = f"非预期OID: {non_expected_oid}"
                print(message)
                if logger:
                    logger.info(f"{ip} - {message}")
        
        # 输出总体结果
        interface_count = len(interface_details)
        config_count = len(config)
        message = f"\n共找到 {interface_count} 个接口,查询了它们 {config_count} 项信息。"
        print(message)
        if logger:
            logger.info(f"{ip} - {message}")
        
        # 按索引排序输出前10个接口示例
        print("前10个接口示例:")
        if logger:
            logger.info(f"{ip} - 前10个接口示例:")
        for idx, details in sorted(interface_details.items(), key=lambda x: int(x[0]))[:10]:
            interface_message = f"  ifIndex.{idx}: {details.get('ifName', 'N/A')}"
            print(interface_message)
            if logger:
                logger.info(f"{ip} - {interface_message}")
        
        snmpEngine.close_dispatcher()
        return interface_details, hostname
        
    except Exception as e:
        print(f"异常: {e}")
        return {}, ""

# 保存查询结果到CSV文件
def save_results(interface_details, hostname, ip, file_path, config=PORT_CONFIG):
    try:
        
        with open(file_path, 'a', newline='', encoding='utf-8-sig') as f:
            writer = csv.writer(f)
            print(f"正在保存结果到 {file_path}...")
            
            # 写入表头
            writer.writerow(["=" * 80])
            line = f"主机名={hostname};设备IP={ip};检查时间:{time.strftime('%Y-%m-%d %H:%M:%S')}"
            writer.writerow([line])
            writer.writerow(["=" * 80])
            
            # 动态生成表头
            headers = [item[2] for item in config]
            writer.writerow(headers)
            
            # 写入数据
            for interface in interface_details.values():
                row = []
                for item in config:
                    row.append(interface.get(item[1], ''))
                writer.writerow(row)
        print(f"成功保存查询结果到{file_path}")
    except Exception as e:
        print(f"保存查询结果失败: {e}")

async def main():
    # 配置参数
    device_file = 'devices.txt'  # 设备IP文件
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    port_result_file = f'snmp_port_results_{timestamp}.csv'  # 端口结果文件
    port_log_file = f'snmp_port_results_log_{timestamp}.log'  # 端口日志文件
    community = 'readtest'  # SNMP团体名
    
    # 设置日志
    port_logger = setup_logger(port_log_file)
    
    # 读取设备IP
    device_ips = read_device_ips(device_file, logger=port_logger)
    if not device_ips:
        port_logger.error("没有设备IP可查询")
        return
    
    # 批量查询设备信息
    for ip in device_ips:
        port_logger.info(f"开始查询设备: {ip}")
        
        # 获取端口信息和主机名
        interface_details, hostname = await get_query(ip, community, config=PORT_CONFIG, logger=port_logger)
        
        # 保存端口结果
        if interface_details:
            save_results(interface_details, hostname, ip, port_result_file, config=PORT_CONFIG)
            port_logger.info(f"{ip} - 成功保存端口结果到 {port_result_file}")
        
        # 避免查询过快,添加短暂延迟
        await asyncio.sleep(1)
    port_logger.info("批量查询完成")
if __name__ == "__main__":
    asyncio.run(main())

6.3 脚本输出结果保存

main01.jpg

6.4 脚本运行日志保存

log01.jpg

6.5 脚本局限性

  1. 仅支持SNMP v2c:没有对SNMP v3进行适配,安全性较差
  2. 功能有限:目前只支持接口状态的查询,无法获取CPU、内存、温度等系统资源信息
  3. 可扩展性不足:添加新的查询项需要修改代码,不够灵活
  4. 错误处理简单:对于SNMP查询失败的情况,处理方式较为简单
  5. 配置固定:SNMP团体名、查询参数等配置硬编码在脚本中,不够灵活

6.6 后续练习中可能尝试的功能

  1. 添加系统资源监控:增加对CPU使用率、内存使用情况、设备温度等信息的查询
  2. 支持SNMP v3:添加对SNMP v3的支持,提高安全性
  3. 配置文件化:将配置参数移到配置文件中,方便用户修改
  4. 结果可视化添加简单的结果可视化功能,如生成状态图表,或日志格式化提升可读性
  5. 告警机制:添加异常状态的告警功能,如接口down时发送通知

七、结语

SNMP作为一种成熟的网络管理协议,在网络设备监控和管理方面发挥着重要作用。本文介绍的批量巡检脚本利用pysnmp库实现了对网络设备接口状态的自动采集和分析,为网络管理员提供了一种高效、便捷的设备管理工具。

虽然该脚本目前存在一些局限性,但其基本功能已经能够满足日常网络巡检的需求。通过进一步的扩展和优化,它可以成为网络管理工作中的得力助手。

在实际应用中,网络管理员可以根据具体需求对脚本进行定制和扩展,例如添加更多的监控指标、实现更复杂的告警机制等,从而构建一个更加完善的网络监控系统。

SNMP协议虽然有其局限性,但在网络设备管理领域仍然是一种不可替代的工具。结合其他管理协议和工具,如NETconf、REST API等,可以构建一个更加全面、高效的网络管理解决方案。


如果说 2024 年是 AI 生成代码(Copilot、Cursor)的元年,那么到了 2026 年,我们已经正式进入了“自然语言即接口、智能体(Agent)即员工”的深水区。

(图注:AI 开发演进时间轴:从早期的 TDD 时代,到 LLM 辅助编码,再到如今由 Testplan 支撑的“安全 AI 操作空间”与个人智能体时代。)

现在,哪怕是一个不懂编程的产品经理,只要动动嘴皮子,智能体就能在一个下午帮你拉起一个 SaaS 级的应用架构。这种生产力的爆发是极其震撼的。

然而,狂欢之下,暗流涌动。当开发流程被极度压缩,系统内部逻辑的可见性正在从“白盒”滑向“黑盒”。你不知道 AI 到底写了什么,你只知道它“看起来能跑”。速度被无限放大,但与此同时,代码失控的风险也达到了前所未有的高度。

在这个“高产但高危”的 AI 开发时代,我们迫切需要一套新的工程方法论。八年前我提出的 TPDD(测试计划驱动开发,Test Plan Driven Development),在今天,迎来了它真正的使命。

一、 危险的失衡:只有“油门”没有“刹车”的 AI 工程

当前绝大多数拥抱 AI 的团队,都陷入了一种结构性的策略失衡:极度关注“能力暴露(Capability Exposure)”,却系统性无视“禁止空间(Forbidden Zone)”。

(图注:AI 控制平衡图。真正的安全 AI 操作空间,必须建立在“能力暴露(绿区:能做什么)”与“禁止空间(红区:绝对不能做什么)”的交集之上。当前多数团队严重偏向绿区而忽视红区。)

什么是能力暴露?就是我们常写的 skill.md。比如告诉 AI:“你能读取 CSV,你能生成 PDF,你能调取天气 API。”大家都在拼命探索“AI 还能干什么”,这种模式在扩张期极其高效,但也极易造成灾难。

因为大家忘了定义“禁止空间”,也就是红线。比如,你没有告诉 AI:“绝对不允许访问系统底层文件、绝对不允许将包含隐私数据的图表发送到外网。”

【笔者观点:没有底线的全能,就是最大的炸弹】
我们现在使用 AI Agent,就像在开一辆马力无穷但没有刹车和安全带的超级跑车。
最近爆火的 OpenClaw 就因为对主机拥有无限制访问权限而遭到安全专家的炮轰。很多开发者享受着“一键生成”的快感,却忽略了 AI 存在幻觉(Hallucination)和提示词注入(Prompt Injection)的风险。如果不划定“禁止空间”,AI 失控删掉你整个数据库,或者把企业机密泄露出去,只是时间问题。在这个时代,“定义 AI 不能做什么”比“教 AI 能做什么”要重要一万倍。

二、 TDD 死亡,TPDD 崛起:当 AI 自己写单元测试时,人类该干嘛?

在传统的敏捷开发中,TDD(测试驱动开发) 是神圣的教条。程序员要先写一段单元测试,然后再写代码让测试通过。

但在 AI 时代,TDD 正在被“内化”。现在,大模型完全可以自己写代码,自己生成测试用例,如果跑不通,它还能自己重构。微观层面的单元级循环,人类已经插不上手了。


(图注:开发循环对比图。左侧传统的 TDD 循环(写测试->写代码->重构)已被 AI 替代;右侧是新的 AI+TPDD 循环,人类退守到高层的“定义测试计划(Define TestPlan)”与“高层验证(High-level Validation)”,中间的代码生成由 AI 完成。)

那人类工程师的价值在哪?答案是:退守到更高维度的“测试计划(TPDD)”控制层。

当 AI 把细节干完了,人类必须去回答三个宏观问题:

  1. 这个功能到底应该长什么样?
  2. 它的物理和逻辑边界在哪?
  3. 到底达到什么标准,才算真正的“验收通过”?

这就是 TPDD 的核心:高层先行 + 验证闭环。我们不再去跟 AI 卷每一行代码的逻辑,而是提前写好一份高维的“开发承诺书”。

【笔者观点:从“泥瓦匠”到“建筑监理”的身份跃迁】
过去我们是泥瓦匠(写代码),现在 AI 成了最高效的 3D 打印建筑机,而我们的身份必须立刻转变为“建筑监理”。
监理不需要自己去和泥,但他必须拿着图纸(TPDD 测试计划)站在旁边,明确指出哪里承重不合格、哪里不能用劣质材料。AI 最不缺的就是执行力,它最缺的就是“Failure Thinking(失败底线思维)”。人类通过 TPDD 为 AI 套上紧箍咒,这才是人机协作的终极形态。

三、 实战利器:用 TestPlan.md 打造 AI 的“安全护城河”

在实操中,我们如何把 TPDD 落地?很简单,给 AI 配备一个与 skill.md 同等地位的 TestPlan.md(或 redline.md


(图注:TestPlan.md 的两大核心作用:左侧为“内部限制(红线与禁止区域)”,右侧为“外部验证(基于 Must/Need/Should 优先级的测试计划)”。)

这个文件只做两件事:内部限制 + 外部验证

以“AI 生成销售报表”为例,你的 TestPlan.md 应该长这样:

  • 【Must Have(必须有)】:输入 CSV,必须生成内容正确的 PDF 报表。
  • 【能力边界】:文件最大不能超过 10MB,每小时最多调用 50 次(防 Token 刷爆)。
  • 【禁止空间(红线)】绝对不允许访问报表以外的本地文件;绝对不允许将数据发送到外部服务;绝对不允许执行任意代码。
  • 【异常预期】:如果缺少字段,必须返回明确报错,而不是瞎编数据。

当 AI Agent 在生成代码或执行任务时,它必须时刻被这个 TestPlan.md 监控和约束。

【笔者观点:将 QA(质量保证)思维变成全员的本能】
在 AI 时代,QA 不应该再是一个边缘化的测试岗位,而应该成为每一个开发者的“出厂设置”。
很多只追求短期速度的“高产型团队”,前期靠狂写 Prompt 出尽风头,但很快就会被无法维护的“技术债屎山”压垮。真正能活到最后的“克制型团队”,一开始就会花大量时间去写 Contract(契约)、写红线。在 AI 时代,防守比进攻更体现一家技术公司的底蕴。

四、 CTO 的行动指南:速度不再是唯一指标

对于技术管理者(CTO)而言,面对 AI 浪潮的冲击,必须立刻在管理层面做出转向:

  1. 拔高控制层级: 保留 TDD 的理念,但不要再让员工去卷单元测试。强制所有 AI 功能模块必须配套提交 TestPlan.md
  2. 强推沙盒机制: 所有引入的第三方 Agent 和 Skill,绝对不能直接接触核心生产数据库,必须在强隔离的沙盒(Sandbox)中运行。
  3. 分级验收: 建立严格的 Must / Need / Should 分级验证机制,确保核心的 Happy Path(完美路径)和 Edge Cases(极端异常)都被覆盖。
  4. Token 预算护栏: 警惕 AI 陷入死循环导致财务失控,建立坚固的 Token 消耗阻断机制。

总结:拥抱限制,方能穿越周期

AI 极大地降低了创造的门槛,但也放大了失控的风险。

在未来的软件开发中,“能力暴露”与“禁止空间”必须如同车之双轮,并行不悖。 速度不再是第一目标。因为当任何人都能用 AI 在一小时内生成一个 App 时,只有那些 “可控、可维护、坚守安全底线” 的系统,才能真正穿越技术的周期,沉淀为长期的商业价值。


👇 欢迎关注我的公众号

在 AI 爆发的深水区,我们一起探索真正能穿越周期的技术价值。
微信搜索 【睿见新世界】 或扫描下方二维码,获取每周硬核技术推文:

微信图片_20260301232734_225_35.jpg

欢迎关注【睿见新世界】!

All Money Back Me Home

一个用于 2Libra 的油猴脚本,提供金币收支可视化报表、筛选统计与徽章展示。
image
image

功能亮点

  • 自动拉取金币流水并生成统计报表
  • 收入 / 支出趋势图与类型分布图
  • 时间范围筛选:最近 7 天、30 天、90 天、自定义、全部
  • 分类筛选:按交易 reason 过滤
  • 用户级缓存隔离(不同账号互不污染)

数据与刷新策略

  • 打开报表时:
    • 第一次打开脚本会全量加载流水,徽章数据,请耐心等待(流水较多的用户)
  • 手动刷新按钮:
    • 同时执行“流水增量更新 + 徽章刷新”
    • 有变化才重渲染并写回缓存

使用说明

  1. 点击个人信息区域,佩戴徽章右边的统计 icon
    image
  2. 查看:
    • 当前余额
    • 累计收入 / 累计支出
    • 每日收支趋势
    • 收入/支出类型分布
  3. 使用筛选器调整时间范围和分类
  4. 需要最新数据时点击“刷新”图标

缓存键说明

脚本按用户隔离缓存(命名空间基于 username/id/token 片段):

  • 流水数据:2libra_audit_data:{namespace}
  • 流水缓存时间:2libra_audit_time:{namespace}
  • 最新流水时间戳:2libra_audit_latest:{namespace}
  • 筛选条件:2libra_audit_filter:{namespace}
  • 徽章数据:2libra_audit_badges:{namespace}
  • 徽章缓存时间:2libra_audit_badges_time:{namespace}

依赖

  • Chart.js
  • DaisyUI

注意事项

  • 如果页面结构大改,按钮注入位置可能需要适配
  • 若报表异常,可清理本地缓存后重试


主要是我自己想看看金币都花哪去了,所以捣鼓了这个脚本。smirksmirk

晒一下你门的金币流水吧,看看你是入不敷出还是富得流油?

脚本安装地址

在数字化办公日益普及的今天,企业内网承载着OA、ERP、CRM等大量核心业务系统。虽然这些系统部署在内部网络,但数据传输过程中的安全隐患依然存在。根据“等保2.0”标准,通信传输中必须采用密码技术保障数据的完整性和保密性 。为内网IP地址部署SSL证书实现HTTPS加密,已成为企业合规运营的刚需。

然而,为内网IP申请SSL证书并非易事。由于内网IP(如192.168.x.x)  属于私有IP段,全球通用的公共CA(证书颁发机构)通常无法通过公网验证其所有权。但这并不意味着没有办法,JoySSL 等国内CA机构提供了完善的解决方案。

一、为什么选择JoySSL申请内网IP证书?

在选择CA时,首先要确认其是否支持签发内网IP证书。目前,仅有少数国产CA支持这一业务,JoySSL便是其中的佼佼者 。

  • 国内验签,数据不出境:符合国家法律法规要求,尤其适合政府、教育、金融等领域 。
  • 支持内网IP验证:无需开放公网端口,通过特定文件验证或邮箱验证即可完成授权。
  • 技术响应迅速:提供中文技术支持,协助解决证书部署中的各种难题。

二、内网IP申请SSL证书详细步骤

以下是基于JoySSL平台的内网IP证书申请全流程,通常只需几分钟即可完成。

第一步:注册账号并获取技术支持

内网IP证书申请入口

  1. 访问 JoySSL 官方网站。
  2. 注册一个账号。为了顺利申请IP证书,在注册过程中务必填写注册码290970。这是为了对接IP证书专员,获取申请支持和免费证书权限。
第二步:提交证书申请
  1. 登录JoySSL后台,在证书选择列表中找到  “IP SSL证书”  或联系客服获取申请链接。
  2. 在申请页面填写您的内网IP地址(例如 192.168.1.100)以及其他必要信息(如组织名称、联系方式等)。
  3. 系统将自动生成CSR(证书签名请求),请妥善保存。
第三步:完成IP所有权验证

CA机构需要确认您对该内网IP拥有管理权。JoySSL支持针对内网环境的灵活验证方式:

  • 文件验证:将CA提供的验证文件上传至该IP地址所在服务器的指定目录下,并确保可通过HTTP(或HTTPS)访问到该文件 。
  • 邮箱验证:如果该IP关联了特定的管理邮箱,可通过查收验证邮件完成确认。
第四步:下载证书并部署到服务器
  1. 验证通过后,CA将签发证书。您可以在JoySSL账户后台下载证书文件包(通常包含 crt 文件和 key 私钥) 。
  2. 部署证书:以最常用的 Nginx 服务器为例:

    • 将证书文件和私钥文件上传至服务器指定目录。
    • 修改Nginx配置文件,添加HTTPS监听:1. - 保存配置后,重载Nginx服务:nginx -s reload

三、客户端配置与注意事项

由于内网IP证书并非由浏览器内置的公网根证书直接信任,您可能需要在访问的客户端设备上进行简单配置。

  1. 导入根证书:为了彻底消除浏览器“不安全”提示,可以在客户端电脑上安装JoySSL的根证书。企业可以通过域控策略批量推送,也可以手动双击安装。
  2. 固定IP地址:确保申请的IP地址是固定的。如果IP发生变更,证书将会失效。

龙虾实在是不得行,终究还不能算商业产品。还是得 claude code ,codex ,cursor ,gemini cli 这些才能真正干活。但是不能在聊天软件控制有点蛋疼,折腾一天,现在终于可以在微信跟飞书上控制这些 AI Agent 。10 分钟就能配好。

使用下面的项目:

github 地址:https://github.com/chenhg5/cc-connect

参考截图:

3e86f8becfb6d09bfc486f986bd75a74.JPG

15aeef6888984fa0eac05dddc83799c3.PNG

5c04ea42ad7ab909a767fa41217dce32.PNG

可以加用户群:

用户群

大家好,我是 Java陈序员

做自媒体、写博客、团队协作,经常会遇到文件托管和图床问题 —— 要么付费图床层层限速、到期失效,要么自建存储配置复杂、耗费精力,免费工具又大多功能残缺、隐私无保障。

今天,给大家介绍一款开源的文件托管工具,基于 Cloudflare 生态打造!

关注微信公众号:【Java陈序员】,获取开源项目分享、AI副业分享、超200本经典计算机电子书籍等。

项目介绍

CloudFlare-ImgBed —— 一款开源的文件托管工具,支持 Docker 和无服务器部署,支持 Telegram、Discord、Cloudflare R2、S3、Huggingface 等多种存储渠道,支持 WebDAV 协议和多种 RESTful API.

功能特色

  • 快速上手:轻松部署,支持 Cloudflare Pages 免费托管和 Docker 部署,满足不同需求
  • 多样上传:支持拖拽、点击、粘贴、URL 等多种上传方式,支持批量上传和目录管理
  • 多种渠道:支持 Telegram Bot、Cloudflare R2、S3 API 等多种存储方式,满足不同需求
  • 安全可靠:支持身份认证、IP 白名单、图片审查、防滥用等多重安全保障
  • 炫酷界面:流畅丝滑的过渡动画、呼吸灯效果、灵动的操作体验,支持深色模式
  • 个性定制:支持自定义背景、Logo、配色、链接前缀等,打造专属图床

快速上手

CloudFlare-ImgBed 支持 Cloudflare Pages 部署、Docker 部署、手动部署多种方式,可根据场景自定义部署。

  • Cloudflare Pages 是推荐的部署方式,具备免费托管、全球 CDN 加速和无需服务器维护的优势。部署方式可参考官方文档:
https://cfbed.sanyue.de/deployment/cloudflare.html
  • Docker 部署适合有自己服务器的用户,提供更多的控制权和自定义能力。

Docker 命令部署

1、创建项目目录

mkdir -p /data/software/cloudflare-imgbed/data
cd /data/software/cloudflare-imgbed

2、创建配置文件 wrangler.toml:

name = "cloudflare-imgbed"
compatibility_date = "2024-07-24"

# 可选:添加环境变量,v2.0 版本后大部分配置已迁移到管理后台
# [vars]

3、拉取镜像

docker pull marseventh/cloudflare-imgbed:latest

4、运行容器

docker run -d \
  --name cloudflare-imgbed \
  -p 7658:8080 \
  -v /data/software/cloudflare-imgbed/data:/app/data \
  -v /data/software/cloudflare-imgbed/wrangler.toml:/app/wrangler.toml \
  marseventh/cloudflare-imgbed:latest

5、启动成功后,浏览器访问

http://{IP/域名:7658}

Docker Compose 部署

1、创建项目目录

mkdir -p /data/software/cloudflare-imgbed/data
cd /data/software/cloudflare-imgbed

2、下载 Docker Compose 文件

# 下载 docker-compose.yml
wget https://raw.githubusercontent.com/MarSeventh/CloudFlare-ImgBed/main/docker-compose.yml

# 或者手动创建
curl -o docker-compose.yml https://raw.githubusercontent.com/MarSeventh/CloudFlare-ImgBed/main/docker-compose.yml

3、如果由于网络原因无法下载 Docker Compose 文件,可以手动创建 docker-compose.yml 并填写如下内容:

version: '3.8'

services:
  imgbed:
    image: marseventh/cloudflare-imgbed:latest
    ports:
      - "7658:8080"
    volumes:
      - ./wrangler.toml:/app/wrangler.toml
      - ./data:/app/data
    restart: unless-stopped

4、创建配置文件 wrangler.toml:

name = "cloudflare-imgbed"
compatibility_date = "2024-07-24"

# 可选:添加环境变量,v2.0 版本后大部分配置已迁移到管理后台
# [vars]

5、启动服务

# 启动服务(后台运行)
docker compose up -d

# 查看日志
docker compose logs -f

6、启动成功后,浏览器访问

http://{IP/域名}:7658

设置访问密码

CloudFlare-ImgBed 默认不需要密码即可上传文件和进入管理后台,建议安装成功后首先进行设置。

1、进入系统管理、系统设置、安全设置菜单

http://{IP/域名}:7658/systemConfig#security

2、输入密码并进行保存

功能体验

文件上传

  • 文件上传

  • 历史记录

系统管理

  • 文件管理

  • 系统状态

  • 上传设置

  • 网页设置

  • 其他设置

本地开发

CloudFlare-ImgBed 依赖 Node.js 环境,需提前安装 Node.js.

1、克隆或下载项目源码

git clone https://github.com/MarSeventh/CloudFlare-ImgBed.git

2、切换到项目根目录,安装依赖

cd CloudFlare-ImgBed
npm install

3、在项目根目录下新建 wrangler.toml 配置文件

name = "cloudflare-imgbed"
compatibility_date = "2024-07-24"

# 如果需要设置环境变量,可以在这里添加
# [vars]
# AUTH_CODE = "your_auth_code"
# TG_BOT_TOKEN = "your_bot_token"
# TG_CHAT_ID = "your_chat_id"

4、运行服务

npm run start

5、启动成功后,浏览器访问

http://localhost:8080

可以说,CloudFlare-ImgBed 是一款兼顾易用性和扩展性的开源文件托管系统,尤其适合需要基于 Cloudflare 生态快速搭建图床/文件托管服务的场景,同时支持多存储渠道和完善的管理能力。无论是个人使用还是小型团队协作,都能满足文件托管的需求。

项目地址:https://github.com/MarSeventh/CloudFlare-ImgBed

最后

推荐的开源项目已经收录到 GitHub 项目,欢迎 Star

https://github.com/chenyl8848/great-open-source-project

或者访问网站,进行在线浏览:

https://chencoding.top:8090/#/

我创建了一个开源项目交流群,方便大家在群里交流、讨论开源项目

但是任何人在群里打任何广告,都会被 T 掉

如果你对这个交流群感兴趣或者在使用开源项目中遇到问题,可以通过如下方式进群

关注微信公众号:【Java陈序员】,回复【开源项目交流群】进群,或者通过公众号下方的菜单添加个人微信,并备注【开源项目交流群】,通过后拉你进群

大家的点赞、收藏和评论都是对作者的支持,如文章对你有帮助还请点赞转发支持下,谢谢!

这是Java19新增的预览版功能,到Java21正式可以使用

简介

虚拟线程是一种用户态下的线程,类似go语言中的goroutines 和Erlang中的processes,虚拟线程并非比线程快,而是提高了应用的吞吐量,相比于传统的线程是由操作系统调度来看,虚拟线程是我们自己程序调度的线程。如果你对之前java提供的线程API比较熟悉了,那么在学习虚拟线程的时候会比较轻松,传统线程能运行的代码,虚拟线程也可以运行。虚拟线程的出现,并没有修改java原有的并发模型,也不会替代原有的线程。虚拟线程主要作用是提升服务器端的吞吐量。

为什么要有虚拟线程

服务器应用程序的伸缩性受利特尔法则(Little’s Law)的制约,与下面3点有关

  • 延迟:请求处理的耗时
  • 并发量:同一时刻处理的请求数量
  • 吞吐量:单位时间内处理的数据数量

比如一个服务器应用程序的延迟是50ms,处理10个并发请求,则吞吐量是200请求/秒(10 / 0.05),如果吞吐量要达到2000请求/秒,则处理的并发请求数量是100。按照1个请求对应一个线程的比例来看,要想提高吞吐量,线程数量也要增加。

java中的线程是在操作系统线程(OS thread)进行了一层包装(目前大部分语言实现采用的线程模型,都是用户态的线程一对一映射到内核线程上,好处是实现简单,统一由操作系统负责调度),OS线程的优点是它足够通用,不管是什么语言/什么应用场景,但OS线程的问题也正是来自于此:

  • OS不知道用户态的程序会如何使用线程,它会给每条线程分配一个固定大小的堆栈,通常会比实际使用的要大很多;
  • 线程的上下文切换要通过内核调度进行,相对更慢;
  • 线程的调度算法需要做兼顾和妥协,很难做特定的优化,像web server中处理请求的线程和视频编解码的线程行为有很大的区别;

为了解决该问题,虚拟线程就出现了。也就是多对多的线程模型:经典的就是Erlang的进程和Go的goroutine,M:N 的映射关系,大量(M)虚拟的线程被调度在较少数量(N)的操作系统线程上运行。用户态的运行时负责调度用户态线程,OS则只需要负责OS线程,各司其职。灵活度更高,开发者基本不用担心线程数爆炸的问题。

与虚拟地址可以映射到物理内存类似,java是将大量的虚拟线程映射到少量的操作系统线程,带来了一些好处:

  • 线程的切换很快,无需系统调用和系统级别的上下文切换
  • 分配线程的开销很低:一方面是创建和销毁很快,另一方面内存使用也更少
  • 竞态条件和线程同步处理起来更简单
  • 且虚拟线程的生命周期短暂,不会有很深的栈的调用
  • 一个虚拟线程的生命周期中只运行一个任务,因此可以创建大量的虚拟线程,
  • 虚拟线程无需池化

另一方面,虚拟线程不能带来什么?

要意识到虚拟线程是更轻量的线程,但并不是"更快"的线程,它每秒执行的CPU指令并不会比普通线程要多。假设有这样一个场景,需要同时启动10000个任务做一些事情:

// 创建一个虚拟线程的Executor,该Executor每执行一个任务就会创建一个新的虚拟线程
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            doSomething();
            return i;
        });
    });
}  // executor.close() is called implicitly, and waits

考虑两种场景:

  1. 如果doSomething()里执行的是某类IO操作,那么使用虚拟线程是非常合适的,因为虚拟线程创建和切换的代价很低,底层对应的可能只需要几个OS线程。如果没有虚拟线程,不考虑ForkJoin之类的工具,使用普通线程的话:
    • Executors.newVirtualThreadPerTaskExecutor()换成Executors.newCachedThreadPool()。结果是程序会崩溃,因为大多数操作系统和硬件不支持这种规模的线程数。
    • 换成Executors.newFixedThreadPool(200)或者其他自定义的线程池,那这10000个任务将会共享200个线程,许多任务将按顺序运行而不是同时运行,并且程序需要很长时间才能完成。
  2. 如果doSomething()里执行的是某类计算任务,例如给一个大数组排序,那么虚拟线程反而还可能带来多余的开销。

总结一下,虚拟线程真正擅长的是等待,等待大量阻塞操作完成。它能提供的是 scale(更高的吞吐量),而不是 speed(更低的延迟)。虚拟线程最适合的是原来需要更多线程数来处理计算无关业务的场景,典型的就是像web容器、数据库、文件操作一类的IO密集型的应用。

虚拟线程的理解

平台线程和虚拟线程

平台线程(platform thread):指Java中的线程,比如通过Executors.newFixedThreadPool()创建出来的线程,我们称之为平台线程。

虚拟线程并不会直接分配给cpu去执行,而是通过调度器分配给平台线程,平台线程再被调度器管理。Java中虚拟线程的调度器采用了工作窃取的模式进行FIFO的操作,调度器的并行数默认是Jvm获取的处理器数量(通过该方法获取的数量Runtime.getRuntime().availableProcessors()),调度器并非分时(time sharing)的。在使用虚拟线程编写程序时,不能控制虚拟线程何时分配给平台线程,也不能控制平台线程何时分配给cpu。

以前任务和平台线程的关系:

使用虚拟线程之后,任务-虚拟线程-调度器-平台线程的关系,1个平台线程可以被调度器分配不同的虚拟线程:

携带器

调度器将虚拟线程挂载到平台线程之后,该平台线程叫做虚拟线程的携带器(言外之意就是,平台线程携带着虚拟线程),调度器并不维护虚拟线程和携带器之间的关联关系,因此在一个虚拟线程的生命周期中可以被分配到不同的携带器,即虚拟线程运行了一小段代码后,可能会脱离携带器,此时其他的虚拟线程会被分配到这个携带器上。

携带器和虚拟线程是相互独立的,比如:

  • 虚拟线程不能使用携带器的标识,Thread.current()方法获取的是虚拟线程本身。
  • 两者有各自的栈空间。
  • 两者不能访问对方的Thread Local变量。

在程序的执行过程中,虚拟线程遇到阻塞的操作时大部分情况下会被解除挂载,阻塞结束后,虚拟线程会被调度器重新挂载到携带器上,因此虚拟线程会频繁的挂载和解除挂载,这并不会导致操作系统线程的阻塞。下面的代码在执行两个get方法和send方法(会有io操作)时会使虚拟线程发生挂载和解除挂载:

response.send(future1.get() + future2.get());

有些阻塞操作并不会导致虚拟线程解除挂载,这样会同时阻塞携带器和操作系统线程,例如:操作系统基本的文件操作,java中的Object.wait()方法。下面两种情况不会导致虚拟线程的解除挂载:

  1. 执行synchronized同步代码(会导致携带器阻塞,所以建议使用ReentrantLock替换掉synchronized)
  2. 执行本地方法或外部函数

虚拟线程和平台线程的区别

从内存空间上来说,虚拟线程的栈空间可以看作是一个大块的栈对象,它被存储在了java堆中,相比于单独存储对象,堆中存储虚拟线程的栈会造成一些空间的浪费,这点在后续的java版本中应该会得到改善,当然这样也是有一些好处的,就是可以重复利用这部分栈空间,不用多次申请开辟新的内存地址。虚拟线程的栈空间最大可以达到平台线程的栈空间容量。

虚拟线程并不是GC root,其中的引用不会出现stop-world,当虚拟线程被阻塞之后比如BlockingQueue.take(),平台线程既不能获取到虚拟线程,也不能获取到queue队列,这样该平台线程可能会被回收掉,虚拟线程在运行或阻塞时不会被GC

  • 通过Thread构造方法创建的线程都是平台线程
  • 虚拟线程是守护线程,不能通过setDaemon方法改成非守护线程
  • 虚拟线程的优先级是默认的5,不能被修改,将来的版本可能允许修改
  • 虚拟线程不支持stop(),suspend(),resume()方法

使用虚拟线程

java中创建的虚拟线程本质都是通过Thread.Builder.OfVirtual对象进行创建的,虚拟线程的API非常非常简单,在设计上与现有的Thread类完全兼容。虚拟线程创建出来后也是Thread实例,因此很多原先的代码可以无缝迁移。创建虚拟线程有三种方式:

  1. 通过Thread.startVirtualThread直接创建一个虚拟线程
//创建任务
Runnable task = () -> {
    System.out.println("执行任务");
};

//创建虚拟线程将任务task传入并启动
Thread.startVirtualThread(task);

//主线程睡眠,否则可能看不到控制台的打印
TimeUnit.SECONDS.sleep(1);
  1. 使用Thread.ofVirtual()方法创建
//创建任务
Runnable task = () -> {
    System.out.println(Thread.currentThread().getName());
};

//创建虚拟线程命名为诺手,将任务task传入
Thread vt1 = Thread.ofVirtual().name("诺手").unstarted(task);
vt1.start();//启动虚拟线程

//主线程睡眠,否则可能看不到控制台的打印
TimeUnit.SECONDS.sleep(1);
  1. 通过ExecutorService创建,为每个任务分配一个虚拟线程,下面代码中提交了100个任务,对应会有100个虚拟线程进行处理。
/*
    通过ExecutorService创建虚拟线程
    ExecutorService实现了AutoCloseable接口,可以自动关闭了
*/
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    //向executor中提交100个任务
    IntStream.range(0, 100).forEach(i -> {
        executor.submit(() -> {
            //睡眠1秒
            try {
                Thread.sleep(Duration.ofSeconds(1));
                System.out.println(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }                    
        });
    });
}

现在平台线程和虚拟线程都是Thread的对象,那该如何区分该对象是平台线程还是虚拟线程?可以利用Thread中的isVirtual()方法进行判断,返回true表示虚拟线程:

//创建任务
Runnable task = () -> {
    System.out.println("执行任务");
};

//创建虚拟线程将任务task传入并启动
Thread vt = Thread.startVirtualThread(task);
System.out.println(vt.isVirtual());

性能对比

public void tryCreateInfiniteThreads() {
    var adder = new LongAdder();
    Runnable job = () -> {
        adder.increment();
        System.out.println("Thread count = " + adder.longValue());
        LockSupport.park();
    };

    // 启动普通线程
    startThreads(() -> new Thread(job));
    // 或是启动虚拟线程
    startThreads(() -> Thread.ofVirtual().unstarted(job));
}

public void startThreads(Supplier<Thread> threadSupplier) {
    while (true) {
        Thread thread = threadSupplier.get();
        thread.start();
    }
}

普通线程:创建到4064个线程后程序报OOM错误崩溃。

.......
Thread count = 4063
Thread count = 4064
[0.927s][warning][os,thread] Failed to start thread "Unknown thread" - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 4k, detached.
[0.927s][warning][os,thread] Failed to start the native thread for java.lang.Thread "Thread-4064"
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
    at java.base/java.lang.Thread.start0(Native Method)
    at java.base/java.lang.Thread.start(Thread.java:1535)
    at com.rhino.vt.VtExample.startThread(VtExample.java:24)
    at com.rhino.vt.VtExample.main(VtExample.java:13)

虚拟线程:创建了超过360万个虚拟线程后被挂起,但没有崩溃,虚拟线程的计数一直在缓慢增长,这是因为被 park 的虚拟线程会被垃圾回收,然后 JVM 能够创建更多的虚拟线程并将其分配给底层的平台线程。

Github上有位老哥做了个更接近真实场景的测试,模拟远程服务请求数据,比较了使用普通线程阻塞式请求、CompletableFeature异步请求、虚拟线程的三种方式的差异,结果显示在连接数少的时候三者差别不大,连接数上去后虚拟线程在吞吐量、内存占用、延迟、CPU占用率方面都有比较大的优势,如下图:

可能这么对比还是不够公平,毕竟一般我们不会直接用这么简单的异步编程,还是会通过各种框架轮子搞。Oracle 的Helidon Níma 号称是第一个采用了虚拟线程的微服务框架,主要的卖点也是性能,可以参考其QPS性能测试数据:

可以看到使用了虚拟线程的web服务器性能很好,与用Netty的差距很小,这也符合预期。相比起来虚拟线程使用起来更简单。

深入虚拟线程

thread = continuation + scheduler

回过头来讨论下:到底什么是"线程"?简单的定义是,"线程"是顺序执行的一系列计算机指令。由于我们处理的操作可能不仅涉及计算,还涉及 IO、定时暂停和同步等,线程会有包括运行、阻塞、等待在内的各种状态,并在状态之间调度流转。当一个线程阻塞或等待时,它应该腾出计算资源(CPU内核),并允许另一个线程运行,然后在等待的事件发生时恢复执行。这其中涉及到两个概念:

  1. continuation(这个词实在不知道怎么翻译才恰当):一系列顺序执行的指令序列,可能会暂停或阻塞,然后恢复执行;
  2. scheduler:顾名思义,负责协调调度线程的机制;

两者是独立的,因此我们可以选择不同的实现。之前的普通线程,在VM层面仅仅是对OS线程的一层简单封装,continuation和scheduler都是交给OS管理,而虚拟线程实现则是在VM里完成这两件事情,当然底层还是需要有相应的OS线程作为载体线程(CarrierThread),并且这个对应并不是固定不变的,在虚拟线程恢复后,完全可能被调度到另一个载体线程。

组合scheduler-OSscheduler-Runtime
continuation-OSJava现在的Thread谷歌对Linux内核修改的User-Level Threads
continuation-Runtime糟糕的选择?虚拟线程

虚拟线程的调用堆栈存在Java堆上,而不是OS分配的栈区内。其内存占用开始时只有几百字节,并可以随着调用堆栈自动伸缩。虚拟线程的运行其实就是两个操作:

  • 挂载(mount):挂载虚拟线程意味着将所需的栈帧从堆中临时复制到载体线程的堆栈中,并在挂载时借用载体堆栈执行。

  • 卸载(unmount):当在虚拟线程中运行的代码因为 IO、锁等原因阻塞后,它可以从载体线程中卸载,然后将修改的栈帧复制回堆中,从而释放载体线程以进行其他操作(例如运行另一个虚拟线程)。对应的,JDK 中几乎所有的阻塞点都已经过调整,因此当在虚拟线程上遇到阻塞操作时,虚拟线程会从其载体上卸载而不是阻塞。

关于scheduler就比较简单了,因为JDK中有现成的ForkJoinPool可以用。work-stealing + FIFO,性能很好。scheduler的并行性是可用于调度虚拟线程的OS线程数。默认情况下,它等于可用CPU核数,也可以使用系统属性jdk.virtualThreadScheduler.parallelism进行调整。

需要注意的是,JDK中的绝大多数阻塞操作将卸载虚拟线程,释放其载体线程来承担新的工作。但是,JDK中的一些阻塞操作不会卸载虚拟线程,因此会阻塞其载体线程。这是因为操作系统级别(例如,许多文件系统操作)或JDK级别(例如,Object.wait())的限制。这些阻塞操作的解决方式是,通过临时扩展scheduler的并行性来补偿操作系统线程的捕获。因此,scheduler的ForkJoinPool中的平台线程数量可能暂时超过CPU核数。scheduler可用的最大平台线程数可以使用系统属性:jdk.virtualThreadScheduler.maxPoolSize进行调整。

虚拟线程源码

试着写一个使用虚拟线程进行网络IO的例子,来窥视下虚拟线程底层的魔法。

下面代码使用了基于虚拟线程的ExecutorService来获取一组URL的响应。每个URL任务会启动一个虚拟线程进行处理。

// record是JDK 14中引入的,这里作为简单的数据类,保存url和响应
record URLData (URL url, byte[] response) { }

public List<URLData> retrieveURLs(URL... urls) throws Exception {
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        var tasks = Arrays.stream(urls)
            .map(url -> (Callable<URLData>)() -> getURL(url))
            .toList();
        return executor.invokeAll(tasks)
            .stream()
            .filter(Future::isDone)
            .map(this::getFutureResult)
            .toList();
    }
}

获取响应的逻辑在getURL中实现,使用同步的URLConnectionAPI来读取数据。

URLData getURL(URL url) throws IOException {
  try (InputStream in = url.openStream()) {
    return new URLData(url, in.readAllBytes());
  }
}

这里我模拟了两个HTTP接口,其中一个响应很慢,因此在运行后不会马上完成。

// test1接口sleep 1s返回,test2接口则sleep 100s
example.retrieveURLs(new URL("http://localhost:7001/test1"), new URL("http://localhost:7001/test2"));

这样就可以用jcmd命令进行线程转储。

$ jcmd `jps | grep VtExample | awk '{print $1}'` Thread.dump_to_file -format=json thread_dump.json

把结果中的普通线程堆栈去掉后,就得到了虚拟线程的堆栈:

{
  "container": "java.util.concurrent.ThreadPerTaskExecutor@5d5a133a",
  "parent": "<root>",
  "owner": null,
  "threads": [
   {
     "tid": "24",
     "name": "",
     "stack": [
        "java.base\/jdk.internal.vm.Continuation.yield(Continuation.java:357)",
        "java.base\/java.lang.VirtualThread.yieldContinuation(VirtualThread.java:370)",
        "java.base\/java.lang.VirtualThread.park(VirtualThread.java:499)",
        "java.base\/java.lang.System$2.parkVirtualThread(System.java:2596)",
        "java.base\/jdk.internal.misc.VirtualThreads.park(VirtualThreads.java:54)",
        "java.base\/java.util.concurrent.locks.LockSupport.park(LockSupport.java:369)",
        "java.base\/sun.nio.ch.Poller.poll2(Poller.java:139)",
        "java.base\/sun.nio.ch.Poller.poll(Poller.java:102)",
        "java.base\/sun.nio.ch.Poller.poll(Poller.java:87)",
        "java.base\/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:175)",
        "java.base\/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:196)",
        "java.base\/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:304)",
        "java.base\/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:340)",
        "java.base\/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:789)",
        "java.base\/java.net.Socket$SocketInputStream.read(Socket.java:1025)",
        "java.base\/java.io.BufferedInputStream.fill(BufferedInputStream.java:255)",
        "java.base\/java.io.BufferedInputStream.read1(BufferedInputStream.java:310)",
        "java.base\/java.io.BufferedInputStream.implRead(BufferedInputStream.java:382)",
        "java.base\/java.io.BufferedInputStream.read(BufferedInputStream.java:361)",
        "java.base\/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:827)",
        "java.base\/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:759)",
        "java.base\/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1684)",
        "java.base\/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1585)",
        "java.base\/java.net.URL.openStream(URL.java:1162)",
        "com.rhino.vt.VtExample.getURL(VtExample.java:59)",
        "com.rhino.vt.VtExample.lambda$retrieveURLs$0(VtExample.java:40)",
        "java.base\/java.util.concurrent.ThreadPerTaskExecutor$ThreadBoundFuture.run(ThreadPerTaskExecutor.java:352)",
        "java.base\/java.lang.VirtualThread.run(VirtualThread.java:287)",
        "java.base\/java.lang.VirtualThread$VThreadContinuation.lambda$new$0(VirtualThread.java:174)",
        "java.base\/jdk.internal.vm.Continuation.enter0(Continuation.java:327)",
        "java.base\/jdk.internal.vm.Continuation.enter(Continuation.java:320)"
     ]
   }
  ],
  "threadCount": "1"
}

作为对比,把代码中的executor改成Executors.newCachedThreadPool(),再dump出直接使用普通线程的堆栈:

{
   "tid": "23",
   "name": "pool-1-thread-2",
   "stack": [
      "java.base\/sun.nio.ch.SocketDispatcher.read0(Native Method)",
      "java.base\/sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:47)",
      "java.base\/sun.nio.ch.NioSocketImpl.tryRead(NioSocketImpl.java:251)",
      "java.base\/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:302)",
      "java.base\/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:340)",
      "java.base\/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:789)",
      "java.base\/java.net.Socket$SocketInputStream.read(Socket.java:1025)",
      "java.base\/java.io.BufferedInputStream.fill(BufferedInputStream.java:255)",
      "java.base\/java.io.BufferedInputStream.read1(BufferedInputStream.java:310)",
      "java.base\/java.io.BufferedInputStream.implRead(BufferedInputStream.java:382)",
      "java.base\/java.io.BufferedInputStream.read(BufferedInputStream.java:361)",
      "java.base\/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:827)",
      "java.base\/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:759)",
      "java.base\/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1684)",
      "java.base\/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1585)",
      "java.base\/java.net.URL.openStream(URL.java:1162)",
      "com.rhino.vt.VtExample.getURL(VtExample.java:59)",
      "com.rhino.vt.VtExample.lambda$retrieveURLs$0(VtExample.java:40)",
      "java.base\/java.util.concurrent.FutureTask.run(FutureTask.java:317)",
      "java.base\/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)",
      "java.base\/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)",
      "java.base\/java.lang.Thread.run(Thread.java:1589)"
   ]
 }

两个堆栈对比一下会发现,除了中间执行的业务逻辑部分是一致的,有两点不同:

  1. 普通线程的入口是Thread.run,而虚拟线程的入口是Continuation,这个类是虚拟线程的核心类,是VM内部对上面所说的continuation的抽象。Continuation有两个关键方法:yield()和run()。

可以试着跑一下这段代码看看输出结果:

public void testContinuation() {
    var scope = new ContinuationScope("test");
    var continuation = new Continuation(scope, () -> {
        System.out.println("C1");
        Continuation.yield(scope);
        System.out.println("C2");
        Continuation.yield(scope);
        System.out.println("C3");
        Continuation.yield(scope);
    });
    System.out.println("start");
    continuation.run();
    System.out.println("came back");
    continuation.run();
    System.out.println("back again");
    continuation.run();
    System.out.println("back again again");
    continuation.run();
}

// Output:
start
C1
came back
C2
back again
C3
back again again

PS:在Java19中还是预览版,需要加上下面的参数:(Java21后已经是正式版了)

--add-opens java.base/jdk.internal.vm=ALL-UNNAMED
  1. 普通线程会阻塞在read本地方法调用上(底层应该就是read系统调用),而虚拟线程则会通过VirtualThread#park挂起,这也对应了上面说的,JDK中几乎所有的阻塞点都已经过调整了。VirtualThread维护了一组state状态,调用park后就会设置成PARKING,可以在注释里看到状态之间的流转逻辑。

在线程dump文件里还能找到一个叫Read-Poller的线程(对应的还有一个写操作的 Write-Poller线程):

{
   "tid": "27",
   "name": "Read-Poller",
   "stack": [
      "java.base\/sun.nio.ch.KQueue.poll(Native Method)",
      "java.base\/sun.nio.ch.KQueuePoller.poll(KQueuePoller.java:66)",
      "java.base\/sun.nio.ch.Poller.poll(Poller.java:363)",
      "java.base\/sun.nio.ch.Poller.pollLoop(Poller.java:270)",
      "java.base\/java.lang.Thread.run(Thread.java:1589)",
      "java.base\/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:186)"
   ]
 }

JDK底层做了什么调整呢?从Read-Poller可以看出,其实就是把原来的阻塞调用改为了非阻塞的IO调用。流程如下:

  1. 在阻塞调用中,检查是否虚拟线程,如果是的话,就注册一个NIO handler,即将文件描述符注册到Read-Poller的selector上。然后调用Continuation.yield()暂停自身。因为我本机是mac,所以线程堆栈里显示的NIO handler用的是KQueue,如果换成Linux,那就是我们熟悉的epoll了。
  2. Read-Poller底层维护了一组文件描述符 - 虚拟线程的映射,当一个文件描述符被注册到Read-Poller上时,同样也会将对应的虚拟线程加到这个映射里。
  3. 当Socket可读时,这个Read-Poller就会得到通知,随即调用wakeup()方法,从映射里找到文件描述符对应的虚拟线程,再将之前park()的虚拟线程unpark(),这样就完成了虚拟线程的唤醒。
/**
 * Unparks any thread that is polling the given file descriptor.
 */
private void wakeup(int fdVal) {
    Thread t = map.remove(fdVal);
    if (t != null) {
        LockSupport.unpark(t);
    }
}

虚拟线程的unpark()方法如下:

/**
 * Re-enables this virtual thread for scheduling. If the virtual thread was
 * {@link #park() parked} then it will be unblocked, otherwise its next call
 * to {@code park} or {@linkplain #parkNanos(long) parkNanos} is guaranteed
 * not to block.
 * @throws RejectedExecutionException if the scheduler cannot accept a task
 */
@Override
@ChangesCurrentThread
void unpark() {
    Thread currentThread = Thread.currentThread();
    if (!getAndSetParkPermit(true) && currentThread != this) {
        int s = state();
        // CAS设置线程状态
        if (s == PARKED && compareAndSetState(PARKED, RUNNABLE)) {
            if (currentThread instanceof VirtualThread vthread) {
                Thread carrier = vthread.carrierThread;
                carrier.setCurrentThread(carrier);
                try {
                    // 提交给scheduler执行
                    submitRunContinuation();
                } finally {
                    carrier.setCurrentThread(vthread);
                }
            } else {
                submitRunContinuation();
            }
        } else if (s == PINNED) {
            // unpark carrier thread when pinned.
            synchronized (carrierThreadAccessLock()) {
                Thread carrier = carrierThread;
                if (carrier != null && state() == PINNED) {
                    U.unpark(carrier);
                }
            }
        }
    }
}

在unpark()中,会将虚拟线程的状态重新设置为RUNNABLE,并且调用submitRunContinuation()方法将任务交给调度器执行,真正执行时,就会调用到Continuation.run()方法。另外,上面调用executor.invokeAll()方法提交任务时,底层同样也是调用了VirtualThread.submitRunContinuation()方法,这里的scheduler默认就是ForkJoinPool实例。

/**
 * Submits the runContinuation task to the scheduler.
 * @param {@code lazySubmit} to lazy submit
 * @throws RejectedExecutionException
 * @see ForkJoinPool#lazySubmit(ForkJoinTask)
 */
private void submitRunContinuation(boolean lazySubmit) {
    try {
        if (lazySubmit && scheduler instanceof ForkJoinPool pool) {
            pool.lazySubmit(ForkJoinTask.adapt(runContinuation));
        } else {
            // 默认shceduler就是ForkJoinPool
            scheduler.execute(runContinuation);
        }
    } catch (RejectedExecutionException ree) {
        // 省略异常处理代码
    }
}

而在park()里,虚拟线程让出资源的关键方法是VirtualThread.yieldContinuation(),可以发现mount()和unmount()操作。


/**
 * Unmounts this virtual thread, invokes Continuation.yield, and re-mounts the
 * thread when continued. When enabled, JVMTI must be notified from this method.
 * @return true if the yield was successful
 */
@ChangesCurrentThread
private boolean yieldContinuation() {
    boolean notifyJvmti = notifyJvmtiEvents;

    // unmount
    if (notifyJvmti) notifyJvmtiUnmountBegin(false);
    unmount();
    try {
        return Continuation.yield(VTHREAD_SCOPE);
    } finally {
        // re-mount
        mount();
        if (notifyJvmti) notifyJvmtiMountEnd(false);
    }
}

mount()和unmount()会在Java堆和本地线程栈之间做栈帧的拷贝,这是Project Loom中为数不多的在JVM层面实现的本地方法,感兴趣的可以去Loom的github库里搜一下continuationFreezeThaw.cpp。其余的大部分代码在JDK中实现, 参见java.base模块下的jdk.internal.vm包。

点赞 + 关注 + 收藏 = 学会了

整理了一个NAS小专栏,有兴趣的工友可以关注一下 👉 《NAS邪修》

Volume Shader BM(俗称 “毒蘑菇测试”)是一款基于 WebGL 的开源浏览器 GPU 基准测试工具。

这个工具测试的是当前打开这个页面的设备,而不是部署它的设备。

也就是说,你部署在 NAS,但你用A电脑打开这个网页,测的是A电脑 GPU 性能,而不是 NAS 的 GPU 性能。

我用的是飞牛 NAS 部署毒蘑菇,其他品牌的 NAS 部署方式也是差不多的。

在“文件管理”里找到“docker”文件夹,在里面创建一个“volumeshader_bm”文件夹。

打开“Docker”应用,在“Compose”面板点击“新增项目”,填入以下内容。

路径选择刚刚在“文件管理”里创建的那个路径。

代码如下:

services:
  volumeshader_bm:
    image: heizicao/volumeshader_bm:latest
    container_name: volumeshader_bm
    ports:
      - 3331:3000
    restart: always

3331 是我给这个项目配置的端口,如果这个端口和你其他项目冲突的话,改过一个没用过的数字就行。

项目构建成功后,打开浏览器,输入 NAS的IP:3331 就可以折磨你的显卡了。

页面左下角是帧数。测测看吧~


以上就是本文的全部内容啦,有疑问可以在评论区讨论~

想了解更多NAS玩法可以关注《NAS邪修》👏

往期推荐:

点赞 + 关注 + 收藏 = 学会了

点赞 + 关注 + 收藏 = 学会了

💡整理了一个 NAS 专属玩法专栏,感兴趣的工友可以戳这里关注 👉 《NAS邪修》

用绿联 NAS 的工友们,平时有没有留意到应用商店里的一款AI相关的应用👉 AI Plugins

相比大家熟悉的 Open WebUI,AI Plugins 支持 GPU 加速,推理能力更强,主打一个本地模型的高效运行。

想要深入了解的工友,可以去绿联官网搜索 AI Plugins 官方文档👇

https://support.ugnas.com/knowledgecenter/#/detail/eyJpZCI6MT...

如果你习惯了 AI Plugins 的界面,又想接入外部的强大模型,最重要的是不想花钱,那么可以试试美团的 LongCat。

本文不是广,纯纯的羊毛分享

在写本文时,LongCat 极其慷慨的给了 2 套模型额度

  • LongCat-Flash-Lite:每天刷新 5000万 token!
  • 其他三款模型(Flash-Chat、Flash-Thinking、Flash-Thinking-2601):共用额外的 500万 token!

加起来每天足足 5500万 token 的免费额度!注意,是每天!

之前我在 《『n8n』推荐几个免费的大模型给学习n8n的工友们使用》 里也推荐过它,当时每天还只有 500 万额度。现在放在 n8n 里用简直爽飞,速度快、能力在线,最主要是免费(能薅多久不清楚,但现在白嫖就是赚)!

既然绿联官方提供了 AI Plugins 这个好用的壳子,LongCat 又如此慷慨,不用白不用。

打开浏览器,访问 LongCat API 开放平台,登录账号后,切换到 API Keys 面板(https://longcat.chat/platform/api_keys

点击创建,生成并复制你的专属 API Key(注意保密!!!)。

回到绿联 NAS,打开 AI Plugins 应用。登录后,点击页面左下角的头像,进入设置

在设置面板中,找到并点击“外部连接”,然后点击右上角的 “+” 号添加一个新连接。

  • URL输入:https://api.longcat.chat/openai
  • 密钥:填入你在第一步复制的 LongCat API Key
  • 模型ID:暂时可选以下 4 个(直接复制模型名称填入)

    • LongCat-Flash-Lite
    • LongCat-Flash-Chat
    • LongCat-Flash-Thinking
    • LongCat-Flash-Thinking-2601

保存配置后,回到 AI Plugins 的首页,新增一个聊天窗

在聊天窗的左上角,你就可以下拉选择刚刚配置好的 LongCat 模型了。


以上就是今天的“薅羊毛”全部内容啦!每天 5500 万额度,足够大家折腾各种有趣的玩法了。

我在 NAS 搭建了一套 n8n,它可以调用大模型帮我完成一些工作,有兴趣的工友可以了解一下n8n 实战教程

你平时在 NAS 上还有什么好玩、实用的 Docker 镜像推荐吗?欢迎在评论区留言,一起讨论折腾!

想了解更多NAS玩法记得关注《NAS邪修》👏

点赞 + 关注 + 收藏 = 学会了

DeepStudent

在 AI 深度融入教育场景的今天,学习者的需求早已不简单满足于在 AI 聊天这一层面。真正高效的学习闭环需要 AI 贯穿全流程:从理解知识、梳理体系,到制作记忆卡片、自动调研出题、批改作文、翻译文献——每一个环节都需要不同类型的 AI 能力协同配合。然而,当前市面上的学习工具大多功能单一,笔记归笔记、对话归对话,数据割裂、流程断裂,难以支撑完整的学习链路。

DeepStudent 是一款开源的、AI 原生的本地优先学习系统,旨在构建从输入到内化的完整学习闭环。它以 Chat V2 对话引擎为支撑,技能系统为核心驱动,以虚拟文件系统为统一数据底座,将智能对话、知识管理、Anki 制卡、深度调研、学术论文、知识导图、题库练习、翻译工作台、作文批改等多项专业技能整合在一个平台中。全部数据本地存储( SQLite + LanceDB + Blob ),安全可控。

通过与硅基流动( SiliconFlow )的深度集成,DeepStudent 的全场景 AI 能力获得了坚实的底层支撑。只需一个硅基流动 API Key ,即可一键配齐对话、推理、嵌入、OCR 、翻译、重排序等完整能力矩阵,让每一位学习者都能快速搭建起自己的 AI 学习基础设施。同时,学习者也可以自由接入多种供应商的 API 来实现不同 AI 的自定义搭配。

1. 安装 DeepStudent

DeepStudent 基于 Tauri 2 构建,支持 Windows 、macOS 、Android 等主流平台,下载安装包后即可使用。

软件主页图

2. 在 DeepStudent 中使用 SiliconFlow 模型

2.1 获取 SiliconFlow API 密钥

  1. 登录 SiliconFlow ( https://cloud.siliconflow.cn )(首次登录将自动完成注册)。
  2. 在控制台中进入"API 密钥"页面。
  3. 点击"新建密钥",为其命名(如 DeepStudent )。
  4. 复制生成的密钥并妥善保存。

2.2 在 DeepStudent 中配置

  1. 打开 DeepStudent ,点击左下角进入设置中心。

  2. 找到 SiliconFlow 专属配置区域——DeepStudent 预置了 9 家模型供应商( SiliconFlow / DeepSeek / 通义千问 / 智谱 AI / 字节豆包 / MiniMax / 月之暗面 / OpenAI / Google Gemini ),其中 SiliconFlow 作为首选供应商拥有独立的快速配置界面。

    注册-3

  3. 输入 SiliconFlow API 密钥。

  4. 点击"一键分配"按钮,DeepStudent 将自动创建并分配好全部所需模型:主对话模型( DeepSeek-V3.2 )、Anki 制卡模型( Qwen3-30B-A3B )、嵌入模型( bge-m3 )、重排序模型( bge-reranker-v2-m3 )、翻译模型( Hunyuan-MT-7B )、轻量任务模型( Ling-mini-2.0 )以及 5 个 OCR 引擎( PaddleOCR-VL-1.5 / PaddleOCR-VL / DeepSeek-OCR / GLM-4.6V / Qwen3-VL-8B ),无需逐个手动添加。(推荐手动分配硅基流动的各个 Pro 系列模型以获得更加卓越的性能)

也可以点击"获取模型列表"从 SiliconFlow 平台拉取全量可用模型,按需选择添加。DeepStudent 会自动推断每个模型的能力属性(多模态、推理、嵌入、工具调用等),并配置最优的适配参数。

3. 开始使用

3.1 AI 智能对话与深度推理

DeepStudent 的 Chat V2 对话引擎专为学习场景打造。搭配 SiliconFlow 提供的 DeepSeek-V3.2 等推理模型,支持多模态文件拖拽上传与自动 ocr (图片、PDF 、Word 等)、深度推理(思维链展示 AI 完整思考过程)、多 Tab 会话、会话分支,以及通过引用面板将知识库中的各种学习资源作为附件注入上下文。

分组

pdf 阅读-1

3.2 技能系统

DeepStudent 的核心驱动力是其技能系统( Skills )。不同于传统 AI 工具将所有指令塞进一个臃肿的 System Prompt ,DeepStudent 将 AI 能力拆分为独立的技能模块,每个技能封装了特定场景的指令与工具集,激活时才渐进披露,既节省 Token 开销,又保证专业性。目前内置 22 项技能(含 5 个整合型技能与 17 个工具组技能),覆盖制卡、调研、论文、导图、题库、记忆、翻译、导师、文献综述、Office 套件、子代理等核心学习场景,同时支持用户通过 SKILL.md 格式自定义扩展。默认启用的"深度学者"策略会主动回忆用户画像、优先检索本地知识库,让每一次对话都具备个性化上下文。

技能系统让 AI 如虎添翼,让 AI 对学习数据具有了灵活的读写能力。

技能系统

3.3 学习资源中心

DeepStudent 内置了一个类似 macOS 访达的学习资源管理器( Learning Hub ),统一管理笔记、PDF 教材、题目集、翻译、作文、知识导图、图片等全类型学习资产。支持导入 PDF 、DOCX 、XLSX 、PPTX 、EPUB 、RTF 、Markdown 、CSV 、HTML 、JSON 及 JPG/PNG/HEIC 等常见图片格式。资源导入后自动进入向量化流水线——经由 SiliconFlow 提供的 OCR 引擎( PaddleOCR-VL / DeepSeek-OCR 等)识别文字,再通过嵌入模型( bge-m3 )生成向量索引存入 LanceDB 。向量索引支持多维度管理,用户可创建不同维度的嵌入空间( text / multimodal ),绑定不同的嵌入模型,满足检索的差异化需求
学习资源管理器

3.3 深度调研

激活"深度调研"技能后,AI 会以多步骤 Agent 模式自动执行完整调研流程:明确目标 → 联网搜索(支持 7 种搜索引擎,需配置)→ 本地知识库检索 → 整理分析 → 生成结构化报告,并自动保存为笔记。适用于课题研究、开题报告、学术综述等场景。

调研-2

3.4 ChatAnki 智能制卡

在对话中用自然语言触发制卡技能(如"把这篇文档做成卡片"),AI 批量生成高质量记忆卡片。内置模板设计师支持可视化编辑,生成结果支持 3D 翻转预览,确认无误后一键同步至 Anki ,打通从"理解"到"记忆"的最后一步。

anki-制卡 2

3.5 更多学习场景

  • 知识导图:在聊天界面一句话即可生成完整学科知识体系,支持 AI 多轮编辑、大纲/导图视图切换与背诵模式。
    知识导图-4

  • 题库练习:上传教材/试卷,AI 自动提取题目集,支持每日练习、模拟考试与 AI 深度解析。

    题目集-2

  • 翻译工作台:全文翻译与逐段双语对照,内置学术、技术、文学等多领域预设。

    翻译-2

  • 作文批改:覆盖高考、雅思、托福、四六级等考试标准,多维度评分与逐句润色建议。

    作文-1

  • 智能记忆:受 mem0 与 memU 启发设计的自进化用户画像系统,自动从对话中提取事实,越用越懂你。

    记忆

  • MCP 扩展:兼容 Model Context Protocol ,可连接如 Context7 等外部工具服务。

  • 学术论文:通过 arXiv 与 OpenAlex 检索论文,支持批量下载 PDF 、多源自动回退、BibTeX / GB/T 7714 / APA 引用格式化。

    论文搜索-1

    论文搜索-3

两人团队历时接近一年的作品,我们正行在通往正式版的道路上,仍有许多不足与亟待打磨之处,希望各位不吝指教。

image
我感觉 情趣内衣 才是最应该评测的, 因为买了觉得不好的话, 一般人也不会退货.
看多了主要就那几个系列, 吊带, 体操服, 碎短裙, 女仆装, 三点式, 和服, 角色 cos.
LA 那款, 和 霏慕 那款 甜美娇妻 款式差不多, 但是它面料更滑更透. 就是价格太贵了, 适合富哥.
倾城鸟 的体操服还可以吧, 这个价格不错了.
霏慕 的碎花连衣裙, 作为情趣大牌, 包装和质量都可以的.
JK 女仆装也不错.
死库水款式是我喜欢的, 但是这款面料很垃圾, 清洗掉色还很大异味.
京造 出的冰丝蕾丝花边碎裙一般般吧.
霏慕 的没得说, 还是整个行业中可以的品牌.
蜜链发货晚了, 没收到货就取消了, 京造的话还是一般.
浪莎 也是牌子货, 但是包装都很随意, 我到手还是打开的, 哎非常垃圾.
日系那个价格在那里, 还算可以吧.

总评下来还是 霏慕 比较稳, 缺点是款式比较少, 风格比较保守.
像我这样比较喜欢死库水风格的话, 相关情趣内衣少的, 买正常款式, 自己动手改情趣也是可以的.
还有那些几十块, 几十套一次性丝袜的那种, 就因人而异了, 总体还是吃模特颜值和身材的.