包含关键字 typecho 的文章

佬友们好,由于写代码经常遇到 json/sql/class/struct 之间转换的需求,因此 vibe 了一个 vscode 插件,支持配置 ai 自动转换代码


项目地址


功能

  • 急速响应:本地简单解析
  • AI 兜底:本地解析失败,AI 直接生成
  • 多语言输出:支持基本所有主流语言
  • 多格式输入:JSON/SQL/CODE/ 自然语言
  • 操作简单: 复制文本然后按下 Ctrl + Alt + V (Mac: Cmd + Alt + V)


演示

[开源自荐 - Paste Go] 一款 json/sql/code/ 自然语言 自动转换成当前语言 class/struct 的 vscode 插件2


欢迎佬友们下载并使用,有好的建议或者遇到 bug 可以直接提 issue 或者 pr,觉得好用可以点个 star,感谢


📌 转载信息
转载时间:
2026/1/20 10:04:53

CLI Proxy API 的核动力驴一天 3 个小版本,每天打开电脑都是打开 render 更新部署。
于是搓了这样一个用 Deno Deploy 的 cron 触发 Render deploy hook 的小工具
每天凌晨 4 点自动更新 render 部署的 cpa(你也可以用来更新其他 render 项目)


去这里复制一下 Deploy Hook 就好了,改一下代码里面占位的 RENDER_HOOK_URL
非常简单的小工具

/**
 * Project: render-auto-deploy (sanitized, hardcoded URL version)
 * Purpose: 用 Deno Deploy Cron 定时触发 Render Deploy Hook(自动 redeploy)
 *
 * ✅ 你需要改的地方(部署时只改这两处):
 * 1) 把 RENDER_HOOK_URL 改成你自己的 Render Deploy Hook URL(包含 service id + key)
 * 2) 如果想换时间,把 CRON_SCHEDULE 改成你要的 cron(注意:Deno.cron 用 UTC)
 *
 * ✅ 验证方法:
 * - 打开 "/":看到 running + 当前 UTC 时间 + schedule
 * - 打开 "/trigger":立刻触发一次 Render 部署(返回 deploy.id)
 * - 去 Render Events:会出现 “Triggered via Deploy Hook”
 *
 * 🔥 时区提醒:
 * - Deno.cron 的 cron 表达式按 UTC 解释
 * - 例:UTC 20:00 = 北京时间次日 04:00
 */

// ======================【部署时改这里 1】======================
// Render Deploy Hook(占位符,换成你自己的)
// 形如:https://api.render.com/deploy/<YOUR_SERVICE_ID>?key=<YOUR_DEPLOY_KEY>
const RENDER_HOOK_URL =
  "https://api.render.com/deploy/<YOUR_SERVICE_ID>?key=<YOUR_DEPLOY_KEY>";

// ======================【部署时改这里 2】======================
// Cron 表达式按 UTC 解释:
// - "0 20 * * *" => 每天 UTC 20:00(北京时间次日 04:00)
// - 测试用: "*/1 * * * *" => 每分钟触发一次(用来验证 cron 是否生效)
const CRON_SCHEDULE = "0 20 * * *";

function nowIso() {
  return new Date().toISOString();
}

async function triggerRenderDeploy(source: "cron" | "http") {
  const runId = crypto.randomUUID();
  console.log(`[${nowIso()}] [${source}] runId=${runId} ⏰ trigger start`);

  // 超时:避免网络卡住导致任务悬挂
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 30_000);

  try {
    const resp = await fetch(RENDER_HOOK_URL, {
      method: "POST", // Render deploy hook 通常 GET/POST 都可,这里用 POST
      signal: controller.signal,
      headers: { "user-agent": "render-auto-deploy/deno" },
    });

    const text = await resp.text().catch(() => "");

    if (!resp.ok) {
      // 抛错 => 让 cron 的 backoffSchedule 生效,自动重试
      throw new Error(
        `Render hook failed: ${resp.status} ${resp.statusText} body=${text.slice(0, 500)}`,
      );
    }

    console.log(
      `[${nowIso()}] [${source}] runId=${runId} ✅ trigger ok body=${text.slice(0, 500)}`,
    );

    return { ok: true, runId, status: resp.status, body: text };
  } finally {
    clearTimeout(timeout);
  }
}

// ====== 定时任务(必须在模块顶层定义,Deno Deploy 才会识别为 Cron)======
Deno.cron(
  "Daily Render Auto Deploy",
  CRON_SCHEDULE,
  // 失败重试节奏(毫秒):1s, 5s, 10s
  { backoffSchedule: [1000, 5000, 10000] },
  async () => {
    try {
      await triggerRenderDeploy("cron");
    } catch (e) {
      console.error(`[${nowIso()}] [cron] ❌`, e);
      throw e; // 继续抛出以触发重试
    }
  },
);

// ====== Web:健康检查 + 手动触发(方便部署后立即验证)======
Deno.serve(async (req) => {
  const url = new URL(req.url);

  // 健康检查:确认服务活着
  if (url.pathname === "/") {
    return new Response(
      `Auto-Deploy is running.\nUTC now: ${nowIso()}\nSchedule(UTC): ${CRON_SCHEDULE}\n`,
      { headers: { "content-type": "text/plain; charset=utf-8" } },
    );
  }

  // 手动触发:GET /trigger
  // 部署完立刻访问一次,看返回里有没有 deploy.id
  if (url.pathname === "/trigger") {
    try {
      const result = await triggerRenderDeploy("http");
      return new Response(JSON.stringify(result, null, 2), {
        headers: { "content-type": "application/json; charset=utf-8" },
      });
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      return new Response(JSON.stringify({ ok: false, error: msg }, null, 2), {
        status: 500,
        headers: { "content-type": "application/json; charset=utf-8" },
      });
    }
  }

  return new Response("Not found", { status: 404 });
});


📌 转载信息
原作者:
sssun
转载时间:
2026/1/20 10:04:34

公司收购 Langfuse,正式进军 LLM 可观测性 (LLM observability) 领域,并推出原生 Postgres 服务,以统一事务型与分析型工作负载。

旧金山 — 2026 年 1 月 16 日 — 实时分析、数据仓库、可观测性 (observability) 以及 AI/ML 领域的领导者 ClickHouse 今日宣布完成 D 轮融资,融资金额达 4 亿美元。本轮由 Dragoneer Investment Group 领投,Bessemer Venture Partners、GIC、Index Ventures、Khosla Ventures、Lightspeed Venture Partners、T. Rowe Price Associates, Inc. 管理的账户,以及 WCM Investment Management 共同参与。

此次融资正值 ClickHouse 持续且加速增长之际。目前,公司通过全托管服务 ClickHouse Cloud 已服务超过 3,000 家客户,年度经常性收入 (ARR) 同比增长超过 250%。在过去三个月中,Capital One、Lovable、Decagon、Polymarket 和 Airwallex 等客户开始采用该平台或扩大了现有部署。这些新客户加入了 ClickHouse 已建立的客户群体,其中包括 Meta、Cursor、Sony 和 Tesla 等 AI 创新者及全球知名品牌。

“ClickHouse 的初衷就是为最严苛的数据工作负载提供卓越的性能和成本效率,而今天的增长势头正是这一战略的最好证明,”ClickHouse 首席执行官 Aaron Katz 表示。“面向未来,我们正在支持统一的事务型与分析型工作负载,让开发者能够在坚实的技术基础之上构建各种由 AI 驱动的应用。同时,我们也在拓展产品能力,引入 LLM 可观测性,帮助 AI 应用构建者在进入生产阶段时,更好地评估 AI 输出的质量和行为。新的资金支持,加上持续的产品执行力,使我们有能力在 AI 时代打造领先的数据与 LLM 可观测性平台。”

图片

对大规模数据基础设施与 AI 的高度确信投资

Dragoneer 成立于 2012 年,由 Marc Stad 创立,采用高度精选、以研究为核心的方法,专注于与少数具有品类定义意义的公司建立长期合作关系。过去十年中,该公司投资了多家领先的数据平台以及多家基础性的 AI 公司。

随着 AI 系统逐步从实验走向生产,对底层数据基础设施提出了更高要求。AI 驱动的应用会产生远高于以往的查询量,对延迟更加敏感,同时还需要持续的评估能力和可观测性。在这样的背景下,真正的价值正越来越集中到那些能够支撑大规模、数据密集型生产工作负载的基础设施平台之上。

“每一次重大的平台变革,最终都会回馈那些最贴近生产环境的基础设施公司,”Dragoneer Investment Group 合伙人 Christian Jensen 表示。“当模型能力不断提升,真正的瓶颈就转移到了数据基础设施上。ClickHouse 的突出之处在于,它能够在大规模 AI 系统运行时,提供所必需的性能、效率和可靠性。”

在严谨的评估过程中,Dragoneer 认为 ClickHouse 已成为现代数据技术栈中具有品类定义意义的领导者。该平台广泛支持关键任务级的实时工作负载,深度嵌入于始终在线、面向客户以及 AI 驱动的系统之中。

ClickHouse 的增长不仅来自对现有系统的替代,更来自对全新工作负载的支持。通过在大规模场景下实现高性价比的实时分析,ClickHouse 让许多过去因延迟或成本受限而无法落地的应用场景成为可能。与主要服务内部分析团队的许多数据基础设施平台不同,ClickHouse 经常直接嵌入到面向终端用户的产品中,在这些场景下,性能和可靠性会直接影响用户体验。

“我们寻找的是在系统绝不能停机时依然值得客户信赖的平台,而 ClickHouse 一直展现出这样的能力,”Jensen 补充道。

LLM 可观测性:ClickHouse 通过收购 Langfuse 进入该市场

ClickHouse 正式宣布收购开源 LLM 可观测性平台 Langfuse。与关注系统健康和性能指标的传统可观测性不同,LLM 可观测性关注的是如何确保非确定性、日益复杂的 AI 系统能够输出准确、安全且符合用户意图的结果。随着 AI 系统不断深入生产工作流,LLM 可观测性已成为构建和运营 AI 应用团队不可或缺的一环。

Langfuse 开源项目增长迅速,截至 2025 年底,已获得超过 2 万个 GitHub Star,每月 SDK 安装量超过 2,600 万次。

“我们之所以在 ClickHouse 之上构建 Langfuse,是因为 LLM 可观测性和评估本质上就是一个数据问题,”Langfuse 首席执行官 Marc Klingen 表示。“如今作为一个团队,我们能够提供更加紧密的一体化体验:更快的数据摄取、更深入的评估能力,以及从生产问题到可量化改进之间更短的闭环。”

图片

Langfuse 联合创始人 Clemens Rawert、Marc Klingen、Max Deichmann

原生 Postgres 服务:ClickHouse 面向 AI 构建者推出统一数据技术栈

ClickHouse 同时宣布推出一个与自身平台深度集成的企业级 Postgres 服务。为了支撑既需要事务处理又需要分析能力的现代实时 AI 应用,ClickHouse 打造了一套统一的数据技术栈,其中包括由 NVMe 存储支撑、具备原生 CDC 能力的高性能可扩展 Postgres。用户只需几次点击,就能将事务数据同步至 ClickHouse,从而解锁最高可达 100 倍的分析性能提升。借助由原生 Postgres 扩展提供支持的统一查询层,开发者可以构建横跨事务与分析的应用,而无需维护多个独立系统。该服务由 ClickHouse 与开源云公司 Ubicloud 联合打造,Ubicloud 团队在 Citus Data、Heroku 和 Microsoft 拥有丰富的产品与工程经验。

“Postgres 与 ClickHouse 在架构上天然互补,是 AI 应用不可或缺的组成部分。通过合作,我们为团队交付了一套真正的一体化技术栈,让生产级 Postgres 负责事务处理,让 ClickHouse 专注分析,并作为一个整体协同运行,”Ubicloud 联合首席执行官兼联合创始人 Umur Cubukcu 表示。“我们非常高兴能在 Ubicloud 与 ClickHouse 携手合作,这正是开源生态系统成功的方式:由值得信赖的团队打造一流产品,并共同成长。”

图片

Ubicloud 联合创始人 Umur、Ozgun 和 Daniel

持续的全球扩张与产品动能

在完成融资并收购 Langfuse 的同时,ClickHouse 也在持续扩展其全球布局和生态体系。过去一年中,公司通过与 Japan Cloud 的合作进入日本市场,并宣布与 Microsoft Azure 围绕 OneLake 建立合作关系。ClickHouse 还在旧金山、纽约、阿姆斯特丹、悉尼和班加罗尔举办了多场用户活动,吸引了超过 1,000 名参与者,演讲嘉宾来自 OpenAI、Tesla、Capital One、Ramp 和 Canva 等公司,并连续第二年举办了 AWS re:Invent Chainsmokers 客户活动。

一系列近期产品进展进一步强化了 ClickHouse 在分析、AI 与可观测性交汇领域的地位。公司在数据湖支持方面持续投入,新增了对 Apache Iceberg、Delta Lake 以及主流数据目录的兼容性。同时,平台扩展了全文搜索能力,这对于包括 AI 可观测性在内的各类可观测性场景正变得愈发关键。此外,ClickHouse 还引入了轻量级更新机制,以支持需求更高、负载更复杂的 AI 驱动型应用。根据近期基准测试结果,ClickHouse 持续提供行业领先的性价比,在性能与成本比上超越主流云数据仓库。

借助 D 轮融资、对 Langfuse 的收购以及原生 Postgres 服务的推出,ClickHouse 已做好加速增长的准备,并将进一步巩固其作为统一数据平台与 AI 可观测性平台的战略地位。

