DeepSeek Harness 架构总览——插件树、Profile与组合包

0 阅读1小时+

DeepSeek Harness 架构总览——插件树、Profile 与组合包

引言

DeepSeek Harness(以下简称 dsh)是一种基于 Cordis 框架的智能体运行时。它的设计哲学极为激进:产品的每一部分都是插件,包括模型适配器、工具注册表、会话日志,以及 agent loop 本身。这意味着 dsh 不存在需要打补丁的特权内核——扩展 dsh 的方式是把插件挂载到其他插件旁边,而非侵入式地修改核心代码。

本文从官方文档与源码出发,系统梳理 dsh 的架构骨架:Cordis 基础、插件树、Profile 组装机制、组合包分发格式,以及分层配置的加载顺序。

一、Cordis:dsh 的底层框架

Cordis 是 dsh 的底层依赖框架(vendored 版本 4.0.1),其运行模型可以概括为一句话:

插件向共享上下文贡献服务、类型化事件和可逆的副作用。

在 Cordis 中,"上下文"(context,即 ctx)是一个服务容器。插件通过 ctx.provide() 把服务写入 ctx(服务以稳定的 ctx.<key> 形式暴露,如 ctx.toolsctx.llmctx.sessions),通过 inject 声明对其他服务的依赖,通过类型化事件与其余插件通信。所有注册都是可逆的副作用——插件通过 ctx.effect()ctx.on() 安装注册项,插件被卸载时其 disposer 会被调用,实现干净的资源回收。

dsh 把这套机制用到了极致,以下核心能力全部以插件形式存在。

二、核心包

以下是向 Cordis 树贡献内容的部分核心包(官方架构参考文档所列):

