2026年1月

公司的 im 有水印,就不发图了,简单总结一下

  1. 大数据/医疗部门因使用 ai 生成代码导致生产环境服务器负载拉爆且历史数据被大范围覆盖
  2. it 部门排查开发电脑的 ai 相关软件,禁止 ai 自动代码生成
  3. 所有研发部门安排时间重新 review 近半年所有代码
  4. 对应研发部门的开发和测试绩效当月为 D(意味着 2026 年没有年终奖了,但是 2025 还是可以有的)


哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈(先笑一下🤣
他们怎么敢的啊,又不是自己的项目,我用 ai 都只拿当搜索引擎,代码都是我自己的测试确认后才写到项目里面去


我一直强调,ai 真的不能帮你背 D 绩效啊喂!

一、概述

LangGraph是LangChain团队开发的低级别编排框架,专为构建、管理和部署长时间运行的有状态AI代理设计,提供持久化执行、灵活控制流和全面内存管理功能,支持循环和条件分支,是开发复杂AI工作流的理想选择。

1.1 核心特点

  • 持久执行:自动保存执行状态,支持故障恢复和断点续跑
  • 循环与分支:突破传统DAG限制,支持复杂的条件判断和循环逻辑
  • 全面内存:集成短期工作内存和长期持久内存,支持跨会话状态保留
  • 人机协作:内置中断机制,允许人工介入审批或修改代理行为
  • 流支持:实时输出执行结果,包括LLM的token级流式响应
  • 可观察性:无缝集成LangSmith,提供完整的执行轨迹和状态转换可视化

二、核心概念

2.1 状态(State)

共享内存,所有节点都可读写的全局数据结构,是代理的"工作记忆"。

  • 定义为Python的TypedDict或dataclass,包含代理需要的所有信息
  • 存储原始数据而非格式化文本,确保不同节点可灵活使用
  • 示例:

    from typing import TypedDict
    class AgentState(TypedDict):
        messages: list  # 对话消息列表
        search_results: list  # 搜索结果
        user_preferences: dict  # 用户偏好

2.2 节点(Nodes)

图的基本执行单元,是接收状态并返回更新的函数。

  • 类型:

    • LLM节点:调用语言模型进行文本理解或生成
    • 工具节点:执行外部API调用、数据库查询等
    • 数据处理节点:转换或分析数据
    • 人工介入节点:暂停执行等待用户输入
  • 定义示例:

    def greet(state: AgentState) -> dict:
        return {"greeting": f"Hello, {state['user_name']}!"}

2.3 边(Edges)

节点间的连接,定义执行流的路径。

  • 普通边:始终执行固定路径
  • 条件边:根据状态决定下一步执行节点
  • 定义示例:

    # 普通边:从"start"到"greet"
    graph.add_edge("start", "greet")
    
    # 条件边:根据状态判断是执行"search"还是"reply"
    def decide_next(state: AgentState) -> str:
        return "search" if state["needs_info"] else "reply"
    graph.add_conditional_edges("greet", decide_next)

三、架构与工作原理

3.1 图结构

LangGraph使用有向图模型表示代理工作流,包含:

  • 特殊节点

    • START:执行入口点
    • END:执行结束点
  • 执行模型:基于"消息传递"的迭代执行,以离散"超步骤"(super-step)推进

3.2 状态管理

  • 短期内存:线程范围内存,随执行结束自动清除
  • 长期内存

    • 存储于独立的Store系统,支持跨会话、跨线程访问
    • 使用namespacekey组织数据,类似文件系统的目录和文件名
    • 支持多种存储后端:内存(开发)、PostgreSQL(生产)、Redis等

3.3 执行流程

  1. 初始化状态并设置入口节点
  2. 执行入口节点,更新状态
  3. 根据边的类型(普通/条件)决定下一节点
  4. 重复直到到达END或达到递归限制(默认25步)
  5. 执行过程中自动保存检查点,支持故障恢复

四、存储方案

LangGraph支持多种存储后端,满足不同场景需求:

存储类型适用场景特点配置示例
InMemoryStore开发测试速度快,无持久化store = InMemoryStore()
PostgresStore生产环境高可靠,支持事务store = PostgresStore("postgresql://user:pass@host/db")
RedisStore分布式系统高性能读写,适合缓存store = RedisStore("redis://host:port")
SQLiteStore轻量级应用文件存储,无需服务器store = SQLiteStore("langgraph.db")

长期记忆配置

from langgraph.store.postgres import PostgresStore
from langgraph.backends import CompositeBackend, StateBackend, StoreBackend

# 配置复合存储:/memories/路径下的数据持久化,其他临时存储
def make_backend(runtime):
    return CompositeBackend(
        default=StateBackend(runtime),  # 临时存储
        routes={"/memories/": StoreBackend(runtime, PostgresStore("..."))}  # 持久存储
    )

五、使用方法

5.1 安装

pip install -U langgraph  # Python版本
npm install @langchain/langgraph  # JavaScript版本

5.2 基本使用步骤

1. 定义状态

from typing import TypedDict
class ChatState(TypedDict):
    messages: list  # 对话消息列表

2. 构建图

from langgraph.graph import StateGraph, START, END
from langchain.llms import OpenAI

# 初始化图
graph = StateGraph(ChatState)

# 定义节点:调用LLM生成回复
def call_llm(state: ChatState):
    llm = OpenAI(temperature=0)
    response = llm.invoke(state["messages"])
    return {"messages": state["messages"] + [response]}

# 添加节点和边
graph.add_node("generate_response", call_llm)
graph.add_edge(START, "generate_response")
graph.add_edge("generate_response", END)

3. 编译并执行

# 编译为可执行应用
app = graph.compile()

# 执行
initial_state = {"messages": [{"role": "user", "content": "Hello!"}]}
final_state = app.invoke(initial_state)
print(final_state["messages"][-1]["content"])  # 输出AI回复

5.3 条件执行与循环

# 定义条件函数:检查是否需要调用工具
def needs_tool(state: ChatState) -> Literal["use_tool", "reply"]:
    last_message = state["messages"][-1]
    return "use_tool" if last_message.get("tool_calls") else "reply"

# 添加条件边
graph.add_conditional_edges("generate_response", needs_tool)

# 添加工具节点和循环边
graph.add_node("use_tool", tool_node)
graph.add_edge("use_tool", "generate_response")  # 循环回LLM节点

六、API参考

6.1 Graph API

核心类

  • StateGraph:构建状态驱动的图,需传入状态类型
  • MessageState:预定义的消息状态,适合聊天应用
  • Checkpointer:管理执行状态的保存和恢复

关键方法

  • add_node(name, function, **kwargs):添加节点,支持重试策略等配置
  • add_edge(from_node, to_node):添加普通边
  • add_conditional_edges(from_node, condition_func):添加条件边
  • compile(checkpointer=None):编译图为可执行应用,支持持久化配置
  • invoke(input_state, config=None):执行图,返回最终状态

6.2 Functional API (简化版)

提供更简洁的方式构建小型工作流:

from langgraph import entrypoint, task

@entrypoint
def my_agent():
    state = {"counter": 0}
    while state["counter"] < 3:
        state = task(increment)(state)  # 调用任务函数
    return state

@task
def increment(state):
    state["counter"] += 1
    return state

result = my_agent()  # 执行

七、开发指南

7.1 构建步骤

  1. 设计工作流:将问题分解为离散步骤,确定节点间依赖关系
  2. 定义状态:确定需要在步骤间共享的数据
  3. 实现节点:为每个步骤编写函数,处理输入状态并返回更新
  4. 连接节点:使用边定义执行顺序,添加必要的条件判断
  5. 添加内存:配置检查点和持久化,实现长期记忆
  6. 测试与调试:使用LangSmith可视化执行过程,检查状态转换

7.2 最佳实践

状态设计

  • 只存储必要信息,避免冗余
  • 保持状态原始,在节点内格式化输出
  • 使用描述性键名,提高可读性

节点设计

  • 单一职责:每个节点专注做一件事
  • 错误处理:为不同错误类型设置适当的处理策略(重试/回退/人工介入)
  • 外部调用:将API调用、数据库操作等封装为独立节点,便于添加重试和监控

内存管理

  • 短期数据存于状态,长期数据使用专用存储
  • 定期清理过时数据,优化存储性能
  • 使用命名空间组织长期数据,便于管理和查询

八、调试与监控

8.1 使用LangSmith集成

LangGraph无缝集成LangSmith,提供全面的可观察性:

# 启用LangSmith追踪
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "..."

# 编译图时启用追踪
app = graph.compile(checkpointer=checkpointer, trace=True)

监控功能

  • 执行轨迹可视化:查看完整执行路径和状态变化
  • 性能分析:测量各节点执行时间,识别瓶颈
  • 异常检测:自动标记执行错误和异常路径
  • 交互式调试:在LangSmith Studio中检查中间状态

8.2 本地调试技巧

  • 断点打印:在节点函数中添加print语句,输出关键状态
  • 分步执行:使用graph.invoke并传入小输入,逐步验证每个节点
  • 错误处理:为节点添加详细的异常捕获和日志记录:

    def safe_node(state):
        try:
            # 正常逻辑
        except Exception as e:
            return {"error": str(e)}  # 返回错误信息而非崩溃

九、部署方案

9.1 自托管部署

使用Docker

# 安装CLI
pip install -U langgraph-cli

# 构建镜像
langgraph build --name my-agent .

# 运行
docker run -p 8124:8124 my-agent

生产配置建议

  • 使用PostgreSQL作为存储后端,确保数据持久化
  • 配置数据加密,保护敏感信息
  • 设置适当的资源限制,防止滥用
  • 使用负载均衡和水平扩展,提高吞吐量

9.2 LangSmith Cloud (原LangGraph Platform)

提供一键式云部署:

  • Lite版本:免费使用,每年限制100万节点执行
  • Enterprise版本:全功能支持,适合大规模生产环境

优势

  • 自动扩展和高可用性
  • 内置监控和告警系统
  • 开箱即用的安全与合规功能
  • 与LangSmith无缝集成,提供完整的可观察性

十、完整示例:构建天气查询代理

# 1. 安装依赖
pip install langgraph langchain openai

# 2. 导入必要模块
from typing import TypedDict, Literal
from langchain.llms import OpenAI
from langchain_core.messages import HumanMessage, AIMessage
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver  # 内存检查点

# 3. 定义状态
class WeatherAgentState(TypedDict):
    messages: list  # 对话消息
    location: str  # 查询的城市
    weather_info: str  # 天气信息

# 4. 定义工具函数
def get_weather(location: str) -> str:
    """简化的天气查询API"""
    if location.lower() == "sf":
        return "60°F, foggy"
    elif location.lower() == "ny":
        return "90°F, sunny"
    else:
        return "Weather data not available for this location"

# 5. 定义节点
def initial_prompt(state: WeatherAgentState) -> dict:
    """询问用户想查询哪个城市的天气"""
    llm = OpenAI(temperature=0)
    response = llm.invoke([HumanMessage(content="Which city's weather would you like to check?")])
    return {"messages": [response]}

def parse_location(state: WeatherAgentState) -> dict:
    """从用户消息中提取城市名"""
    last_message = state["messages"][-1]
    location = last_message.content.strip().lower()
    return {"location": location, "messages": state["messages"] + [AIMessage(content=f"Checking weather for {location}...")]}

def get_weather_info(state: WeatherAgentState) -> dict:
    """调用天气工具获取信息"""
    weather = get_weather(state["location"])
    return {"weather_info": weather, "messages": state["messages"] + [AIMessage(content=f"Weather in {state['location']}: {weather}")]}

# 6. 构建图
graph = StateGraph(WeatherAgentState)

# 添加节点
graph.add_node("initial_prompt", initial_prompt)
graph.add_node("parse_location", parse_location)
graph.add_node("get_weather_info", get_weather_info)

# 添加边定义执行流
graph.add_edge(START, "initial_prompt")
graph.add_edge("initial_prompt", "parse_location")
graph.add_edge("parse_location", "get_weather_info")
graph.add_edge("get_weather_info", END)

# 7. 添加内存支持
checkpointer = MemorySaver()  # 使用内存检查点保存状态
app = graph.compile(checkpointer=checkpointer)

# 8. 执行代理
first_run = app.invoke({})
print("First run output:")
for msg in first_run["messages"]:
    print(f"{msg['role'].capitalize()}: {msg['content']}")

print("\nSecond run (with state persistence):")
# 第二次执行会保留之前的对话状态
second_run = app.invoke({})
for msg in second_run["messages"]:
    print(f"{msg['role'].capitalize()}: {msg['content']}")

十一、总结

LangGraph是构建复杂AI代理的强大框架,通过状态驱动的图结构,提供了持久执行、灵活控制流和全面内存管理能力。使用LangGraph,开发者可以轻松构建具有记忆、能够处理复杂逻辑的AI代理,适用于客服、研究助手、自动化工作流等多种场景。

模型角度

  • T0: claude 默认的、gemini 、chatgpt5.2
  • 国产模型大差不大

在逻辑实现和技术方案制定上,感觉国外的这 3 个更符合我的审美。。。

agent

白嫖了同学的 claude code ,token 管够的情况下很爽。代码接受率很高,业务屎山也改得动,给的技术方案可落地性也很强。应该是用的国内中转的,速度稍慢。

后面换了 glm code plan ,差距挺大的,只能日常处理点简单编码逻辑,尤其在提示词和代码的理解上和原生 claude 差距巨大。需要给特别明确的 prompt 才能做好。

windsurf 出了 idea 的插件,自带的 swe1.5 ,感觉水平不比国产的差,速度极快,体验还不错。但是自动补全经常出不来。直接用 windsurf ide 体验更好。

trae 整体可用,但用多了排队很烦。

JB 家在 AI 时代整体落后了,插件的体验和原生 ide 差距还是有的。不过原生 ai ide 越强,更能体现 JB 在没 AI 时代的强大。windsurf ide 也能写 java ,ai 的配套已经挺强大了,但整个 java+spring 的编码配套还是不如 idea 的。

如果你在做量化研究、实盘盯盘,或者高频信号监控,可能已经遇到过这样的问题:
港股、美股都要看,但行情接口分散在不同平台,字段不统一、时间规则不同,接入成本远高于预期。

一开始你可能会觉得这是“小问题”,多写几层适配就能解决。但真正跑起来后,会发现维护成本会持续放大,尤其是在高频或长时间运行的场景下。

多市场行情接入,难点并不在“拿到数据”

在实际开发中,真正消耗精力的通常是这些地方:

  • 港股、美股 API 结构差异大,需要额外做字段映射
  • tick 数据更新频繁,处理不当容易阻塞
  • 部分接口在行情活跃时延迟明显,甚至丢数据

这些问题往往不会立刻暴露,但会逐渐影响策略判断和系统稳定性。

一个更工程化的思路:统一数据入口

当你需要长期维护系统时,一个覆盖多市场、数据口径一致的行情源会明显降低复杂度。

在实时行情场景下,相比 REST 轮询,用 WebSocket 订阅的方式更接近“监听市场”而不是“反复查询状态”。
你只需要维护一条连接,就能持续接收行情变化,延迟和资源消耗都更可控。

像AllTick这类聚合型行情接口,本质上解决的是“多市场适配”这个工程问题:
同一套接入方式,同时覆盖港股和美股,不需要为不同市场维护多套逻辑。

实战示例:Python WebSocket 订阅港股+美股


下面是我用的一个简单示例,直接抓取港股腾讯(00700.HK)和美股苹果(AAPL.US):

import websocket
import json

# AllTick WebSocket URL
ws_url = "wss://api.alltick.co/realtime"

def on_message(ws, message):
    data = json.loads(message)
    # 简单打印最新行情
    print(f"{data['symbol']} - 最新价: {data['price']} 时间: {data['timestamp']}")

def on_open(ws):
    # 订阅港股和美股行情
    msg = {
        "action": "subscribe",
        "symbols": ["00700.HK", "AAPL.US"]
    }
    ws.send(json.dumps(msg))

ws = websocket.WebSocketApp(ws_url, on_message=on_message, on_open=on_open)
ws.run_forever()

几个要点:

  • symbols 字段可以自由组合港股、美股股票代码
  • WebSocket 推送省去了轮询的麻烦
  • 我通常会在回调里加一点数据缓存和异常处理,保证程序稳定

实际使用后的变化

在把港股和美股行情统一接入之后,几个变化非常直观:

  • 数据结构统一,策略层代码更干净
  • WebSocket 推送减少了延迟和轮询压力
  • 系统稳定性更好,排查问题更容易

很多之前看起来像“策略不稳定”的情况,实际上是数据层噪音造成的。

实战中需要注意的细节

即便使用统一接口,仍然有一些工程细节需要你自己把控:

  • 时间处理:不同市场交易时间不同,时间戳必须统一标准
  • 高频数据控制:tick 数据建议异步处理或限流,避免内存堆积
  • 调试方式:先订阅少量标的跑通流程,再逐步扩展

这些点不复杂,但直接影响系统是否能稳定运行。

总结

港股和美股的实时行情接入,本身并不是难题。
真正拉开效率差距的,是你是否在一开始就选对了数据接入方式。

统一的数据源、实时推送机制、可维护的结构设计,会让你把更多精力放在策略和逻辑本身,而不是反复处理接口差异。

如果你正在做跨市场行情相关的开发,这个方向值得你认真评估一次。

在图像生成领域,扩散模型因其训练稳定和泛化能力强已逐渐走入主流行列。然而,面对海报、PPT、科普图等需要准确传达复杂信息的「知识密集型」场景时, 传统模型存在指令理解与细节刻画难以兼顾的短板。 另一个长期存在的问题是生成图像中的文字经常出现笔画错误或难以辨识,严重影响实用价值。

基于此,智谱于 2026 年 1 月联合华为开源了新一代图像生成模型 GLM-Image。 该模型基于昇腾 Atlas 800T A2 和昇思 MindSpore AI 框架完成全流程训练。其核心特点是采用了创新的 「自回归+扩散解码器」混合架构(9B 自回归模型 + 7B DiT 解码器), 将语言模型的深度理解能力与扩散模型的高质量生成能力相结合。

此外,模型通过改进 Tokenizer 策略,原生支持从1024×1024 到 2048×2048 的任意比例图像生成,无需重新训练。 GLM-Image 的创新性还体现在以下两个方面:

*解决文字渲染难题: 在 CVTG-2K 和 LongText-Bench 权威评测中,其文字准确率等关键指标均位列开源模型第一,显著提升了图像中文字的生成准确性。

*定义高性价比应用: 在 API 调用模式下,生成单张图片的成本仅需 0.1 元,成本仅为主流闭源模型的 1/10 至 1/3,为商业化应用提供了高性价比选择。

目前,「GLM-Image 精准语义高保真图像生成模型」已上线 HyperAI 官网(hyper.ai)的教程版块, 快来输出无限创意吧!

在线体验: https://go.hyper.ai/2jcCU

效果示例:

Demo 运行

1.进入 hyper.ai 首页后,选择「GLM-Image 精准语义高保真图像生成模型」,或进入「教程」页面选择。页面跳转后,点击「在线运行此教程」。


2.页面跳转后,点击右上角「克隆」,将该教程克隆至自己的容器中。

注:页面右上角支持切换语言,目前提供中文及英文两种语言,本教程文章以英文为例进行步骤展示。

3.选择「NVIDIA RTX Pro 6000」以及「PyTorch」镜像,按照需求选择「Pay As You Go(按量付费)」或「Daily Plan/Weekly Plan/Monthly Plan(包日/周/月」,点击「Continue job execution(继续执行)」。

HyperAI 为新用户准备了注册福利,仅需 $1,即可获得 20 小时 RTX 5090 算力(原价 $7),资源永久有效。


4.等待分配资源,当状态变为「Running(运行中)」后,点击「Open Workspace」进入 Jupyter Workspace。

效果演示

页面跳转后,点击左侧 README 页面,进入后点击上方 Run(运行)。


待运行完成,即可点击右侧 API 地址跳转至 demo 页面


以上就是 HyperAI超神经本期推荐的教程,欢迎大家前来体验!

教程链接:

https://go.hyper.ai/2jcCU

前言

在数字化运维与业务监控的实践中,仪表板(Dashboard)与汽车的仪表盘同等重要,它不仅是数据可视化的载体,更是团队快速定位问题、洞察数据趋势的核心工具。观测云在平台中内置了大量通用组件、云服务的仪表板模板。但如果你希望从零开始构建个性化的仪表板,又或者对自己绘制的仪表板不够满意,那么这篇文章将教授你几个小技巧,帮助你有效提升仪表板的质量。

观测云简介

观测云是一个统一实时监测平台,它提供全面的系统可观测性解决方案,帮助用户快速实现对云平台、云原生、应用及业务的监控需求。观测云的核心功能包括:基础设施监测,日志采集和分析,用户访问监测(RUM),应用性能监测(APM),服务可用性监测(拨测),安全监测,智能监控等等。这款产品能够帮助工程师全面了解端到端的用户体验追踪,了解应用服务的每一次调用,以及全面监控云时代的基础设施。此外,观测云还具备快速发现系统安全风险的能力,为数字化时代提供安全保障。更多信息可以访问观测云官网:https://www.guance.com

基础仪表板的绘制

让我们进入到观测云,创建一个属于您自己的仪表板。首先,你需要找到仪表板的入口「场景」-「仪表板」,点击「新建仪表板」-「新建空白仪表板」。

图片

其次,可以从侧滑窗口中选择适合展示数据的图表类型,拖拽到左边的空白画布中。

图片

以常用的「时序图」为例,拖动到画布中即可打开「新建图表」弹窗,此时通过数据筛选控件来选择需要展示的指标。

图片

如图所示,我们已经展示了一条指标曲线,它代表的含义为:指标集为 cpu,指标名为 usage_total,按照 host 进行分组并统计 Avg 平均值,只显示 host=DESKTOP-F9E75IG 的值(过滤条件),点击右下角的保存即可。

图片

此时你已经完成了第一张图表的制作,通过一张张图表的叠加,你很快能得到一个完整的仪表板,不过它看上去有些简陋,我们需要更多技巧对仪表板的美观程度和易读性进行优化。

图片

仪表板的优化

新增标题和描述

恰当的标题能让用户第一时间知道图表展示的指标及其含义,而图表的描述能够起到有效补充说明。我个人的习惯是将图表名设置为指标的英文名,然后在「描述」中补充该指标的中文含义。如下所示:

图片

保存生效的效果如下,图表左上角将展示标题,而鼠标 hover 到帮助按钮上则会悬浮显示描述。

图片

数据单位

一部分指标在采集到观测云后没有单位,因此在绘制仪表板时需要注意补充单位。

图片

别名

图表的曲线会显示对象的名称,并且对象名称会随着分组条件的增多而变得复杂。例如下图,由于添加了 namespace,pod_name 等多个分组条件,对象名称显得很长,很不直观。好在我们可以通过配置「别名」来简化对象名称的显示。

图片

在图表配置的「别名」处,我们选择对应的指标序列,并用 {{}} 将分组条件包起来作为变量,例如下图中的分组条件 pod_name 就写为 {{pod_name}} ,效果如下所示。

图片

我们也支持用多个变量作为别名,写法为 {{分组条件1}}-{{分组条件2}} ,例如 {{namespace}}--{{pod_name}} ,效果如下图所示:

图片

现在,曲线上显示的对象名称已经比原始的名称简洁、清晰了很多。

图例

图表默认没有带上图例,除非将鼠标 hover 上去,否则无法看到什么颜色的曲线代表哪一个对象,如下图的左侧所示。而「图例」则可以将对象的名称和统计值显示到图表中,如下图的右侧所示。

图片

添加图例的方式如下,可选择将图例放置在图表的底部或者右侧,并显示单个或者统计值,将 Avg、Max、Last 一起显示出来是个不错的选择。

图片

分组

当仪表板中需要展示很多张图表时,使用「分组」来将不同含义的图表分门别类地归类就十分有必要了,这会让仪表板的显示更具有条理,用户能通过分组快速找到自己关注的图表,如下图所示。

图片

给仪表板添加分组时,只需要在侧滑菜单中找到「分组」这个图表类型,拖动到左侧画布即可。

图片

取一个有意义的分组名称,选择一个与众不同的颜色,保存即可。

图片

下图即为新创建的分组,后续添加的图表即可拖动到该分组下。

图片

锁定时间

如果你希望每次进入到仪表板,都查看到固定时间区间(例如最近1天)的数据,应该如何实现呢?

我们很容易注意到仪表板的时间控件,可以在这里固定整个仪表板的时间。

图片

如果需要将单个图表的时间进行固定,而其他图表的时间则跟随仪表板的时间控件,也很好实现。我们进入单个图表的编辑窗口,打开「高级配置」,将时间锁定为指定区间即可。

图片

配置完成后,这个图表的右上角会出现你锁定的时间区间,该时间不受仪表板整体的时间区间控制,如下图所示。

图片

视图变量

视图变量允许用户通过下拉菜单来选择特定对象的监控数据,从而根据自身需求动态筛选和分析数据。如下图所示,该仪表板包括了 Cluster、Namespace 和 Node 三个视图变量,用户可以进行自由筛选。

图片

我们首先添加一个简单的视图变量,需求是通过选择 host_ip 来过滤单台主机的数据。在仪表板中点击「添加视图变量」。

图片

图片

host_ip 是 cpu 相关指标数据的一个标签,因此我们从指标类型- CPU指标集里面选择 host_ip 作为视图变量的来源,然后将「变量名」和「显示名」都与之保持一致,保存窗口即可。

图片

此时返回仪表板,就会看到刚才添加的 host_ip 视图变量,从下拉菜单中可以筛选主机 IP。如下图所示:

图片

你可能会发现筛选结果之后,对下方的图表没有任何作用,因为我们还需要在图表中的过滤条件配置变量,使仪表板的额视图变量与图表的过滤条件进行联动。再次进入图表编辑界面,添加一个过滤条件,字段选择为 host_ip,值选择「视图变量」,如下图所示。

图片

返回仪表板,这时仪表板的视图变量 host_ip 就可以与图表中的监控对象 host_ip 进行联动了。我们可以通过下拉菜单来筛选不同的主机,从而观察不同主机的监控指标。

图片

如果是有多个视图变量,且视图变量之间有依赖关系,例如我们针对 Pod 的监控是首先选择 namespace,再选 Pod,那我们又应该如何配置呢?这就要用到「级联」的写法,让我们来再来回顾一下刚才本章开头的那张图片。选择 Cluster 后,Namespace 的值随 Cluster 的取值而联动,Node 的值又随 Namespace 的取值而联动。

图片

我们研究一下这组视图变量的写法,不难发现规律是在 show_tag_value 函数的后面跟随了一个 {key=value} 的过滤条件,其固定写法为 {key='#{value}'} ,key 和 value 都取自上一级的变量名,表示该变量随上一级变量的值而联动。如下图所示,当用户在界面上选择了 cluster_name_k8s 的值后,该值就会传入 namespace 作为过滤条件,从而实现变量联动。

图片

图片

小结

通过上面的几个小技巧,相信你已经跃跃欲试将自己的仪表板更上一层楼了。在得到令自己满意的仪表板后,你可以选择将仪表板开放给团队、配置为定时报告、投放到大屏幕、关联到日志或链路查看器中,又或者在接收到告警时一键查看这张仪表板。我们非常期待你通过仪表板来向你的团队/客户呈现数据的价值。

在现代知识型组织中,企业的核心竞争力正从“单点突破”向“全流程模块化优化”转移。模块化业务拆解软件不仅是项目结束后的总结文档,更是将复杂的业务过程通过结构化的数据回溯,转化为可量化、可进化的动态智力资产的架构引擎。

一、 为什么现代管理必须重视“模块化”拆解?

缺乏有效拆解工具的组织往往陷入“经验黑盒”困境:成功无法被精准复制,失败的根源被掩盖在碎片化的信息中。模块化业务拆解软件的核心价值在于:

  • 消除认知偏误:通过全量数据的客观还原,确保拆解基于真实发生的业务节点,而非参与者的主观记忆。
  • 支撑深层根因探究:支持在拆解过程中下钻子环节,应对长周期、高协作密度的复杂项目评估需求。
  • 实现效能自动度量:无需手动统计,各阶段的投入产出比、耗时偏差自动向上级看板聚合,辅助决策。
  • 拆解成果资产化:将验证有效的改进动作沉淀为标准化模板,实现跨团队、跨项目的快速经验迁移。

---

二、 模块化拆解的技术路径:三层评价架构

构建模块化业务拆解体系需要遵循“过程回溯”与“逻辑重构”的逻辑:

  1. 宏观项目层(Project Context):定义拆解的业务边界、最初目标及最终交付全景。
  2. 效能节点层(Performance Nodes):将业务链条拆解为关键里程碑,各节点记录当时的决策背景、资源投入与实际产出。
  3. 原子行为层(Atomic Insights):拆解的最末端,聚焦于具体动作的得失,具备明确的改进建议和落实跟踪机制。

---

三、 核心技术实现与算法示例

模块化业务拆解软件的底层逻辑涉及效能得分算法、异常趋势捕捉及递归式数据回溯。

1. 基于加权算法的节点效能自动评分

在模块化拆解中,项目的总效能得分由各关键环节的执行质量自动驱动。以下为 JavaScript 实现的效能评分逻辑:

JavaScript

/**
* 根据各环节表现自动计算项目模块化拆解效能得分
* @param {Object} project 项目拆解对象(包含子任务节点数组)
* @returns {number} 聚合后的效能综合得分
*/
function calculateEfficiencyScore(project) {

// 基准情况:如果是原子行动项,返回其预定目标达成度(0-100)  
if (\!project.subNodes || project.subNodes.length \=== 0) {  
    return project.goalAchievementRate || 0;  
}

// 汇总所有效能节点的加权得分  
const totalWeightedScore \= project.subNodes.reduce((sum, node) \=\> {  
    // 每个节点可根据重要性分配权重  
    const weight \= node.weight || (1 / project.subNodes.length);  
    return sum \+ (calculateEfficiencyScore(node) \* weight);  
}, 0);

// 更新项目的模块化拆解效能显示  
project.finalScore \= Math.round(totalWeightedScore);  
return project.finalScore;  

}

2. Python:效能偏离度的动态分析引擎

利用效能模型,自动对比“计划节点”与“实际轨迹”,识别出导致整体效率下降的关键环节:

Python

class EfficiencyAuditEngine:

def \_\_init\_\_(self):  
    \# 预设标准效能库:项目类型 \-\> 预期耗时/资源基准  
    self.benchmarks \= {  
        "Product\_Launch": {  
            "Design": {"time": 48, "resource": 3},  
            "Dev": {"time": 120, "resource": 8},  
            "QA": {"time": 24, "resource": 2}  
        }  
    }

def analyze\_deviation(self, project\_data, project\_type):  
    """对比实际轨迹与基准,识别拆解关键点"""  
    standards \= self.benchmarks.get(project\_type)  
    if not standards:  
        return "未找到匹配的项目效能基准"

    for node, actual in project\_data.items():  
        benchmark \= standards.get(node)  
        if benchmark:  
            time\_deviation \= (actual\['time'\] \- benchmark\['time'\]) / benchmark\['time'\]  
            if time\_deviation \> 0.15:  
                print(f"\[Review Focus\] 节点 '{node}' 存在显著负向偏差: {time\_deviation:.2%}")  
                \# 自动触发根因分析引导  
                self.\_trigger\_root\_cause\_prompt(node)

def \_trigger\_root\_cause\_prompt(self, node\_name):  
    print(f"  \-\> 已生成 '{node\_name}' 环节的 5-Whys 拆解工作单")

3. SQL:跨项目效能瓶颈识别与经验溯源

通过递归查询,识别组织中长期存在的“重复性错误”或“低效环节”:

SQL

WITH RECURSIVE ReviewHierarchy AS (

\-- 初始行:选择需要拆解的顶层项目  
SELECT id, project\_name, parent\_id, efficiency\_score, review\_date   
FROM efficiency\_reviews WHERE parent\_id IS NULL  
UNION ALL  
\-- 递归关联各层级子任务的拆解数据  
SELECT r.id, r.project\_name, r.parent\_id, r.efficiency\_score, r.review\_date  
FROM efficiency\_reviews r  
INNER JOIN ReviewHierarchy rh ON r.parent\_id \= rh.id  

)
SELECT

project\_name,   
AVG(efficiency\_score) as avg\_score,  
COUNT(\*) as review\_count  

FROM ReviewHierarchy
GROUP BY project\_name
HAVING avg\_score \< 70 -- 识别效能持续低迷、亟待流程重塑的领域
ORDER BY avg\_score ASC;

---

四、 工具分类与选型思路

在实施模块化业务拆解时,不同架构的工具侧重点有所不同:

工具优势亮点
板栗看板支持看板式模块化业务拆解管理,可视化流程与状态,便于任务重组与跟踪
Monday.com强大的工作流与自动化功能,支持构建复杂的模块化业务管理视图
Asana灵活的项目与任务数据库结构,适合构建结构化的业务拆解知识库
Jira独特的敏捷看板与问题追踪机制,支持精细化业务模块关联与分析
Trello专为团队业务协作设计,集成清单、附件和自动化规则功能

---

五、 实施中的风险控制与管理优化

  • 防止“形式化拆解”:如果拆解成了文字游戏,会导致团队抵触。应遵循“拆解为了改进,而非为了问责”的文化导向。
  • 确保改进闭环同步:拆解发现的问题必须自动转化为“改进任务”并指派负责人,防止结论被遗忘。
  • 动态调整评价基准:随着团队能力的提升,效能拆解的基准值应定期进行重新对标,驱动组织持续进化。

---

六、 结语

模块化是组织进化的必经之路。 模块化业务拆解软件不仅通过技术手段解决了“盲目总结”的问题,更将组织的每一次经历转化为可以指导未来决策的有效资产。当组织的每一次拆解都能以全景的形式精准呈现时,企业才能真正实现从“低效率重复”向“高水平螺旋上升”的本质跨越。

在早期的Oracle数据库的版本中,一般情况下一个数据库服务器只创建一个数据库。当创建的数据库比较多的时候,就需要更多的数据库服务器。这对服务器资源(CPU、内存、存储)来说是一种浪费。从Oracle数据库 12c开始,Oracle数据库引入了多租户特性,即容器数据库。该特性可以在一个数据库服务器上创建容器数据库,并管理多个可插拔数据库。从而降低了成本并提高了服务器资源的利用率。视频讲解如下:
https://www.bilibili.com/video/BV1fXkeBAEh8/?aid=115920110358...

Oracle Multitenant Container Database(CDB),即多租户容器数据库是从Oracle 12c引入的一个新的特性。它指的是可以容纳一个或者多个可插拔数据库(Pluggable Database,简称PDB)的数据库,这个特性允许在CDB容器数据库中的体系架构创建并且维护多个数据库。在CDB容器数据库中创建的数据库就是PDB数据库,而每个PDB在CDB中是相互独立存在的。在单独使用PDB时,与普通数据库无任何区别。CDB容器数据库也叫作根数据库,其主要作用就是容纳并管理所有相关的PDB数据库及其元数据。CDB也可以单独使用,从操作使用上看,CDB也与普通数据库无任何区别。下图展示了多租户容器数据库的体系架构。
image.png

从图中可以看出,Oracle多租户容器数据库的体系架构由三个部分组成,它们分别是:Root、PDB Seed和PDBs。下表详细说明了每一部分的功能和作用。
image.png

从Oracle数据库 12c R2版本开始,Oracle对多租户容器数据库的功能进行了增强,在CDB root容器中可以创建一个叫做Application Root的容器,可在其内创建多个依赖于Application root的Application PDB。如下图所示。
image.png

要使用Oracle数据库提供的多租户容器数据库的功能,首先就必须要创建CDB的环境。其本质就是创建CDB的根数据库Root。创建CDB中的根数据库Root可以通过DBCA的图形工具来进行创建,也可以通过执行SQL的脚本来创建。

  • 使用DBCA创建根数据库Root
    image.png
  • 使用SQL脚本创建根数据库Root
SQL> create database cdb2  
      user sys identified by password user system identified by password
      logfile group 1 ('/u01/app/oradata/cdb2/redo1a.log',
                       '/u02/app/oradata/cdb2/redo1b.log') size 100m,
              group 2 ('/u01/app/oradata/cdb2/redo2a.log',
                       '/u02/app/oradata/cdb2/redo2b.log') size 100m 
      character set al32utf8 national character set al16utf16  
      extent management local datafile '/u01/app/oradata/cdb2/system01.dbf' size 325m 
      sysaux datafile '/u01/app/oradata/cdb2/sysaux01.dbf' size 325m 
      default temporary tablespace tempts1 tempfile '/u01/app/oradata/cdb2/temp01.dbf' size 20m 
      undo tablespace undotbs datafile '/u01/app/oradata/cdb2/undotbs01.dbf' size 200m
      enable pluggable database 
      seed   file_name_convert = ('/u01/app/oradata/cdb2',
                                  '/u01/app/oradata/cdb2/seed');

在成功创建了CDB环境后,就可以进一步基于根数据库Root来创建多个PDB数据库。

  • 使用DBCA创建PDB
    image.png
  • 使用SQL脚本创建PDB
SQL> create pluggable database cdb1pdb3 admin user pdb3sys identified by password 
     file_name_convert= ('/u01/app/oracle/oradata/CDB1/pdbseed',
                         '/u01/app/oracle/oradata/CDB1/cdb1pdb3'); 

一、什么是国密SSL证书?

国密SSL证书是基于中国自主研发的加密算法(SM2算法)  ,符合国家密码管理局、公安部和工信部的安全标准,旨在提高我国网络通信的安全性和自主可控性。

它的工作原理与传统SSL证书类似,主要用于加密网站通信,确保数据在传输过程中不被第三方窃取或篡改。不同的是,国密SSL证书使用的是国密算法,而非传统的RSA或ECC算法。

二、国密SSL证书的优势

更安全的加密算法

国密SSL证书采用SM2算法,基于椭圆曲线密码技术,相比RSA具有更高的安全性和计算效率。

SM3哈希算法替代SHA系列,避免国际算法的安全隐患。

符合国家政策要求

国密SSL证书由国家认可的机构颁发,符合国内合规要求,适用于政府、金融、医疗等对数据安全要求较高的行业。

双证书兼容性

部分国密SSL证书支持双算法模式(国际算法+国密算法),保证兼容传统国际算法的浏览器,同时在国密环境下运行时使用SM2算法,确保系统过渡平稳。

三、国密SSL证书的申请流程

打开JoySSL官网,注册时填写注册码230970,获取大额优惠跟技术支持。

1. 确定证书类型

根据需求选择合适的国密SSL证书,通常有以下几种类型:

  • 单域名证书(适用于单一网站)
  • 多域名证书(适用于多个站点)
  • 通配符证书(适用于同一主域名下的所有子域名)

2. 生成CSR文件

CSR(证书签名请求)是申请SSL证书时的必要文件,需要在服务器上生成。生成时,需选择SM2算法,并填写组织信息、域名等相关信息。

3. 提交企业认证信息

国密SSL证书需要验证申请者的合法身份,通常需要提供:

  • 企业营业执照或组织机构代码
  • 域名所有权证明
  • 联系人信息(电话、邮箱)

4. 证书颁发与安装

审核通过后,CA机构会签发国密SSL证书,申请者需要将证书安装到服务器上,并配置HTTPS访问。

5. 测试与优化

安装完成后,建议使用SSL检测工具检查证书是否正确安装,同时优化服务器的SSL/TLS配置,确保安全性和兼容性。

四、总结

国密SSL证书基于我国自主的加密算法。相比传统SSL证书,国密SSL证书在数据安全性、合规性和国产化兼容性方面具有明显优势。

申请流程包括选择证书类型、生成CSR文件、提交企业认证信息、证书签发与安装等步骤,整体流程与传统SSL证书类似,但需要确保服务器和应用支持国密算法。

基于YOLOv8的蚊蝇位置智能检测识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!

源码包含:完整YOLOv8训练代码+数据集(带标注)+权重文件+直接可允许检测的yolo检测程序+直接部署教程/训练教程

基本功能演示

https://www.bilibili.com/video/BV1zYrhBxEau/

源码在哔哩哔哩视频简介处

项目摘要

本项目基于 YOLOv8 深度学习检测模型,结合 PyQt5 图形界面,实现了对蚊子和苍蝇的自动检测与定位。项目核心特点包括:

  1. 多输入源支持:可处理单张图片、图片文件夹、视频文件以及实时摄像头输入。
  2. 高精度识别:利用定制蚊蝇数据集训练,准确识别蚊子与苍蝇,同时兼顾背景样本,降低误报率。
  3. 开箱即用:提供完整源码、训练数据、预训练权重及部署教程,用户可直接运行检测系统或继续训练自定义模型。
  4. 可视化界面:PyQt5 图形界面直观展示检测结果,支持边框显示、类别标注、置信度显示等功能。
  5. 灵活扩展:项目结构清晰,可快速扩展到其他小型生物检测任务或多分类目标检测场景。

通过本项目,用户可实现蚊蝇数量监测、位置统计及风险评估,为实验室、公共卫生、农业及城市环境管理提供智能化工具。

前言

随着智能视觉技术的发展,小型害虫检测在公共卫生、农作物管理及环境监测中具有重要意义。传统人工检测方法不仅耗时长、效率低,而且容易漏检或误判。借助 YOLO 系列目标检测算法,本项目提供了一种快速、准确、可扩展的蚊蝇检测解决方案。

项目基于无人机或固定摄像头拍摄的实验样本,通过训练专用数据集,使模型能够在复杂背景下自动识别蚊子和苍蝇位置。结合 PyQt5 图形界面,用户无需掌握深度学习底层技术即可完成检测、可视化及数据统计。

一、软件核心功能介绍及效果演示

核心功能

  1. 图片检测

    • 支持单张图片检测,自动标注蚊子和苍蝇位置。
    • 输出标注图与 YOLO 格式检测结果。
  2. 批量图片处理

    • 支持文件夹中所有图片的批量检测。
    • 自动生成检测报告,包括数量统计及置信度分析。
  3. 视频检测

    • 支持本地视频文件输入,实时识别视频中的蚊子与苍蝇。
    • 可选择保存检测后的视频,标注框清晰展示目标。
  4. 摄像头实时检测

    • 支持 USB 摄像头或笔记本内置摄像头实时捕捉并检测蚊蝇。
    • 界面显示实时检测帧,支持帧率与置信度调节。
  5. 检测结果可视化

    • 在 PyQt5 界面中显示目标框、类别及置信度。
    • 支持结果导出,包括图片、视频和 CSV 数据。
  6. 训练与模型管理

    • 提供完整训练代码与数据集标注示例。
    • 可加载自定义权重继续训练或微调模型。
    • 支持 YOLOv8 标准训练流程,包括训练集划分、超参数配置和结果可视化。

效果演示

  • 图片示例

    • 检测后每只蚊子与苍蝇都会被框出,类别和置信度清晰显示。
  • 视频示例

    • 视频播放时,模型实时标注移动的目标,统计目标数量并可导出检测数据。
  • 实时摄像头示例

    • 界面上可即时显示检测框与数量统计,操作简单,无需命令行操作。

二、软件效果演示

为了直观展示本系统基于 YOLOv8 模型的检测能力,我们设计了多种操作场景,涵盖静态图片、批量图片、视频以及实时摄像头流的检测演示。

(1)单图片检测演示

用户点击“选择图片”,即可加载本地图像并执行检测:

image-20260112012732195


(2)多文件夹图片检测演示

用户可选择包含多张图像的文件夹,系统会批量检测并生成结果图。

image-20260112012821538


(3)视频检测演示

支持上传视频文件,系统会逐帧处理并生成目标检测结果,可选保存输出视频:

image-20260112012846148


(4)摄像头检测演示

实时检测是系统中的核心应用之一,系统可直接调用摄像头进行检测。由于原理和视频检测相同,就不重复演示了。

image-20260112012858804


(5)保存图片与视频检测结果

用户可通过按钮勾选是否保存检测结果,所有检测图像自动加框标注并保存至指定文件夹,支持后续数据分析与复审。

image-20260112012943268

三、模型的训练、评估与推理

YOLOv8是Ultralytics公司发布的新一代目标检测模型,采用更轻量的架构、更先进的损失函数(如CIoU、TaskAlignedAssigner)与Anchor-Free策略,在COCO等数据集上表现优异。
其核心优势如下:

  • 高速推理,适合实时检测任务
  • 支持Anchor-Free检测
  • 支持可扩展的Backbone和Neck结构
  • 原生支持ONNX导出与部署

3.1 YOLOv8的基本原理

YOLOv8 是 Ultralytics 发布的新一代实时目标检测模型,具备如下优势:

  • 速度快:推理速度提升明显;
  • 准确率高:支持 Anchor-Free 架构;
  • 支持分类/检测/分割/姿态多任务
  • 本项目使用 YOLOv8 的 Detection 分支,训练时每类表情均标注为独立目标。

YOLOv8 由Ultralytics 于 2023 年 1 月 10 日发布,在准确性和速度方面具有尖端性能。在以往YOLO 版本的基础上,YOLOv8 引入了新的功能和优化,使其成为广泛应用中各种物体检测任务的理想选择。

image-20250526165954475

YOLOv8原理图如下:

image-20250526170118103

3.2 数据集准备与训练

采用 YOLO 格式的数据集结构如下:

dataset/
├── images/
│   ├── train/
│   └── val/
├── labels/
│   ├── train/
│   └── val/

每张图像有对应的 .txt 文件,内容格式为:

4 0.5096721233576642 0.352838390077821 0.3947600423357664 0.31825755058365757

分类包括(可自定义):

image-20260112013102185

image-20260112013042045

3.3. 训练结果评估

训练完成后,将在 runs/detect/train 目录生成结果文件,包括:

  • results.png:损失曲线和 mAP 曲线;
  • weights/best.pt:最佳模型权重;
  • confusion_matrix.png:混淆矩阵分析图。
若 mAP@0.5 达到 90% 以上,即可用于部署。

在深度学习领域,我们通常通过观察损失函数下降的曲线来评估模型的训练状态。YOLOv8训练过程中,主要包含三种损失:定位损失(box_loss)、分类损失(cls_loss)和动态特征损失(dfl_loss)。训练完成后,相关的训练记录和结果文件会保存在runs/目录下,具体内容如下:

image-20260112013024393

3.4检测结果识别

使用 PyTorch 推理接口加载模型:

import cv2
from ultralytics import YOLO
import torch
from torch.serialization import safe_globals
from ultralytics.nn.tasks import DetectionModel

# 加入可信模型结构
safe_globals().add(DetectionModel)

# 加载模型并推理
model = YOLO('runs/detect/train/weights/best.pt')
results = model('test.jpg', save=True, conf=0.25)

# 获取保存后的图像路径
# 默认保存到 runs/detect/predict/ 目录
save_path = results[0].save_dir / results[0].path.name

# 使用 OpenCV 加载并显示图像
img = cv2.imread(str(save_path))
cv2.imshow('Detection Result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

预测结果包含类别、置信度、边框坐标等信息。

image-20260112013207795

四.YOLOV8+YOLOUI完整源码打包

本文涉及到的完整全部程序文件:包括python源码、数据集、训练代码、UI文件、测试图片视频等(见下图),获取方式见【4.2 完整源码下载】:

4.1 项目开箱即用

作者已将整个工程打包。包含已训练完成的权重,读者可不用自行训练直接运行检测。

运行项目只需输入下面命令。

python main.py

读者也可自行配置训练集,或使用打包好的数据集直接训练。

自行训练项目只需输入下面命令。

yolo detect train data=datasets/expression/loopy.yaml model=yolov8n.yaml pretrained=yolov8n.pt epochs=100 batch=16 lr0=0.001

4.2 完整源码

至项目实录视频下方获取:https://www.bilibili.com/video/BV1zYrhBxEau/

image-20250801135823301

包含:

📦完整项目源码

📦 预训练模型权重

🗂️ 数据集地址(含标注脚本)

总结

本项目基于 YOLOv8 深度学习检测模型与 PyQt5 图形界面,实现了蚊子与苍蝇的高效、智能化检测与定位。通过专用数据集训练,系统能够在复杂背景下准确识别目标,同时提供图片、视频及摄像头多种输入方式。

项目核心优势包括:

  1. 高精度识别:模型在小型目标和复杂背景下表现稳定,误报率低。
  2. 多场景适用:支持单张图片、批量图片、视频和实时摄像头输入。
  3. 可视化与易用性:界面直观,标注清晰,用户无需深度学习经验即可使用。
  4. 可扩展性:源码结构清晰,可快速应用于其他小型生物检测任务或扩展目标类别。
  5. 开箱即用:提供完整训练流程、权重文件和部署教程,用户可直接上手或自定义训练。

整体而言,本项目为公共卫生监测、实验室研究和环境管理提供了一个 快速、可靠、可视化的智能检测解决方案,降低人工检测成本,提高数据收集效率,为小型害虫监控提供了可落地的技术工具。

继上次微软免费的 Microsoft 365 E3 全局 https://v2ex.com/t/1172827

看到隔壁有人发,我想起了去年也验证过,方法很简单,当然前提你有 edu 邮箱,没有的话去 google 搜个社区大学注册。

去年起微软针对教育用户(学生)免费提供 2 年的 Microsoft 365 Premium 订阅。按微软价格的话,大概人民币 3000 多。

Microsoft 365 Premium 以每月 19.99 美元的价格,同时提供微软 Office 套件的使用权限与 Copilot Pro 的功能。该订阅包含更高的功能使用限额,以及 Copilot Labs 、Actions 等 Copilot Pro 专属功能的访问权限等。同时你可以通过电子邮件邀请最多 5 个人加入你的 Microsoft 365 家庭版订阅。

截图

具体方法

必备条件 edu 邮箱。
邮箱

登录你的微软个人账号,依次点击下面链接,进入验证,填写你的大学 edu 邮箱。
邮箱
个人版:
https://checkout.microsoft365.com/acquire/purchase?language=EN-US&market=HK&requestedDuration=Month&scenario=microsoft-365-student&client=poc&campaign=StudentFree12M

高级版:
https://checkout.microsoft365.com/acquire/purchase?language=EN-US&market=HK&requestedDuration=Month&scenario=microsoft-365-premium&client=poc&campaign=StudentPremiumFree12M
我的订阅

支付方式支持 paypal ,支付宝等,验证订阅后,你可以直接取消自动续费,就不会扣费风险!

本文梳理了一套通过 6 个步骤清晰展示系统设计思维的应对框架,包括澄清需求、定义成功标准、画出高层架构、设计数据层,到扩展性与可靠性,最后考虑权衡取舍。

系统设计面试并不是考你会不会背各种技术名词,而是看你能不能在有限时间里,有条理的拆解问题、做出合理的架构决策,并把自己的思路讲清楚

面试的评分标准其实是“思考方式”,而不是“系统有多炫酷”。因此需要一套可重复执行的流程,把几十分钟的面试时间拆分成若干阶段,每一阶段回答一个明确的问题。

接下来就介绍这套能够帮助你顺利通过各种系统设计面试的框架。


50 分钟作战计划

下面是一份 50 分钟时间切片路线图:

- 0–5 分钟:澄清需求
- 6–12 分钟:定义成功标准
- 13–22 分钟:画出高层架构
- 23–32 分钟:设计数据层
- 33–42 分钟:讨论扩展性与可靠性
- 43–50 分钟:收尾与权衡总结

路线图可以按阶段展开,每个阶段都对应面试过程中呈现在白板或文档上的“可见成果”。

阶段 1:先澄清再设计(0–5 分钟)

永远不要直接开始画图。第一步应该是:用问题把“题目”变成“需求”。

可以围绕以下维度澄清:

  • 用户与规模:有多少用户、日活?是 100 万还是 10 亿?
  • 核心用例:最重要的 1~2 个场景是什么?例如仅做照片分享,还是要包含完整的社交功能?
  • 客户端形态:只考虑移动端,还是移动 + Web?
  • 地理分布:是否是全球分布,是否有多区域部署需求?
  • 时延要求:例如“Feed 打开时间需要控制在 500ms 以内”。

对于面试官抛出的“设计 Instagram”之类的问题,可以先反问:

“我们是只关注图片流(Photo Feed),还是要覆盖整个产品?是否支持视频?大致用户量级是多少?”

这一阶段的目标:用 2–3 分钟让双方对“要构建的东西”达成共识,让后面的设计有清晰边界。

阶段 2:写下什么叫“成功”(6–12 分钟)

在澄清了范围之后,第二步是明确定义功能性和非功能性需求,包括:

  • 功能性需求:比如“用户可以上传图片、关注他人、看到关注对象的动态流、对内容点赞与评论”等;
  • 非功能性需求:比如“高可用性(High Availability)达到 99.9%”、“Feed 加载延迟(Latency)小于 500ms”、“可以扩展到 1 亿日活用户”、“允许最终一致性(Eventual Consistency)”。

把这些需求点写在白板上或共享文档中,就相当于和面试官形成了“设计合约”:后续所有架构选择,都要能解释清楚“这是为了满足哪条需求”。

这一阶段的目标:让面试官看到你不是在“凭感觉设计”,而是在对齐“什么设计是成功的”。

阶段 3:先画大图,再补细节(13–22 分钟)

到了真正画架构图的时候,强调一个原则:先画大块(High-Level Components),再深入具体实现,而不是一开始就纠结字段、索引或具体中间件。

典型高层架构可以包括:

┌─────────┐
│  Users  │
└────┬────┘
     │
     ↓
┌─────────────┐
│  CDN/Cache  │
└─────┬───────┘
      │
      ↓
┌──────────────┐      ┌──────────────┐
│ Load Balancer│─────→│ Load Balancer│
└──────┬───────┘      └──────┬───────┘
       │                     │
       ↓                     ↓
┌─────────────┐      ┌─────────────┐
│ API Servers │      │Media Service│
└──────┬──────┘      └──────┬──────┘
       │                    │
       ↓                    ↓
┌─────────────┐      ┌─────────────┐
│  Database   │      │Object Storage│
└─────────────┘      └─────────────┘
  • 客户端(Mobile / Web);
  • CDN(Content Delivery Network)和缓存(Cache),用于分发静态资源与热门内容;
  • 负载均衡(Load Balancer),把流量分发到后端服务;
  • API 服务(API Servers),承载业务逻辑;
  • 媒体服务(Media Service),负责图片/视频的处理与存储;
  • 数据库(Database),保存用户、关系、元数据;
  • 对象存储(Object Storage),保存实际的图片/视频文件。

在讲解数据流时,可以用一句简短的“端到端路径”来串起来,例如:

用户上传图片 → API 服务处理请求 → 媒体服务转码与压缩 
                            ↓
                        写入对象存储
                            ↓
                        在数据库中记录元数据
                            ↓
                        返回可访问 URL

这一阶段的目标:让面试官在脑中形成清晰的“系统鸟瞰图”,知道所有关键组件长什么样、怎么互相连接。此时还不必深入到每个组件内部实现。

阶段 4:谈数据,而不是只谈服务(23–32 分钟)

后半段时间建议重点放在“数据层设计”上,因为这最能体现工程判断力。

可以从以下几个维度展开:

  1. 关系型数据库(SQL)还是非关系型数据库(NoSQL)?

    • 用户资料与关注关系这类强一致(ACID)需求高的场景,更适合用 SQL;
    • 时间线 / Feed 这类读多写少、允许最终一致性的场景,更适合用可横向扩展的 NoSQL。
  2. 数据模型与访问模式:

    • 例如关注关系可以用 follows 表建复合主键,避免重复关注;
    • Feed 可以预计算并按用户保存为去范式(Denormalized)结构,加快读取。
  3. 缓存策略:

    • 缓存哪些内容:用户资料、热门内容、活跃用户 Feed 等;
    • 为什么要缓存:相比直接查数据库,内存缓存(如 Redis)能把几十毫秒的查询压缩到几毫秒,在每秒上万请求的场景下能“挽救”大量数据库资源。
-- SQL 适用于用户与关注关系
CREATE TABLE users (
    user_id BIGINT PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    created_at TIMESTAMP
);

CREATE TABLE follows (
    follower_id BIGINT,
    followed_id BIGINT,
    created_at TIMESTAMP,
    PRIMARY KEY (follower_id, followed_id)
);
// NoSQL (比如 Cassandra) 更适合
{
  user_id: "user_123",
  feed: [
    {post_id: "post_456", timestamp: 1634567890},
    {post_id: "post_789", timestamp: 1634567850}
  ]
}

这一阶段的目标:展示你能够根据访问模式选择合适的存储,并且讲清楚“为什么这样选”以及“放弃了什么”。

阶段 5:把系统放进真实世界(33–42 分钟)

系统上线后会面对流量波动、节点故障、网络抖动等各种现实问题。这个阶段要重点回答两个问题:

  • 当流量变成 10 倍时,系统如何扩展?
  • 当部分组件失败时,系统如何优雅降级?

可以从以下角度展开:

  • 水平扩展:

    • 应用服务前增加更多无状态实例,通过负载均衡分发;
    • 数据库通过读写分离与只读副本承压。
  • 容错与高可用:

    • 复制:关键数据多副本存储;
    • 熔断器:下游服务异常时快速失败并降级到缓存结果;
    • 限流:防止恶意或异常流量;
    • 优雅降级:尽量提供“部分可用”的体验,例如主功能可用、部分统计或推荐暂时不可用。

熔断器示例代码:

class CircuitBreaker:
    def __init__(self, threshold=5):
        self.failures = 0
        self.threshold = threshold
        self.state = "CLOSED"  # CLOSED, OPEN, HALF_OPEN
    
    def call(self, func):
        if self.state == "OPEN":
            return cached_response()
        
        try:
            result = func()
            self.failures = 0
            return result
        except Exception:
            self.failures += 1
            if self.failures >= self.threshold:
                self.state = "OPEN"
            raise

上面用简短的伪代码演示了熔断器(Circuit Breaker)如何在失败次数超过阈值时“打开”并立即返回缓存数据,面试中不必照搬代码,但可以用语言说明:自己理解“失败隔离”与“自我恢复”的重要性

这一阶段的目标:让面试官看到你不仅会“搭系统”,还能放到高并发、高故障率的真实环境里去思考。

阶段 6:干净利落的收尾(43–50 分钟)

最后 5~7 分钟,重点不是继续加新组件,而是:

  1. 用 30~60 秒复述你的整体方案:

    • 系统主干架构;
    • 关键技术选择(例如 SQL 用在用户与关系,NoSQL 用在 Feed,与 CDN 配合做全局分发);
    • 如何扩展与保证可靠性。
  2. 主动点出几项关键权衡:

    • 比如“用 NoSQL 做 Feed,换来快速读取与易扩展,但牺牲了一些查询灵活性与强一致性”;
    • “预计算 Feed 提升打开速度,但增加了存储开销以及可能短时间内呈现旧数据的风险”。
  3. 抛出开放性问题:

    • 例如:“如果需要,我可以进一步深入某个组件,比如 Feed 生成策略或多区域容灾,您更希望听哪一块?”

这一阶段的目标

  • 把零散的讨论收拢成结构清晰的故事;
  • 让面试官感到“即使时间到了,这个人依然在有条理的思考权衡,而不是随意堆砌技术名词”。

真正的秘诀

系统设计面试中不需要做的事情

  • 不必一上来就报一堆云服务的品牌名;
  • 不必急着切成复杂的微服务;
  • 不必在一开始就画出所有细节;
  • 不必给出“这个就是最佳方案”的结论。

相反,更重要的是:

  • 从澄清问题开始,而不是从方案开始;
  • 按阶段逐步搭建系统,而不是一口气抛出完整架构图;
  • 所有选择都有理由,能讲出“为什么这样设计”;
  • 诚实面对权衡,承认每个选择都有利有弊;
  • 保持对话,主动和面试官互动,而不是独角戏式的画完就走。

这也是为什么同一套技术栈,在不同候选人嘴里,呈现出的“成熟度”会完全不同:真正拉开差距的是“解释方案的方式”和“面对不确定性的态度”。


行动清单

下面是一份非常务实的练习建议,简要整理成可执行清单:

  1. 选 5 个不同的系统设计题,用这套框架完整走一遍;
  2. 给自己计时,习惯在压力下也能按阶段推进;
  3. 录下自己的讲解过程,回看时关注“哪里讲得不清楚、哪里跳步太快”;
  4. 在练习中刻意练习“讲清楚权衡”的能力,而不是背标准答案;
  5. 面试时记住:对方要看的,是思考路径与沟通能力,而不是一张完美无缺的架构图。

要点回顾

  • 系统设计面试考察的是结构化思维与沟通,而不是技术名词堆砌。
  • 在面试过程中,可以用“澄清需求 → 定义成功 → 画大图 → 设计数据层 → 讨论扩展性与可靠性 → 收尾与权衡”这六个阶段来组织自己的输出。
  • 数据层设计是展现工程判断的关键环节,要能结合访问模式解释 SQL / NoSQL、缓存与预计算等选择。
  • 讨论扩展性与可靠性时,应从水平扩展、复制、限流、熔断与优雅降级等角度说明“系统如何在真实世界中生存”。
  • 收尾阶段用简短复盘与权衡总结,把整场讨论串成一个完整故事,并主动邀请面试官选择可以进一步深入的部分。

Hi,我是俞凡,一名兼具技术深度与管理视野的技术管理者。曾就职于 Motorola,现任职于 Mavenir,多年带领技术团队,聚焦后端架构与云原生,持续关注 AI 等前沿方向,也关注人的成长,笃信持续学习的力量。在这里,我会分享技术实践与思考。欢迎关注公众号「DeepNoMind」,星标不迷路。也欢迎访问独立站 www.DeepNoMind.com,一起交流成长。

本文由mdnice多平台发布

在推进采购数字化的过程中,很多企业都会遇到一个现实问题:市场上号称“数字化采购 / 采购 SaaS / SRM”的平台很多,但真正专注于采购场景、并且在企业中被广泛采用的,到底有哪些?

有的企业刚开始调研,希望先了解行业主流平台;有的已经立项,却发现不同厂商定位差异很大;也有不少采购负责人,在ERP采购模块和独立采购SaaS之间反复权衡。

如果你正处在采购系统选型或前期评估阶段,这篇文章将从行业视角,梳理当前专注于数字化采购的主流SaaS平台,并提供一套更理性的选型参考思路。

需要先说明的是,所谓“排名靠前”,并不等同于“最适合所有企业”。不同规模、不同行业、不同采购成熟度的企业,关注重点完全不同。本文不会简单给出“谁最好”的结论,而是帮助你建立判断框架,避免选型走弯路。

一、 市场格局与平台共性:什么样的平台算“靠前”?

目前,数字化采购SaaS市场已进入规模化应用阶段,厂商众多,定位各异。这并不是一个“赢家通吃”的市场,采购场景的复杂性决定了没有一家平台能通吃所有客户。

在实践中,被市场认为“排名靠前”或主流的平台,通常具备一些共性特征:

客户基础扎实,行业覆盖广:已服务大量中大型企业客户,案例覆盖制造、零售、工程等多个行业,而非局限于单一领域。

产品成熟度高:不仅功能完整,更在复杂流程配置、多组织权限、合规风控等企业级能力上经过验证。

交付与服务能力稳定:具备成熟的实施方法论和专业团队,能保障系统成功落地与持续应用。

生态集成能力强:能与ERP、财务、OA等企业核心系统稳定对接,打破数据孤岛。

二、 主流平台深度测评:五大典型路径解析

市场上的领先平台,根据其背景、优势和目标客群,可以归纳为几种典型路径。了解这些路径,比单纯记名字更有助于你做出选择。

类型一:深耕流程的“行业专家型” —— 【正远科技】

这类平台通常从深厚的业务流程管理(BPM)或特定行业咨询背景成长而来,其核心优势在于 对采购业务本质的深度理解与极强的流程定制能力

1、正远科技

正远科技是一家在流程管理领域扎根超过20年的厂商。他们的数字化采购方案以 自研SRM系统ZeroCloud低代码平台 为核心,不是简单的功能堆砌,而是围绕“供应商管理、价格管理、采购执行协同”三大核心业务模块进行深度设计。

核心优势

流程柔性极强:依托低代码平台,企业可以像搭积木一样,自主配置符合自身合规要求和审批习惯的采购流程,特别适合流程复杂、个性化要求高的大型企业。

行业理解深入:长期服务威高集团、南山集团等大型制造企业,其解决方案能深度匹配制造业对物料、供应商、质量协同的严苛要求。

全链路覆盖:从供应商准入、绩效评估,到询比价招标、订单协同、收货对账,实现了采购业务的全周期数字化管理。

适合谁流程复杂、追求深度定制化,且希望采购系统能与自身管理体系高度融合的大中型企业,尤其是制造业、工程建筑等对流程管控要求严格的行业。

类型二:生态整合的“巨擘型” —— 【用友与金蝶】

这类平台源自国内ERP巨头,其最大优势在于 与财务、供应链、生产等系统“天生一体”的无缝集成,数据流转顺畅,能实现真正的业财一体化。

2、用友YonBuilder & 金蝶云·苍穹

用友采购云:背靠用友庞大的ERP生态,对于已使用用友系统的企业,集成成本最低。其战略寻源模块强大,特别擅长处理国企、大型集团复杂的招标采购与合规需求。

金蝶采购云:基于云原生的金蝶云·苍穹平台构建,在系统敏捷性和弹性方面有优势。其供应商协同门户体验出色,AI辅助定价等智能化场景应用较快。

共同优势:安全性高、系统稳定、生态整合度无与伦比。能完美支持多组织、多账簿的集团型管控。

适合谁:已经或计划全面使用该品牌ERP系统的大型集团企业、国有企业及上市公司,尤其适合将采购合规与财务控制视为生命线的客户。

类型三:产业互联的“供应链协同型” —— 【企企通】

这类平台的核心定位在于 连接与协同,其目标不是简单地管理内部采购流程,而是构建一个连接采购商与海量供应商的在线协同网络,实现供应链端的降本增效。

3、企企通

企企通是国内专注于供应链协同和SRM领域的领先平台。它的核心价值在于打通企业与其供应商之间的数据流与业务流,将传统的线下、离散的采购协作,转变为线上、实时、自动化的协同网络。

核心优势

构建供应商协同门户:为企业搭建一个专属的、面向所有供应商的在线门户。供应商可通过该门户自助完成接收订单、确认交期、发货通知、在线对账、开具发票等全链路操作,极大减轻采购方的沟通负担。

强化战略寻源与供应商绩效:提供完善的招标、询比价管理工具,并基于真实的交货、质量、服务数据,实现供应商绩效的客观量化评估,为优化供应商体系提供数据支撑。

适合谁:供应链结构复杂、供应商数量众多、对外协同成本高昂的中大型制造、零售或连锁企业。尤其适合那些希望将数字化从内部管理延伸至整个供应链生态,以提升供应链整体韧性与效率的客户。

类型四:敏捷普惠的“中小企业优选型” —— 【支道】

这类平台精准聚焦中小企业市场,在成本、易用性和上线速度上做到了极致平衡,降低了采购数字化的入门门槛。

4、支道

支道提供以无代码平台为核心的一站式解决方案,其采购管理作为开箱即用的场景模板,让非技术人员也能通过拖拽搭建系统。

核心优势性价比高、部署快、极其灵活。能快速响应中小企业在发展过程中不断变化的采购管理需求。

适合谁IT预算和能力有限,但急需实现采购基础流程数字化、规范化,并追求高性价比的中小企业,是迈出采购数字化第一步的稳妥选择。

三、 如何选择:避开误区,找到你的“最适路径”

看到这里你会发现,没有“最好”,只有“最适合”。选型中最常见的误区就是“只看功能列表,不看自身基因”。在行动前,建议先内部厘清这几个问题:

1、我们采购数字化的首要目标是什么? (是降本合规,还是提升协同效率?)

2、我们当前的采购流程成熟度和IT基础如何?

3、我们更看重系统的“开箱即用”,还是“深度定制”?

4、我们是否有足够的资源(预算、团队)来应对系统实施和后续变革?

选型逻辑参考:

如果你是流程复杂、管控要求高的大型集团,优先考虑“行业专家型”或“生态巨擘型”。

如果你是正在规范化、寻求效率突破的中大型企业,“行业专家型”或“通用平台型”的平衡性可能更佳。

如果你是期望解决同外部供应商之间的沟通滞后、数据孤岛问题,那么打造一个高效的 “供应链协同网络”可以是首要战略。

如果你是追求实用、快速见效的中小企业,“敏捷普惠型”是一个务实的起点。

结语

采购数字化不是一次简单的软件采购,而是一场涉及流程、组织和数据的深层变革。所谓“排名靠前”的平台,都是在特定路径上积累了深厚优势的伙伴。

最理性的做法,是抛开模糊的“排名”焦虑,回归自身业务现状与发展蓝图。在理解不同平台类型基因的基础上,选择那条与自身阶段最匹配、能陪伴你持续成长的数字化路径。希望这份测评与梳理,能为你带来清晰、实用的选型洞察。

一、QAT 调优流程

流程总览:

针对征程 6H/P 的硬件特性,以 int8+int16+fp16 的混合精度量化为主要调优配置,会增加较多的 fp16 设置来优化量化精度

注意:

征程 6H/P 上会用到更多 fp16 高精度和 GEMM 类算子双 int16 等的配置,为了配置方式更加简单灵活,QAT 量化工具提供了一套新的 qconfig 量化配置模板,具体使用方式和注意事项参考:

<u>【地平线 J6 工具链入门教程】QAT 新版 qconfig 量化模板使用教程</u>

调优原则:

如上是一个标准的对称量化公式,产生误差的地方主要有:

  1. round 产生的舍入误差。例如:当采用 int8 量化,scale 为 0.0078 时,浮点数值 0.0157 对应的定点值为 round(0.0157 / 0.0078) = round(2.0128) = 2,浮点数值 0.0185 对应的定点值为 round(0.0185 / 0.0078) = round(2.3718) = 2,两者均产生了舍入误差,且由于舍入误差的存在,两者的定点值一致。 对于舍入误差,可以使用更小的 scale,这样可以使得单个定点值对应的浮点值范围变小。由于直接减小 scale 会导致截断误差,所以常用的方法是使用更高的精度类型,比如:将 int8 换成 int16,由于定点值范围变大, scale 将减小。
  2. clamp 产生的截断误差。当 qmax * scale 无法覆盖需要量化的数值范围时,可能产生较大截断误差。例如:当采用 int8 量化,scale 为 0.0078 时,qmax * scale = 127 * 0.0078 = 0.9906,大于 0.9906 的值对应的定点值将被截断到 127。 对于截断误差,可以使用更大的 scale。scale 一般是由量化工具使用统计方法得到,scale 偏小的原因是校准数据不够全,校准方法不对,导致 scale 统计的不合理。比如:某一输入的理论范围为 [-1, 1],但校准或 qat 过程中,没有观测到最大值为 1 或最小值为 -1 的样本或观测到此类样本的次数太少。应该增加此类数据或者根据数值范围,手动设置固定 scale。在截断误差不大的情况下,可以调整校准参数,通过不同的校准方法和超参缓解截断误差。

因此,QAT 量化精度调优以减少上述两种误差为基本原则,下文将针对 QAT 每个阶段做调优介绍:

注意:

征程 6H/P 平台的浮点模型量化友好设计以及 QAT 模型改造等内容和征程 6E/M 一致,仍可参考该文章对应章节:

<u>【地平线 J6 工具链进阶教程】J6 E/M 工具链 QAT 精度调优</u>

1.1 模型检查

完成模型改造和量化配置后,调用 Prepare 接口时会对模型做算子支持和量化配置上的检查,这些检查一定程度上反映了模型量化存在的问题。对于不支持的算子将以报错的形式提醒用户,一般有两种情况:

  1. 未正确进行模型的量化改造。Prepare 过程中 QAT 量化工具会对模型进行 trace 来获取完整的计算图,在这个过程中会完成算子替换等的优化,对于这些已替换的算子,输入输出类型如果是 torch.tensor 而非经过 QuantStub 转化后的 qtensor,则会触发不支持算子的报错,表现为 xxx is not implemented for QTensor
  2. 确实存在不支持的算子。工具链已支持业界大量的常用算子,但对于部分非常见算子的不支持情况,需考虑进行算子替换或者作为算子需求向工具链团队导入。

Prepare 运行成功后会在当前目录下自动保存模型检查文件 model_check_result.txtfx_graph.txt,建议参考下列解读顺序:

  1. 算子融合检查。算子融合作为 QAT 量化工具的标准优化手段,常见的融合组合为 Conv+ReLU+BN 和 Conv+Add 等,未融合的算子会在 txt 文件中给出,未按预期融合的算子可能是因为共享没有融合成功或者是 QAT 量化工具的融合逻辑变更(针对新版 qconfig 量化模板 enable\_optimize=True 情况,见<u>【地平线 J6 工具链入门教程】QAT 新版 qconfig 量化模板使用教程</u>),需要检查代码,确认未融合的情况是否符合预期:
# 示例:未融合的Conv+Add算子
Fusable modules are listed below:
name       type------  -------------------------
model.view_transformation.input_proj.0.0(shared) 
<class'horizon_plugin_pytorch.nn.qat.conv2d.Conv2d'>
model.view_transformation._generated_add_0        
<class'horizon_plugin_pytorch.nn.qat.functional_modules.FloatFunctional'>

未融合的算子对模型性能会有一定影响,对于精度的影响需视量化敏感度具体分析,一般来说,Conv/Linear+ReLU+BN 可能会因为算子复用导致未融合,此时建议手动修改融合;在 OE 3.5.0 以及之后版本使用新 qconfig 模板下,Conv+Add 默认不会融合,可不修改

  1. 共享模块检查。一个 module 只有一组量化参数,多次使用将会共享同一组量化参数,多次数据分布差异较大时,会产生较大误差:
# 示例:该共享模块被调用8次
Each module called times:
name      called times
---------  --------------   
...
model.map_head.sparse_head.decoder.gen_sineembed_for_position.div.reciprocal                          
8

called times > 1 的模块可能有很多个,全部改写成非共享是一劳永逸的。对于修改简单且精度影响大的共享算子如 QuantStub,强烈建议取消共享;对于 DeQuantStub 算子,共享不会对模型精度产生影响,但是会影响 Debug 结果的分析,也建议取消共享,修改方式参考征程 6E/M“模型改造”章节。

例如下面的共享模块,量化表示的最大值为 128 * 0.0446799 ≈ 5.719,在第一次使用中,输出范围明显小于 [-5.719, 5.719],误差较小, 第二次使用中,输出范围超出 [-5.719, 5.719],数值被截断,产生了较大误差。两次数值范围的差异也造成了统计出的 scale 不准确,因此该共享模块必须修改

+-+-+-+-+-+-+--+-+-+-+-+|   | mod_name | base_op_type   | analy_op_type  | shape  | quant_dtype |  qscale |base_model_min | analy_model_min | base_model_max |   analy_model_max ||-+-+--+-+-+-+-+-+-+-+-+...| 1227 | model.map_head.sparse_head.decoder.gen_sineembed_for_position.div | horizon_plugin_pytorch.nn.div.Div  | horizon_plugin_pytorch.nn.qat.functional_modules.FloatFunctional.mul  | torch.Size([1, 1600, 128])| qint8  |  0.0446799 | 0.0002146 | 0.0000000 | 4.5935526 |  4.5567998 |...| 1520 | model.map_head.sparse_head.decoder.gen_sineembed_for_position.div | horizon_plugin_pytorch.nn.div.Div  | horizon_plugin_pytorch.nn.qat.functional_modules.FloatFunctional.mul | torch.Size([1, 1600, 128]) | qint8 |  0.0446799 | 0.0000000 | 0.0000000 |  6.2831225 |  5.7190272 |...

上面共享算子的修改方式可以参考:

class Model(nn.Module):def __init__(self, ) -> None:super().__init__()...
        self.steps = 2for step in range(self.steps):setattr(self, f'div{step}', FloatFunctional())def forward(self, data):...for step in range(self.steps):
            data = getattr(self, f'div{step}').div(x)...

对于不带权重的 function 类算子都可以参考上面的拆分方式,但是也存在部分共享算子或模块带有权重参数拆分起来比较复杂,是否需要拆分建议先根据量化敏感度进行分析。带有权重参数算子拆分时需要复制权重,拆分方式可以参考:

class Model(nn.Module):def __init__(self, ) -> None:super().__init__()...
        self.steps = 3
        self.conv0 = nn.Conv2d(...)
        shared_weight = self.conv0.weight
        shared_bias = self.conv0.bias
        for step in range(1, self.steps):setattr(self, f'conv{step}', nn.Conv2d(...))getattr(self, f'conv{step}').weight = shared_weight
            getattr(self, f'conv{step}').bias = shared_bias
  
    def forward(self, data):...for step in range(self.steps):
            data = getattr(self, f'conv{step}')(x)...

上述共享算子修改生效后,在 model_check_result.txt 文件中可见到无该算子共享相关的信息:

# 修改生效后下面信息将不再显示
Modules below are used multi times:
name      called times
------  --------------
xxxxx                2

此外,未调用的模块也会在文件中体现,called times 为 0,当 Calibration/QAT/模型导出出现 miss\_key 时,可以检查模型中是否有模块未被 trace。

  1. 量化配置检查。txt 文件中会给出模型量化精度的统计信息:
# 算子输入量化精度统计input dtype statistics:+---+--+--+--+| module type                                                                |   torch.float32 |   qint8 |   qint16 ||---+---+--+--+| <class 'horizon_plugin_pytorch.nn.qat.stubs.QuantStub'>                    |             290 |      15 |        0 || <class 'horizon_plugin_pytorch.nn.qat.linear.Linear'>                      |               5 |     117 |        9 || <class 'horizon_plugin_pytorch.nn.qat.stubs.DeQuantStub'>                  |               0 |       8 |        0 |...# 算子输出量化精度统计
output dtype statistics:+---+--+--+--+| module type                                                                |   torch.float32 |   qint8 |   qint16 ||---+--+--+--+| <class 'horizon_plugin_pytorch.nn.qat.stubs.QuantStub'>                    |               0 |     123 |      182 |...# 使用fp16量化精度的算子,量化精度统计+---+--+--+--+--+| module type                                                                |   torch.float32 |   qint8 |   qint16 |   torch.float16 ||-----+--+--+--+--|| <class 'horizon_plugin_pytorch.nn.qat.stubs.QuantStub'>                    |              34 |       0 |        0 |               0 || <class 'torch.nn.modules.padding.ZeroPad2d'>                               |               0 |      11 |        0 |               0 || <class 'horizon_plugin_pytorch.nn.qat.functional_modules.FloatFunctional'> |              48 |      14 |        9 |              50 |...

重点检查的信息有:

  • <class 'horizon_plugin_pytorch.nn.qat.stubs.QuantStub'> 的 input dtype 应为 torch.float32,对于 qint8 或者 qint16 的 input dtype,一般是冗余的 QuantStub 算子可以改掉,不会对精度产生影响但可能会对部署模型性能有影响(算子数量)
  • 正常来说模型中的算子不应出现 torch.float32 的输入精度(除下文 c 情况),如上图的 <class 'horizon_plugin_pytorch.nn.qat.linear.Linear'>,需要检查是否漏插 QuantStub 未转定点,未转定点的算子在导出部署模型时会 cpu 计算从而影响模型性能。对于模型中的一些浮点常量 tensor,工具已支持自动插入 QuantStub 转定点,建议获取最新版本
  • 对于 GEMM 类算子(Conv/Matmul/Linear)作为模型输出时支持高精度输出(征程 6E/M 支持 int32 输出,征程 6B/H/P 支持浮点输出),体现到这里则是 <class 'horizon_plugin_pytorch.nn.qat.stubs.DeQuantStub'> 的 input dtype 应为 torch.float16torch.float32,对于 qint8qint16 输入的 DeQuantStub 需要检查是否符合高精度输出的条件,符合条件但未高精度输出的需修改。此外对于下面左图的结构,也建议优化为右图结构来保证高精度输出的优化

  • qint8 和 qint16 算子的占比,可以协助判断是否配置全 int16 生效;torch.float16 算子的占比,可以协助判断是否配置 fp16 生效

txt 文件同时会给出逐层的量化配置信息:

# 激活逐层qconfig
Each layer out qconfig:+--+--+--+--+--+--+| Module Name| Module Type | Input dtype | out dtype | ch_axis | observer ||--+--+--+--+--+---|# 固定scale| quant | <class 'horizon_plugin_pytorch.nn.qat.stubs.QuantStub'>                    | [torch.float32] | ['qint16']| -1  | FixedScaleObserver(scale=tensor([3.0518e-05], device='cuda:0'),zero_point=tensor([0], device='cuda:0')) |# QAT训练激活scale更新| mod2.1.attn.q | <class 'horizon_plugin_pytorch.nn.qat.conv2d.Conv2d'>  | ['qint16']  | ['qint16'] | -1 | MinMaxObserver(averaging_constant=0.01) |# QAT训练激活scale不更新| mod2.1.FFN.out_conv.1.0| <class 'horizon_plugin_pytorch.nn.qat.conv2d.Conv2d'> | ['qint16']| ['qint16']| -1| MinMaxObserver(averaging_constant=0)  |# 激活fp16 qconfig| bev_fusion.multi_view_cross_attn.32.global_cross_window_attn._generated_add_2[add]| <class 'horizon_plugin_pytorch.nn.qat.functional_modules.FloatFunctional'> | [torch.float16, torch.float32]                     | [torch.float16] | FakeCast(dtype=torch.float16, min_val=-0.0009765625, max_val=0.0009765625)  | |# 权重逐层qconfig
Weight qconfig:+-----+----+-----+------+---+| Module Name | Module Type | weight dtype|ch_axis|observer ||---+-------+----+----+---|| mod1.0 | <class 'horizon_plugin_pytorch.nn.qat.conv2d.Conv2d'> |qint8 | 0 | MinMaxObserver(averaging_constant=0.01) |

重点检查的信息有:

  • 每层算子的输入输出 dtype、权重的 dtype,是否符合量化配置;若和量化配置不符合,比如配置了 int16,但是算子显示为 int8,则需要关注下算子回退信息,例如在旧模板下 Conv+Add 融合时 Conv 不支持 int16 输入,会导致前序算子输出回退到 int8。新的 qconfig 量化配置模板下算子回退过程需查看 qconfig\_changelogs.txt,详细参考:https://developer.horizon.auto/blog/13112
  • 配置了 fix scale 的算子,是否正确显示 FixedScaleObserver 信息,scale 值是否正确
  • 逐层算子的 observer 是否正确:权重默认 MinMaxObserver,QAT 校准时激活默认 MSEObserver,QAT 训练时激活默认 MinMaxObserver
  • 若为 QAT 训练阶段且配置了固定校准的激活 scale,查看 averaging\_constant,判断是否生效,生效为 averaging\_constant=0(即不更新 scale),默认为 0.01(更新 scale)

对于 fx_graph.txt,可以从中获取到模型中 op/module 的上下游调用关系,例如当存在算子 called times 为 0 未被调用的情况,可以通过 Graph 定位到上下文算子从而定位未被调用的原因(通常因为在 init 函数中定义了但在 forward 中没有调用,也可能存在逻辑判断或循环次数变化的情况);此外当出现导出的部署模型(bc 模型)精度异常,也可以通过 Graph 信息来排查是否是导出计算图改变导致的

# 模型Graph图结构信息
Graph:
opcode       name        target            args           kwargs
----         -----       -------           -------        -------
placeholder    input_0    input_0              ()         {}
call_module    quant       quant            (input_0,)     {}
call_module  traj_decoder_src_proj_0_0  traj_decoder_src_proj.0.0                                             (quant,)  {}
call_function  scope_end    <function Tracer.scope_end at 0x7f4477d7dc60>   ('traj_decoder_src_proj.0',) {}
call_function  __get__    <method-wrapper '__get__' of getset_descriptor object at 0x7f460922b800>  (traj_decoder_src_proj_0_0,) {}
call_function  __getitem__       <slot wrapper '__getitem__' of 'torch.Size' objects>     (__get__, 0)   {}
call_function  __getitem___1      <slot wrapper '__getitem__' of 'torch.Size' objects>   (__get__, 1)  {}
call_function  __getitem___2     <slot wrapper '__getitem__' of 'torch.Size' objects>   (__get__, 2)   {}
call_function  __getitem___3      <slot wrapper '__getitem__' of 'torch.Size' objects>   (__get__, 3) {}
call_function  permute     <method 'permute' of 'torch._C.TensorBase' objects>   (traj_decoder_src_proj_0_0, 0, 2, 3, 1)  {}...

重点关注的 Graph 信息:

  • opcode 为算子调用类型
  • name 为当前算子名称,需注意和 model_check_result.txt 中的 module.submodule 名称区别
  • target 为算子输出
  • args 为算子输入

1.2 QAT 校准

1.2.1 int8+int16+fp16 混合精度调优

如果模型中吸收了前后处理的相关算子和操作,这部分默认需要 fp16 精度进行量化

对于 int8+int16+fp16 混合精度而言,主要的量化配置如下(配置方式参考<u>【地平线 J6 工具链入门教程】QAT 新版 qconfig 量化模板使用教程</u>):

  • 基础配置: TAE 算子(Conv/Matmul/Linear)双 int8、其他算子 fp16
  • 精度优化配置: TAE 算子(Conv/Matmul/Linear)单 int16(部分双 int16)、其他算子 fp16
  • 精度上限配置: TAE 算子(Conv/Matmul/Linear)双 int16、其他算子 fp16
  • 性能上限配置: 全局 int8,建议仅在测试模型最优性能(精度无保证)或作为高精度耗时优化的对比参考时配置

同样的对于较难量化的模型而言,初始应使用精度上限配置,在这个配置下解决量化流程可能的问题,优化量化风险较大的算子/模块,往往通过 Debug 工具进行定位,但在使用 Debug 工具较难定位到量化瓶颈时,可以使用分步量化的小技巧(参考本文最后章节"调优技巧"),也即对选中算子取消量化后对比精度,如定位到前后处理的算子/模块产生明显掉点,建议从模型中剥离;定位到模型中算子/模块,可以使用设置 fix\_scale 和拆分共享模块等方式,或者从量化友好角度修改浮点模型(参考征程 6E/M 量化调优对应章节:<u>【地平线 J6 工具链进阶教程】J6 E/M 工具链 QAT 精度调优</u>)

精度上限配置下的模型较难满足部署侧的延时要求,因此解决掉上述的量化瓶颈后需要回归到基础配置。在基础配置上通过敏感度的分析结果,增加 TAE 的 int16 算子,也就是精度优化配置。在基础配置和精度优化配置下精度达标的模型,视延时情况可能需要进一步做性能优化,主要方向为:

  1. 基础配置下,回退 fp16 性能瓶颈算子到低精度 int8
  2. 精度优化配置下,回退双 int16 的 TAE 算子到单 int16,回退 fp16 性能瓶颈算子到低精度 int8

精度优化配置下如果 int16 算子比例已超出部署预期但精度仍有一定差距,则可以考虑回退部分 int16 算子后尝试 QAT 训练;基础配置下精度表现距离浮点差距较小(量化精度/浮点精度 > 90%,经验值),直接尝试 QAT 训练,在 量化精度/浮点精度 >= 95%(经验值)的情况下,建议优先尝试固定校准激活 scale 的 QAT 训练(仅调整权重感知量化误差)

对于不同精度配置下的 QAT 校准,都有一些校准超参可以调整,需要用户结合具体模型去做调参优化,其中主要的参数有校准数据的 batch size、校准的 steps,详细的参数参考:

  1. 基础调优手段:<u>调优指南\_基础调优手段</u>
  2. 高级调优手段:<u>调优指南\_高级调优手段</u>

由于征程 6H/P 平台使用了较多浮点 FP16 精度,该精度下数值范围超限场景有以下常见的优化方法和优缺点总结:

image.png

总结:

int8+int16+fp16 混合精度调优的重点应放在 TAE 双 int16+ 其他算子 fp16 的调优上,这里需要把使用问题,量化不友好模块等等各种千奇百怪的问题都解决,看到模型的精度上限,然后根据模型部署的性能要求进行 TAE int8 和 int16 混合精度的调优,最后对非 TAE 算子进行 int8+fp16 混合精度的调优,最终达成部署精度和部署性能的平衡。

1.2.2 Debug 产出物解读

征程 6H/P 平台 Debug 产出物的解读和征程 6E/M 一致,仍可参考该文章对应章节:<u>【地平线 J6 工具链进阶教程】J6 E/M 工具链 QAT 精度调优</u>

Badcase 调优

对于实车或回灌反馈的可视化 badcase,利用 Debug 工具的调优流程为:

1.3 QAT 训练

大部分模型仅通过 QAT 校准就可以获得较好的量化精度,对于部分较难调优的模型,以及还需要继续优化误差类指标的模型,通常校准设置的高精度比例导致延时超过部署上限,但精度仍无法达标,这种情况可以尝试 QAT 训练来获得满足预期性能-精度平衡的量化模型。

根据前文所述,在 QAT 校准 量化精度/浮点精度 >= 95%(经验值) 的情况下,充分利用校准阶段较好的激活量化参数,优先尝试固定校准激活 scale 的 QAT 训练(仅调整权重感知量化误差),设置方式具体参考征程 6E/M 精度调优的“模型改造”章节:<u>【地平线 J6 工具链进阶教程】J6 E/M 工具链 QAT 精度调优</u>

参考浮点训练,QAT 训练在大部分配置保持和浮点训练一致的基础上,也涉及到部分超参的调整来提升量化训练的精度,例如 QAT 的学习率、weight\_decay、迭代次数等,详细的参数调整策略参考:

  1. 基础调优手段:<u>调优指南\_基础调优手段</u>
  2. 高级调优手段:<u>调优指南\_高级调优手段</u>

浮点和 QAT 训练中都涉及到对 BN 的状态控制,在浮点训练中可能会采用 FreezeBN fine-tune 的方式来提升模型精度,在多任务训练中也会采用 FreezeBN 的技巧。因此在 QAT 训练中,提供了 FuseBN 和 WithBN 两种训练方式:

  1. FuseBN 即在 Prepare 后,QAT 训练前将 BN 的 weight 和 bias 吸收到 Conv 的 weight 和 bias 中,在训练过程中不再单独更新,这一吸收过程是无损的。FuseBN 也是 QAT 默认的训练方式。
  2. WithBN 则是在 QAT 训练阶段保持 Conv+BN 不融合,带着 BN 进行训练,BN 的参数单独更新,在训练结束后转成部署模型时再做融合。浮点训练阶段如果采用了 FreezeBN 的训练方式,QAT 训练时需设置 WithBN 来对齐浮点训练方式,设置方式如下:
from horizon_plugin_pytorch.qat_mode import QATMode, set_qat_mode
set_qat_mode(QATMode.WithBN)

通过观察 QAT 训练过程的 Loss 变化来初步判断 QAT 训练的量化效果,一般来说和浮点最后的 Loss 结果越接近越好,Loss 过大可能难以收敛,Loss 过小可能影响泛化性,对于异常的 Loss 建议的优化手段:

  1. 异常 INF 和 NAN 的 Loss 值,或者初始 Loss 极大且无收敛迹象,按如下顺序排查:

    1. 去掉 prepare 模型的步骤,用 qat pipeline finetune 浮点模型,排除训练 pipeline 的问题,Loss 如果仍异常,需要检查训练链路的配置如优化器 optimizer 和 lr\_updater 等
    2. 保持当前 QAT 训练配置,只关闭伪量化节点后观察训练的 Loss 现象,理论上和浮点有微小差异
from horizon_plugin_pytorch.quantization import set_fake_quantize, FakeQuantState
...
set_fake_quantize(qat_model, FakeQuantState._FLOAT)
train(qat_model, qat_dataloader)
  1. 在排查完链路问题后出现初始 Loss 较大,有收敛迹象但收敛较慢,这种情况可以尝试调整学习率,延长 QAT 迭代次数,因为 QAT 训练本质上是对已收敛浮点模型的 fine-tune,本身存在一定的随机性,用较大的学习率可以快速波动到一个理想精度(依赖一些中间权重的评测)
  2. 对于少数模型,QAT 训练以及尝试了多次超参调整后精度仍无法达标,建议回归 QAT 校准阶段增加少量高精度算子(增加 GEMM 类算子 int16,以及其他算子增加 FP16)、回归浮点结构检查是否还存在量化不友好的结构如使用了大量 GeLU 等(参考征程 6E/M 精度调优对应章节<u>【地平线 J6 工具链进阶教程】J6 E/M 工具链 QAT 精度调优</u>)

1.3.1 QAT 训练效率

由于 QAT 训练过程需要感知模型量化所带来的损失,因此模型中会被插入必要的量化相关的节点:数据观测节点 Observer 和伪量化节点 FakeQuant。数据观测节点会不断统计模型中数据的数值范围,伪量化节点会根据量化公式对数据做模拟量化和反量化,两者都会存在开销,此外就是 QAT 工具内部会对部分算子例如 LN 层做拆分算子的实现,因此相同配置下的 QAT 训练效率是会略低于浮点训练效率,具体还和模型参数规模、算子数量等有关。

对于用户可明显感知到的 QAT 训练效率降低,建议的优化手段有:

  1. 使用 QAT 工具提供的算子,这些算子优化了训练效率,例如 MultiScaleDeformableAttention(<u>参考手册</u> )
  2. 更新到最新的 horizon-plugin-pytorch 版本,新版本会有持续的 bug fix 和新特性优化,如模型中某些结构或者算子训练耗时增加明显,可以向工具链团队导入

1.4. 模型导出部署

完成 QAT 精度调优后得到的模型仍是 PyTorch 模型,需要使用简单易用的接口来一步步导出编译成部署模型:PyTorch模型 -> export -> convert-> compile

export 得到 qat.bc; convert 得到 quantized.bc; compile 得到 hbm

由于导出生成物中计算差异的存在,对于每个生成物需简单验证其精度,可通过单张可视化或 mini 数据集,过程中如存在精度掉点,请参考<u>【地平线 J6 工具链进阶教程】J6 E/M 工具链 QAT 精度一致性问题分析流程</u>

二.调优技巧

2.1 分部量化

下面这种方式仅适用于 Calib 阶段,QAT 阶段因为模型已经适应了量化误差,关闭伪量化精度无法保证

from horizon_plugin_pytorch.utils.quant_switch import GlobalFakeQuantSwitch 
class Model(nn.Module):     
    def _init_(...):     
    def forward(self, x):         
        x = self.quant(x)         
        x = self.backbone(x)         
        x = self.neck(x)         
        GlobalFakeQuantSwitch.disable() # 使伪量化失效         # --------- float32 ---------         ​
        x = self.head(x)         
        # ---------------------------         ​
        GlobalFakeQuantSwitch.enable() # 重新打开伪量化         return self.dequant(x)

2.2 部分层冻结下的 QAT 训练

模型 QAT 训练时,要求模型为 train() 状态,此时若部分层冻结,则需要对应修改状态,参考代码如下:

from horizon_plugin_pytorch.quantization import (
    QuantStub,
    prepare,
    set_fake_quantize,
    FakeQuantState,)

qat_model = prepare(model, example_inputs=xxx, qconfig_setter=(xxx))
qat_model.load_state_dict("calib_model_ckpt.pth")

qat_model.train()# 关闭requires_grad可固定权重不更新,但Drop、BN仍然会更新for param in qat_model.backbone.parameters():
    param.requires_grad = False# 配置eval()可固定Drop、BN不更新,但不会固定权重,因此两者需要配合使用
qat_model.backbone.eval()
set_fake_quantize(qat_model.backbone, FakeQuantState.VALIDATION)#配置head的FakeQuant为QAT状态
set_fake_quantize(qat_model.head, FakeQuantState.QAT)

2.3 Calib/QAT 过程 NaN 值定位

出现 NaN 值可通过下面的修改在 calib/qat forward 过程中报错,从而定位到具体的算子:

from horizon_plugin_pytorch.quantization.fake_quantize import FakeQuantize
FakeQuantize.check_nan_scale='forward'#默认为save,在torch.save时检查是否有nan,有nan会报错
qat_model = prepare(model, (input), default_qat_qconfig_setter)

常见的可能出现 NaN 值的结构:

Multi-head Attention 的 attn mask,需要手动做数值的 clamp

最近更新了自己做的一款 iOS App ,叫 LiveUp 。

这个 App 的定位其实很简单:
把「当前最重要的一件事」直接放在锁屏上。

很多时候我们并不是想打开一个 App ,
只是想确认一件事——
比如取件码、会议时间、倒计时,或者一句正在做的事。

而这些问题刚好 iOS 的实时活动能解决。

所以这次更新,我做了一个比较大的方向调整:

  • 不再死磕航班 / 火车 / 外卖等垂直场景
  • 不要求用户选类型、填复杂表单
  • 只做一件事:输入一段文本 → 直接生成锁屏 Live Activity

现在 LiveUp 的使用方式大概是:

  • 粘贴一段短信(比如取件码)
  • 或者对 Siri 说一句话(比如“10 点开会”)
  • 或者直接输入一句你现在最重要的事

App 会尝试从文本里抽取关键信息,
然后生成适合「锁屏 / 灵动岛 / 小组件」展示的内容。

锁屏上会是偏“人话”的展示(可以直接读懂在干嘛),
灵动岛则保持极简,只显示最关键的值。

目前刚更新不久,还在不断调整方向中。
如果你平时也会用实时活动,或者对这种“单一聚焦”的工具感兴趣,
欢迎提出批评和意见 🙏

App Website: https://getliveup.com/zh

我先来

推荐做法

  • 引导 AI 出方案你来 review, review 通过了以后,先让 AI 先写测试用例,让测试用例失败,让 AI 自动修复测试用例,你来 review 修复方案和代码。
  • 多用 plan 模式,确认没问题了才让 AI 生成代码,不要一把梭不停的让 AI 修改代码 ,改来改去可能越改越奇怪,最好是把计划做好,这样效率和准确率更高。

不推荐

  • 你来出方案,让 AI 实现,你来测试,让 AI 修复代码。

Intro 随着AI相关技术特别是大语言模型的逐步火热,衍生出一系列传统安全中不存在的一些攻击模式,例如Agent Security、MCP Security等等攻击范式,本文主要集中的是随着AI浪潮带来的MFV(Model Format Vulnerability),核心是对不可信的外部文件内容在非沙箱环境中进行执行,造成了代码执行漏洞的危害 What 首先我们来熟悉一下什么是MFV漏洞 为便于进行模型的share过程,我们通常会将训练后的机器学习模型、深度学习模型甚至大语言模型通过序列化的方式将其保存在一个特定的文件格式中,例如现在LLM常用的.safetensors.gguf的模型文件格式,又比如机器学习中常用的.h5模型格式文件,在第三方获取到分享的模型之后在模型的加载过程中对其中的键值对进行反序列化操作,同样的,也是因为反序列化这一操作,导致存在有安全漏洞的产生,可能导致任意代码执行等危险 当前针对MFV漏洞核心存在有两类漏洞类型: 1 Deserialization: 也即是反序列化的方式,因为在进行模型模型加载过程中对存储的对象进行反序列化操作还原对象的过程中,对于精心制作的模型,在反序列化的过程中将会执行对应的恶意代码 2 Backdoors:对于后门攻击其包含了多种中毒方式,包含有数据中毒等等攻击方式,其主要是在原始模型的基础上嵌入了一些隐藏的恶意功能 Deserialization Threats Archive Slip 在传统的Web漏洞中存在一类漏洞利用方式,也即是zip slip漏洞,其成因是由于在对zip压缩包进行解压缩的过程中,未对压缩包中包含的entry进行安全过滤,其存在有类似于..这类似跨目录的标识符作为entry导致能够进行一种另类的"任意文件上传"。 而在AI机器学习领域中同样存在有开放框架使用ZIP等压缩包格式的文件进行模型数据的存储,同样这些漏洞格式也会造成archive slip漏洞,具体的过程可描述为在进行模型加载的过程中,对模型进行反序列化的操作过程中,同样的如何遭遇了类似于../~$D:这类在不同的操作系统代表不同含义的特殊字符,可能会导致目录穿越等 例如NVIDIA的NeMo框架

其所对应的漏洞CVE-2022-22821则是由于这个原因导致的zip slip漏洞 https://github.com/NVIDIA-NeMo/NeMo/security/advisories/GHSA-rpx7-33j2-xx9x

Model Config Executable 在模型加载的过程中,对于模型的配置同样也是外部可控的一点,若采用了类似于Hydra这类存在有动态执行方法能力的框架进行配置文件的加载,若加载了一些其中注入有特定的恶意代码的模型文件,在进行模型加载的过程中,使用Hydra框架对配置进行解析,将会导致任意代码执行等漏洞 简单了解一下什么是Hydra https://github.com/facebookresearch/hydra Hydra是一个由Facebook团队开源的用于优雅配置复杂应用程序的Python框架,主要包括以下核心功能 1 分层配置系统 - 从多个源创建复杂配置 2 命令行覆盖 - 通过命令行轻松修改配置值 3 动态 Tab 补全 - 配置选项的命令行自动补全 4 远程执行 - 本地运行或远程启动应用程序 5 多任务执行 - 从单个命令使用不同配置运行多个作业 6 插件架构 - 通过插件扩展功能,支持优化、作业启动等 之后就是简单的使用Hydra

可以通过如上方式进行配置文件的加载,其核心是通过@hydra.main()这一个装饰器进行配置文件的初始化 而回到我们的AI模型相关场景,当攻击者向第三方平台,例如hunggingface或者ModelScope上传了一个包含有恶意代码的权重文件,攻击者采用了Safetensors采用YAML或者JSON的格式去存储模型元数据,受害者在下载了该模型准备使用类似于NeMo以及ml-flextok这类使用了Hydra框架进行模型元数据配置文件的加载以及解析时将会执行其中的恶意代码逻辑 我们可以将以上场景抽象为以下的代码: 1 创建一个yaml文件用于模拟携带有恶意代码的模型文件 2创建一个python代码利用hydra进行配置文件的加载以及对象的实例化进行代码执行

其也能够观察到系统命令的执行,导致了文件写入 分析其成因,进入到instantiate函数中,查看其实例化过程

根据配置文件的数据类型的不同进行不同的处理,这里命令执行我们构建的yaml文件是Dict数据类型 1 首先使用_prepare_input_dict_or_list将配置文件转化成一个Dict字典 2 在经过OmegaConf对字典进行处理后,核心使用instantiate_node去对节点进行实例化操作 3 其中对于_target_这一个特殊的key对应的value使用_resolve_target进行解析

4 该函数中传递的target值则为我们上面实例中配置文件中的_target_字段值,也即是os.system,其通过_locate进行待实例化的类和函数

5 其通过.进行划分,一步一步使用import_module进行模块的导入

6 在获取到了target对应的函数后,回到步骤3中展示的函数,调用_call_target函数结合传递的参数进行函数的执行 通过以上对于Hydra框架中对序列化的配置文件的对象进行反序列化的过程导致的恶意代码实例的分析,我们可以明白造成该类漏洞的原因是由于外部输入可控以及未对_target_进行安全校验直接进行函数定位 GGUF Model Template GGUF文件格式由llama.cpp团队创建,其通常用于保存模型的训练数据,该文件格式针对模型的快速加载和存储进行优化。 chat template在大语言模型中较为常见,通过系统提示词以及用户提示词提升LLM的回复效果屡见不鲜,同样的,在LLM中常用的GGUF格式文件中同样有着chat template的身影。提到template,就不得不联想到传统安全中的一类漏洞,也即是SSTI漏洞,其是由于模板内容可控的同时,渲染模型所采用的模板引擎执行时未在沙箱环境中执行,导致了任意代码执行的产生。对于Python语言来讲,常见开发框架DjangoFlask等都基于jinja2模板引擎进行模板渲染。

同样的,GGUF格式文件同样使用了Jinja2模板引擎进行提示词的格式化,所以导致了在加载带有Jinja模板恶意代码的GGUF文件时将会执行恶意代码 同样对于这类漏洞的实例可以参考CVE-2024-34359

其核心是由于llama-cpp-python在加载.gguf格式保存的模型时,直接使用了self.metadata["tokenizer.chat_template"]从元数据中获取了模型中保存的chat_template值,并将其传递给了llama_chat_format.Jinja2ChatFormatter创建了一个chat行为的处理器handler,该类使用了Jinja2模板引擎,且未在安全的沙箱环境中处理,若元数据中的chat_template字段中存在有符合Jinja2语法的代码,在进行问答时将会触发其中的恶意代码

Pickle Model Pickle为Python用于序列化与反序列过程的原生库,在传统安全中Pickle库导致的反序列化漏洞也曾不出穷。其在加载一些不受信任的序列化数据将会在反序列化的过程中造成任意代码执行等危害。 基于其上的变种框架还有cloudpickledilljoblib等等,又比如Numpy中的numpy.save(.., allow_pickle=True, )以及PyTorch中的torch.save(model.state_dict(), ..) Joblib Model Joblib 是一个轻量级的 Python 库,专门用于提供高效的管道式计算作业处理。它主要解决科学计算和数据处理中的三个核心问题:避免重复计算、并行化代码执行以及高效持久化 Python 对象

而其中提及到的高效持久化的实现核心是采用了Pickle库对其进行了实现,则导致了反序列化的安全问题 相关的实例可以参考CVE-2024-34997漏洞 https://security.snyk.io/vuln/SNYK-PYTHON-JOBLIB-6913425

使用了pickle.dump方法进行了恶意类的序列化过程,在完成序列化后,使用Joblib提供的NumpyArrayWrapper类进行反序列化过程,在这个过程中将会动态执行其中的__reduce__魔术方法,也即是执行了系统命令 Pytorch Model 同样的,对于Pytorch框架,如何其使用了pickle库进行模型的序列化过程,在反序列化的过程中将会导致__reduce__魔术方法的调用 https://huntr.com/bounties/84d6dc11-23aa-499a-9a62-45596a4d7ef5 对于pytorch来讲,其中的torch.save以及torch.load底层分别使用了Pickle进行序列化以及反序列化,在反序列化的过程中将会执行恶意代码

Ref https://github.com/NVIDIA-NeMo/NeMo/security/advisories/GHSA-rpx7-33j2-xx9x https://github.com/abetlen/llama-cpp-python/security/advisories/GHSA-56xg-wfcc-g829 https://security.snyk.io/vuln/SNYK-PYTHON-JOBLIB-6913425

以下内容来源于DataforAI社区,作者Data for AI

当 AI 遇见数据:一场面向工程实践的技术交流

大模型并没有直接带来 AI 应用的成熟。真正决定 AI 能否规模化落地的,正在从模型本身,转移到数据、上下文与基础设施

与此同时,数据基础设施也正经历一轮深刻演进:从传统的数据湖仓,到多模态数据管理;从 SQL 查询引擎,到面向 AI 的数据解析与治理能力。这些变化,正在重新定义我们构建 AI 应用的方式。

1 月 24 日(周六)下午Data for AI 社区 将携手 ALC Beijing (Apache Local Community Beijing) 举办 Data for AI Meetup Beijing,邀请来自产业、开源社区与学术界的一线实践者,围绕 AI 时代的数据基础设施演进 展开深入交流。

本次 Meetup 汇聚了来自 字节跳动火山引擎 / Daft 社区、OceanBase社区、北京大学、Datastrato / Apache Gravitino 社区、Zilliz / Milvus 社区的技术专家,深度剖析 AI 时代数据基础设施的技术演进路径。

📍 本次 Meetup 核心看点

  • 多模态数据处理引擎实践:

    Daft 在 AI 数据预处理与训练加载中的工程经验

  • AI 原生元数据平台:

    Apache Gravitino 1.1.0 的关键能力与治理实践

  • Agent 数据基座设计:

    记忆、检索与数据统一的工程解法

  • Data-centric AI 方法论:

    面向大模型的数据准备与质量体系

  • 混合检索实践:

    向量 + 全文检索在真实业务中的优化路径

  • 开源探索:

    Skill 驱动的上下文工程平台化可能性

  • 圆桌讨论:

    下一代面向 AI 应用的数据基础设施如何设计与落地


多模态数据处理的新范式

AI 训练对数据处理提出了全新挑战。火山引擎 AI 数据湖服务架构师 琚克俭 将分享 Daft 在多模态数据处理上的工程实践,聚焦图像、视频、文本等异构数据在统一处理、预处理与训练加载阶段的性能与架构挑战。

这一分享直面当前 AI 工程的核心痛点:传统数据引擎已难以支撑多模态 AI 工作负载,而 Daft 通过全新的架构设计,在数据预处理和训练加载环节实现了显著的性能提升。

元数据治理进入 AI 原生时代

Datastrato VP of Engineering 史少锋 将深度解析 Apache Gravitino 1.1.0 的核心升级,包括 Lance REST 支持、Generic Lakehouse Catalog、Iceberg 安全增强等关键特性。

当 AI 团队需要在多个集群间管理训练数据、推理数据和模型元数据时,传统的元数据工具往往各自为政。Apache Gravitino 1.1.0 通过统一的元数据治理架构,让跨引擎、跨存储的数据协同变得标准化、可管理,大幅降低 AI 工程中的数据协同成本。

上下文工程:Agent 落地的数据基座

OceanBase 技术专家 汤庆 将深度解析当下最热的「上下文工程」话题。他指出,企业级 Agent 面临三大核心挑战:如何让 Agent 拥有可靠的「记忆」(记忆管理)、如何让 Agent「理解」复杂文档(知识检索),以及如何统一处理向量、文本、结构化数据(数据统一)。

这三款 AI 产品的协同设计给出了答案:PowerMem 基于艾宾浩斯遗忘曲线构建智能记忆系统并支持多智能体隔离,PowerRAG 提供多引擎 OCR 与向量 + 全文的混合检索能力,seekdb 则作为 AI 原生数据库统一管理多模态数据并兼容 MySQL 生态。这套方案的核心价值在于:用数据架构的确定性,对抗 Agent 行为的不确定性。

面向大模型时代的 Data-centric AI 基础设施

北京大学助理教授 张文涛 将从学术与工程结合的视角,系统阐述 AI 从「模型为中心」到「数据为中心」的范式转变。当大模型能力趋同,数据质量正在成为决定模型性能的关键变量。

张文涛团队主导开发的 DataFlow 数据准备系统已在大模型预训练、企业知识库构建等场景得到验证。本次分享将深入解析 LLM 数据工程的完整流程:如何获取数据(爬取、解析、合成、标注),如何处理数据(过滤、改写、配比),以及如何评估数据质量。这套开源工具链与方法论,正在为 AI 开发者降低数据工程的门槛。

从向量检索到混合查询:Context Engineering 实践

Zilliz 资深解决方案架构师 刘汉卿 将系统回顾从 Prompt Engineering 到 Context Engineering 的演进路径。随着 RAG 技术从单一向量检索发展到 GraphRAG 与全文检索的混合查询阶段,检索系统已经从「找到相似内容」进化到「理解查询意图并精准召回」。

在这个演进过程中,一个关键趋势是:用向量计算代替多轮LLM推理,通过检索层的优化来提升 AI 应用的性能与稳定性。刘汉卿将结合企业知识库、推荐系统、智能助理等场景,分享混合查询的工作流搭建经验,以及在金融、医疗、法律、教育等行业的实际落地案例。

上下文工程的平台化探索

独立开源开发者 袁怿(Sam Yuan)将从前瞻视角探讨 2026 年上下文工程的技术趋势。如果说 2025 是 Agent 元年,那么随着上下文工程的快速演进,一个关键问题正在浮现:上下文能力是否应该从「各自实现」走向「横向平台化」?

袁怿将上下文工程拆解为三个维度:工具调用(空间维度)、RAG(信息密度维度)与 Memory(时间维度)。他将以最近进入 AAIF 的 Skill 机制为切入点,对比 Skill 与传统 Function Call 的本质差异,并结合他在开源社区贡献的 StructuredContextLanguage 项目,展示以渐进式加载为代表的平台化思路——让 AgentOS 像操作系统管理进程一样,统一管理上下文资源。


圆桌论坛:下一代面向 AI 应用的 Data Infra 的设计和落地

从多模态数据处理到 AI 原生元数据平台,从上下文工程到混合检索系统——本次 Meetup 的所有分享指向同一个命题:在 Agent 时代,数据不再只是「被调用的资源」,而正在成为被理解、被约束、被治理的核心能力。

越来越多团队在实践中遇到相似挑战:Agent 需要访问的数据分散在不同系统中,权限、语义与上下文边界不清;模型可以生成「看似合理」的请求,却难以保证结果的安全性与一致性。这些问题往往无法通过 Prompt 或单点优化解决。

我们特邀到前 Apple 数据与机器学习平台负责人 谭涛(Kwaai AI Lab 顾问)、Datastrato 创始人 CEO 堵俊平、北京大学助理教授 张文涛 三位圆桌嘉宾,围绕三个核心问题展开讨论:

  • 意图与执行解耦:如何让 Agent 的数据请求既灵活又可控?
  • 访问规则原生化:能否在系统层面保证数据访问的安全性与一致性?
  • 上下文边界管理:如何让 Agent Builder 在不理解底层架构的前提下获取「该拿的数据」?

这些讨论并不立马给出最终答案,而是帮助我们勾勒下一代面向 AI 应用的数据基础设施轮廓——一个更开放、更可治理、也更适合 Agent 时代的技术底座。

活动信息

时间

2026 年 1 月 24 日(周六)13:10 – 18:00

地点

北京 · 原点学堂(东升大厦 A 座 10 层)(不提供线上直播)

立即报名:

👉 访问链接:https://www.huodongxing.com/event/3843480320400

⚠ 名额有限,需审核通过(请详实填写报名信息,并通过主理人的微信添加请求,确认审核状态)

这是一场面向 AI & Data 工程实践者的技术深度交流。

无论你是正在构建企业级 Agent 系统的架构师,

还是关注 Data-centric AI 的研发工程师,

都能在这里找到有价值的技术洞察和落地经验。

Community Over Code,期待与你在北京相聚。

阅读更多 Voice Agent 学习笔记:了解最懂 AI 语音的头脑都在思考什么