了解更多:

关于 ClickHouse:

 

ClickHouse 是一个快速的开源列式数据库管理系统,专为大规模实时数据处理与分析而设计。ClickHouse Cloud 以高性能为核心,提供卓越的查询速度与并发能力,非常适合需要从海量数据中即时获取洞察的应用。随着 AI 智能体 (AI Agent) 越来越多地嵌入软件系统,并生成频率更高、复杂度更大的查询请求,ClickHouse 提供了一个高吞吐、低延迟的引擎,专门用于应对这一挑战。ClickHouse 受到 Sony、Tesla、Memorial Sloan Kettering、Lyft 和 Instacart 等领先企业的信任,帮助团队通过一个可扩展、高效且现代化的数据平台释放数据价值并做出更明智的决策。欲了解更多信息,请访问 clickhouse.com。

关于 Dragoneer Investment Group:

Dragoneer 是一家以增长为导向的投资机构,资产管理规模超过 300 亿美元。该机构与在公有和私有市场中打造品类定义型公司的创始人及管理团队长期合作。迄今为止,已有 50 多家 Dragoneer 投资的公司成功上市。其投资组合包括 Airbnb、Amwins、Atlassian、Databricks、Datadog、Meta、Nubank、OpenAI、Revolut、ServiceNow、Snowflake、Spotify 和 Uber。

关于 Langfuse:

Langfuse 是一个用于构建、测试和监控 LLM 应用及 AI 智能体的开源平台。团队使用 Langfuse 来追踪和调试智能体工作流、运行评估,并持续衡量和改进生产环境中 AI 输出的质量。Langfuse 既提供托管云服务,也支持在生产规模下自托管。作为增长最快的 LLM 工程平台之一,Langfuse 拥有 20,470 个 GitHub Star、每月超过 2,600 万次 SDK 安装量以及 600 多万次 Docker 拉取,并受到《财富》50 强中 19 家公司和《财富》500 强中 63 家公司的信任。欲了解更多信息,请访问 langfuse.com。

关于 Ubicloud:

Ubicloud 正在打造开源版的 AWS,在裸金属和公有云之上交付核心云服务。Ubicloud 由打造分布式 PostgreSQL 的 Citus Data 团队创立 (该公司已被 Microsoft 收购)。其旗舰数据库产品 Ubicloud PostgreSQL 提供企业级托管 Postgres 体验,并具备行业领先的性价比。Ubicloud 在 AI、计算、PostgreSQL 和 Kubernetes 等领域提供的服务每周支撑超过 100 万台虚拟机运行,可帮助客户将云成本降低多达 70%。Ubicloud 获得了 Y Combinator 及其他知名硅谷投资机构的支持。欲了解更多信息,请在 X 上关注 Ubicloud @ubicloudHQ,或访问 ubicloud.com。

/END/

征稿启示

面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出 &图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com。

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

你有多久没打电动了?还记得小时候玩过什么游戏吗?

我是90后,第一次接触的游戏机是小霸王,玩的就是红白机这代的游戏。但真正给我生成情怀的还得是 GBA。口袋妖怪红绿蓝、金银水晶,再到后面的火红叶绿和各种宝石;马里奥赛车;龙珠大冒险;舞空斗剧。

时间长了多少有点怀念了。那么有没有一种可能,一个“客户端”能包含N台游戏机模拟器呢?我找到 EmulatorJS。

下载 EmulatorJS

在电脑安装 EmulatorJS 的方法很简单。

首先电脑需要安装 Node.js 环境,打开 Node.js 官网(https://nodejs.org/)直接下载安装好就行(很简单,我不贴教程了)。

接着打开 EmulatorJS 的代码仓库(https://github.com/EmulatorJS/EmulatorJS),用下面这套命令把代码克隆到本地。

git clone git@github.com:EmulatorJS/EmulatorJS.git

如果你电脑没安装 git 工具,在浏览器打开 EmulatorJS 的 GitHub 地址,下载 ZIP 文件到电脑,然后解压就行。

安装依赖

EmulatorJS 代码下载成功后,接下来需要使用 npm 下载 EmulatorJS 项目用到的依赖文件(一些工具类的代码)。所以要安装好 Node.js 环境。

装好 Node.js 环境后,打开终端,进入到 EmulatorJS 项目的目录。

  • 在终端可以通过 cs xxxxxx 的方式进入 EmulatorJS。
  • 在 Windows 也可以打开 EmulatorJS 文件夹,然后右键,打开终端。

打开终端后,输入以下代码安装 EmulatorJS 的依赖文件。

npm i

如果网络没问题的话,安装好依赖文件后,EmulatorJS 目录下会出现一个 node_modules 文件夹,里面就是 EmulatorJS 需要用到的依赖文件。

其实安装好依赖后就可以运行 EmulatorJS 了,但如果你想在“不联网”的情况下也能运行 EmulatorJS,还需要下载指定模拟器的文件。

模拟器文件在这里:https://cdn.emulatorjs.org/nightly/data/cores

你想运行哪台游戏机,就下载对应的文件。

比如我想玩 GBA,那就搜索“gba”。如果要兼容老浏览器,那就下载 xxx-legacy-wasm.data 这类文件,如果你用的是最新版的 Chrome,直接下载 mgba-wasm.data 也行。

把模拟器文件放到 EmulatorJS 项目的这个地方,以后就可以离线运行 EmulatorJS 了。

EmulatorJS/data/cores

我想玩 GBA,所以我就只放了 mgba-legacy-wasm.data 进来。

如果无法打开模拟器文件的网址,我也准备了一份放在百毒碗盘。

🐱:喵喵嗨嘻咪喵呀呦喵喵呀嘤咪喵呀咪喵咪呀哇咪咪哇哼喵喵喔咝喵喵咕嘶咪咪啊咪咪喵嘿嗷喵咪嘿咔喵喵咕咔喵喵嘿咕喵喵嘿呜咪咪嗨嗝喵咪嘿呦喵喵呀嗯喵咪咕咔咪喵嘿哇咪喵嗨咝咪咪嘿哒喵喵喔嘶喵喵呀哇咪咪喔咝咪咪哇呜咪咪嗯呀喵咪嘤嘟咪喵嘿咝喵咪呦嗡喵喵哈哈喵喵嘤哒咪喵啊哇喵咪嘿嘤喵咪嘛喔喵喵嘤咩喵咪嘤嗯喵咪嘿哒咪咪嘿喔咪咪嘤哇喵咪嘿嘤咪喵呦啊喵喵呦嗯咪喵嘤呦喵咪嗨啪咪咪呦喔咪喵嗨咕喵喵呦呜咪咪哇咝咪喵啊喵喵咪啊啊咪咪嘿嘤咪喵哈哒喵咪嗨啊咪咪嗨咕喵咪嘿嗷咪咪啊哼

复制上面这段内容,到「光刻符文」小软体,选择“符文 - 土猫”解开吧。直接发百毒的🔗怕某些平台不给过。

运行 EmulatorJS

安装好所有依赖文件后,在终端输入这条命令按回车键就可以运行 EmulatorJS 了。

npm run start

把游戏拖进去就可以直接运行了。

以 GBA 为例,可以随时保存和读取游戏进度。

其他功能就不多介绍了,自己研究吧~


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

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

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

Reubah 是一款基于网页的工具,具备图片格式转换、优化、批量处理(背景移除即将推出)和多种文档格式转换功能,支持暗黑模式与 API,无文件存储且自动清理,可通过 Docker 或本地部署,界面简洁易用。

本次使用的是群晖 NAS 部署 Reubah,其他品牌的 NAS 操作步骤类似。

首先在“File Station”里找到“docker”文件夹,在“docker”文件夹里创建“reubah”文件夹。

打开“Container Manager”,新增一个项目。

项目名称填 reubah

路径选择刚刚在“docker”文件夹里创建的“reubah”。

来源选择“创建 docker-mompose.yml”。

然后填入以下代码(需要注意代码格式,空格和换行这些)。

services:
  reubah:
    image: ghcr.io/dendianugerah/reubah:latest
    container_name: reubah
    ports:
      - "8081:8081"
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    restart: unless-stopped

8081:8081 这句,冒号左侧的数字是可以改的,右侧那个不能改。

输入完代码后点击“下一步”。

勾选“通过 Web Station 设置网页门户”,然后点击“下一步”,等待 docker 下载相关代码。

最后一步是打开“Web Station”(没有这个工具就去“套件中心”下载)。

新增一个网络门户,参考下图选项。

需要注意,端口要输入一个和其他项目不冲突的数字,我这里输入的是 2347

完成上面所有操作后,在浏览器打开 NAS的IP + reubah端口号 就可以访问 Rebuah 了。

比如我的是 http://192.168.31.85:2347

在图片格式转换这边,还支持 iPhone 的实况照片格式(HEIC)转换。

常见的 jpeg、png、webp、gif、bmp 以及将图片转换成 pdf 都是支持的。

文件格式这边包含常见的pdf、docx、doc、odt、txt 和 rtf。

切换到 Batch Processing 面板还可以做批量处理。


以上就是本文的全部内容啦,想了解更多NAS玩法可以关注《NAS邪修》

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

第六十三章 运动侦测实验

乐鑫AI库中提供了一种名为运动侦测API接口的功能。该功能的原理非常简单:只需要获取两张图像数据,然后通过AI计算判断这两个图像是否匹配。如果图像不匹配,则说明当前处于运动状态;如果图像匹配,则说明当前图像处于相对静止状态。本章,我们调用乐鑫AI库的运动侦测API接口来实现运动侦测功能。
本章分为如下几个部分:
63.1 硬件设计
63.2 软件设计
63.3 下载验证

63.1 硬件设计

1.例程功能

本章实验功能简介:使用乐鑫官方的ESP32-WHO AI库对OV2640和OV5640摄像头输出的数据进行运动侦测。

2.硬件资源

1)LED灯
LED-IO1

2)XL9555
IIC_INT-IO0(需在P5连接IO0)
IIC_SDA-IO41
IIC_SCL-IO42

3)SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555)

4)CAMERA
OV_SCL-IO38
OV_SDA- IO39
VSYNC- IO47
HREF- IO48
PCLK- IO45
D0- IO4
D1- IO5
D2- IO6
D3- IO7
D4- IO15
D5- IO16
D6- IO17
D7- IO18
RESET-IO0_5(XL9555)
PWDN-IO0_4(XL9555)

3.原理图

本章实验使用的KPU为ESP32-S3的内部资源,因此并没有相应的连接原理图。

63.2 软件设计

63.2.1 程序流程图

程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

图63.2.1.1 程序流程图

63.2.2 程序解析

在本章节中,我们将重点关注两个文件:esp_motion_detection.cpp和esp_motion_detection.hpp。其中,esp_motion_detection.hpp主要声明了esp_motion_detection函数,其内容相对简单,因此我们暂时不作详细解释。本章节的核心关注点是esp_motion_detection.cpp文件中的函数。
接下来,我们将详细解析esp_motion_detection_ai_strat函数的工作原理。

TaskHandle_t camera_task_handle;
TaskHandle_t ai_task_handle;
QueueHandle_t xQueueFrameO = NULL;
QueueHandle_t xQueueAIFrameO = NULL;


/**
 * @brief       摄像头图像数据获取任务
 * @param       arg:未使用
 * @retval      无
 */
static void esp_camera_process_handler(void *arg)
{
    arg = arg;
    camera_fb_t *camera_frame = NULL;

    while (1)
    {
        /* 获取摄像头图像 */
        camera_frame = esp_camera_fb_get();

        if (camera_frame)
        {
            /* 以队列的形式发送 */
            xQueueSend(xQueueFrameO, &camera_frame, portMAX_DELAY);
        }
    }
}

/**
 * @brief       摄像头图像数据传入AI处理任务
 * @param       arg:未使用
 * @retval      无
 */
static void esp_ai_process_handler(void *arg)
{
    arg = arg;
    camera_fb_t *face_ai_frameI = NULL;
    camera_fb_t *face_ai_frameI2 = NULL;

    while(1)
    {
        /* 以队列的形式获取摄像头图像数据 */
        if (xQueueReceive(xQueueFrameO, &face_ai_frameI, portMAX_DELAY))
        {
            if (xQueueReceive(xQueueFrameO, &face_ai_frameI2, portMAX_DELAY))
            {
                /* 判断图像是否出现运动 */
                uint32_t moving_point_number = dl::image::
get_moving_point_number(
(uint16_t *)face_ai_frameI->buf,
(uint16_t *)face_ai_frameI2->buf,
face_ai_frameI->height,
face_ai_frameI->width, 8, 15);

                if (moving_point_number > 50)
                {
                    printf("Something moved\r\n");
                    /* 此处是在图像中绘画检测效果 */
                    dl::image::draw_filled_rectangle(
(uint16_t *)face_ai_frameI2->buf, 
face_ai_frameI2->height, 
face_ai_frameI2->width, 0, 0, 40, 40);
                }
                else
                {
                    printf("Something not moved\r\n");
                }
                
                esp_camera_fb_return(face_ai_frameI);
                /* 以队列的形式发送AI处理的图像 */
                xQueueSend(xQueueAIFrameO, &face_ai_frameI2, portMAX_DELAY);
            }
        }
    }
}

/**
 * @brief       AI图像数据开启
 * @param       无
 * @retval      1:创建任务及队列失败;0:创建任务及对了成功
 */