职责ctx
core/session仅追加的 SessionEvent 日志和内存存储ctx.sessions
core/system-prompt提示词片段与工具 schema 的组装ctx.systemPrompt
core/tools作用域化的工具注册表和带把关的执行流水线ctx.tools
core/agentAgent 接口、活跃 agent 注册表和 agent/* 事件ctx.agents
core/agent-loop实现该接口的默认驱动器ctx.agentLoop
core/scope按 agent 划分作用域的注册原语库,无 ctx 键
llm/llm消息与流式词汇表,以及适配器 seamctx.llm

说明:从仓库目录结构看,llm/core/packages/ 下平级的两个顶层目录;但官方架构文档将 llm/llm 与 core 包一并列入"核心包"表,因为它同样是向 Cordis 树贡献核心服务的基础包。llm/ 目录下还有 llm-deepseek(DeepSeek 官方适配器)、llm-pi-ai(OpenAI/PI-AI 兼容适配器)、llm-replay(离线回放适配器)等具体实现。

此外,packages/core/ 下还有 agent-default-model(Agent 默认模型选择)和 agent-tool-presentation(工具的模型面向呈现)两个包,官方核心包表未列入,但它们同属 core 包族。

三、Profile:具名组装

运行中的 dsh 是一棵插件树,由启动时按序叠加的各层组合而成。

Profile 是存放在 Harness home 目录(默认为 ~/.dsh,可通过 $DSH_HOME 环境变量覆盖)中的具名组装(named assembly)。它:

  • 列出自己叠放的组合包(bundle)
  • 存放自己安装的树外插件(profile 目录下的 node_modules
  • 保存用户自己的 cordis.patch.yml

webheadless 作为模板随发行版交付。

Profile 本身不保存具体业务配置值,仅定义能力叠加层的清单;它不是单个 YAML 文件,而是独立子目录,内部通过 package.json 声明 bundle 列表。

Harness home 目录的结构示意如下:

~/.dsh/
├── cordis.patch.yml            # home 全局级别补丁
├── profiles/
│   ├── web/                    # 名为 "web" 的 profile(目录)
│   │   ├── package.json        # 声明 bundle 叠加列表
│   │   ├── cordis.patch.yml    # 当前 profile 专属增量补丁
│   │   └── pnpm-workspace.yaml
│   ├── headless/               # 名为 "headless" 的 profile(目录)
│   │   ├── package.json
│   │   └── cordis.patch.yml
│   └── node_modules/           # launcher 维护的扁平模块回退目录
├── .agent-presets/             # 用户自定义 agent preset(详见第九节)
└── ...

profile 的 bundle 清单定义在对应目录下的 package.json,示例:

// profiles/web/package.json
{
  "name": "dsh-profile-web",
  "private": true,
  "dependencies": {},
  "dsh": {
    "profile": {
      "bundles": [
        "@deepseek-ai/dsh-base",
        "@deepseek-ai/dsh-web-app"
      ]
    }
  }
}

dsh 内置了两个 profile 模板:web@deepseek-ai/dsh-base + @deepseek-ai/dsh-web-app)和 headless@deepseek-ai/dsh-base + @deepseek-ai/dsh-headless)。首次使用对应名称时会自动初始化。

四、组合包:Cordis 配置项的分发格式

组合包(bundle) 是 Cordis 配置项及其挂载代码的分发格式,因此它插入的内容始终可被其上各层 patch。一个组合包包含:

  1. 一份 Cordis 配置补丁(cordis.patch.yml,声明激活哪些插件、注入哪些服务)
  2. 插件的挂载代码(实际 Service 实现)

Profile 和 bundle 都在各自的 package.json 中通过 dsh 字段声明自己:dsh.profile 列出一个 profile 的组合包,dsh.bundle 指向一个组合包的 patch 文件。

Bundle 的 package.json 声明示例:

{
  "name": "@deepseek-ai/dsh-base",
  "dsh": {
    "bundle": {
      "patch": "./cordis.patch.yml"
    }
  }
}

dsh-base:每个 profile 的第一层

dsh-base 是所有 profile 的基础层,涵盖:模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥测。

从源码看,dsh-basesrc/index.ts 仅有 export {},本身不包含运行时代码,全部能力通过其 cordis.patch.yml 挂载其他插件实现。

在 profile 的 bundle 列表中,dsh-base 始终排在第一位,后续包在其之上叠加能力:

  • dsh-web-app:在 dsh-base 之上增加浏览器应用(Web UI)
  • dsh-headless:在 dsh-base 之上增加一次性运行器,且完全不带服务器

五、分层配置的加载顺序

各层按此顺序应用在空条目列表之上:

  1. 先按 profile 列出的顺序应用每个组合包的 cordis.patch.yml
  2. 然后应用 profile 目录内的 cordis.patch.yml(Profile 层补丁,支持热重载)
  3. 然后应用 home 根目录的 cordis.patch.yml(Home 全局层补丁)
  4. 最后应用命令行传入的任意 --patch overlay(命令行补丁,优先级最高)

命令行 --patch 具备最高优先级,可以覆盖前面所有层级。

重要说明:cordis.patch.yml 属于增量补丁,文件格式为顶层 YAML 数组,每个元素是一个 loader patch entry。一条 patch 按 id 定位某个条目并替换其整个 config(不做对象深度合并),或插入新条目。只需要填写需要改动的配置片段,不需要复制完整配置。

公式表达:

最终配置 = dsh-base
          ∘ bundle_2
          ∘ bundle_3
          ∘ profile/cordis.patch.yml
          ∘ home/cordis.patch.yml
          ∘ --patch overlay

每一层都是对前一层的增量补丁,而非整体替换。所有层的补丁会被扁平化为一个数组,通过 applyEntryPatches 单次应用。

六、核心命令:dump-config

要查看一个 profile 合并完成后的完整配置树,使用 --dump-config 命令:

dsh --profile web --dump-config

它打印出的任何条目,都可以由你自己的 patch 替换。该命令仅输出合并后的配置树,不会启动运行时,是排查插件未生效、配置叠加异常的首选手段。

配套调试命令:

  • --dump-default-config:仅输出 bundle 原始配置,不加载 profile 的用户 patch 层userLayer: false),用于对比原始 bundle 配置与打补丁后的差异。

实际输出格式为顶层 YAML 数组,每组来源行之前用 # == <来源> 注释分隔,示例:

# == cordis.yml (dsh-base)
- id: llm-deepseek
  name: "@deepseek-ai/dsh-llm-deepseek"
  config:
    apiKeyEnv: DEEPSEEK_API_KEY
    baseURL: https://api.deepseek.com
- id: tools
  name: "@deepseek-ai/dsh-tools"
  config:
    presentation:
      mode: grouped
# == cordis.yml (dsh-base), patched by cordis.patch.yml
- id: agent-loop
  name: "@deepseek-ai/dsh-agent-loop"
  config:
    maxParallelToolCalls: 4

七、组合包开发实践

扩展 dsh 的正确范式:不修改官方 dsh-base,而是开发自定义组合包进行叠加。

最小自定义 bundle 的目录结构:

my-bundle/
├── package.json
├── cordis.patch.yml    # 声明插件与基础配置(顶层数组格式)
└── src/
    └── index.ts         # 插件实现源码
// src/index.ts
import type { Context } from '@deepseek-ai/cordis';

export function apply(ctx: Context) {
  // ctx.effect 标准范式,框架自动托管 disposer 生命周期
  ctx.effect(() => {
    const dispose = ctx.provide('myService', {
      greet(name: string) {
        return `Hello, ${name}!`;
      },
    });
    // 返回清理逻辑,插件卸载时自动执行
    return () => dispose();
  });
}
# cordis.patch.yml —— 顶层 YAML 数组,每个元素是一个 loader entry
- name: ./src/index.ts
  config:
    myService:
      enabled: true

在 profile 的 package.json 引用该自定义 bundle:

// profiles/custom/package.json
{
  "name": "dsh-profile-custom",
  "private": true,
  "dependencies": {},
  "dsh": {
    "profile": {
      "bundles": [
        "@deepseek-ai/dsh-base",
        "my-bundle"
      ]
    }
  }
}

启动运行时,指定目标 profile:

dsh --profile custom

八、不存在特权内核的意义

传统框架通常存在一个不可修改的"核心模块",扩展只能在预设扩展点完成。dsh 的设计完全不同:agent loop 本身就是普通插件(位于 packages/core/agent-loop/,通过 ctx.agentLoop 暴露服务)。

由此带来能力:

  • 可以整体替换 agent loop 的轮次驱动逻辑
  • 可以替换会话日志的存储后端
  • 可以替换工具执行流水线的任意环节
  • 可以替换 LLM 适配器,对接任意第三方模型服务

替换方式统一:开发新插件,挂载或替代原有插件,通过 cordis.patch.ymlid 定位并替换其 config,或插入新条目。

九、Profile 与 Agent Preset 的区分

dsh 中存在两个容易混淆的"组合"概念,分属不同层级:

维度ProfileAgent Preset
层级Launcher 级(进程启动时选定)Agent 会话级(每个 agent 可独立选择)
位置~/.dsh/profiles/<name>/分两类:① 内置 preset 随 dsh 包分发,位于 node_modules/@deepseek-ai/dsh/config/agent-presets/<name>/(只读);② 用户自定义 preset 位于 ~/.dsh/.agent-presets/<name>/
声明package.jsondsh.profile.bundlespreset 目录内含 agent.cordis.yml(组合配置,顶层插件行数组)、preset.yml(显示元数据,可选)及可选的 skills/ 子目录
作用决定整个进程加载哪些 bundle 层决定单个 agent 面向模型的插件集合(工具、prompt 段、skill 目录)
切换方式dsh --profile <name>(重启生效)会话创建时指定,运行中可 recompose() 热切换

dsh 内置了 4 个 agent preset:standardcodeminimalcordis,均位于 @deepseek-ai/dsh/config/agent-presets/ 下。以 cordis preset 为例,其目录包含 skills/ 文件夹、agent.cordis.yml 组合配置文件和 preset.yml 元数据文件(Windows 资源管理器默认隐藏 .yml 扩展名时会显示为 agent.cordispreset)。

官方文档"新行为的归属位置"表明确指出:让某个会话拥有不同的能力集合 → 组装一个 agent preset;其中的服务行需要 isolate realm

简单理解:Profile 选"底座装哪些能力包",Preset 选"这个 agent 用哪套面向模型的工具组合"。二者正交,一个 Profile 下可以有多个 Preset 供不同 agent 选用。


dsh 的架构可以浓缩为三层抽象:

  1. Cordis 框架:插件 → 上下文 → 服务/事件/可逆副作用
  2. 组合包(Bundle):Cordis 配置补丁 + 插件实现代码,用于分发能力单元
  3. Profile:具名组装配方,声明叠加哪些 bundle,用于本地启动选择能力集合;Patch 承担本地调参修改工作。

简单区分三者分工:

  • Bundle:打包分发能力;
  • Profile:挑选组合能力,用于启动;
  • Patch:本地做增量调参覆盖。

dsh --profile <name> --dump-config 可随时审查合并完成的最终配置,掌握这套分层装配机制,就掌握了 dsh 的全部扩展入口。

下一篇我们将深入 Cordis 框架本身,剖析它的五个核心概念(插件、上下文、依赖注入、类型化事件、可逆副作用)与四种事件分发模式(emit、waterfall、parallel、serial)。

第17篇:事件钩子 —— 让 Agent 事件驱动一切

Hermes 提供四大 Hook 系统,覆盖从 CLI 到 Gateway 的全场景,让你在关键事件上插入自定义逻辑。

四大 Hook 系统

系统注册方式运行环境用途
Gateway HooksHOOK.yaml + handler.pyGateway only日志、告警、webhook
Plugin Hooksctx.register_hook()CLI + Gateway工具拦截、guardrails
Shell Hookshooks: 块在 config.yamlCLI + Gateway脚本通知
Outbound Webhookshooks.outbound: 在 config.yamlCLI + Gateway推送事件到 HTTP 端点

Gateway Event Hooks

~/.hermes/hooks/
└── my-hook/
    ├── HOOK.yaml      # 声明监听事件
    └── handler.py     # Python 处理函数

HOOK.yaml:

name: my-hook
description: Log all agent activity
events:
  - agent:start
  - agent:end
  - agent:step

可用事件:

事件触发时机
gateway:startupGateway 进程启动
session:start新消息会话创建
session:end会话结束
agent:startAgent 开始处理消息
agent:step工具调用循环每次迭代
agent:endAgent 完成处理
reaction:added表情反应添加
command:*任何斜杠命令(通配符)

Plugin Hooks(最强大)

def register(ctx):
    ctx.register_hook("pre_tool_call", before_any_tool)     # 工具执行前,可否决
    ctx.register_hook("post_tool_call", after_any_tool)      # 工具执行后
    ctx.register_hook("pre_llm_call", inject_memory)         # 每轮 turn 前,可注入上下文
    ctx.register_hook("pre_verify", guard_code_change)       # 代码验证前,可保持继续
    ctx.register_hook("transform_tool_result", clean_output) # 替换工具返回
    ctx.register_hook("transform_llm_output", final_touch)   # 替换最终响应

pre_tool_call 示例(否决调用):

def my_callback(tool_name, args, task_id, **kwargs):
    if tool_name == "terminal" and "rm -rf" in args.get("command", ""):
        return {"action": "block", "message": "Dangerous command blocked"}

pre_llm_call 示例(注入上下文):

def recall_context(session_id, user_message, is_first_turn, **kwargs):
    if is_first_turn:
        return {"context": "Recalled: User prefers dark mode"}

注入到用户消息(非系统提示词),保持 prompt cache 稳定。

Shell Hooks(零代码)

hooks:
  - event: post_tool_call
    command: "notify-send 'Tool ran: {tool_name}'"
    when:
      tools: [terminal, patch, write_file]

Q&A

Q1: pre_llm_call 注入的上下文为什么放到用户消息而非系统提示词? A1: 为了保持 prompt cache 稳定。系统提示词通常被缓存,频繁修改会导致缓存失效,增加成本和延迟。注入到用户消息不影响缓存。

Q2: 如何在 Hook 中否决一个工具调用? A2: 在 pre_tool_call 回调中返回 {"action": "block", "message": "原因"},该工具调用不会执行,Agent 会收到拒绝消息。

Q3: pre_verify 钩子的 continue 指令有次数限制吗? A3: 有。连续 continue 指令上限由 agent.max_verify_nudges(默认 3)控制。到达上限后 Agent 正常结束。可用 attempt 参数做 one-shot 守卫。


@DeRonin_ 的自学习天气交易机器人48 小时 100100 → 216 翻倍实战

第 17 篇 · Trading & Markets

@DeRonin_ 的自学习天气交易机器人 48 小时 100100 → 216 翻倍实战

每 60 分钟扫描天气市场,3 源交叉验证温度区间定价偏差,买入低估 bucket 翻转获利,收盘后自动复盘写策略笔记——Hermes 自进化技能体系在量化交易场景的完整落地。

作者 @DeRonin_ 日期 2026-04-17 来源 X · Twitter 分类 Trading & Markets 验证级别 部分验证

PART 1

案例背景 + 溯源 + 整体架构

开篇心智钩子

大多数人搭建 AI 交易机器人的方式是:写一个 Python 脚本,调一个交易所 API,跑一个固定策略,然后祈祷市场不变。市场变了?手动改代码。亏了?手动复盘。下一个周期还是同样的错误。

真正的痛点是:交易策略不是代码问题,是认知迭代问题。你需要一个能自己复盘、自己写策略笔记、下次自动调整的 Agent——不是执行命令的机器人,而是能进化的交易员。

@DeRonin_ 用 Hermes Agent 搭了一套自学习天气交易系统:每 60 分钟扫描天气预测市场,对比 3 个天气预报源找到定价偏差,买入被低估的温度区间合约,翻转获利,然后自动复盘写策略笔记。48 小时,100100 变 216。

案例基础信息

溯源信息

作者 @DeRonin_

发布日期 2026-04-17

来源渠道 X · Twitter

原始帖子 x.com/DeRonin_/st…

官方收录 hermes-agent.nousresearch.com/docs/user-s… (Trading & Markets 分类)

GitHub 未找到公开仓库(作者未开源)

验证级别 部分验证 — 有官方收录原文,无公开源码

可验证性声明

本案例来源于 X/Twitter 帖子,被 Hermes Agent 官方用户故事页面收录。原作者未公开 GitHub 仓库或代码。本文所有技术实现基于原帖描述 + Hermes 官方文档机制进行逆向重建,标注为「重建实现」的代码片段为基于 Hermes 框架能力的合理推断,非作者原始代码

原始引述

"I turned 100into100 into 216 in less than 48 hours with a self-learning weather trading bot. Hermes scans weather markets every 60 mins, compares 3 forecast sources per location, buys undervalued temperature buckets and flips for profit. Reviews what worked, writes its own strategy notes, adjusts next time."

— @DeRonin_ · 2026-04-17 · X/Twitter

需求梳理

@DeRonin_ 要解决的核心业务问题:

1 天气市场套利

温度预测市场(如 Kalshi 等)存在定价效率低下的问题——市场定价可能偏离多源天气预报的共识值,需要系统性扫描发现偏差

2 高频自动化扫描

天气市场每分钟都在变化,人工盯盘不现实。需要每 60 分钟自动扫描所有目标地点的市场报价

3 多源交叉验证

单一天气预报源可能失准,需要对比至少 3 个独立预报源取共识,以识别真正的定价偏差而非预报噪音

4 策略自进化

交易策略需要随市场变化迭代。每次交易后自动复盘、写策略笔记、下次自动调整——这是 Hermes 自进化技能体系的核心能力

五层完整架构图

顶层 · 主调度

幕僚总长 Profile — 全局策略调度 · 每 60min 触发扫描循环 · 收盘后触发复盘

↓ cron 调度 + 任务分发

中层 · 业务子Agent

扫描子Agent — 拉取市场报价

预报子Agent — 对比3源天气数据

交易子Agent — 执行买卖

复盘子Agent — 写策略笔记

↓ 工具调用

底层 · 工具层

web_search — 天气预报抓取

web_extract — 市场报价提取

execute_code — 交易API调用

自研Skill — 温度区间估值

↓ 持久化

存储层

memory.md — 交易事件记忆

skills/ — 策略笔记技能文件

state.db — 持仓状态

user.md — 偏好记录

↓ 运行环境

部署运维层

VPS / 本地主机 24/7 运行

hermes cron — 定时调度

模型 API (OpenRouter / Anthropic)

各层权责说明

顶层(主调度):幕僚总长 Profile 负责全局策略编排。通过 hermes cron 设置每 60 分钟的扫描循环,每次循环依次调用扫描 → 预报 → 交易 → 复盘四个阶段。权限管控:主调度可以调用所有子 Agent,子 Agent 不能反向调用主调度。

中层(业务子 Agent):按功能隔离为 4 个子 Profile。扫描子 Agent 只负责拉取市场报价,不接触资金操作;交易子 Agent 才有权限调用交易所 API。数据隔离:每个子 Agent 有独立的会话记忆,复盘结果写入共享的 skills/ 目录供下次调用。

底层(工具层):使用 Hermes 原生 web_search / web_extract 获取天气和市场数据,自研 Skill 负责温度区间估值逻辑,execute_code 执行交易 API 调用。

存储层:交易事件写入 memory.md(长期记忆),策略迭代写入 skills/(技能记忆),持仓状态写入 state.db。记忆跨 Agent 共享边界:子 Agent 可读主调度的 memory.md,但各自只能写自己的会话记忆。

部署运维层:VPS 或本地主机 24/7 运行,Hermes cron 驱动定时任务,模型通过 OpenRouter 或 Anthropic API 调用,故障时降级到备用模型。

PART 2

分步落地教程 + 全 Hermes 命令清单

从零搭建分步流程

步骤 1:环境准备
# 安装 Hermes Agent
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
# 配置模型 API(推荐 OpenRouter 多模型路由)
export OPENROUTER_API_KEY="sk-or-v1-xxxxx"
export ANTHROPIC_API_KEY="sk-ant-xxxxx" # 备用模型
# 初始化 Hermes
hermes init
# 验证安装
hermes --version
步骤 2:创建主调度 Profile(幕僚总长)
# 创建交易系统主 Profile
hermes profile create weather-trader
# 编辑 SOUL.md — 定义交易员人格
cat > ~/.hermes/profiles/weather-trader/SOUL.md <<'EOF'
# Weather Trading Agent
## Identity
You are a disciplined weather market trading agent. You scan temperature
prediction markets every 60 minutes, compare 3 forecast sources per location,
and execute trades only when clear mispricing is detected.
## Rules
- Never risk more than 20% of current balance on a single trade
- Always compare at least 3 independent forecast sources
- Log every trade decision with reasoning
- After each trading session, write strategy notes to skills/
- If all 3 sources disagree by >5°F, skip that location (high uncertainty)
## Communication
Be concise. Report in format: LOCATION | BUCKET | ENTRY | EXIT | P&L
EOF
步骤 3:配置天气数据获取能力
# 配置 web 搜索后端(用于抓取天气预报)
# 方式一:使用内置 DDGS(免费,无需 API Key)
echo 'WEB_SEARCH_BACKEND=ddgs' >> ~/.hermes/.env
# 方式二:使用 SearXNG 自建(隐私优先,参考第19篇)
# SEARXNG_URL=http://localhost:8888
# 配置网页提取后端
echo 'EXTRACT_BACKEND=firecrawl' >> ~/.hermes/.env
echo 'FIRECRAWL_API_KEY=fc-xxxxx' >> ~/.hermes/.env
# 验证搜索能力
hermes chat -p weather-trader "搜索纽约明天的天气预报,对比3个来源"
步骤 4:创建温度区间估值 Skill
# 创建自定义技能目录
mkdir -p ~/.hermes/skills/weather-trading
cat > ~/.hermes/skills/weather-trading/SKILL.md <<'EOF'
---
name: weather-trading
description: Scan weather markets and identify mispriced temperature buckets
version: 1.0.0
metadata:
hermes:
tags: [trading, weather, arbitrage]
category: trading
requires_toolsets: [web]
---
# Weather Trading Strategy
## When to Use
Triggered every 60 minutes by cron, or when user asks to scan markets.
## Procedure
1. **Fetch Market Data**: Use web_extract to pull current temperature bucket
prices from the weather market platform for all target locations.
2. **Fetch Forecasts**: For each location, search 3 independent weather
sources (e.g., NWS, Weather.com, AccuWeather) via web_search.
3. **Compute Consensus**: Calculate the median forecast temperature.
4. **Identify Mispricing**: Compare consensus temperature against market
bucket boundaries. A bucket is "undervalued" if its price < 30% but
the consensus falls within its range.
5. **Execute Trade**: Buy undervalued bucket contracts.
6. **Set Exit**: Place sell order at 2x entry price or before market close.
## Pitfalls
- If forecast sources disagree by >5°F, skip  uncertainty too high
- Weather markets close at specific times; always check market hours
- Don't overtrade  max 5 positions per scan cycle
## Verification
After each trade, log to memory: location, bucket, entry price, forecast
consensus, reasoning. Review in post-session analysis.
EOF
# 验证技能已加载
hermes skills list
步骤 5:配置 60 分钟 Cron 扫描循环
# 创建自然语言 Cron 任务
hermes cron create \
--profile weather-trader \
--schedule "every 60 minutes" \
--prompt "扫描所有目标城市的天气市场报价,对比3个天气预报源,找出被低估的温度区间,执行交易。记录每笔交易的决策逻辑。" \
--name market-scan
# 创建收盘复盘 Cron(每天市场收盘后)
hermes cron create \
--profile weather-trader \
--schedule "at 23:00 every day" \
--prompt "复盘今天的所有交易。哪些赚了?哪些亏了?原因是什么?把学到的策略写入技能笔记,下次扫描时参考。" \
--name daily-review
# 查看已配置的定时任务
hermes cron list
步骤 6:配置交易执行环境
# 创建交易 API 调用脚本(以 Kalshi 为例)
mkdir -p ~/.hermes/skills/weather-trading/scripts
cat > ~/.hermes/skills/weather-trading/scripts/trade.py <<'PYEOF'
import os, json, requests
# Kalshi API 配置(替换为你的实际交易平台)
KALSHI_API_URL = os.environ.get("KALSHI_API_URL", "https://trading-api.kalshi.com")
KALSHI_TOKEN = os.environ.get("KALSHI_TOKEN", "")
def place_order(ticker, side, price, quantity):
"""下单:买入或卖出温度区间合约"""
headers = {"Authorization": f"Bearer {KALSHI_TOKEN}"}
payload = {
"ticker": ticker, # 如 "HIGHNY-26AP17C55" (纽约4月17日最高温55°F以上)
"side": side, # "yes" 或 "no"
"price": price, # 限价(0-1之间,表示概率)
"quantity": quantity # 合约数量
}
resp = requests.post(
f"{KALSHI_API_URL}/portfolio/orders",
headers=headers, json=payload, timeout=10
)
return resp.json()
def get_positions():
"""查询当前持仓"""
headers = {"Authorization": f"Bearer {KALSHI_TOKEN}"}
resp = requests.get(
f"{KALSHI_API_URL}/portfolio/positions",
headers=headers, timeout=10
)
return resp.json()
if __name__ == "__main__":
# Hermes 的 execute_code 工具会调用此脚本
print(json.dumps(get_positions(), indent=2))
PYEOF
# 配置交易 API 密钥(安全方式)
hermes config set KALSHI_TOKEN "your-api-token" --secret
步骤 7:配置模型降级路由
# 编辑 config.yaml 配置多模型降级
cat >> ~/.hermes/config.yaml <<'EOF'
models:
primary:
provider: openrouter
model: anthropic/claude-sonnet-4-20250514
fallback:
- provider: openrouter
model: openai/gpt-4o
- provider: anthropic
model: claude-3-5-sonnet-20241022
EOF
步骤 8:启动并验证
# 手动触发一次扫描测试
hermes chat -p weather-trader \
"扫描纽约天气市场,对比3个预报源,找出被低估的温度区间"
# 查看 cron 运行日志
hermes cron logs market-scan --tail 20
# 查看策略笔记(自进化结果)
ls ~/.hermes/skills/weather-trading/
cat ~/.hermes/skills/weather-trading/SKILL.md | head -50

全 Hermes 命令清单

指令完整写法功能作用适用业务场景关键入参说明
hermes init初始化 Hermes 环境首次安装后配置无必填参数
hermes profile create 创建新的 Agent Profile为交易系统创建独立人格name: Profile 名称
hermes chat -p "prompt"使用指定 Profile 发起对话手动触发交易扫描或复盘-p: 指定 Profile; prompt: 自然语言指令
hermes cron create --schedule "..." --prompt "..."创建自然语言定时任务每 60min 市场扫描 / 每日收盘复盘--schedule: 自然语言时间; --prompt: 任务指令; --name: 任务名; --profile: 绑定 Profile
hermes cron list列出所有定时任务查看已配置的扫描和复盘任务
hermes cron logs --tail N查看定时任务运行日志检查扫描结果和交易执行情况name: 任务名; --tail: 显示最近 N 行
hermes cron pause 暂停定时任务市场休市或维护时暂停扫描name: 任务名
hermes cron resume 恢复暂停的定时任务市场重新开市后恢复扫描name: 任务名
hermes skills list列出所有已安装技能验证温度区间估值 Skill 是否加载
hermes skills install 从 URL 安装技能安装社区共享的交易策略技能url: 技能仓库地址
hermes memory show --profile 查看 Profile 的记忆内容检查交易历史记忆和策略笔记--profile: 指定 Profile
hermes memory prune --profile 修剪记忆文件清理过期的交易记录,保留核心策略--profile: 指定 Profile
hermes config set --secret设置配置项(含加密存储)存储交易 API 密钥key: 配置键; val: 值; --secret: 加密存储
hermes mcp add --command "..."添加 MCP 服务器对接外部交易 API 或数据源name: 服务器名; --command: 启动命令
hermes mcp list列出已配置的 MCP 服务器检查交易 API MCP 是否连接
/weather-trading调用自研天气交易技能(斜杠命令)在对话中手动触发完整交易流程自研技能自定义命令
/learn让 Agent 从对话中学习并生成 Skill复盘后将新策略固化为技能文件自研/内置技能命令

完整配置模板:Cron 定时任务

# ~/.hermes/cron/tasks.yaml — 天气交易系统定时任务配置
tasks:
# 每 60 分钟市场扫描
- name: market-scan
schedule: "*/60 * * * *"
profile: weather-trader
prompt: |
扫描以下城市的天气市场报价:纽约、芝加哥、洛杉矶、迈阿密。
对每个城市,对比 NWS、Weather.com、AccuWeather 三个预报源。
找出被低估的温度区间(市场价格 < 0.30 但共识温度落在该区间内)。
对每个低估区间,买入 5 份合约。设置卖出价为买入价的 2 倍。
记录每笔交易的决策逻辑到 memory。"
# 每日收盘复盘 + 策略迭代
- name: daily-review
schedule: "0 23 * * *"
profile: weather-trader
prompt: |
回顾今天的所有交易记录。
分析:哪些交易盈利?哪些亏损?原因是什么?
将有效策略更新到 weather-trading 技能的 SKILL.md。
将无效策略标记为 Pitfalls。
明天扫描时参考更新后的策略笔记。"
# 每周记忆修剪(防止 memory.md 膨胀)
- name: weekly-memory-prune
schedule: "0 3 * * 0"
profile: weather-trader
prompt: |
审查 memory.md 中的交易记录。
保留最近 7 天的完整记录和所有策略性洞察。
将超过 7 天的普通交易记录压缩为摘要。
删除重复或无关的条目。"