uint8_t esp_motion_detection_ai_strat(void)
{
    /* 创建队列及任务 */
    xQueueFrameO = xQueueCreate(5, sizeof(camera_fb_t *));
    xQueueAIFrameO = xQueueCreate(5, sizeof(camera_fb_t *));
xTaskCreatePinnedToCore(esp_camera_process_handler,
                       "esp_camera_process_handler", 4 * 1024, NULL, 
5, &camera_task_handle, 1);
xTaskCreatePinnedToCore(esp_ai_process_handler, "esp_ai_process_handler", 
6 * 1024, NULL, 5, &ai_task_handle, 1);

    if (xQueueFrameO != NULL 
        || xQueueAIFrameO != NULL 
        || camera_task_handle != NULL 
        || ai_task_handle != NULL)
    {
        return 0;
    }
    
    return 1;
}

上述原理非常简单:只需要在ai_task_handle任务下获取两张图像数据,然后通过AI计算判断这两个图像是否匹配。如果图像不匹配,则说明当前处于运动状态;如果图像匹配,则说明当前图像处于相对静止状态,最后,我们使用消息队列将当前图像数据传输至LCD进行显示。

63.3 下载验证

程序下载成功后,当检测到图像变化时,图像左上角有蓝色块闪烁。

Magnet Axiom 9.9 Windows x64 Multilingual - 数字取证与分析

Digital Forensic Software

请访问原文链接:https://sysin.org/blog/magnet-axiom/ 查看最新版。原创作品,转载请保留出处。

作者主页:sysin.org


Magnet Axiom

形象标识

在一个案件中恢复并分析所有的证据

在一个案件文件中,同时检查来自移动设备、云端、计算机和车辆来源的数字证据,以及第三方提取数据。使用强大且直观的分析工具,自动快速呈现与案件相关的证据。

产品图像

新工具如何消除干扰寻找证据

涉及调查的数字设备数量正在增长,平均每人约有六台设备*,这使得取证、处理和分析在后勤上变得复杂、耗时且成本高昂。像 Axiom 这样的工具让调查人员能够简化工作流程 (sysin),从大量数字干扰中快速定位、恢复和收集证据。

*2022 年 IDC MarketScape

新增功能

Magnet AXIOM 9.9.0.46675 — 发布说明 (2025-12-08)

🔎 主要“工件 (Artifacts)”更新/新增

RSMF 导出 (RSMF Exports) — Cyber

  • 群聊消息 (group chat messages) 导出时,现在可以按时间段 (time period) 或按消息数量 (number of messages) 来分组。
  • 如果导出的文件大于 2 GB,将自动拆分成多个文件,以便在 Relativity 中处理。

新增工件 (New Artifacts)

  • Apple Notes 嵌入对象 (Apple Notes Embedded Objects) | iOS
  • 云端 ChatGPT 项目文件 (Cloud ChatGPT Project Files) | Cloud
  • Microsoft Teams 活动 (Microsoft Teams Activity) | iOS
  • Whoo 应用位置 (Whoo Locations) | Android
  • Whoo 应用用户 (Whoo Users) | iOS

更新工件 (Updated Artifacts)

  • Apple Maps Trips | iOS — 更新为在 SQLite 查看器中支持 “以 protobuf 格式查看 (View as protobuf)”
  • Apple Notes | iOS — 更新了解密机制,以支持 iOS 18 的变动
  • Microsoft Teams Messages | Android — 更新为将附件 (attachments) 与消息 (messages) 关联 (link)
  • Microsoft Teams Messages | 电脑 (Computer) — 更新,现在除了 .pst 文件,也处理 .msg 邮件文件
  • Outlook Emails | 电脑 — 同样新增对 .msg 文件 (除了 .pst) 的处理支持
  • Owner Information | iOS — 更新填充 “设置日期 (Setup Date)” 的方法
  • Signal Messages – Windows | 电脑 — 更新,加入对 “Reactions (表情/反应)” 和 “编辑历史 (Edit history)” 的支持
  • Telegram | iOS — 更新,支持 Telegram 版本 12.1.1

云 (Cloud) 相关

  • 在获取 (acquire) Microsoft OneDrive 帐户数据时,文件版本历史 (File Version History) 现在包括所有历史版本 (not just the latest)
  • 作为云数据来源 (OpenAI datasource),现在可以获取并处理 ChatGPT 的库 (Library) 和项目 (Project) 文件

⚙️ 处理/分析/导出 (Processing/Examining/Exports) 更新

  • 已将 Axiom Process 更新为使用最新的 Passware SDK。
  • RSMF 导出 (Cyber) 现在可以按时间段或聊天消息分组 (sysin),且当导出文件超过 2 GB 时会自动拆分为多个文件,以便在 Relativity 中处理。
  • 已更新为包含最新的 ReversingLabs YARA 规则 (YARA rules) — 有助于恶意软件/恶意文件检测。

🐛 Bug 修复 (Bug fixes)

  • 修复:之前 Axiom Process 可能无法从 GalleryEncryptedDb 恢复来自 Snapchat Memories 的附件/片段。 (MARS-3364)
  • 修复:之前 EXIF 日期 (EXIF date) 值格式不一致的问题 — 有时不会按 yyyy-mm-dd 格式呈现。 (CARS-1703)
  • 修复:之前 Firefox 缓存记录 (Firefox Cache Records) 在某些情况下可能未能完整恢复媒体文件。 (CARS-1418)
  • 修复:如果获取一个公开 Instagram 帐户 (Public account) 且该用户没有任何帖子 (posts),之前获取可能失败。 (CA-3491)
  • 修复:之前获取 iCloud 备份 (iCloud backups) 时,对于 iOS 26 和 18.6 设备可能失败。 (CA-3518)
  • 修复:当处理一个 Slack 导出 (Slack export) 时,附件 (attachments) 之前可能不会被下载 (sysin)。 (CA-3597)
  • 修复:在处理 iMessage 时,如果两个不同消息 (separate messages) 使用了相同名字 (name) 的附件 (attachment),可能导致错误 — 已修复。 (CA-3484)

Axiom 功能简介

使用 Magnet Axiom,在一个案件文件中恢复、分析并报告来自移动设备、计算机、云端和车辆的数据信息。

  • 强大的数据提取能力
  • 移动端工作流
  • 高级分析工具
  • Magnet One 增强支持

强大的数据提取能力

数据提取界面

轻松恢复已删除的数据,并以“数据工件优先”的方式在一个案件文件中分析来自移动设备、计算机、云端和车辆的数字证据。发现文件或工件的完整历史,以构建案件并证明意图。Magnet Axiom 为最新设备和数据来源提供最及时的数据工件支持。

关键要点

  1. 在同一案件中获取并分析来自移动设备、云端和计算机的证据。
  2. 处理来自 Google、Facebook 和 Instagram 等提供商的授权数据返回。
  3. 检查来自云端来源(如 Google、WhatsApp 等)的开源和用户账户数据。
  4. 从提取、数据恢复到案件文件构建,一步完成图像处理。

移动端工作流

移动端工作流

无论你使用哪种提取工具,Magnet Axiom 都能获取最多的数据,并为 iOS 和 Android 设备提供最佳的分析效果。随着 Magnet Graykey 直接集成到 Axiom 中,加载移动端证据进行深度分析变得更加轻松。

关键要点

  1. 接收并处理移动设备提取内容,直接集成 Magnet Graykey,并支持 Cellebrite、Oxygen、Berla 等第三方工具。
  2. Axiom 直观的 Mobile View 视图帮助你和相关人员在 Axiom 与 Portable Case 中轻松浏览和交互移动证据。
  3. 利用 Axiom 内强大的数据雕刻功能,发现图片、聊天记录和浏览历史。
  4. 通过 KnowledgeC、Android Motion Photos、iOS Wallet、Samsung myFiles、地理位置数据等工件,揭示详细的主体信息。
  5. 利用移动设备的令牌和钥匙串进行自动解密。

高级分析工具

Magnet AXIOM 产品界面

通过 Magnet Axiom 的分析工具自动发现更多证据,让你专注于案件相关信息。借助 Magnet CopilotMedia ExplorerCloud Insights DashboardMagnet.AIConnectionsTimelineEmail Explorer 等功能 (sysin),快速找到所需证据。

关键要点

  1. 使用 Magnet.AIThorn 等机器学习工具自动检测潜在的非法图片,如儿童虐待、毒品和武器内容。
  2. 使用 Connections 快速了解工件、人物或设备之间的关联。
  3. 借助 Media Explorer 从图像和视频中快速提取智能洞察。
  4. 使用 Timeline 可视化所有证据来源中的事件。
  5. 按日期、时间范围、特定工件或关键词筛选数据,快速找到相关证据。
  6. 通过早期访问 Magnet Copilot 等新 AI 工具,快速识别深度伪造媒体并提取相关证据。

借助 Magnet One 提升效率与协作

Magnet One

将 Axiom 与其他数字取证解决方案整合,贯穿整个工作流程,实现更快速、更高效的调查。Magnet One 可轻松简化工作流程 (sysin),并支持取证人员、调查员、检察官、指挥人员和机构领导之间的无缝协作。

关键要点

  1. 轻松提交数字取证实验室请求并创建案件,节省时间与精力。
  2. 通过互联的工作流程减少手动步骤,提高工作效率。
  3. 在每个阶段监控 Axiom 处理任务进度,处理完成后自动通知调查人员。
  4. 与调查团队实时协作,确保所有人都能保持同步。

下载地址

Magnet Axiom 9.9.0.46675 for Windows x64 Multilingual (内置简体中文和繁体中文界面语言)

请访问:https://sysin.org/blog/magnet-axiom/

相关产品:

更多:HTTP 协议与安全

近日,美团 LongCat 团队正式对外发布并开源 LongCat-Flash-Thinking-2601。作为已发布的 LongCat-Flash-Thinking 模型的升级版,LongCat-Flash-Thinking-2601 在 Agentic Search(智能体搜索)、Agentic Tool Use(智能体工具调用)、TIR(工具交互推理)等核心评测基准上,均达到开源模型 SOTA 水平。

该模型尤其在工具调用上表现出卓越的泛化能力,在依赖工具调用的随机复杂任务中性能超越了 Claude,可大幅度降低真实场景下新工具的适配训练成本;同时它是首个完整开源并支持在线免费体验「重思考模式」的模型,同时启动 8 个大脑飞速运转,确保思考周全、决策可靠。

目前该功能已经可以在 https://longcat.ai 网站免费体验(仅选择深度思考功能时会触发重思考模式)。

01 创新的「重思考」模式:让模型学会“深思熟虑”

全新升级的「重思考」模式,让模型学会了“深思熟虑”再行动,遇到高难度问题时,模型会把思考过程拆成并行思考和总结归纳两步来做:

并行思考阶段,模型会同时独立梳理出好几条推理路径,就跟人面对难题时会琢磨不同解法一个道理,还会特意保证思路的多样性,生怕漏掉最优解;

总结归纳阶段,对多条路径进行梳理、优化与合成,并将优化结果重新输入,形成闭环迭代推理,推动思考持续深化。

除此之外,我们还专门设计了额外的强化学习环节,针对性打磨模型的总结归纳能力,让 LongCat-Flash-Thinking-2601 真正实现“想清楚再行动”。

02 智能体工具调用能力登顶开源 SOTA

经过全面严谨的评估显示,LongCat-Flash-Thinking-2601 模型在编程、数学推理、智能体工具调用、智能体搜索维度表现全面领先:

  • 编程能力:LongCat-Flash-Thinking-2601 在 LCB 评测中取得 82.8 分,OIBench EN 评测获 47.7 分,成绩处于同类模型第一梯队,展现出扎实的代码基础能力。
  • 数学推理能力:在开启重思考模式后表现突出,LongCat-Flash-Thinking-2601 在 AIME-25 评测中获 100.0 分(满分),IMO-AnswerBench 中以 86.8 分达到当前 SOTA。
  • 智能体工具调用能力:在 τ²-Bench 评测中拿到 88.2 分,VitaBench 评测中获得 29.3 分,均获得开源 SOTA 水平,在多领域工具调用场景下表现优异,适配实际应用需求。
  • 智能体搜索能力:在 BrowseComp 任务中取得 73.1 分(全模型最优),RW Search 评测获 79.5 分,LongCat-Flash-Thinking-2601 具备强劲的信息检索与场景适配能力,达到开源领先水平。

同时,为了更好的测试智能体模型的泛化能力,我们提出了一种全新的评测方法——通过构建一套自动化任务合成流程,支持用户基于给定关键词,为任意场景随机生成复杂任务。每个生成的任务都配备了对应的工具集与可执行环境。由于这类环境中的工具配置具有高度随机性,我们通过评估模型在该类环境中的性能表现,来衡量其泛化能力。实验结果表明,LongCat-Flash-Thinking-2601 在绝大多数任务中保持领先性能,印证了其在智能体场景下强大的泛化能力。

03 核心技术突破:既能“打硬仗”也能“抗干扰”

3.1 环境扩展与多环境强化学习 :从“靶场”到“实战”

传统智能体大多只在几个简单模拟环境里训练,就像士兵只练过靶场,到了真实“战场”就掉链子。而基于“环境扩展+多环境强化学习”核心技术,为模型打造了多样化的“高强度练兵场”,构建了多套高质量训练环境,每套集成 60 余种工具并形成密集依赖关系图谱与复杂联动,支撑起高度复杂的任务场景。实验证明,训练环境越丰富,模型在未知场景中的泛化能力越强。得益于这套方案,LongCat-Flash-Thinking-2601 在智能体搜索、智能体工具调用等核心基准测试中稳居前列。尤其在复杂随机的分布外任务中性能优于 Claude。