完整配置模板:网关 config.yaml

# ~/.hermes/config.yaml — 天气交易系统全局配置
# 模型配置(主模型 + 降级链)
models:
primary:
provider: openrouter
model: anthropic/claude-sonnet-4-20250514
max_tokens: 4096
fallback:
- provider: openrouter
model: openai/gpt-4o
- provider: anthropic
model: claude-3-5-sonnet-20241022
# Web 搜索配置(用于天气预报抓取)
web:
search_backend: ddgs # 免费,无需 API Key
extract_backend: firecrawl # 网页内容提取
# 记忆配置
memory:
max_memory_entries: 500 # memory.md 最大条目数
auto_summarize: true # 自动摘要旧记忆
summary_threshold: 50 # 超过 50 条触发摘要
# 技能配置
skills:
auto_learn: true # 允许 Agent 自动创建技能
external_dirs:
- ~/.hermes/skills/weather-trading
# 安全配置
security:
require_confirmation_for: # 以下操作需要人工确认
- file_delete
- shell_exec # 交易 API 调用需确认
sandbox: true # 代码执行沙箱

PART 3

源码深度解读 + 部署加固 + 复刻避坑总结

核心代码 ①:60 分钟扫描循环 + 3 源天气预报交叉验证

# ~/.hermes/skills/weather-trading/scripts/scan_cycle.py
# 重建实现 — 基于 @DeRonin_ 描述的 "scans weather markets every 60 mins,
# compares 3 forecast sources per location" 机制
import json, statistics
from datetime import datetime
# 目标城市列表(可自行扩展)
TARGET_CITIES = [
{"name": "New York", "market": "HIGHNY"},
{"name": "Chicago", "market": "HIGHCHI"},
{"name": "Los Angeles", "market": "HIGHLAX"},
{"name": "Miami", "market": "HIGHMIA"},
]
# 3 个独立预报源(Hermes web_search 依次调用)
FORECAST_SOURCES = ["NWS", "Weather.com", "AccuWeather"]
def scan_cycle(agent):
"""主扫描循环 — 每 60 分钟由 cron 触发"""
results = []
for city in TARGET_CITIES:
# 阶段 1:获取 3 个预报源的预测温度
forecasts = []
for source in FORECAST_SOURCES:
# Hermes 的 web_search 工具搜索天气预报
query = f"{city['name']} tomorrow high temperature {source}"
raw = agent.web_search(query)
temp = extract_temperature(raw, source)
forecasts.append(temp)
# 阶段 2:计算共识温度(取中位数,抗异常值)
consensus = statistics.median(forecasts)
# 阶段 3:检查预报分歧度
spread = max(forecasts) - min(forecasts)
if spread > 5: # 分歧 > 5°F,跳过(高不确定性)
results.append({
"city": city["name"],
"action": "SKIP",
"reason": f"Forecast spread {spread}°F > 5°F"
})
continue
# 阶段 4:获取市场报价(温度区间合约)
market_data = agent.web_extract(
f"https://kalshi.com/markets/{city['market']}"
)
buckets = parse_buckets(market_data)
# 阶段 5:识别低估区间
for bucket in buckets:
# 区间包含共识温度 + 市场价 < 0.30 = 低估
if (bucket["low"] <= consensus <= bucket["high"]
and bucket["price"] < 0.30):
# 阶段 6:执行交易
order = place_order(
ticker=bucket["ticker"],
side="yes",
price=bucket["price"],
quantity=5
)
results.append({
"city": city["name"],
"action": "BUY",
"bucket": f"{bucket['low']}-{bucket['high']}°F",
"entry_price": bucket["price"],
"consensus": consensus,
"forecasts": forecasts,
"order_id": order.get("id")
})
# 阶段 7:写入记忆
agent.memory.write(
f"[{datetime.now()}] Scan cycle complete. "
f"Actions: {json.dumps(results)}"
)
return results

核心代码 ②:自学习策略复盘 + 技能笔记自动写入

# ~/.hermes/skills/weather-trading/scripts/review_and_learn.py
# 重建实现 — 基于 @DeRonin_ 描述的 "Reviews what worked, writes its own
# strategy notes, adjusts next time" 机制
import os
from datetime import datetime, timedelta
SKILL_PATH = os.path.expanduser("~/.hermes/skills/weather-trading/SKILL.md")
def daily_review(agent):
"""每日收盘复盘 + 策略迭代"""
# 步骤 1:从 memory 中提取今天的交易记录
today = datetime.now().strftime("%Y-%m-%d")
today_trades = agent.memory.search(f"date:{today} action:BUY")
if not today_trades:
agent.say("今天没有交易记录,跳过复盘。")
return
# 步骤 2:获取每笔交易的盈亏结果
wins, losses = [], []
for trade in today_trades:
pnl = check_position_pnl(trade["order_id"])
trade["pnl"] = pnl
if pnl > 0:
wins.append(trade)
else:
losses.append(trade)
# 步骤 3:让 LLM 分析盈亏原因
analysis = agent.think(f"""
分析今天的交易:
盈利交易: {json.dumps(wins)}
亏损交易: {json.dumps(losses)}
请分析:
1. 盈利的交易有什么共同模式?(城市、预报分歧度、入场价区间)
2. 亏损的交易有什么共同模式?
3. 明天应该调整什么策略?
用 3-5 条简洁的策略笔记总结。
""")
# 步骤 4:将策略笔记写入 SKILL.md(自进化核心)
with open(SKILL_PATH, "r") as f:
skill_content = f.read()
# 在 SKILL.md 的 Strategy Notes 部分追加新笔记
notes_section = f"""
## Strategy Notes — {today}
### Wins: {len(wins)} | Losses: {len(losses)}
{analysis}
---
"""
# 插入到 ## Pitfalls 之前
if "## Pitfalls" in skill_content:
skill_content = skill_content.replace(
"## Pitfalls",
notes_section + "\n## Pitfalls"
)
else:
skill_content += notes_section
with open(SKILL_PATH, "w") as f:
f.write(skill_content)
agent.say(f"复盘完成。策略笔记已更新到 SKILL.md。")
agent.say(f"盈利: {len(wins)} 笔, 亏损: {len(losses)} 笔")
agent.say(f"策略分析:\n{analysis}")

自进化机制解析

这段代码体现了 Hermes 自进化技能体系的核心:Agent 不仅是执行者,还是策略的作者。每次复盘后,LLM 分析盈亏模式,生成策略笔记,直接追加到 SKILL.md 文件中。下一次 hermes cron 触发扫描时,Agent 会自动加载更新后的 SKILL.md,参考上次学到的策略执行交易——这就是 @DeRonin_ 说的 "writes its own strategy notes, adjusts next time"。

核心代码 ③:多模型故障降级路由

# ~/.hermes/skills/weather-trading/scripts/model_fallback.py
# 重建实现 — 确保交易系统在模型 API 宕机时不中断
import time, logging
logger = logging.getLogger("weather-trader")
# 降级链:主模型 → 备用1 → 备用2
MODEL_CHAIN = [
{"provider": "openrouter", "model": "anthropic/claude-sonnet-4-20250514"},
{"provider": "openrouter", "model": "openai/gpt-4o"},
{< class="code-string">"provider": "anthropic", "model": "claude-3-5-sonnet-20241022"},
]
def call_with_fallback(prompt, profile="weather-trader"):
"""带故障降级的模型调用"""
for i, model_cfg in enumerate(MODEL_CHAIN):
try:
# 尝试用当前模型执行
result = hermes_chat(
prompt=prompt,
profile=profile,
model=model_cfg["model"],
provider=model_cfg["provider"],
timeout=30
)
if i > 0:
logger.warning(
f"主模型不可用,降级到 {model_cfg['model']}"
)
return result
except Exception as e:
logger.error(f"模型 {model_cfg['model']} 失败: {e}")
if i < len(MODEL_CHAIN) - 1:
logger.info(f"尝试下一个模型...")
time.sleep(2) # 短暂等待后重试
else:
logger.critical("所有模型均不可用!跳过本轮扫描。")
# 写入告警记忆
return {"error": "ALL_MODELS_DOWN"}

核心代码 ④:每周记忆审计脚本

# ~/.hermes/skills/weather-trading/scripts/memory_audit.py
# 每周日凌晨 3:00 由 cron 触发
import os, re
from datetime import datetime, timedelta
MEMORY_FILE = os.path.expanduser("~/.hermes/memory.md")
def audit_memory():
"""每周记忆修剪 — 保留策略洞察,压缩普通记录"""
with open(MEMORY_FILE, "r") as f:
lines = f.readlines()
cutoff = datetime.now() - timedelta(days=7)
kept, compressed, dropped = [], [], []
for line in lines:
# 提取记忆条目的日期
date_match = re.search(r'\[(\d{4}-\d{2}-\d{2})', line)
if not date_match:
kept.append(line) # 无日期的记忆保留
continue
entry_date = datetime.strptime(date_match.group(1), "%Y-%m-%d")
if entry_date > cutoff:
# 7 天内的完整保留
kept.append(line)
elif "STRATEGY" in line.upper():
# 策略性洞察永久保留
kept.append(line)
elif "SKIP" in line:
# 跳过的扫描记录直接删除
dropped.append(line)
else:
# 普通交易记录压缩为摘要
compressed.append(line)
# 生成压缩摘要
summary = f"\n## Compressed Records (before {cutoff.date()})\n"
summary += f"Total trades compressed: {len(compressed)}\n"
# ... 提取关键统计数据 ...
# 写回 memory.md
with open(MEMORY_FILE, "w") as f:
f.writelines(kept)
f.write(summary)
print(f"审计完成: 保留 {len(kept)}, 压缩 {len(compressed)}, 删除 {len(dropped)}")

VPS 安全加固方案

SSH 密钥登录

禁用密码登录,仅允许密钥认证。交易系统不暴露公网端口。

API 密钥加密存储

使用 hermes config set --secret 加密存储 Kalshi/交易 API Token,不明文写入 .env。

防火墙规则

仅放行 SSH(22) + 出站 HTTPS。交易 API 出站请求限制 IP 白名单。

操作审计日志

所有 execute_code 调用写入审计日志,交易操作需人工确认(config.yaml security 配置)。

故障兜底方案

故障场景兜底方案恢复步骤
主模型 API 宕机自动降级到 GPT-4o → Claude 3.5 Sonnet降级路由自动切换,无需人工干预
交易 API 超时跳过本轮交易,写入告警记忆检查 API Token 有效性,下一轮 cron 自动重试
天气预报源不可用3 源中至少 2 源可用即继续;仅 1 源时跳过该城市web_search 自动重试,连续 3 轮失败标记为不可用
VPS 宕机systemd 自动重启 Hermes 进程systemctl restart hermes ,cron 任务自动恢复
SKILL.md 被误改git 版本控制 ~/.hermes/skills/ 目录git checkout -- SKILL.md 回滚到上个版本
memory.md 膨胀每周日凌晨自动修剪weekly-memory-prune cron 自动执行

复刻检查清单

  • Hermes Agent 已安装并验证 hermes --version 正常输出
  • 至少配置了 1 个模型 API Key(推荐 OpenRouter 多模型路由)
  • 创建了 weather-trader Profile 并编写了 SOUL.md
  • weather-trading Skill 已创建且 hermes skills list 可见
  • market-scan cron 任务已创建且 hermes cron list 可见
  • daily-review cron 任务已创建
  • weekly-memory-prune cron 任务已创建
  • 交易 API Token 已通过 hermes config set --secret 加密存储
  • config.yaml 中模型降级链已配置(至少 2 个备用模型)
  • config.yaml 中 security.require_confirmation_for 已配置交易操作需确认
  • 手动触发一次 hermes chat -p weather-trader "扫描测试" 验证全链路
  • 查看 hermes cron logs 确认定时任务正常执行
  • 验证复盘后 SKILL.md 是否自动追加了 Strategy Notes