同时我们针对性扩展 自研强化学习基础设施(DORA),在保留原有高效异步训练特性的基础上实现大规模多环境智能体的稳定并行训练,通过均衡搭配多环境任务、按难度与训练进度智能分配算力,最大化提升训练效率与资源利用率,筑牢能力根基。此外,我们还从复杂度、多样性双维度严控训练任务,配套专属数据库及优化方案,杜绝模型“偏科”与训练漏洞,让这套全流程方案持续赋能模型,稳居智能体能力第一梯队。

稳定上涨的多环境混合强化学习训练曲线

多环境强化学习训练下不同 OOD 测试集上的 RL Scaling 表现

3.2 噪声环境下的稳健训练:让智能体更“抗造”

现实世界的智能体环境充满不确定性,API 调用失败、返回异常信息、观测数据不完整等“噪声”问题,极易导致模型决策失误。为此,我们在训练数据的过程中主动注入多类噪声,模拟 API 的调用失败、返回错误信息、数据缺失等场景,并用课程学习(Curriculum Learning)的方式循序渐进去做模型的训练,在训练过程中逐步增加噪声的类型与强度——如果类比成教小孩骑车,我们首先在平坦路面做练习,等技能成熟后再逐步增加路面的复杂度。

可以看到,带噪声环境下未经过稳健训练的模型的表现会出现大幅衰减,Claude 也无法适应全部的噪声类型。而经过这套系统化的抗干扰训练,LongCat-Flash-Thinking-2601(Training w/ Noise 组)拥有了极强的环境适应能力,哪怕在复杂、不理想的场景中,也能稳定发挥、高效完成任务。

带噪声 / 无噪声评测集下的模型表现对比

开源与部署:低门槛接入,加速智能体应用落地

为降低开发者使用门槛,美团 LongCat 团队同步开放模型权重、推理代码与在线体验能力,支持从快速试用至深度开发的全流程需求:

开源平台

在线体验与调用

欢迎开发者下载、部署并体验 LongCat-Flash-Thinking-2601,同时也欢迎您在 LongCat API 开放平台申请免费调用额度。如果您在智能体开发、大模型推理优化等领域有合作想法或反馈,我们期待与您交流。

| 关注「美团技术团队」微信公众号,在公众号菜单栏对话框回复【2024年货】、【2023年货】、【2022年货】、【2021年货】、【2020年货】、【2019年货】、【2018年货】、【2017年货】等关键词,可查看美团技术团队历年技术文章合集。

| 本文系美团技术团队出品,著作权归属美团。欢迎出于分享和交流等非商业目的转载或使用本文内容,敬请注明“内容转载自美团技术团队”。本文未经许可,不得进行商业性转载或者使用。任何商用行为,请发送邮件至 tech@meituan.com 申请授权。

Claude Code × 智谱 BigModel 实战集成指南

本文记录一次 Claude Code + 智谱 BigModel(GLM Coding 套餐) 的完整体验,从 CLI 安装、IDE 集成,到使用 Claude Code 零手写代码 搭建一个可运行的 AI 后端工程,并对整体体验做一个总结。


一、什么是 Claude Code?

Claude Code 是 Anthropic 推出的 本地 AI 编码助手(CLI + IDE 插件),核心能力包括:

  • 在本地代码仓库中直接对话式开发
  • 理解项目结构、自动生成/修改代码
  • 支持多种 IDE(VS Code / JetBrains 全家桶)
  • 支持通过 兼容 Anthropic API 的第三方模型 接入(如智谱 GLM)

这意味着:即使不使用 Anthropic 官方模型,也可以完整使用 Claude Code 的工程化能力。


二、Claude Code CLI 安装

macOS / Linux / WSL

curl -fsSL https://claude.ai/install.sh | bash

Windows PowerShell

irm https://claude.ai/install.ps1 | iex

Windows CMD

curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd

安装完成后,终端中可直接使用:

claude

三、IDE 集成能力

1️⃣ Claude Code Desktop

  • 官方桌面客户端
  • 适合直接在本地项目中进行对话式开发

PixPin_2026-01-19_19-48-04.png

2️⃣ VS Code

  • 官方插件支持
  • 与当前 Workspace 深度绑定

PixPin_2026-01-19_19-43-51.png

3️⃣ JetBrains 系列(官方支持)

  • IntelliJ IDEA
  • PyCharm
  • GoLand
  • WebStorm
  • PhpStorm
  • Android Studio

PixPin_2026-01-19_19-53-17.png

实际体验中,对 多文件工程、后端项目结构 的理解能力非常强。

四、接入智谱 BigModel(GLM Coding 套餐)

Claude Code 可以通过 Anthropic API 兼容协议 接入智谱大模型。

4.1 注册账号

👉 https://www.bigmodel.cn/glm-coding

4.2 创建 API Key

登录后进入:

👉 https://bigmodel.cn/usercenter/proj-mgmt/apikeys

创建新的 API Key 并保存。


4.3 使用官方自动化工具(强烈推荐)

智谱提供了 Coding Tool Helper,可自动完成:

  • Claude Code 安装
  • API Key 配置
  • MCP Server 管理
  • 模型套餐加载
一条命令完成配置
npx @z_ai/coding-helper

按照交互提示操作即可,无需手动修改复杂配置。


4.4 启动 Claude Code

进入任意代码目录,执行:

claude

首次启动时若提示:

Do you want to use this API key?

选择 Yes 即可。


五、模型配置与切换

默认模型映射

ANTHROPIC_DEFAULT_OPUS_MODEL   → GLM-4.7
ANTHROPIC_DEFAULT_SONNET_MODEL → GLM-4.7
ANTHROPIC_DEFAULT_HAIKU_MODEL  → GLM-4.5-Air

手动配置(可选)

编辑文件:

~/.claude/settings.json
{
  "env": {
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "glm-4.5-air",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-4.7",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-4.7"
  }
}

验证模型状态

重新打开终端并运行:

claude

在 Claude Code 中输入:

/status

即可看到当前模型配置状态。


六、资源包与福利

  • ✅ 注册即送 体验 Token

PixPin_2026-01-19_20-12-56.png

  • ✅ 实名认证赠送 500 万 GLM-4.7 Token

PixPin_2026-01-19_20-13-54.png

👉 资源包管理:
https://bigmodel.cn/finance-center/resource-package/package-mgmt

对于个人开发者和技术验证阶段非常友好。


七、实战体验:零手写代码搭建 AI 后端

在 Claude Code 中直接输入需求:

请帮我集成 FastAPI、LangChain、LangGraph、langchain-ollama、Milvus,并构建好项目结构:

  • FastAPI 接口
  • Token 认证(非 JWT)
  • 使用 SQLite 生成和校验 Token
  • Milvus 作为向量数据库
  • Ollama 作为本地模型推理

PixPin_2026-01-19_20-15-16.png

结果:

  • 一次生成即成功运行
  • ✅ 自动生成项目结构
  • ✅ 自动生成依赖、启动方式、示例接口
  • ✅ Token 认证逻辑清晰、可直接落地

全程未手写一行代码,仅做了运行验证。


八、总结

一句话评价:Claude Code + GLM-4.7 = 当前最强中文友好的工程级 AI 编码体验之一

优点

  • 工程理解能力强(不是“代码片段级”)
  • 对后端框架 / AI 工程非常友好
  • CLI + IDE 双形态,贴近真实开发流
  • 国产模型接入,成本可控、速度稳定

适合人群

  • 后端 / AI 工程师
  • 想快速验证 AI 架构方案的团队
  • 对 Agent / RAG / 工程化落地有需求的开发者
结论
如果你已经在做 AI 工程,而不是只写 Demo,Claude Code 非常值得一试。

原文链接:https://www.nocobase.com/cn/blog/4-open-source-data-managemen...

引言

当我们提到数据管理工具,脑海中往往会浮现出数据仓库、数据管道或分析平台。这类工具通常用于数据的存储、同步、清洗和分析,在现代数据体系中确实扮演着重要角色。

在开发者社区中,有不少工程师表达过这样的感受:他们尝试过一些被广泛推荐的数据管理工具,却发现这些工具最终只是不断叠加到技术栈中,并没有带来预期中的改善。

甚至有人直言如果真的想要一个完全符合自身需求的方案,往往只能在现有工具的基础上自行修改、取舍,甚至接受不完美作为常态。

reddit.PNG

今天这篇文章,我们会聚焦业务系统中的数据管理问题。如果你正在寻找一些数据管理工具,这篇文章或许会有帮助。

💡阅读更多:4个适合企业业务流程的轻量化软件(附真实案例)

数据管理工具真正在解决什么问题?

数据管理工具解决的问题,往往是以下几个方面:

  • 业务数据的结构化与组织

将零散的信息转化为有结构的数据模型,明确字段、类型和约束,使数据可以被长期维护和复用。

  • 数据实体之间的关系管理

描述不同业务对象之间的关系,例如一对多、多对多关系,并确保这些关系在系统中始终保持一致。

  • 数据访问权限与角色控制

不同角色对数据拥有不同的可见性和操作权限,既要保证安全性,又不能阻碍协作效率。

  • 围绕数据变更的流程与协作

数据并不是静态的。创建、修改、审批、回滚、同步,这些行为往往需要明确的流程和规则,而不仅仅是一次写入。

  • 随着系统变化保持数据一致性

当业务变化、需求增长、系统规模扩大时,数据结构和规则也必须能够随之调整,而不至于频繁推倒重来。

这些问题并不一定复杂,但它们贯穿了几乎所有业务系统的生命周期。从最初的几张表,到后期几十甚至上百个数据实体,数据管理的挑战往往是逐步累积的,而不是一次性爆发。

正因为这些问题在不同阶段、不同团队中的表现形式差异很大,数据管理工具也逐渐分化成了不同的类型。

数据管理工具的四种常见类型

  1. 数据基础设施与数据仓库类工具

这一类工具主要关注数据的集中存储与分析,典型使用者是数据工程师和数据分析团队。

常见的代表性产品包括:

  • Snowflake
  • Google BigQuery
  • Amazon Redshift
  1. 数据集成与数据管道类工具

数据集成与管道工具的核心职责是在不同系统之间移动数据,让数据能够从业务系统流入分析或存储层。

常见工具包括:

  • Fivetran
  • Airbyte
  • Talend
  1. 数据治理与数据质量管理工具

当组织的数据体系逐渐复杂之后,数据治理和质量管理工具开始发挥作用。

典型产品包括:

  • Collibra
  • Alation
  • Informatica
  1. 面向业务系统的数据管理工具

与前几类工具不同,这一类工具直接服务于业务系统本身,是业务数据产生、变化和协作的主要场所。

这类工具通常具备以下特征:

  • 数据模型与业务逻辑紧密结合
  • 数据主要由用户操作产生和维护
  • 权限控制和流程配置是核心能力

而这类工具它们本身又有各自的侧重点,适合用在不同的业务场景中。只有选择了最适合的产品,他们才能发挥出自己的最大价值。

⚠️ 注意:接下来本文讨论的数据管理工具,特指直接服务于业务系统的数据建模、关系、权限与流程管理工具,而非数据仓库或分析平台。

我们会从四个维度来展开讨论:

  1. 数据建模
  2. 关系
  3. 权限
  4. 流程
  5. 扩展性

让我们开始吧!

NocoBase

官网:https://www.nocobase.com/

GitHub:https://github.com/nocobase/nocobase

GitHub Star 数:21.2k

NocoBase 是一个开源、以数据模型为核心的 AI 业务系统构建平台(也是无代码/低代码开发平台),通过可配置的数据建模、权限、流程与插件机制,帮助团队构建和迭代复杂的业务系统,而不仅仅是提供一个通用的数据后端或管理界面。

NocoBase1.png

  1. 数据建模

NocoBase 的核心思路是让业务系统以数据模型为中心。你可以接入已有的数据源(支持 MySQL、PostgreSQL、MariaDB 等关系型数据库),或者自己重新定义数据集合、字段等。再在其上叠加界面、权限与流程。

NocoBase2.png

当业务变化导致字段或结构调整时,系统的其它层能够更稳定地跟随,而不是每次都从 UI 或脚本层打补丁。

NocoBase 可以让数据结构本身可维护、可迭代,并且能长期承载业务规则,而不是一次性建完就冻结。

  1. 关系

面向业务系统时,数据关系往往比字段更关键。客户、订单、合同、审批、任务等对象天然是关联的,且关系会随着业务发展变复杂。

NocoBase3.png

NocoBase 的方向是让关系建模成为系统的一等能力,你可以围绕业务实体建立清晰的关系结构,并在后续的权限、流程、页面交互中持续复用这些关系,而不是把关系逻辑分散在各处。

  1. 权限

权限是 NocoBase 的优势之一,它强调细粒度控制,可以从系统层一路细到行级、字段级,并支持一个用户拥有多个角色等常见企业场景。

NocoBase4.png

对这类业务系统数据管理工具来说,权限不是附加选项,而是业务规则的一部分。你需要控制的是:

  • 能看哪些记录
  • 能改哪些字段
  • 能执行哪些动作
  • 不同角色在同一页面看到的模块是否不同

这些能力在 NocoBase 的权限体系里是被明确覆盖的。

  1. 流程

当数据变更需要审批、通知、自动化处理时,系统就进入流程驱动的阶段。NocoBase 的工作流相关能力以插件形式提供,涵盖审批、邮件通知、自定义动作事件等常见节点,用来把数据变更从人工改字段升级为有规则的业务流程。