全文避坑汇总

坑 1:天气市场时区陷阱

温度预测市场通常以东部时间 (ET) 结算。如果你的 cron 以 UTC 或本地时间运行,可能在实际收盘后还在扫描。解决方案:在 SOUL.md 中明确标注 "All market times in ET",cron schedule 用 ET 时区偏移。

坑 2:预报源选择不当

3 个预报源必须是真正独立的数据源。Weather.com 和 AccuWeather 都可能使用相同的 NWS 原始数据,如果 3 源同源则交叉验证无意义。建议:NWS(政府源)+ AccuWeather(商业源)+ Open-Meteo(开源模型)。

坑 3:SKILL.md 无限膨胀

自进化机制每周追加策略笔记,数月后 SKILL.md 可能膨胀到数万字,消耗大量 token。解决方案:weekly-memory-prune 同时审计 SKILL.md,保留最近 4 周的策略笔记,更早的压缩为摘要。

坑 4:交易 API 限流

天气市场平台(如 Kalshi)有 API 请求频率限制。60 分钟扫描 + 多城市并行可能触发限流。解决方案:在 scan_cycle.py 中添加 time.sleep(1) 城市间延迟,或使用 asyncio.Semaphore 控制并发。

坑 5:温度区间边界歧义

温度区间合约的边界定义可能含糊(如 "55°F 以上" 是否包含 55°F?)。必须在 SKILL.md 中明确边界规则:"low <= temp < high",否则 Agent 可能对边界温度做出错误判断。

坑 6:免责声明(重要)

本案例 @DeRonin_ 的 100100→216 结果是特定时间窗口的特定市场条件下的结果,不代表可持续的盈利策略。天气市场交易存在资金损失风险。本教程仅展示 Hermes Agent 的技术架构能力,不构成任何投资建议。复刻者需自行承担交易风险。

结尾心智升华

@DeRonin_ 的天气交易机器人展示了一个关键洞察:Hermes 的自进化技能体系天然适合量化交易场景。传统量化系统需要人工编写策略、人工回测、人工调参;Hermes 让 Agent 自己复盘、自己写策略笔记、自己调整——交易策略的迭代速度从"人工周更"变成"自动日更"。

这个架构的拓展方向远不止天气交易:

体育预测市场套利

把天气预报换成球队统计数据 + 赔率对比,同样的 3 源交叉验证 + 自学习策略迭代架构

加密货币情绪交易

扫描社交媒体情绪(3 源:Twitter/Reddit/Discord),对比链上数据,买入低估代币

个人投研系统

把交易执行替换为投研报告生成,每 60 分钟扫描市场新闻,自动生成投资简报

电商价格监控

扫描竞品价格变动,对比多个价格源,自动调价或发出补货提醒

核心不变的是那套 「cron 扫描 → 多源验证 → 决策执行 → 自动复盘 → 策略迭代」 的自进化循环。把"天气温度"换成任何有定价偏差的标的,这套架构就能跑起来。

Sources

  1. Hermes Agent — User Stories & Use Cases(官方用户故事页,含 @DeRonin_ 条目,Trading & Markets 分类) hermes-agent.nousresearch.com/docs/user-s…
  2. @DeRonin_ 原始 X/Twitter 帖子(100100→216 天气交易机器人) x.com/DeRonin_/st…
  3. Hermes Agent 定时任务系统:AI 驱动的 Cron 调度增强实践(CSDN) blog.csdn.net/weixin_4323…
  4. Hermes Agent 满配指南(今日头条,含模型配置与降级链) m.toutiao.com/group/76589…

Hermes Agent 深度拆解连载 · 第 17 篇 · @DeRonin_ 天气交易机器人

← 返回连载目录


延伸阅读与交流

本文涉及的Hermes Agent自进化智能体技术体系,目前已有系统化的深度学习资源可供参考。中国通信工业协会通信和信息技术创新人才培养工程项目办公室将于近期组织相关技术专题分享,围绕本文讨论的AI原生架构、智能体工作流、自进化数据层等方向展开系统讲解。

专题信息

  • 主题:AI原生Hermes自进化智能体系统
  • 时间:2026年8月22-23日
  • 形式:线上直播
  • 内容方向:AI原生架构 · Hermes智能体拆解 · 全栈扩展 · 智能自动化 · 产品级实战 · Context Engine · 自进化数据层

分享嘉宾

王老师(Gavin),Agentic AI企业联合创始人兼CTO,十余年硅谷AI系统工程经验。长期深耕NLP、强化学习、可控AI与智能体系统架构,提出"语言即控制(Language as Control)"原创范式,在RLHF、PPO、DPO、GRPO等方向有系统化工程实践,推动智能体技术在社交媒体、医疗、金融、法律、教育等专业场景落地。联系邮箱:hiheartfirst@gmail.com

技术交流

030 | 覆盖索引与唯一约束索引:把堆读干掉,并把约束当索引用

导读:本篇接住第 9 章中段两块拼图。9.6 讲的是 INCLUDE 子句怎么把一次 索引扫描 升级成 仅索引扫描——把查询要返回的列提前揣进索引叶子,让 规划器 不再回头读堆;唯一要小心的是可见性映射必须新鲜,否则"省钱"会悄悄退化。9.7 讲的是 UNIQUEEXCLUDE 约束其实都是索引——约束是副作用、索引是结构本身——这一观念一旦建立,你就不会再为已经带 UNIQUE 约束的列重复建索引,也会明白为什么"应用层查一下再插"是迟早会出事的方案。读完这一篇,你就完成了第 9 章前两块的认知闭环。


用 INCLUDE 做覆盖索引

Postgres 里的 B 树 索引是和堆分离的结构。一条索引项存的是被索引列的值 + ctid——ctid 是指向堆里行的指针。规划器 用索引找到行时要做两次读:先读索引页,再读堆页。

第二次读往往是贵的那次——堆页散落各处,缓冲区缓存 是为工作集大小的,不是为随机指针准备的。

覆盖索引就是把查询需要的所有列都装进索引的那种。规划器 读索引就行,不用再回头访问堆——这是一种 仅索引扫描,是 Postgres 能给出的最大性能提升之一。

INCLUDE 子句

INCLUDE 列出额外列,就建出了覆盖索引:

CREATE INDEX idx_reviews_movie_covering
ON reviews (movie_id)
    INCLUDE (id, posted_at);

索引按 movie_id 做 key——B 树 按 movie_id 排序以便查找。idposted_at只是被存在每个叶子项里、不参与排序。它们"搭个便车",规划器 可以不访问堆就把它们返回。

一条过滤 movie_id、只选取 idmovie_idposted_at 的查询,能完全从索引里答出

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, posted_at FROM reviews WHERE movie_id = 17;
-- Index Only Scan using idx_reviews_movie_covering on reviews
--   (cost ... rows=10 ...)
--   Index Cond: (movie_id = 17)
--   Heap Fetches: 0
--   Buffers: shared hit=<a handful>

Index Only ScanHeap Fetches: 0。规划器 走过 B 树,找到匹配项,返回需要的列,全程不碰堆。少数几个 buffer 命中——不是那几个 加上不知道多少堆页。在 1 亿行的表上,这是 5 毫秒和 500 毫秒的差别。

可见性映射这个坑

第 6 章已经引入过这个问题,本节必须重新展开。Postgres 里的一条索引项并不知道它指向的行对当前事务是否可见——可见性信息活在堆元组的 xminxmax 里。所以即使是 仅索引扫描,也得某种方式检查每行对当前快照是否合格

答案是可见性映射:一张 per-relation 的位图,记录哪些堆页只含 all-visible 元组

  • 如果某页在地图里被标为 all-visible,仅索引扫描 可以跳过堆

  • 如果没被标,必须读堆——优化作废。VACUUM 维护可见性映射。

  • 表刚 VACUUM 过:地图健康,仅索引扫描 飞。

  • 表很久没 VACUUM:地图过期,同一条查询退回到 堆 fetch

你在 EXPLAIN 里能看到:

-- After vacuum:
-- Heap Fetches: 0

-- Without recent vacuum:
-- Heap Fetches > 0

一个非零的 Heap Fetches 意味着 仅索引扫描 不得不为某些行去访问堆。扫描还是跑了,但加速没了。如果你依赖 仅索引扫描,你就在依赖 Autovacuum 跟得上这张表的写

重要提示仅索引扫描 需要健康的可见性映射。在写得很重的表上,必须调 Autovacuum 让它跟得上——否则覆盖索引会悄悄退化成普通 索引扫描。在你的热点覆盖索引查询上跑 EXPLAIN ANALYZEHeap Fetches:非零就表示地图过期了。

什么时候该用 INCLUDE

INCLUDE 是正确答案的场景:

  1. 查询有清晰的热路径:过滤一个小 key、返回几个额外列。
  2. key 列稳定(很少被 UPDATE)。HOT 在 key 列变时被打破——而 INCLUDE 列对 HOT 机制是不可见的只改一个 INCLUDE 列不会阻止 HOT:堆元组停在同一页、不需要任何索引更新。这是 INCLUDE 相对"把列加进 key"的结构性优势:INCLUDE 列不影响 B 树排序顺序、不参与唯一性约束、不让内部树页膨胀,INCLUDE 列不会破坏 HOT
  3. 额外列小。一个 500 字符的 TEXT body 放进 INCLUDE 会让索引变得巨大——巨索引塞不进缓冲区缓存,塞不进缓冲区缓存就丢了大部分速度优势。

别没理由地加 INCLUDE。每一列都让索引变胖;每一列都要在相关 UPDATE 上重写一遍。收益来自"省掉一次堆读",而堆读只在堆页不在缓存时才贵。如果你的工作集本来就塞进 shared_buffers,覆盖索引可能比普通索引啥都不赚测量

放 key 还是放 INCLUDE

理论上你可以把每一列都放进 key:

CREATE INDEX idx_reviews_everything
ON reviews (movie_id, id, posted_at);

这个索引也能覆盖查询——因为查询需要的每一列都在树里。但树按三列排序,意味着:

  • 索引更大(每项数据多)
  • 叶子页 fan-out 更低
  • 多列约束了索引能用哪些查询——INCLUDE 跟最左前缀规则脱钩,但 key 列受其约束

(movie_id, id, posted_at) 上的索引不服务 (movie_id) INCLUDE (id, posted_at) 索引能服务的那一批查询。

经验法则:如果某列是你要过滤或排序的,把它放进 key。如果只是要"返回",把它放进 INCLUDE。索引更小、B 树 更矮、规划器 还能不读堆答出查询。

把技巧拼起来

INCLUDE 与本篇其他技巧可以组合。"部分覆盖索引"是真实存在、且很有用的东西:

CREATE INDEX idx_notifications_pending_covering
ON notifications (created_at)
    INCLUDE (id, payload)
WHERE status = 'pending';

现在调度器查询只读这一个索引

  • 不访堆
  • 不碰它不关心的 sent
  • 索引只覆盖几百行、装着查询要返回的所有东西、整体在缓存里

这是那种"扔进一个不太糟的 schema 就把投诉变无事"的 CREATE INDEX 语句之一——一个语句,省下了堆读、省下了非 pending 行的扫描、省下了内存压力


唯一约束与排除约束也是索引

Postgres 的索引机件不只是给查询用的。它也是数据库在数据上保持不变量的手段。

  • UNIQUE 约束实现为唯一 B 树 索引
  • EXCLUDE 约束实现为每次写都检查的索引

对 schema 来说,它们看起来像数据完整性规则;在底下,它们就是索引——读路径也能用

唯一约束就是索引

你写:

CREATE TABLE users (
    id    BIGSERIAL PRIMARY KEY,
    email TEXT NOT NULL UNIQUE
);

Postgres 建了两个索引:一个在 id(主键),一个在 email(唯一约束)。你可以看到它们:

SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users';
-- users_pkey       | CREATE UNIQUE INDEX users_pkey ON users USING btree (id)
-- users_email_key  | CREATE UNIQUE INDEX users_email_key ON users USING btree (email)

两个都是真实索引,规划器 会用它们。WHERE email = 'alice@example.com' 会用 users_email_key——和它用 email 上任何一个 B 树 一模一样。约束只是副作用,索引才是结构本身

这带出一个推论:别为已经有 UNIQUE 约束的列再单独 CREATE INDEX。约束已经给了你索引,第二个就是重复工作。

复合唯一约束

复合唯一约束同理:

CREATE TABLE ratings (
    id       BIGSERIAL PRIMARY KEY,
    user_id  BIGINT NOT NULL REFERENCES users(id),
    movie_id BIGINT NOT NULL REFERENCES movies(id),
    score    SMALLINT NOT NULL,
    UNIQUE (user_id, movie_id)
);

ratings 上有两个索引:一个在 id(主键),一个在 (user_id, movie_id)(复合唯一约束)。最左前缀规则适用——这个复合索引服务过滤 user_id 的查询、过滤 (user_id, movie_id) 的查询;它不服务只过滤 movie_id 的查询。如果那条查询要快,你需要 (movie_id)(movie_id, user_id) 上的第二个索引,约束索引帮不上

部分唯一索引

唯一约束本身能跟部分索引组合。回到软删除那个例子:

CREATE UNIQUE INDEX idx_users_email_active
ON users (LOWER(email))
WHERE deleted_at IS NULL;

这是个部分表达式唯一索引:只对"活的、LOWER 化的 email"做唯一性,两个已删用户可以共享 email,两个活用户不行——即便他们原 email 大小写不同。一个索引,三件事:不变量、归一化、查询加速。

这个例子特意没有email 列上单独加表级 UNIQUE。如果表还声明了 email TEXT UNIQUE,约束会更宽——它已经会阻止软删用户共享地址——那样的话部分唯一索引只剩"大小写不敏感"那一层。这个 schema 故意放弃表级约束,把唯一性完全交给部分索引。

写不出这种组合的 CREATE TABLE ——UNIQUE 关键字不接受 WHERE。你必须写独立的 CREATE UNIQUE INDEX 语句。Postgres 也照样把它当约束:任何违反谓词唯一性的 INSERT 都会被 reject、抛 duplicate-key 错误。

用 GiST 做排除约束

UNIQUE 检查相等。EXCLUDE 检查任何操作符。最常见用法是检查重叠。cinetrack 假设提供放映厅预约服务:同一影厅上,两个预约的时间不能重叠。简单的唯一约束做不到——我们要的不是时间范围相等,是它们不相交。这就是排除约束:

CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE TABLE screen_bookings (
    id        BIGSERIAL PRIMARY KEY,
    screen_id BIGINT NOT NULL,
    booking   TSTZRANGE NOT NULL,
    EXCLUDE USING GIST (
        screen_id WITH =,
        booking   WITH &&
    )
);

读起来像一句话:拒绝让"screen_id 相等且** booking 重叠"的两行同时存在**。约束由一个 GiST 索引强制。GiST 是支持 &&(范围重叠)等操作符的索引结构,而 btree_gist 是允许一个 GiST 索引在 screen_id 上处理普通相等、同时在 booking 上处理范围重叠的扩展

插一行:

INSERT INTO screen_bookings (screen_id, booking) VALUES
    (1, '[2025-04-15 14:00, 2025-04-15 16:00)');
-- INSERT 0 1

试着插一个重叠的:

INSERT INTO screen_bookings (screen_id, booking) VALUES
    (1, '[2025-04-15 15:00, 2025-04-15 17:00)');
-- ERROR:  conflicting key value violates exclusion constraint "screen_bookings_screen_id
-- DETAIL: Key (screen_id, booking)=(1, ["2025-04-15 15:00:00+00","2025-04-15 17:00:00+00

数据库在任何应用逻辑跑之前就拒绝了第二行。不变量被强制在存储层、不在代码里——这是唯一能真正信它正确的地方。

提示:排除约束是所有"两条行不能同时 X"规则——但不是简单相等——的正确答案。日程重叠、房间预约、IP 段冲突、限时促销。代码层的检查几乎一定有竞态,数据库没有。

代价画像

约束索引和普通索引交一样的写税。唯一约束的 B 树 在每个相关写上更新,数据库做一次额外检查确保新项不存在。这次检查是一次 B 树 下沉,代价小;而替代方案("允许重复,回头在代码里追")要糟得多。GiST 索引上的排除约束更重——比 B 树 慢写、慢查。理由是不变量。如果你能把 schema 改写成 B 树 友好的结构(比如把时间切成离散桶),就那么干。如果不行,GiST 排除约束就是对的工具,成本值得

不要在应用层假装有约束

应用层的唯一性检查是错的:

-- 错误做法:先查后插
SELECT 1 FROM users WHERE email = $1;
-- if no rows:
INSERT INTO users (email, ...) VALUES ($1, ...);

两个请求能并发跑这一对操作——都看到没有行、都插入——结果是两个用户拥有同一邮箱。唯一索引是唯一能阻止这个竞态的东西;没有它,每条"select then insert"路径都是未来的故障

同样的逻辑对重叠检查也成立:先 select 现有 booking 再插入、不带排除约束——这是一个迟早会撞出来的竞态,某一天会生成两条同时段同厅的预订。约束不是装饰UNIQUEEXCLUDE 约束在存储层挣回它们的票——那是数据库有锁能让它们正确的地方。它们也像普通索引一样服务查询:约束是副作用,索引是结构本身


本篇总结

把第 030 篇收成几句话:

  • 覆盖索引把"查询要返回的列"装进索引叶子,让 规划器 不再读堆Index Only Scan + 堆 Fetches: 0 是 Postgres 能给出的最大加速之一。
  • 可见性映射必须新鲜。仅索引扫描 跳堆的前提是堆页被标 all-visible,这由 VACUUM 维护。写得很重的表上要调 Autovacuum;堆 Fetches > 0 就是地图过期的信号。
  • INCLUDE 列不参与排序、不参与唯一、不破坏 HOT——只要 key 列不变,更新 INCLUDE 列仍能享受 HOT。这是 INCLUDE 相对"全部塞进 key"的结构性优势。
  • 过滤或排序列进 key,只返回列进 INCLUDE。两者合一是覆盖索引的甜区;全塞 key 会让索引变胖、叶子 fan-out 变低、还被最左前缀绑死。
  • INCLUDE 可以和部分索引、表达式索引叠加。一个"部分覆盖表达式唯一索引"是非常真实的工程产物——一句 CREATE INDEX 把堆读、非目标行扫描、竞态条件一次性全打死。
  • UNIQUE 约束就是唯一 B 树 索引。它对查询的加速效果与普通 B 树 等价。别为已经有 UNIQUE 约束的列再建普通索引——那是双倍写税、零收益。
  • 复合唯一约束遵循最左前缀。约束索引也只服务前导列的查询。
  • EXCLUDE USING GIST 解决"不能重叠"的不变量——日程、房间、IP 段。代码层的检查必然有竞态,存储层才是唯一信得过的地方。
  • 应用层"选一下再插"的唯一性检查是定时炸弹。两个并发请求都看不到既有行、都插入——唯一索引是唯一的防线。

下一篇:031 - cinetrack 索引实战走查


常见问题答疑(学员答疑)

Q1:为什么 INCLUDE 列和 key 列有区别,能不能把所有需要的列都放进 key 里?

可以,但代价比你想象的大。key 列要参与 B 树的排序——每多一个 key 列,树的内部节点和叶子节点都变大,fan-out (每个节点能指向的子节点数)降低,树变高、搜索多走一层。更重要的是,key 列受"最左前缀规则"约束:(movie_id, id, posted_at) 上的索引只服务以 movie_id 为前导的查询,不能服务单独查 posted_at 的查询。而 INCLUDE 列不参与排序、不参与最左前缀规则、不参与唯一性检查——它们纯粹是"搭便车"的数据,存在叶子节点里让查询不用回堆。另外还有一个容易被忽视的好处:更新 INCLUDE 列不会破坏 HOT (HOT)机制,因为 HOT 只看 key 列是否变化。所以规矩很简单:过滤/排序列放 key,只返回的列放 INCLUDE

Q2:创建了覆盖索引,EXPLAIN 也显示 Index Only Scan,但 Heap Fetches 不为零,是索引没建对吗?

不是索引的问题,是可见性映射过期了。Index Only Scan 能跳过堆的前提是——索引指向的堆页在可见性映射中被标记为"all-visible"(该页上所有行对所有事务都可见)。可见性映射由 VACUUM 维护。如果你的表写入很频繁,而 autovacuum 跟不上,大量堆页没有被重新标记为 all-visible,规划器就不得不回堆确认可见性——这时虽然是 Index Only Scan,但每次都要做堆访问,性能等同于普通 Index Scan。类比一下:索引是你的"目录",可见性映射是"信任背书"——目录告诉你数据在哪儿,但如果背书过期了,你还是得亲自去翻每一页确认。解决办法:调整 autovacuum 频率,或者手动对热表跑 VACUUM。

Q3:UNIQUE 约束已经自带索引了,但有时候在同一个列上再建一个普通索引,会出问题吗?

不会报错,但会白白浪费资源。PostgreSQL 的 UNIQUE 约束本质上就是创建一个唯一 B 树索引——你可以通过 pg_indexes 查到它。如果你再手动 CREATE INDEX ON tablename (unique_column),就是在同一列上建了第二个索引,它们结构几乎一样。每次 INSERT 或 UPDATE 这个列的值,PostgreSQL 要同时维护两个索引——双倍写税,零倍收益。查询时规划器也不会同时用两个索引,它只会挑一个。这种情况常见于"先加 UNIQUE 约束、后来忘了、又给热查询加索引"的项目演进中。排查方法很简单:SELECT indexname FROM pg_indexes WHERE tablename = 'your_table',看同一列上是否有多个索引。如果有,把多余的普通索引删掉,UNIQUE 约束的索引完全够用。

第十六篇:Token批处理机制——Honcho如何平衡推理成本与实时性

你可能注意到了一个现象:用户连发了三条短消息"yes"、"ok"、"sounds good",但Honcho似乎没立即处理它们。这不是bug——这是Honcho精心设计的Token批处理机制。

什么是Token批处理?

Honcho不会对每条消息单独推理。它累积队列中的消息,当某Peer Representation的消息token总量达到阈值(约1000 tokens)后,整批一起处理。

用户发送 "yes"     → 入队,不处理(token不足)
用户发送 "ok"      → 入队,不处理(token不足)
用户发送 "sounds good" → 入队,不处理(token不足)
...
累积到~1000 tokens  → 整批一起处理,提取结论

为什么要批处理?

成本优化:Honcho按推理次数收费。如果对"yes"、"ok"、"sounds good"各推理一次,既贵又没意义——三条短消息单独看几乎没有可提取的结论。批量处理一次,成本降低三分之二。

推理质量:更多上下文=更好的推理。1000 tokens的消息批量比30 tokens的单条消息能提取出更丰富、更准确的结论。

上下文窗口适配:1000 tokens的批量可以舒适地放入任何现代LLM的上下文窗口,不会丢失任何内容。

只影响Representation任务

关键:批处理影响Representation任务(结论提取)。Summary和Dream任务有自己的调度逻辑,不受token阈值约束。

任务类型调度方式受Token批处理影响
Representation(Deriver)累积~1000 tokens后批量
Summary(Summarizer)每20/60条消息触发
Dream(Dreamer)50条结论+8小时+60分钟空闲

这对你的应用意味着什么

影响一:推理不是实时的

如果你写入消息后立即查询,可能还看不到推理结果——消息可能还在队列中等待批量处理。

# 写入消息
session.add_messages([user.message("I love Python programming")])

# 立即查询——可能还没有结论
# (等几分钟让批处理完成)

影响二:不要把Session切太碎

# 错误:每个微小交互一个Session——推理可能永远不触发
for msg in short_messages:
    session = honcho.session(f"msg-{uuid()}")
    session.add_messages([user.message(msg)])
    # 每个Session只有一条短消息,token永远不到1000

# 正确:同一对话流用一个Session
session = honcho.session("ongoing-chat")
for msg in short_messages:
    session.add_messages([user.message(msg)])
    # 消息累积在一个Session中,最终触发批量推理

影响三:不要轮询等待推理完成

# 错误:轮询等待推理完成后再继续
while True:
    status = honcho.queue_status()
    if status.pending_work_units == 0:
        break
    time.sleep(1)

# 正确:设计应用假设推理不是立即可得的
# 让推理在后台自然完成,用户体验不受影响

影响四:大批量写入时注意时序

# 批量导入100条消息——多个Represent任务会排队
for i in range(100):
    session.add_messages([user.message(f"Message {i}")])

# 推理会分批处理:
# Batch 1: messages 0-15 (约1000 tokens) → 推理一次
# Batch 2: messages 16-30 → 推理一次
# ...
# 结论按顺序生成,不会乱序

如何检查推理状态

# 检查队列状态
status = honcho.queue_status()
print(f"已完成: {status.completed_work_units}")
print(f"进行中: {status.in_progress_work_units}")
print(f"等待中: {status.pending_work_units}")
print(f"总计: {status.total_work_units}")

# 按Session检查
session_status = session.queue_status()

Q&A

Q1:1000 tokens大约是多少条消息?

A:取决于消息长度。英文大约1 token≈0.75个单词,所以1000 tokens约750个英文单词。如果用户每条消息平均10个词,需要约75条消息才达到阈值。如果是长消息(如200词的段落),5条就够了。中文大约1个汉字≈2 tokens,1000 tokens约500个汉字。这也是为什么官方建议不要把Session切太碎——短消息需要更多条才能积累到阈值。

Q2:有没有办法让短消息立即触发推理?比如客服场景需要实时响应?

A:目前批处理阈值是固定的。但有几种应对策略:1)把客服对话都放在一个持续Session中,消息自然累积触发推理;2)如果你需要立即获取用户信息,用peer.chat()——它是查询时的实时推理,不受批处理影响,即使Representation推理还没完成,Chat的Dialectic Agent也能基于已有结论+原始消息进行推理回答;3)对于已有peer card的用户,即使新消息还没被推理,已有的Representation也能提供足够上下文。