NocoBase5.png!

这类能力的意义在于:数据管理不再只是 CRUD,而是围绕数据变更的协作和控制,例如发起审批后才能修改关键字段,或在某个动作触发后执行一系列数据处理。

  1. 扩展性

NocoBase 的扩展方式以插件体系为中心,你可以把能力拆成模块来组合,例如工作流节点、API 文档、移动端配置、UI 的区块等都以插件方式出现。

NocoBase6.png

对面向业务系统的工具来说,扩展性通常不是指能不能写代码,而是指系统在长期变化中能否:

  • 以模块化方式增加能力
  • 以较低成本适配新流程与新权限要求
  • 在不推倒重来的前提下持续扩容系统边界

如果你的数据复杂性主要来自业务变化本身,例如关系变多、权限变细、流程变长,那么选择工具时就不应只看搭建速度,而应优先评估数据建模、关系、权限、流程与扩展能力是否属于一等能力。NocoBase 就是围绕这些维度设计的一类代表。

Directus

官网:https://directus.io/

GitHub:https://github.com/directus/directus

GitHub Star 数:33.9k

Directus 的核心定位是一个开源 Headless CMS 与开放数据平台,它通过自动为任意 SQL 数据库生成实时 API 和可视化管理界面,使开发者和业务用户都能高效管理和访问结构化数据。

Directus1.png

  1. 数据建模

Directus 的出发点是让数据库成为系统的核心。它直接建立在现有数据库之上,通过可视化方式管理表结构、字段、约束和元数据。

Directus2.png

这种方式的优势在于:

  • 数据结构高度透明,几乎等同于数据库本身
  • 非常适合数据库优先、Schema 相对稳定的系统
  • 对技术团队而言,可控性和可预测性都很强

Directus 更偏向于为已有或清晰定义的数据模型,提供一个统一、可管理的系统入口

  1. 关系

Directus 对关系的处理同样紧贴数据库层。

  • 一对多、多对多关系直接映射数据库结构
  • 关系本身是 Schema 的一部分,而不是额外的业务抽象

Directus3.png

这种方式的好处是关系定义非常清晰,不容易失真。

但同时也意味着当业务关系频繁变化时,系统的调整成本更多集中在 Schema 层,而不是更高层的业务抽象。

  1. 权限

Directus 的权限支持角色、集合、字段级别的访问控制,并且与数据模型高度绑定。

Directus4.png

在实际使用中,Directus 的权限体系更像是:

  • 围绕数据访问的安全控制机制
  • 而不是围绕业务流程的规则系统

这使它非常适合对谁能访问哪些数据有严格要求的场景,但当权限逻辑与业务流程强耦合时,往往需要额外的设计或配合外部系统。

  1. 流程

在流程层面,Directus 提供的能力相对较少。

  • 主要通过事件、Hooks、Webhooks 等机制响应数据变化
  • 更偏向数据变更触发行为,而非完整的业务流程编排

Directus5.png

因此,它更适合作为系统后端的数据与 API 层,而不是承担复杂审批、跨角色协作流程的核心系统。

  1. 扩展性

Directus 的扩展思路以后端可编程为主:

  • 可以通过自定义扩展、Hooks、API 扩展逻辑
  • 与前端或其他系统解耦程度较高

Directus6.png

这种扩展方式对开发者非常友好,但也意味着系统能力的增长更多依赖代码层面的投入,而不是通过配置或插件组合完成。

Budibase

官网:https://budibase.com/

GitHub:https://github.com/Budibase/budibase

GitHub Star 数:27.5k

Budibase 是一个开源的内部业务工具构建平台,强调通过低代码方式快速搭建 CRUD 型业务应用,适合交付效率优先、系统复杂度相对可控的业务场景。

Budibase1.png

  1. 数据建模

Budibase 的数据建模以应用所需的数据结构为核心,而不是以业务模型为核心。

  • 可以快速定义表、字段和基础约束
  • 更关注够用即可,而非高度抽象或可扩展建模
  • 数据模型通常服务于某一个具体应用,而不是系统级复用

Budibase2.png

在数据管理视角下,它更像是为某个内部应用准备数据结构。

  1. 关系

Budibase 支持基本的数据关系,但关系能力更多是为了满足页面展示和简单业务逻辑。

Budibase3.png

  • 适合一对多等常见关系
  • 对复杂、多层级、跨模块关系的支持相对有限
  • 关系往往和具体页面、表单绑定得较紧

这使它在面对关系逐步复杂化的业务系统时,扩展成本会明显上升。

  1. 权限

Budibase 提供角色与用户级别的权限控制,覆盖了内部工具中最常见的场景:

  • 不同角色看到不同页面
  • 控制某些操作是否可执行

但整体来看,权限模型更偏向应用层控制,而不是系统级、数据级的精细治理。

Budibase4.png

对于权限逻辑本身就是业务核心的系统(例如多角色、多数据范围的场景),通常需要额外设计或规避复杂需求。

  1. 流程

在流程层面,Budibase 提供的是轻量级自动化能力

Budibase5.png

  • 基于事件触发的自动操作
  • 简单的逻辑判断与动作执行

Budibase6.png

这类能力非常适合处理常见的内部流程自动化,但并不以复杂审批流或跨角色协作为主要目标。

  1. 扩展性

Budibase 的扩展能力主要体现在:

  • 组件和插件生态
  • 与外部服务的集成能力

它更强调在已有应用上快速补充功能

Budibase7.png

Appsmith

官网:https://www.appsmith.com/

GitHub:https://github.com/appsmithorg/appsmith

GitHub Star 数:38.9k

Appsmith 是一个面向开发者的开源低代码工具,通过代码与组件结合的方式,快速搭建管理界面和操作型应用。

Appsmith1.png

  1. 数据建模

Appsmith 本身并不以数据建模作为核心能力。

  • 更多是连接已有数据源(数据库、API、服务)
  • 数据结构通常定义在外部系统中
  • Appsmith 负责的是如何操作这些数据

在数据管理视角下,它假设这些问题已经在别处被处理好了。

Appsmith2.png

  1. 关系

由于数据关系主要存在于外部数据源中,Appsmith 对关系的支持更多体现在:

  • 如何在界面中展示和操作关联数据
  • 如何通过查询或脚本拼接多表结果

关系逻辑往往分散在查询、脚本和页面逻辑中,而不是作为系统层的一等能力存在。

  1. 权限

Appsmith 提供了基本的访问控制能力,主要集中在:

  • 应用级、页面级权限
  • 控制哪些用户可以访问或编辑某个工具

Appsmith3.png

但权限模型更多服务于工具使用安全。

  1. 流程

在流程方面,Appsmith 更偏向前端交互和操作流程

  • 用户点击按钮 → 触发查询或脚本
  • 基于事件的简单逻辑控制

它并不试图内建完整的业务流程引擎,复杂流程通常需要通过外部系统或自定义代码来实现。

Appsmith4.png

  1. 扩展性

Appsmith 的扩展性主要体现在开发者可控性上:

  • 可以编写 JavaScript 脚本
  • 可以自由组合 API、数据库和组件
  • 对技术人员非常灵活

Appsmith5.png

但这种扩展方式更适合工具级定制。

总结

回到文章最初的问题,为什么在社区中经常能看到对数据管理工具的失望情绪?

看完文章你应该有了答案:不同团队口中的数据管理,其实是完全不同的。

有的团队关心的是:

  • 数据如何安全、稳定地暴露为 API
  • 数据结构是否与数据库保持一致

有的团队关心的是:

  • 如何快速搭建一个可用的内部系统
  • 页面和操作能否尽快交付

基于这篇文章讨论的内容,我整理出这张对比表,从数据管理视角,对几种典型开源工具进行的对照。

维度NocoBaseDirectusBudibaseAppsmith
核心定位业务系统构建数据后端 / Headless CMS内部业务应用内部操作工具
数据建模系统级、可迭代的数据模型数据库优先,Schema 映射应用级数据结构依赖外部数据源
关系管理作为一等能力贯穿系统直接映射数据库关系基础关系支持通过查询与脚本处理
权限模型细粒度、与业务规则强耦合数据访问安全为核心应用层角色控制页面 / 应用级权限
流程能力内建工作流与审批能力事件 / Flow 驱动轻量自动化前端交互流程
扩展方式插件化、系统级扩展后端扩展与 Hooks组件与集成脚本与 API 组合

建议你可以亲自体验和尝试这些方案,希望你能找到最适合的数据管理工具。

相关阅读:

因为不满一直在使用的 Wokabulary 订阅制抢钱,也不想用功能花里胡哨的 app ,我怒做了一款背单词的 app ,名叫「遣词」,目前适用于 iPhone 。

「遣词」 app 宣传海报,朱红色背景中带有「遣词」 logo 和名称,以及「立即加入 TestFlight 测试」字样

遣词的功能很简单:添加生词到生词本之后,会根据你对生词的熟练度(从陌生到滚瓜烂熟有六个阶段),安排你的生词练习规划。熟练度每高一级,对应生词练习间隔时间越长(和艾宾浩斯理论接近)。每个生词都有自己的熟练度设定。练习中答对单词则提升一级熟练度;反之降级。

比 Wokabulary 更好的是:陌生级别的生词不会调用听写,而是启用跟写功能,目的是以最低门槛让你学习一次单词;即使在比陌生级别更高的熟练度下听写错误,也不会立即让你到下一题,而是会清空你的作答,要求你重新对着答案跟写一次才能继续。我觉得这样的设计可以帮助你更好地学习单词的正确拼写。

除了练习,现在实现的功能还包括手动添加生词、多语言生词本等,未来会添加 Anki 导入、云同步和里程碑功能。现在功能比较简陋,但如果你希望帮助打造更好的背单词 app ,欢迎点击 这里 加入测试。

0x01 研究背景

在自回归生成模型(Autoregressive Model)中,LLM每生成一个新token,都会将此前生成的序列作为输入。若每一步都重新计算全部注意力(Q、K、V 矩阵),计算量将随序列长度平方级增长。在长上下文和高并发场景下,这一开销会迅速成为系统瓶颈。为此,主流推理框架普遍引入KV-Cache技术。 KV-Cache通过缓存此前token的Key(K)和Value(V)向量,在下一步生成时只需计算新的Query(Q),即可直接复用前面的K/V,从而显著降低重复计算量。实践中,KV-Cache 通常能在保持模型精度不变的前提下,带来约5-8倍的推理加速。这一机制已经成为vLLM、SGLang、DeepSpeed-Inference等高性能推理引擎,以及Hugging Face generate(use_cache=True)接口的默认能力。

随着2024–2025年多租户推理服务(如vLLM、SGLang、TensorRT-LLM)的大规模部署,系统在单模型、多租户共享的前提下,又进一步引入跨请求的前缀缓存共享(prefix caching)。当不同请求的prompt存在相同前缀时,系统可以直接复用已有KV-Cache,大幅摊薄Prefill成本并提升吞吐。然而,当这种共享与复用机制扩展到多租户并发环境时,KV-Cache不再只是一个“性能优化组件”,而是演变成新的攻击面:攻击者可以通过观测Prefill 时间、TTFT等性能差异发起时序侧信道攻击,通过篡改缓存内容实施History Swapping(生成轨迹劫持),或者通过对Key向量注入扰动发动Cache Corruption(缓存腐败),从而导致跨租户信息泄露、话题漂移甚至下游任务性能显著下降。

0x02 KV缓存工作机制与共享复用原理

下面是KV-Cache工作原理的示意图。

KV-Cache工作原理

KV-Cache工作原理图

接下来我们用文字详细拆解,更深入了解KV缓存工作机制。

2.1 两阶段推理:Prefill与Decode

KV-Cache的核心做法分为两阶段。

(1)Prefill阶段(Prompt阶段)一次性计算输入序列的K/V并写入缓存 模型读取完整输入的prompt,计算出所有token的Key/Value向量并写入缓存。 公式表示为:

image-20251229205745845

此时缓存中的K/V向量构成了后续生成阶段的基础。

(2)Decode阶段(生成阶段)仅对新token计算Q/K/V,并复用历史K/V完成注意力计算 当模型生成新token时,仅需计算该token对应的Q、K、V向量。

image-20251229205757073

然后与缓存中已有的K/V拼接,直接完成注意力计算。这样便避免了重复计算前面N−1个token的注意力结果。

2.2 past_key_values

在Hugging Face Transformers框架中,KV-Cache在接口层面通过 past_key_values 对象实现。该对象并非一个抽象的控制开关,而是模型前向推理过程中实际生成、并可跨生成步骤复用的中间状态。它以分层的结构保存已处理历史Token的Key和Value张量,从而支撑自回归生成的增量计算。

从结构上看,past_key_values通常是一个长度为模型层数的列表或元组,其中每一层对应一对 (K, V)张量。不同模型的具体维度布局可能存在差异,但其核心语义一致:存储历史序列的注意力键值表示,以便后续生成时直接复用。

在推理流程中,Prefill 阶段会对完整的提示词进行计算,并首次生成past_key_values。进入Decode阶段后,若将此缓存作为输入传递给模型,模型通常只需为新输入的Token计算其对应的Key和Value,并将其追加至现有缓存末尾,从而避免了历史部分的重复计算。这种基于past_key_values的复用是框架的原生机制,其带来的加速直接源于注意力计算的真实削减,因此更适合作为评估系统性能及分析相关安全影响的工程基准。相比之下,通过sleep()或人为插桩制造“快慢差异”的方法仅能模拟现象,难以反映实际推理系统的缓存行为。此外,Transformers框架的generate()接口通常通过参数use_cache=True来启用此缓存机制。vLLM、SGLang、DeepSpeed-Inference在系统层面也普遍实现了类似机制,以降低生成延迟并提升吞吐量。

2.3 多租户场景下的前缀缓存与最长前缀匹配

在多请求并发且显存资源受限的推理服务中,为提升吞吐并降低重复的Prefill开销,系统常采用前缀缓存策略。其核心思想是当新请求的提示词(更准确地说是其Token序列)与某条已缓存的序列存在前缀重合时,系统可直接复用该前缀部分对应的KV-Cache,仅需对未命中的后续Token执行增量计算。

当缓存池中存在多个可能的候选前缀时,命中判定通常遵循最长前缀匹配(LPM)原则:在所有缓存条目中,系统会选择与新请求Token序列匹配长度最长的那一条作为复用对象,以最大化缓存利用率,减少重复计算。在工程实现上,这依赖于能够高效进行Token序列前缀匹配的数据结构或索引机制,例如前缀树(Trie)、基于前N个Token的分层哈希,或基于序列哈希值的多级索引。

根据匹配程度,命中效果可分为两类:一是完全命中,即请求的绝大部分或全部前缀已在缓存中,Prefill阶段的计算量显著下降;二是部分命中,即仅能复用较短的前缀,系统仍需对剩余后缀执行完整的Prefill计算。无论是“是否命中”还是“命中长度”,都会直接反映在可观测的系统性能指标上,例如Prefill时间、首Token延迟的分布等。

当前主流引擎(如vLLM的PagedAttention、LMCache)进一步通过分页管理和压缩技术缓解显存碎片,但前缀共享引入的侧信道与内存安全风险依然突出,这也是后续攻击面的根源。

0x03 KV-Cache的主要攻击面原理介绍

在理解KV-Cache的核心优化机制与共享原理后,我们可以看到其高效性背后隐藏的脆弱性。下面详解三大主要攻击面:时序侧信道攻击、操纵攻击与腐败攻击。

3.1 KV-Cache时序侧信道攻击

在共享KV-Cache的系统中,攻击者通过测量响应时间或请求处理顺序,推断缓存是否命中(hit),从而还原其他用户的Prompt(提示词)。

image-20251028173225854

时序侧信道攻击完流程图

设定还原的语句是"Imagine you are an IT expert",攻击者已经成功还原出"Imagine you are",并尝试还原下一个token "an"。下面我们根据上图分步骤拆解一下攻击过程。

步骤1:Generate candidates

攻击者在本地用小模型、模板或启发式方法生成可能的下一个token候选集合,例如:

  • Imagine you are an
  • Imagine you are a
  • Imagine you are the

把未知的victim prompt逐步转化为一系列候选前缀/后缀,便于后续probe。优点是减少搜索空间。


步骤2:Generate dummy

  • Candidate请求:每个请求包含一个候选后缀(比如Imagine you are an)。目标是看哪一个candidate与victim的缓存前缀最长匹配而“命中”缓存。
  • Dummy请求:随机或不相关的prompt(用来制造队列/填充调度槽位),以便控制调度顺序或避免直接暴露自己的probe请求导致缓存污染判断混淆。

步骤3:Send three request batches in turn

攻击者按这个顺序把三组请求发到服务器(可能是同一API key,也可能跨多个短时间窗口发出)。核心就是在调度队列里把candidate放在中间,观察它是否因为缓存命中而更快返回。

步骤4:Observe the returning order

攻击者记录三批请求的返回顺序和时间(TTFT/latency)。若candidate的响应比其前后的dummy显著更快或优先到达,就可推断该candidate是命中了缓存(即victim的prompt与该candidate共享较长前缀)。

3.2 History Swapping 攻击

image-20251230101845847

History Swapping操纵攻击原理图

攻击者通过结构化替换或注入KV-Cache内容,来“劫持”模型的生成轨迹,强制引导输出转向攻击者指定的主题或行为。这种攻击利用KV-Cache编码了不仅仅是上下文,还包括话题规划(topic trajectory)和结构化推理(structural planning)的特性。

设定攻击场景:受害者Prompt为“Give a precise technical explanation of espresso extraction variables”(讨论咖啡萃取),攻击者希望劫持输出到恒星生命周期主题。用户可见Prompt不变。

步骤1: 预生成目标主题KV-Cache

攻击者离线使用相同模型,基于目标主题Prompt生成一段完整KV-Cache块(topic_cache)。

步骤2: 启动正常生成并等待替换点

从受害者Prompt开始自回归生成,监控已生成token数,直到达到预设swap_token(例如序列的20%-60%处)。

步骤3: 执行块级覆盖替换

计算替换段长度(swap_percent,如25%-75%最近timestep),在全层(或指定早/晚层)用topic_cache对应部分直接覆盖当前缓存。

步骤4: 继续生成并观察劫持

模型基于篡改缓存继续输出。常见效果:立即/延迟主题偏移、原主题与攻击主题交替、或生成重复崩溃。

3.3 KV-Cache 腐败攻击

image-20251230101806716

KV-Cache腐败攻击原理图

攻击者通过向KV-Cache注入扰动(perturbation),破坏注意力机制的完整性,导致输出偏差、性能下降或幻觉增加。这种攻击视KV-Cache为“内存腐败”类似漏洞,扰动键向量(Key vectors)即可放大影响。

设定攻击场景:在正常生成或RAG任务中,攻击者向KV-Cache的Key向量注入扰动,导致注意力偏差、性能下降或幻觉增加。

步骤1: 选择目标层与时机

确定最脆弱层(通常中层,如LLaMA-2第12层)和扰动应用频率(连续或间歇)。

步骤2: 选择扰动变体

  • MTI-Gaussian:添加高斯噪声(σ=0.1-5.0)
  • MTI-Zeroing:概率置零Key条目
  • MTI-Rotation:施加正交旋转(15°-90°)
  • 可结合梯度优化以最大化目标影响

步骤3: 注入扰动到Key向量

在生成过程中,按选定策略对Key向量应用扰动δ。

步骤4: 观察输出效果

监控下一token分布偏移(KL散度上升)、下游任务性能下降15–30%、或RAG幻觉率增加5%-12%。中层扰动放大效果最显著。

0x04 代码实现

测试为纯CPU环境下完成,基于Python3.8+的Hugging Face Transformers与PyTorch运行124M参数的gpt2模型。

4.1 KV-Cache时序侧信道攻击

实验1:基础缓存时序测量

验证KV-Cache复用是否产生物理上可观测的时间差异。

我们实现了一个多租户LLM服务的 KVServer 类,支持:

  1. 最长前缀匹配 (LPM):实现类似vLLM的Prefix Caching
  2. 精确计时:仅测量Prefill阶段的KV 计算,排除tokenization开销
  3. 缓存管理:LRU淘汰策略

核心实现

@dataclass
class _CacheEnt:
    """KV-Cache 条目"""
    prompt: str
    input_ids: torch.Tensor
    past_kv: Tuple
    ts: float

class KVServer:
    """多租户KV-Cache服务器"""

    def _lpm(self, q_ids: torch.Tensor):
        """Longest Prefix Match - 缓存必须是查询的前缀"""
        best = None
        best_len = 0

        for cached, ent in self._cache.items():
            c_ids = ent.input_ids[0].tolist()
            q = q_ids[0].tolist()

            # 计算共同前缀长度
            mlen = 0
            for i, (a, b) in enumerate(zip(c_ids, q)):
                if a == b:
                    mlen = i + 1
                else:
                    break

            # 缓存有效条件:缓存是查询的前缀(mlen == len(cached))
            if mlen > best_len and mlen == len(c_ids) and len(c_ids) <= len(q):
                best = ent
                best_len = mlen

        return (best, best_len) if best else None

    def process(self, prompt: str, max_new=1, uid="anon", write_cache=True):
        """处理请求,返回详细的时序数据"""
        input_ids = self.tok.encode(prompt, return_tensors="pt")
        t0 = time.perf_counter()

        cache_r = self._lpm(input_ids)

        with torch.no_grad():
            if cache_r:
                # 缓存命中路径:复用past_key_values
                ent, matched = cache_r
                self._hits += 1

                if input_ids.shape[1] > matched:
                    # 部分匹配:计算增量部分
                    delta_ids = input_ids[:, matched:]
                    out = self.model(
                        delta_ids,
                        past_key_values=ent.past_kv,
                        use_cache=True
                    )
                    past_kv = out.past_key_values
                else:
                    # 完全命中:直接复用
                    past_kv = ent.past_kv

                prefill_t = (time.perf_counter() - t0) * 1000
                hit = True
            else:
                # 缓存未命中路径:完整前向传播
                self._miss += 1
                out = self.model(input_ids, use_cache=True)
                past_kv = out.past_key_values
                prefill_t = (time.perf_counter() - t0) * 1000
                hit = False

        # ... 生成阶段与缓存写回

运行结果

可以看到上面第一次请求Prefill用了大约380ms,这是模型执行完整前向传播的时间。对于GPT-2,这意味着要进行12层TransformerBlock的矩阵乘法运算。

然后当二次请求完全相同的时候Prefill仅仅为0.024ms ,几乎就只有内存操作时间。这个原因是因为past_key_values已存在,模型跳过了所有Attention层的Q×KT​计算,仅需简单的张量拼接。

实验2:prompt探测攻击

为了验证攻击者能否通过时序差异识别受害者的Prompt

核心代码:

def experiment_2_exact_match_attack():
    # 步骤1: 受害者缓存敏感Prompt
    victim_prompts = [
        "My secret password is hunter2",
        "My API key is sk-1234567890abcdef",
    ]

    for prompt in victim_prompts:
        server.process(prompt, uid="victim", write_cache=True)

    # 步骤2: 攻击者构造候选列表
    candidates = [
        "My secret password is hunter2",      # ✓ 匹配
        "My secret password is wrong",        # ✗ 不匹配
        "My secret code is hunter2",          # ✗ 不匹配
        "My API key is sk-1234567890abcdef",  # ✓ 匹配
        "My API key is sk-wrong-key",         # ✗ 不匹配
    ]

    # 步骤3: 逐个探测
    discovered = []
    for cand in candidates:
        r = server.process(cand, uid="attacker", write_cache=False)
        t = r['prefill_ms']

        if t < 1.0:  # 阈值判定
            discovered.append((cand, t))

    return discovered

运行结果

image-20251230222154860

我们可以从上面实验结果看到当探测内容与缓存完全一致时,模型无需任何计算,直接返回缓存指针。时间差异非常大。

我们可以从攻击者视角的视角来看到这件事:

1.攻击者构造"密码候选列表"(类似字典攻击),逐个探测。

2.只要有一个候选的响应时间<1ms,攻击者就能推断出被攻击者的完整prompt。

攻击简易流程图如下。

1

4.2 History Swapping攻击

实验目标:在不改变用户可见Prompt的情况下,通过替换推理过程中的past_key_values片段,把模型输出从“受害者话题”劫持到“攻击者话题”。

  • 受害者请求:正常的业务问题(例如“如何制作咖啡”)。
  • 攻击者能力


    • 能在同一推理进程/同一GPU的Worker内“写入或污染”共享的KV-Cache(例如:推理引擎实现了前缀缓存复用、调度/缓存对象复用存在隔离缺陷、或插件/监控/扩展组件可触达缓存对象)。
    • 攻击者提前离线生成一段目标主题的K-Cache。
    • 攻击效果:用户看到的prompt没变,但输出内容发生明显“叙事漂移”。

核心思路

  1. 攻击者用目标主题prompt跑一次Prefill,得到attacker_cache
  2. 受害者开始生成,达到某个swap_at_token时刻。
  3. 在所有层(或关键层)将受害者cache的一段时间步区间(如中间30%-60%)用attacker_cache的片段覆盖。

核心实现:

def gen_with_swap(model, tok, prompt: str, max_tok=30,
                 atk_cache=None, swap_at=2):
    """带缓存替换的生成函数"""
    ids = tok.encode(prompt, return_tensors="pt")
    generated = []

    # Prefill 阶段
    with torch.no_grad():
        out = model(input_ids=ids, use_cache=True)
        past = out.past_key_values
        logits = out.logits[0, -1, :]

    # 逐 token 生成
    for step in range(max_tok):
        tok_id = torch.argmax(logits).item()
        generated.append(tok_id)

        # 关键:在指定步数替换缓存
        if step == swap_at and atk_cache is not None:
            past = _swap_mix(past, atk_cache)

        nxt = torch.tensor([[tok_id]])
        with torch.no_grad():
            out = model(input_ids=nxt, past_key_values=past, use_cache=True)
            past = out.past_key_values
            logits = out.logits[0, -1, :]

    return tok.decode(generated, skip_special_tokens=True)