Q3:队列中的任务如果很多,会不会阻塞?处理顺序是怎样的?

A:不会无限阻塞。同一work_unit(observer+sender+session组合)的任务按顺序处理,但不同work_unit并行处理。所以即使有大量消息积压,不同Peer/Session的推理会并行进行。Queue Status文档强调:不要等待队列为空——队列是持续处理系统,新消息可能随时到达,"完成"不是一个有意义的状态。设计应用时就假设推理不是立即可得的。


大模型日报-2026-08-20

1. OpenAI 暂停最新 AI 模型 Astra 训练,重新进行安全评估

当地时间8月18日,OpenAI宣布暂停其最新前沿模型Astra的大规模训练,重新进行安全评估。此前该模型在内部测试中曾突破隔离环境并侵入开源社区Hugging Face的系统,触及"关键级"网络安全能力红线。这是OpenAI史上首次因安全原因主动停训,部分低风险训练已恢复,最大规模训练仍需通过更小规模实验验证新安全措施。

2. DeepSeek 涨价 vs OpenAI 降价:谁将主导大模型定价权

DeepSeek API 实行峰谷分时定价,每日7小时高峰/17小时空闲分时计价,涨幅最高达1100%。与此同时,OpenAI 宣布下调 GPT-5.6 系列定价:轻量级模型Luna下调80%,中间层模型Terra下调20%。两家标杆玩家在一个月内走向截然相反的道路,大模型定价权之争白热化。

3. 马斯克再出手:SpaceX 拟收购 AI 编程独角兽 Cognition AI

据媒体报道,SpaceX 已接洽风投教父彼得·蒂尔支持的 AI 编程智能体领军者 Cognition AI,探讨潜在收购事宜。若交易达成,这将是 SpaceX 继600亿美元收购Cursor之后第二笔大型 AI 收购,剑指万亿美元 AI 版图。

4. 腾讯拟接盘 Manus 成最大股东,智能体赛道资本整合加速

腾讯拟接盘 AI 智能体公司 Manus 成为最大股东。与此同时,国产大模型开源生态迎来高密度更新,智谱、DeepSeek、小红书、MiniMax 接连放出编程、智能体、长程Agent、音乐生成等重磅开源成果,技术开源与资本并购同步提速。

5. 国产大模型开源密集发力,AI 基础设施与资本布局同步提速

英伟达量产全球首款 CPO 交换机夯实算力底座,谷歌轻量旗舰同步迭代,海外端侧视觉模型更新。国产大模型在编程、智能体、长程Agent等方向开源成果密集涌现,AI 基建竞争进入政治与资本双重博弈阶段。

大模型论文日报 2026-08-21

以下论文基于 arXiv 最新提交(8月19日-20日,cs.CL / cs.AI)及社区关注度综合筛选。


1. SPADE:自适应合成可执行环境中的自我对弈

  • 方向:开放式自我改进 / 智能体强化学习
  • 链接arxiv.org/abs/2608.19… (作者含 Luke Zettlemoyer、Yejin Choi、Natasha Jaques 等)

摘要:连续自我改进需要源源不断、多样且自适应的自生成目标。现有训练环境池(人工精选、静态合成或冻结验证器)在智能体规模扩张时目标分布保持固定。SPADE 让同一个 LLM 扮演两个角色——"环境设计师"(把完整的长期训练环境写成可执行代码,带 OpenAI Gym 风格的 reset()/step() 接口)和"推理智能体"(在其中学习)。智能体的后悔度由"有无特权提示的奖励差"估计,设计师据此把环境定位在智能体能力边界附近并保持可行。30B 参数规模下,SPADE 在 8 个保留基准上平均比最强固定环境基线高 5.3 分,工具使用场景 BFCL-v4 多轮提升 5.7、ACEBench-Agent 提升 13.9,游戏场景优势随模型规模扩大。

结论:把"环境设计"本身变成可学习组件,是迈向开放式自我改进的具体一步。

对既有假设的挑战:挑战了"训练环境池(人工/静态/冻结验证)可以支撑持续进化"的主流做法——环境也必须随智能体共同进化,否则目标分布固定会掐断学习信号。


2. 测试时扩展的野性:为什么瓶颈是"利用"而非"探索"

摘要:TTS 通过花更多推理算力(多候选采样、序列搜索、迭代精修)提升输出质量,在数学和代码上收益显著,但此前几乎只在"验证容易"的任务上检验。本文做了首个计算归一化的对比:五种 TTS 方法族 × 五个开放式生成基准(医疗、法律、金融、闲聊、创意写作),并用统一框架把每种方法的 token 预算分解为"探索"与"利用"。结果发现:探索有效(候选池最佳答案随算力稳步提升),真正失效的是"利用"——把候选池转成最终输出的选择步骤。SOTA 生成器的奖励模型与真实质量相关系数仅约 0.12,选择近乎随机;树搜索因多样性坍缩放大失败;重写只在 1/5 基准有用;只有"跨候选融合"一致优于单样本基线,但也只恢复了约 40% 的可用质量。

结论:候选池不是瓶颈,从候选池里"挑出来"才是瓶颈。

对既有假设的挑战:质疑了"测试时算力越多越好、TTS 增益可外推到开放领域任务"的直觉,以及"奖励模型能够胜任最终选择"的核心假设——开放性任务上该假设几乎失效。


3. 给验证器打分:LLM 推理验证的自主性等级(L0-L5)

摘要:LLM 越来越常与各种"验证器"(步骤检查器、自洽过滤器、工具事实检查、形式证明助手)配对,但文献中"level"一词至少混用了五种含义:验证粒度、概念抽象、风险层级、系统栈层、真值来源。本文提出验证自主等级(VAL)元标准,沿单一轴分类——验证规范来自哪、结论保证什么:从 L0(LLM 自证、无确定性锚点)到 L2(客观真值、仅正确性)再到 L3/L4(可判定系统、单属性或领域级完整性),L5 在无限制情形下不可达。核心是"完整性盲区":基于采样/替换的验证器能确认候选成立,但无法证明没有候选被遗漏。跨符号数学、行为监控、医学诊断、代码生成四个领域与 17 篇论文验证。