def _swap_mix(vic_cache, atk_cache):
    """混合策略:保留 10% 受害者前缀,替换中间 85% 为攻击者缓存"""
    from transformers.cache_utils import DynamicCache

    # 提取张量
    if hasattr(vic_cache, "key_cache"):
        v_k = [vic_cache.key_cache[i].clone() for i in range(len(vic_cache.key_cache))]
        v_v = [vic_cache.value_cache[i].clone() for i in range(len(vic_cache.value_cache))]
        a_k = [atk_cache.key_cache[i] for i in range(len(atk_cache.key_cache))]
        a_v = [atk_cache.value_cache[i] for i in range(len(atk_cache.value_cache))]
    else:
        # 兼容 tuple 格式
        v_k = [vic_cache[i][0].clone() for i in range(len(vic_cache))]
        v_v = [vic_cache[i][1].clone() for i in range(len(vic_cache))]
        a_k = [atk_cache[i][0] for i in range(len(atk_cache))]
        a_v = [atk_cache[i][1] for i in range(len(atk_cache))]

    new_cache = DynamicCache()

    for layer in range(len(v_k)):
        vk, vv = v_k[layer], v_v[layer]
        ak, av = a_k[layer], a_v[layer]

        seq_len = vk.shape[2]
        atk_len = ak.shape[2]

        # 替换策略:保留 10%,替换 10%-95%
        start = int(seq_len * 0.1)
        end = int(seq_len * 0.95)
        swap_sz = min(end - start, atk_len)

        nk = vk.clone()
        nv = vv.clone()

        if swap_sz > 0:
            # 关键:切片替换
            nk[:, :, start:start+swap_sz, :] = ak[:, :, :swap_sz, :]
            nv[:, :, start:start+swap_sz, :] = av[:, :, :swap_sz, :]

        new_cache.key_cache.append(nk)
        new_cache.value_cache.append(nv)

    return new_cache

image-20251229201348226

在我们进行swap_at_token 之后,输出出现明显话题漂移,原本应该是咖啡的制作方面的东西,结果话题漂移到了星空上面。

4.3 KV-Cache腐败攻击

实验:Cache Corruption(扰动Key 向量)

实验目标:在生成过程中对KV-Cache的Key张量注入扰动(噪声/置零/旋转),观察注意力机制被破坏后带来的输出质量退化(重复、语义漂移、幻觉倾向上升)。

更贴近实战的场景设定

  • 共享显存/共享推理Worker:攻击者通过越权写入或内存破坏类漏洞(例如缓存指针复用错误、越界写、错误的张量视图复用)影响到其他请求的KV。
  • RAG/Agent场景:缓存腐败会显著增加“把检索内容读错/拼接错”的概率,表现为幻觉或逻辑断裂。

扰动策略

  • corrupt_gaussian:K = K + N(0, σ^2)
  • corrupt_zeroing:以概p 将Key条目置零
  • corrupt_rotation:对Key的embedding子空间做正交旋转(简化实现为对最后维度两两旋转)

核心实现:

#高斯噪声
def corrupt_gaussian(cache, sig=1.0):
    """对 Key 向量添加高斯噪声:K = K + N(0, σ²)"""
    ts = _extract(cache)
    out = []
    mid = len(ts) // 2

    for i, (k, v) in enumerate(ts):
        if abs(i - mid) <= 1:  # 中层更敏感
            noise = torch.randn_like(k) * sig
            out.append((k + noise, v.clone()))
        else:
            out.append((k.clone(), v.clone()))

    return _rebuild(out)

#随机置零
def corrupt_zeroing(cache, p=0.3):
    """以概率 p 将 Key 条目置零"""
    ts = _extract(cache)
    out = []
    mid = len(ts) // 2

    for i, (k, v) in enumerate(ts):
        if abs(i - mid) <= 1:
            mask = (torch.rand_like(k) > p).float()
            out.append((k * mask, v.clone()))
        else:
            out.append((k.clone(), v.clone()))

    return _rebuild(out)

#正交旋转
def corrupt_rotation(cache, deg=45.0):
    """对 Key 的 embedding 子空间做正交旋转"""
    ts = _extract(cache)
    out = []
    mid = len(ts) // 2

    rad = np.radians(deg)
    c, s = np.cos(rad), np.sin(rad)

    for i, (k, v) in enumerate(ts):
        if abs(i - mid) <= 1:
            nk = k.clone()
            d = k.shape[-1]
            # 对最后维度两两旋转
            for j in range(0, d - 1, 2):
                kj = k[:, :, :, j].clone()
                kj1 = k[:, :, :, j + 1].clone()
                nk[:, :, :, j] = c * kj - s * kj1
                nk[:, :, :, j + 1] = s * kj + c * kj1
            out.append((nk, v.clone()))
        else:
            out.append((k.clone(), v.clone()))

    return _rebuild(out)

实验结果如下

image-20251230223603113

可以看到在不同扰动策略下模型输出的内容发生明显变化。

0x05 防御与缓解措施

以下从架构、系统、审计三层总结主流缓解措施,结合最新研究(如SafeKV、KV-Cloak),旨在平衡安全性、性能与部署成本。

5.1 架构层防御

  • 租户级缓存隔离:通过Tenant ID、Session Scope或用户唯一标识符划分KV命名空间,完全禁止跨租户共享。适用于高敏感场景,虽牺牲部分吞吐,但彻底消除侧信道。
  • 选择性共享:仅允许非敏感前缀共享,结合细粒度隐私策略(如基于内容分类)决定复用范围。
  • 缓存生命周期管理:单次请求后自动清除,或设置TTL过期策略,减少驻留时间泄露风险。
  • LPM随机化与分区:在最长前缀匹配中引入随机扰动,或按哈希分区缓存池,打乱命中可预测性。

5.2 系统层防御

  • 噪声注入与延迟模糊化:在缓存命中路径插入±Δt随机延迟,或对时间指标添加噪声,隐藏TTFT/顺序差异(针对时序攻击)。
  • 缓存内容混淆:使用可逆矩阵变换对KV向量加密/混淆,仅授权方可逆转。
  • 扰动检测与完整性校验:实时监控KV向量范数/哈希变化,检测异常扰动(针对腐败攻击)或引入dropout-mask随机化/注意力平滑,减轻操纵影响。
  • 参数与接口限制:禁止外部暴露use_cache、position_ids等敏感参数;结合速率限制(throttling)阻断高频探测请求。
  • 机密计算集成:利用TEE(Trusted Execution Environment)加密KV-Cache内存,防止物理/侧信道访问。

5.3 审计与合规层

  • 缓存审计器:记录每条请求的缓存命中/共享日志,绘制租户-缓存命中矩阵,便于事后追溯。
  • 异常行为监控:基于机器学习检测异常调度模式(如批量相似前缀探测),自动告警或隔离。
  • 合规框架支持:在SOC 2、ISO 27001或GDPR下,强制日志不可篡改,并定期审计共享安全性。

参考资料

https://openreview.net/pdf?id=gUj2fxQcLZ

https://www.ndss-symposium.org/wp-content/uploads/2025-1772-paper.pdf

https://huggingface.co/docs/transformers/en/kv_cache

https://arxiv.org/abs/2312.07104

https://www.arxiv.org/pdf/2510.17098

https://arxiv.org/pdf/2511.12752

https://pub.towardsai.net/lets-build-an-optimizer-for-a-gpt-model-from-scratch-in-pytorch-kv-caching-4d3f1f9516fa

题目描述

请设计⼀个函数,⽤来判断在⼀个矩阵中是否存在⼀条包含某字符串所有字符的路径。路径可以从矩阵中的任意⼀个格⼦开始,每⼀步可以在矩阵中向左,向右,向上,向下移动⼀个格⼦。如果⼀条路径经过了矩阵中的某⼀个格⼦,则该路径不能再进⼊该格⼦。 例如矩阵:

中包含⼀条字符串 " bcced " 的路径,但是矩阵中不包含 " abcb " 路径,因为字符串的第⼀个字符 b占据了矩阵中的第⼀⾏第⼆个格⼦之后,路径不能再次进⼊该格⼦。

示例1

输⼊:[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcced"
返回值:true

思路及解答

DFS回溯

主要的思路是对于每⼀个字符为起点,递归向四周拓展,然后遇到不匹配返回 false ,匹配则接着匹配直到完成,⾥⾯包含了 回溯 的思想。步骤如下:

针对每⼀个字符为起点,初始化⼀个和矩阵⼀样⼤⼩的标识数组,标识该位置是否被访问过,⼀开始默认是false 。

  1. 如果当前的字符索引已经超过了字符串⻓度,说明前⾯已经完全匹配成功,直接返回 true
  2. 如果⾏索引和列索引,不在有效的范围内,或者改位置已经标识被访问,直接返回 false
  3. 否则将当前标识置为已经访问过
  4. 如果矩阵当前位置的字符和字符串相等,那么就字符串的索引加⼀,递归判断周边的四个,只要⼀个的结果为 true ,就返回 true ,否则将该位置置为没有访问过(相当于回溯,退回上⼀步),返回 false 。矩阵当前位置的字符和字符串不相等,否则同样也是将该位置置为没有访问过(相当于回溯,退回上⼀步),返回 false 。

⽐如查找 bcced :

public class Solution {
    public boolean hasPath(char[][] matrix, String word) {
        // write code here
        if (matrix == null || word == null || word.length() == 0) {
            return false;
        }
        
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                boolean[][] flags = new boolean[matrix.length][matrix[0].length];
                
                boolean result = judge(i, j, matrix, flags, word, 0);
                if (result) {
                    return true;
                }
            }
        }
        return false;
   }
    
   public boolean judge(int i, int j, char[][] matrix, boolean[][] flags, String words, int index) {
        if (index >= words.length()) {
            return true;
        }
       
        if (i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length || flags[i][j]) {
            return false;
        }
        flags[i][j] = true;
        
        if (matrix[i][j] == words.charAt(index)) {
            if (judge(i - 1, j, matrix, flags, words, index + 1)
            || judge(i + 1, j, matrix, flags, words, index + 1)
            || judge(i, j + 1, matrix, flags, words, index + 1)
            || judge(i, j - 1, matrix, flags, words, index + 1)) {
                return true;
            } else {
                flags[i][j] = false;
                return false;
            }
        } else {
            flags[i][j] = false;
            return false;
        }
    }
}
  • 时间复杂度:O(3^k × m × n),其中k为单词长度,m、n为矩阵尺寸。每个点有3个方向可选(不能回退)
  • 空间复杂度:O(k),递归栈深度和visited数组空间

方向数组优化

使用额外的访问标记数组来记录路径状态。