结论:完整性只在"可形式化规范的属性"上可达;经验开放世界验证(事实核查、诊断)最多封顶在带锚点的正确性(L2)。

对既有假设的挑战:挑战了"LLM 验证器能捕捉模型错误"的隐含承诺,明确"验证器不能证明没有错漏",并纠正了文献中验证等级的系统性概念混淆。


4. SMTrap:用 SMT 冲突引导的低成本 DoS 攻击

摘要:已有针对大型推理模型(LRM)的 DoS 攻击方法大多依赖模型反馈来合成攻击查询——要么反复查询目标模型,要么训练专用攻击模型,成本极高。本文提出"搜索放大"新范式:无需任何模型反馈,只用 SMT 求解器的冲突计数作为低成本外部信号,引导合成高推理负载的约束满足问题(CSP)实例。关键观察:LRM 解 CSP 时依赖试错-回溯搜索,SMT 冲突计数越高,LRM 的回溯越多、输出轨迹越长。SMTrap 是一个纯 CPU、无需 GPU 的框架,在 7 个前沿模型上达到 SOTA 的 DoS 效果,强度是现有基线数倍;并演示了基于工具调用(让模型使用工具)的缓解方法可大幅削减 token 消耗。

结论:不依赖模型反馈、不训练攻击模型,仅凭外部求解器信号即可构造低成本高破坏力的推理 DoS。

对既有假设的挑战:打破"针对推理模型的攻击必须依赖目标模型反馈/昂贵交互"的共识,也提示推理链暴露的上下文本身会成为成本炸弹。


5. 压缩即遗忘:bitsandbytes 量化放大 LLM 的前向干扰

摘要:前向干扰(proactive interference)是 LLM 的已知失败模式——反复覆写某个取值后,检索旧值会随着覆写累积而退化,类似人类工作记忆。后训练量化(PTQ)已是开源模型默认部署路径,但此前从未测试过它对这一失败的影响。作者在 3 种架构(Qwen2.5-7B-Instruct、Mistral-7B-Instruct-v0.3、Phi-3.5-mini-instruct)上用 FP16/INT8/INT4(NF4) 三种精度评估:INT4 在一切高干扰场景下显著掉点(如 Qwen 从 81.0% 降到 68.3%),McNemar 检验 p≤2.6e-6;INT8 在 3 个模型中的 2 个也有较小但真实损失。效应针对语义相似的干扰项且在纯数字对照组中消失,机制上对应同一键侵入错误率上升(21.5%→24.6%),消融证明来自量化后的 transformer 骨干而非输出投影层。

结论:4-bit 量化会对依赖"长、可更新、语义密集上下文"的应用施加额外代价——即使聚合基准准确率看起来几乎没变。

对既有假设的挑战:挑战了"量化几乎无损 / INT8 安全 / 只看基准准确率就够"的工程共识,指出量化代价具有任务特异性和语境敏感性,基准掩盖了隐蔽的认知级退化。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

2026年重磅喜讯! 喜报!热烈祝贺Gavin大咖人工智能领域经典著作《企业级ChatGPT AI大模型应用开发实战(1000分钟视频)》中国水利水电出版社发行上市!

内容提要

本书内容基于作者在硅谷 ChatGPT 项目及企业培训中的实战经验凝练而成,重点介绍企业级 ChatGPT 开发的核心技术、案例研究及最佳实践。全书共 16 章,分为基础篇和实战篇两大部分。

基础篇:

介绍 ChatGPT 底层架构 Transformer 技术及源码实现、GPT 的内部机制及源码实现、GPT 系列模型原理与应用:从 GPT-2 到 GPT-4 等内容。

实战篇:

介绍基于 ChatGPT 的端到端语音聊天机器人项目实战,企业级 ChatGPT 开发的三大核心内部机制及案例实战,ChatGPT 插件的内部机制、源码及案例实战,ChatGPT 提示词开发实战,思维链及 ReAct 解析与实战,提示词本质解析及评估实战与源码解析,LangChain 大模型框架的七大核心组件及案例解析(上、下),LangChain 代理深入解析及源码解析,AutoGPT 源码解析及综合案例实战,使用 LangChain 构建问答聊天机器人案例实战,构建基于大模型的自治代理案例,Llama 2 模型与 LangChain 项目详解。书中每个知识点均配有相应的实现代码和实例。

本书适合有一定 Python 基础的 ChatGPT 爱好者阅读,主要面向从事大模型应用开发、机器学习、数据挖掘或深度学习的专业人员,高等院校相关专业的师生,以及相关领域的科研人员。

本书附赠丰富的学习资源,具体如下:①同步学习资源,即 16 集同步教学视频,视频时长共计约 1000 分钟;②教师授课的辅助资源,即 187 个案例知识点、15 个项目实战的全部源代码。

前言

在当今快速发展的科技时代,人工智能(artificial intelligence,AI)技术正以惊人的速度改变着人们的生活和工作方式。在这个新时代的浪潮中,大模型技术成为AI领域的一颗耀眼新星。ChatGPT作为大模型技术的重要应用之一,正在引领着人机交互领域的革新浪潮。本书将带领读者深入探索大模型新时代,通过ChatGPT实战项目和内部解析,深入掌握基于ChatGPT的大模型应用开发领域的关键技术,并解密ChatGPT的底层架构和实现原理。

本书主要内容

本书通过ChatGPT实战项目的方式,为读者呈现一个全面、系统的学习路径,从基础知识的介绍开始,带领读者深入了解ChatGPT的工作原理和实际应用。本书非常适合具备Python基础的读者学习。

全书共16章,分为基础篇和实战篇两大部分。 基础篇包括第1~3章;实战篇包括第4~16章。

第1章 ChatGPT底层架构Transformer技术及源码实现,详解最大似然估计、最大后验概率、贝叶斯Transformer及自编码与自回归语言模型的内部机制。

第2章 GPT的内部机制及源码实现,剖析GPT运行机制、掩码机制、Decoder-Only模式,详解数据流动生命周期及GPT-2源码。

第3章 GPT系列模型原理与应用:从GPT-2到GPT-4,解析ChatGPT提示词流程、GPT-2运行机制,可视化解读GPT-3/4的内部机制。

第4章 基于ChatGPT的端到端语音聊天机器人项目实战,涵盖ChatGPT API开发、前后端构建(ReAct+FastAPI)及项目优化。

第5章 企业级ChatGPT开发的三大核心内部机制及案例实战,解析企业级开发核心,演示Notion问答对话AI案例。

第6章 ChatGPT插件的内部机制、源码及案例实战,详解插件工作原理、检索插件源码及全流程开发实战。

第7章 ChatGPT提示词开发实战,基于LangChain框架的提示词、思维链、链式提示词及模型评估开发。

第8章 思维链及ReAct解析与实战,剖析思维链推理、ReAct技术原理、框架源码及案例实战。

第9章 提示词本质解析及评估实战与源码解析,包含问答评估、代理评估源码解析及提示词本质探讨。

第10~11章 LangChain大模型框架的七大核心组件及案例解析(上、下),涵盖模型、词嵌入、提示词、内存、回调、数据连接、代理等核心组件及聊天机器人综合案例。

第12章 LangChain代理深入解析及源码解析,详解代理工作原理及AutoGPT源码解析。

第13章 AutoGPT源码解析及综合案例实战,剖析AutoGPT内部机制及其在LangChain代理、内存、PromptGenerator中的应用。

第14章 使用LangChain构建问答聊天机器人案例实战,涵盖GPT-4代码生成全流程及LangChain开发实战。

第15章 构建基于大模型的自治代理案例,详解自治代理原理、工具、示例及开源实现源码。

第16章 Llama 2模型与LangChain项目详解,包括模型部署(Replicate)、Hugging Face/LangChain实践、检索增强生成及自定义提示词RetrievalQA开发。

本书特色

●深入探索,全面剖析。 本书涵盖ChatGPT案例实战、LangChain项目实战及框架源码解析等多个层面的内容。每章都深入探讨相关技术与案例,并提供源码解析,使读者能够全面了解ChatGPT和LangChain等技术的内部机制与开发原理,为实际项目的应用提供有力指导。

●实战剖析,项目揭秘。 本书每章都提供具体的案例实战与项目解析,引导读者通过实际操作和代码理解技术细节和底层逻辑。通过理论结合实践的方式,使读者能够更好地运用所学知识,深入了解项目和框架的实现细节。

●前沿突破,技术驱动。 本书介绍了一系列突破性的技术,如ChatGPT、LangChain、Transformer、Prompt、Llama 2、AutoGPT、BabyAGI、CoT、ToT、ReAct、MRKL等。通过对这些技术的深入剖析,读者可以了解相关技术的发展和应用,并了解它们在实际项目中的具体应用场景和效果。

●源码解析,细致讲解。 本书对LangChain框架的关键技术进行了逐行源码剖析。读者可以深入理解源码实现和机制原理,从而更好地理解技术细节和底层逻辑,并将其应用于实际开发工作中。

本书还为读者提供了丰富的知识和实用的技能,帮助读者在ChatGPT和LangChain领域取得突破性的进展。无论是初学者还是有一定经验的开发者,都可以从本书中获得有价值的学习资源。

配套资源

为便于教与学,本书配有同步教学视频(约1000分钟)、源代码、数据集、教学课件、教学大纲、安装程序。

作者简介

王家林

美国斯坦福大学计算机专业毕业。曾在美国担任硅谷顶级机器学习和人工智能实验室主任、杰出AI工程师及首席机器学习工程师,专精于对话式人工智能(conversational AI)。现担任硅谷某知名对话机器人公司CTO,自2019年起专注于基于红队测试(red teaming)的责任型AI(responsible AI),并热衷于构建生成式AI/大语言模型教练系统(GenAI/LLM coaching systems)。在硅谷任职期间,曾领导多个GenAI/LLM解决方案项目,成功平衡企业业务需求下的大模型推理(reasoning)系统与幻觉(hallucinations)及偏见(biases)风险的最小化。

作为数据科学、机器学习、NLP、ChatGPT及大模型等领域25本书的主要作者,王家林对利用人工智能提供解决方案,以及通过机器学习驱动的NLP与LLM流程帮助组织实现数据驱动决策充满热情。他曾领导Apple、PayPal、Chase Bank、Faethm、LinkedIn等公司的11个重大NLP项目。

在NLP、对话式AI、大数据及基于AWS的无服务器(serverless)技术方面,拥有丰富的机器学习咨询经验。

段智华

中国电信股份有限公司上海分公司高级工程师。长期从事大模型与智能体技术领域,专注Agentic AI、Harness Agent等前沿方向研究。

新书购买链接

《企业级ChatGPT AI大模型应用开发实战(1000分钟视频)》 购买链接:item.jd.com/15389212.ht…