public class Solution {
    public boolean exist(char[][] board, String word) {
        if (board == null || board.length == 0 || board[0].length == 0 || word == null) {
            return false;
        }
        
        int m = board.length, n = board[0].length;
        boolean[][] visited = new boolean[m][n];
        char[] words = word.toCharArray();
        
        // 遍历矩阵中的每个单元格作为起始点
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dfs(board, visited, words, i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    private boolean dfs(char[][] board, boolean[][] visited, char[] word, int i, int j, int index) {
        // 终止条件1:找到完整路径
        if (index == word.length) {
            return true;
        }
        
        // 终止条件2:越界或已访问或字符不匹配
        if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || 
            visited[i][j] || board[i][j] != word[index]) {
            return false;
        }
        
        // 标记当前单元格为已访问
        visited[i][j] = true;
        
        // 向四个方向进行DFS搜索
        boolean found = dfs(board, visited, word, i + 1, j, index + 1) ||  // 向下
                       dfs(board, visited, word, i - 1, j, index + 1) ||  // 向上
                       dfs(board, visited, word, i, j + 1, index + 1) ||  // 向右
                       dfs(board, visited, word, i, j - 1, index + 1);    // 向左
        
        // 回溯:恢复访问状态
        visited[i][j] = false;
        
        return found;
    }
}
  • 时间复杂度:O(3^k × m × n),其中k为单词长度,m、n为矩阵尺寸。每个点有3个方向可选(不能回退)
  • 空间复杂度:O(k),递归栈深度和visited数组空间

时间空间复杂度与方法一相同,但代码更易扩展(如需要八方向移动时只需修改DIRECTIONS数组)

原地标记优化(最优)

通过修改原矩阵来标记访问状态,节省空间。

public class Solution {
    public boolean exist(char[][] board, String word) {
        if (board == null || board.length == 0 || word == null || word.length() == 0) {
            return false;
        }
        
        char[] words = word.toCharArray();
        
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (dfsOptimized(board, words, i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    private boolean dfsOptimized(char[][] board, char[] word, int i, int j, int index) {
        // 边界检查和字符匹配检查
        if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || 
            board[i][j] != word[index]) {
            return false;
        }
        
        // 找到完整路径
        if (index == word.length - 1) {
            return true;
        }
        
        // 原地标记:将当前字符临时替换为特殊标记(不能出现的字符)
        char temp = board[i][j];
        board[i][j] = '#';  // 标记为已访问
        
        // 四个方向搜索
        boolean res = dfsOptimized(board, word, i + 1, j, index + 1) ||
                     dfsOptimized(board, word, i - 1, j, index + 1) ||
                     dfsOptimized(board, word, i, j + 1, index + 1) ||
                     dfsOptimized(board, word, i, j - 1, index + 1);
        
        // 回溯:恢复原始字符
        board[i][j] = temp;
        
        return res;
    }
}

关键技巧:

  1. 临时修改:将访问过的board[i][j]改为'#'(或其他不在字母表中的字符)
  2. 自动避障:后续搜索遇到'#'会因字符不匹配而自动跳过
  3. 状态恢复:回溯时恢复原始字符,确保不影响其他路径搜索

算法分析:

  • 时间复杂度:O(3^k × m × n),与前述方法相同
  • 空间复杂度:O(1),显著优化!仅使用常数空间(递归栈空间不可避免)

还在最终测试中,testflight 内测用户招募中,上架后送码,感兴趣的留下 邮箱 即可 我发送 TF 链接


全新实时记录功能

📱 + ⌚️ 双设备联动

在 iPhone 上轻点「实时」按钮,Apple Watch 即刻开始记录。横屏放置手机,实时查看:

  • ⏱️ 精准计时 — 120pt 超大字体,黑暗中一眼可见
  • ❤️ 实时心率 — 跳动的心形图标,感受每一次心跳
  • 🔥 消耗卡路里 — 见证你的努力

结束后,选择 Solo 或伴侣模式,完善记录细节。


核心亮点

🎯 Apple Watch 独立运行

无需掏出手机,抬腕即可开始。HealthKit
深度整合,自动采集心率数据,记录完成后无缝同步到 iPhone 。

🌙 沉浸式横屏体验

全黑背景 + 主题色点缀,专为私密场景设计。Liquid Glass
控制按钮,一键暂停/锁定/结束。

👤 Solo 模式

独处时光同样值得记录。专属「助兴方式」标签:小说、视频、ASMR 、幻想...

🎨 个性化主题

Rose / Ocean / Sage / Lavender / Amber —
五种主题色,动态切换应用图标,玻璃质感设计。

☁️ iCloud 同步

数据安全存储在你的私人 iCloud ,多设备无缝同步。


隐私至上

  • 所有数据仅存储在本地或你的私人 iCloud
  • 无需注册账号
  • 无广告、无追踪
  • 应用图标可自定义,不显眼





隔壁 V 有个人发了一个这种工具,但是收费的,免费复盘几个仓位。我一直是浪哥的粉丝,但是又不想为了复盘几个仓位付订阅费,之前跟浪哥学习的时候都没给浪哥付过费。索性自己根据复盘习惯写了一个工具,支持多周期多交易对对比复盘,方便看多交易对共振。顺便开源给想学习 bit 浪浪交易系统的人一个学习的渠道(学习那个页面我是喂给 notebooklm 浪哥所有复盘视频得出的策略,如果想要 notebooklm 的链接可以私信)

仓库地址 —GitHub - Xeron2000/retraq

仓库里面有示例网站,有感兴趣的佬友可以看一下,顺便求一下 star





📌 转载信息
原作者:
Xeron
转载时间:
2026/1/20 08:27:02

对于可以复制的表格已经有佬友提出过解决方案,但对于创建者禁止复制 & 创建副本 & 下载的飞书多维表格怎么办呢?如果遇到了下图的情况,本文应该可以有些帮助。
【教程】如何提取 & 整理无法复制和下载的飞书多维表格1

打开开发者工具(按 F12 或者 Ctrl+Shift+I),切换到 网络 部分,清空请求后刷新,可以看到好多条刷出来的请求。按照大小从大到小排序后最大的那些就是表格内容部分。

挑选你要下载的表格的请求点进去,在 请求URL 部分可以看到一个链接,手动访问可以看到其加密后的数据:

records 部分复制下来,按经验这是 base64 编码。而其开头 H4sI 是一个很有名的字符串指纹,其 base64 解码后的字节序列是 1f 8b 081f 8b 是 Gzip 压缩文件的文件头,08 表示其使用了 Deflate 压缩算法。

将其先 base64 解密后再解压可以得到一个标准 JSON 文件,我偷懒起见,直接选择了提取其中字符串,过滤无意义内容就能得到最后的清洗过的干净文件了。

最后的清洗部分因为需求和文件不同,也没法给出一个统一的代码,如果有相关问题可以提问,感谢各位佬友。


📌 转载信息
转载时间:
2026/1/20 08:26:51

背景:因为我有时候会在闲鱼发布接单,所以需要一个封面,但是这个封面要自己创建,就想着做个东西可以生成。

目前是只有这一个样式,可以自定义尺寸。
可自由编辑内容。
下载生成 png 图片。
免费使用。
服务部署在 CF 上。
ai 写的,调了半天才弄好。

工具地址:https://tool.fologde.com/skill-poster/

页面长这样:

实际发布闲鱼效果:


📌 转载信息
原作者:
yifang-self
转载时间:
2026/1/20 08:26:39

前言

不满意 cli 内部调用其它模型 api 交互方式,全自主开发了 CCB, 区别呢,举了例子对比,

  • 传统方式:木匠在盖一个楼,为了完成瓦匠的活临时借了瓦匠的工具或是短期雇佣了瓦匠,完全是主从关系,随从可控性能力都受到极大限制。
  • CCB 方式:木匠把瓦匠等工人叫过来,组建了一个施工队,全部都是全职工,每个都具备完整的能力。而 ccb 做到的就是它们直接相互感知和流畅通讯。 当然还包括设计了漂亮好用的界面:

具体来说:

  • [CCB] 是一个纯底层软件,只负责多模型挂载和低 token 交互,没有任何自动化和工作流设计,完全区别于其他项目,目前大家都是手动指派哪个模型负责干啥。也可以看出新的交互方式具有不可替代性
  • [CCB] 项目经过一个月发展,目前已经是 4.1.3 版本,各种系统都能稳定运行了,丝滑流畅多模型通讯。用户也有上百人,用户群 200 人左右,非常热闹
  • [CCB] 目前是以 CC 为主,协调调用 CX,OC 和 GE,后续功能包括:CX 为主调用另外三者(就要推出); 同种 cli 不限一个(尽快推出);手机远程(正在筹备)。

为什么造轮子

  • [CCA](Claude code autoflow) 可以看作是 [CCB] 的自动化插件,或是上层建筑,可以自由组合不同 cli 的角色,自动调配工作。

  • CCA 的优点完全来源于 [CCB] 可见、可控、低 token 占用特点,流畅自动化是水到渠成的事。可以自由设置和调配不同 cli 的工作(将灵活度拉满)。

  • 完全基于项目, 安装后,通过 cca add . 方式在当前目录引入 cca 控制 cca delete . 即可删除当前目录。
    目前插件内置了两种角色配置,可灵活切换:

  • 配置 1: 默认 CXGO 模式,其中 cc 负责宏观和推进任务,cx 负责微观设计并监督 oc 完成代码编写,迭代完成反馈 cc。ge 负责 git 文档维护和大型代码的 review。

  • 配置 2: Trio 模式:去掉了 oc 具体代码实现有 cx 完成。

  • 后续将开放更多角色配置,尤其是等 cx 可以切换为主控后,角色搭配将更丰富。 也支持用户根据自己工作和习惯自定义角色。

地址,欢迎使用和小星星:

对 ccb 不熟的 l 佬可以查看:


📌 转载信息
转载时间:
2026/1/20 08:25:26

LLM幻觉问题至今没有根治方案。RAG能缓解一部分,但成本高、架构复杂,而且只适用于有外部知识源的场景。而对于模型"应该知道但经常搞错"的那类问题,比如历史事件的时间线、人物履历的细节,RAG帮不上什么忙。

Chain-of-Verification(CoVe)的思路是既然模型会在生成时犯错,那就让它生成完之后再检查一遍自己的输出,把能发现的错误纠正掉,然后再给用户看。

听起来像是废话?关键在于"怎么检查"。

直接让模型审视自己刚写的东西,它大概率会坚持原有立场,这是确认偏差在作祟。CoVe的核心贡献是发现了一个绕过这个陷阱的方法:验证时必须把原始输出藏起来,让模型在"失忆"状态下重新回答事实性问题,然后用这些独立验证的答案去校对初稿。

某种意义上,这是给LLM装上了"系统2"思维:快思考先出初稿,慢思考再做验证。

工作流程:起草、规划、验证、修复

CoVe不是什么新的模型架构,它是一种提示编排模式,它把生成过程拆成四步:先让模型写初稿(这时候幻觉随便来),然后让它针对自己的初稿生成一组事实核查问题,接着独立回答这些问题,最后用验证过的事实重写原文。

Factored:真正的关键

前面说的四步里,第一、二、四步都是常规提示工程,用思维链就能搞定,而第三步才是核心。

让LLM一边看着自己写的东西一边验证,会有一个问题,这个在学术上管这叫"sycophancy",也就是说模型会顺着自己的话往下说。草稿就在上下文窗口里摆着,概率分布会被带偏,模型倾向于认同自己刚编出来的东西。

所以最简单的解决办法是把上下文剥掉。

CoVe论文里发现,回答验证问题时必须把原始草稿藏起来。举个例子:如果你问"根据这个草稿,X是不是在1998年发生的?"模型八成会点头同意自己。但如果你只问"X是什么时候发生的?"它就得老老实实从训练权重里检索答案,没有偏差可言。

隔离验证问题就是逼模型去查自己的知识库,而不是复读自己刚说过的话。

代码实现

下面是CoVe流程的Python实现,封装成一个类。注意第三步里的CRITICAL注释,那就是Factored验证的精髓。

 classChainOfVerification:  
    def__init__(self, llm):  
        self.llm=llm  

    defrun(self, query):  
        # Step 1: Baseline Generation
        # Let the model hallucinate freely here.
        draft_prompt=f"Question: {query}\nAnswer:"  
        draft=self.llm.generate(draft_prompt)  
        print(f"--- DRAFT ---\n{draft}\n")  

        # Step 2: Plan Verifications
        # Ask the model to identify what needs checking.
        plan_prompt=f"""  
        Context: {query}  
        Draft: {draft}  
        Task: Create a list of 3-5 verification questions to check the facts   
        in the draft. Output ONLY the questions.  
        """  
        plan_text=self.llm.generate(plan_prompt)  
        questions=self.parse_questions(plan_text)
        print(f"--- QUESTIONS ---\n{questions}\n")  

        # Step 3: Factored Verification (The Key Step)
        verification_results= []  
        forqinquestions:  
            # CRITICAL: Do NOT include 'draft' in this prompt context.
            # We want the raw model weights to answer this, uninfluenced by the previous lie.
            verify_prompt=f"Question: {q}\nAnswer:"  
              
            # Low temperature is crucial here for factual retrieval
            answer=self.llm.generate(verify_prompt, temperature=0)  
            verification_results.append((q, answer))  

        # Step 4: Final Synthesis
        # Now we bring it all together.
        verification_context=self.format_pairs(verification_results)  
        synthesis_prompt=f"""  
        Original Query: {query}  
        Draft Response: {draft}  
          
        Verification Data:  
        {verification_context}  
          
        Task: Rewrite the Draft Response to be fully accurate.   
        Remove any details contradicted by the Verification Data.  
        """  
        final_response=self.llm.generate(synthesis_prompt)  
          
        returnfinal_response  

    defparse_questions(self, text):  
        return [line.strip() forlineintext.split('\n') if'?'inline]  

    defformat_pairs(self, pairs):  
         return"\n".join([f"Q: {q}\nA: {a}"forq, ainpairs])

CoVe和RAG该怎么选?

每次聊到CoVe,总有人问:为什么不直接用RAG?

两者解决的是不同问题。

RAG适用于模型根本不可能知道答案的场景,比如你公司Q3的销售数据。CoVe适用于模型理论上应该知道、但可能搞混或偷懒的场景,比如按时间顺序列出纽约市历任市长。

而且研究表明两者可以混用:先用CoVe验证RAG检索回来的文档是否真的相关,再决定要不要用。代价是成本翻倍,但在医疗、法律这种高风险场景下,还是可行的。

从Vibe Coding到系统2代理

关注2026年初Agentic爆发的人,大概都听过"Ralph Wiggum"技术这个梗。

名字来自《辛普森一家》里那个喊着"我在帮忙!"却啥也没干成的角色。这技术的核心就是把LLM塞进一个while循环,让它反复尝试直到单元测试通过。暴力验证,Token消耗会爆表但最后确实能撞出正确答案。虽然听起来很好笑,实际上还挺管用。

工具增强版CoVe

opencode、OpenDevin、Windsurf这些现代自主代理已经在用"工具增强"版本的CoVe了。

它们不再只是问自己"这代码对不对",而是直接动手:先写代码,然后在沙盒里跑npm test或linter,读stderr输出,根据真实报错来修。

这就把CoVe的验证环节从概率猜测变成了确定性判断。

2026年的新拓扑:分支验证

最前沿的做法已经不是简单的线性循环了。是分支。

分支拓扑下,代理不是失败了就重试一次。它会同时提出三个修复方案,在三个隔离容器里并行跑,哪个能让构建变绿就提交哪个。

验证的消耗

这是2026年工程实践必须面对问题

Vibe Coding走系统1路线:快、便宜、但有20%左右的幻觉率,做原型够用。系统2代理反过来:慢、Token成本翻10倍、但可靠性过硬,生产环境离不开。

也就是说是拿计算资源换安心,当业务从聊天机器人升级到自主工程师,这笔成本不是能不能接受的问题,而是必须付的保险费——除非你想承担"Ralph Wiggum式"的风险,比如AI自己把数据库删了。

总结

CoVe的代价很明确:延迟。

生成初稿、生成问题、并行验证、综合重写,整套流程跑下来,Token消耗和响应时间基本翻四倍。对于实时聊天场景,这个延迟可能难以接受。但换个角度看,异步报告生成、代码审查、自动邮件起草这类任务,多等几秒换来输出可信度的大幅提升,这笔账怎么算都划算。

更值得关注的是CoVe带来的转变:过去几年,行业把大量精力投入到"如何让模型生成得更好"上——更大的参数、更多的数据、更精细的对齐。CoVe指向了另一个方向:与其追求一次生成就完美,不如承认模型会犯错,然后在架构层面把纠错机制build进去。

这和软件工程的演进路径很像。早期写代码追求一次写对,后来发现测试驱动开发、持续集成、灰度发布这些"验证优先"的实践才是规模化的正确姿势。

CoVe不会是终点,我们未来大概率会看到更多CoVe与RAG、外部工具、多模型交叉验证的组合方案。

https://avoid.overfit.cn/post/1f3da2d8396d44c6bab8bfea80405cb6

作者:Digvijay Mahapatra