OpenClaw 深度解析与源代码导读 · 第2篇:Skills——能力扩展平面与源码中的「目录即技能」

摘要 :OpenClaw 默认只能对话;Skills 把「领域说明书」以 目录 + SKILL.md(YAML 头信息 + 正文) 的形式挂进工作区,让模型在需要时再用 read 工具 拉取全文,从而在不动内核的前提下扩展行为边界。本篇对齐仓库 D1 文档与 Agent Skills 社区规范,说明 Built-in Tools / Skills / Plugins 的分层;并基于本地源码 固定 commit 走读 监听目录 → 解析 SKILL.md → 拼装 <available_skills> 提示块 → 会话快照与热更新 的链路,顺带澄清 ClawHub 安装产物skills/<slug>/.clawhub/lock.json)与「技能目录」的关系。

关键词 :OpenClaw;Skills;SKILL.md;ClawHub;会话快照;渐进式披露;源码走读;Agent Skills

姊妹篇 :全系列术语与读源码约定见 OpenClaw 深度解析与源代码导读 · 第1篇:系列导读------术语、版本与读源码方法

源码版本说明 :下文引用路径均相对于 openclaw/openclaw 仓库根目录;本地阅读使用的 commit 为 0dd4958bc8a78d26b3b526b1f2e63b15110c64a22026-04-11,请以该 SHA 为准对照行号;若你跟踪 main,请自行 git checkout 后再核对)。

关于 Skills 的基本概念,我们之前也出过一系列的博客,参考如下:

顺序 文章 核心内容(通俗说)
1 LangGraph 7 · 技能 Skills 技能是什么 :文件夹 + SKILL.md,符合 Agent Skills;智能体走 发现 → 选择 → 加载 → 使用,能力不写死在代码里。
2 LangGraph 17 · 三级加载与 Token 优化 渐进披露(Progressive Disclosure) :L1 只看元数据、L2 拉全文说明、L3 再按需读 references/,在规模化技能库时省 token。
3 LangGraph 18 · Skill 四种形态 形态与路由:Inline / File-based / External / Meta;任务执行与「技能创作」类需求可以分岔到不同子图。
4 LangGraph 19 · SkillToolset 工具化加载list_skills / get_skill_details / load_skill_resource,由 Agent 自主决定何时拉菜单、读详情、打开附录。
5 LangGraph 20 · Meta Skills 让系统自己写技能 :按规范生成 SKILL.md静态校验有限轮 LLM 修复落盘discover_skills 注册校验;并可与第 19 篇的三工具执行路径放在同一张图里用意图路由切换。
6 LangGraph 21 · Skills 6 · 从意图到可上线技能 创作流水线Capture Intent 收成结构化规格 → 撰写 SKILL.md静态校验 + 自动修复 小循环 → evals 冒烟 (Executor 按技能作答、Judge 按 rubric 打分)→ 不通过则 refine 大修订 → 落盘 + discover_skills ;把「格式合规」推进到「可测、可迭代、能上线」。

1 为什么要单独讲 Skills?

可以把 OpenClaw 想成一家餐厅:Gateway 负责排号与前厅调度,Brain 是主厨的脑子,Hands 是真炒菜的手。Skills 则像「预制酱料包 + 菜谱卡片」 ------不改造灶具(内置工具),也不把整条供应链搬进后厨(MCP 插件),而是告诉模型:在什么场景下、去读哪一份说明、相对路径如何解析到磁盘上的真实文件

💡 理解要点 :Skills 解决的是 「如何把人类可维护的操作知识,安全地交给模型按需加载」 ;它不是又一个「函数注册表」,而是 以文档为边界的扩展单元


2 OpenClaw 里的 Skills 是什么?

OpenClaw 通过 Agent Skills 兼容的技能目录 教模型如何使用工具。每个技能是一个文件夹,内含带 YAML frontmatter 的 SKILL.md 与说明正文;运行时再从多个根目录 发现 → 过滤 → 把「目录级摘要」写进系统侧提示 ,需要细节时由模型用 read 打开完整文件。

这与「手机上的 App」类比仍然贴切:

  • 每个技能是自包含的扩展,描述能力边界与使用方式。
  • 独立安装、更新、移除;ClawHub 提供社区分发(类似 npm 之于 JavaScript 包)。
  • 技能通常落在工作区的 skills/ 下(另有用户级、项目级、捆绑包等来源,见下一节)。

🔍 实际例子 :为团队写一个「内部 API 调用规范」技能:frontmatter 里写清 name / description,正文里写步骤、错误码、示例 curl;模型只在任务命中描述时才去读全文,避免每轮对话都把几千字塞进上下文。


3 三种能力类型:Built-in Tools / Skills / Plugins

类型 含义 典型来源
Built-in Tools 核心能力(读文件、Shell、web_fetch 等) 随 OpenClaw 安装提供
Skills SKILL.md 为主的原生扩展 工作区自建、ClawHub 安装、捆绑技能等
Plugins 基于 MCP 的集成 任意兼容 MCP 的服务端

对多数用户而言,Skills 是把「操作知识」产品化的主路径;Plugins 更适合把外部系统以工具形态接进来。官方文档在 Skills(OpenClaw) 中把 位置(从哪加载)可见性(是否进 allowlist) 分成两维控制------后文在「多 Agent」一节会点到 agents.defaults.skills


4 渐进式披露:为什么「只给目录」是对的?

Agent Skills 文档渐进式披露(progressive disclosure) 概括成三层:

  1. 目录层:会话开始时只看到技能名与简短描述(控制 token)。
  2. 指令层 :任务匹配时再用 read 打开完整 SKILL.md
  3. 资源层:脚本、参考文档、附件按需在后续步骤加载。

🔍 实际例子(真实技能 seo-checklist :这是一个放在技能目录里的 「SEO 审查说明书」 ------当用户要检查或优化 博客文章、落地页等内容的搜索引擎表现时,模型按技能约定工作:先用 YAML 头里的 name / description 判断任务是否属于「SEO 场景」 ;命中后再打开 SKILL.md,按清单逐项看 标题长度与关键词位置、元描述、H2/H3 层级、首段与关键词密度、段落可读性、内外链 等;若还需要更细的规则(例如关键词研究流程、E-E-A-T、技术 SEO、链接策略),再按需 读取同目录下的 references/seo-guidelines.md,避免一上来就把长附录塞进系统提示。某些编排里正文会写「用资源加载工具读附录」;在 OpenClaw 里,对附录文件再执行一次 read(解析为技能目录内的绝对路径) 即可,语义仍是 先读 SKILL.md、再读附录

  • L1(目录层) :提示里只出现 seo-checklist + 一句 description (「SEO 优化检查清单...当用户需要检查或优化内容的 SEO 时使用」),模型用它判断要不要进入 SEO 审查流程,不必 预载 checklist 全文。

    py 复制代码
    ---
    name: seo-checklist
    description: SEO 优化检查清单,适用于博客、网页等内容。涵盖标题、元描述、标题层级、关键词布局、可读性等。当用户需要检查或优化内容的 SEO 时使用。
    ---
  • L2(指令层) :判定相关后,read 打开完整 SKILL.md,按章节化步骤产出审查结论或改写建议。

    py 复制代码
    # SEO 检查清单
    
    当用户请求检查或优化内容的 SEO 时,请先使用 load_skill_resource 工具读取 `references/seo-guidelines.md` 中的详细指南,并按以下 checklist 逐项审核:
    
    ## 1. 标题(Title)
    - 长度 50-60 字符
    - 主关键词靠近开头
    - 避免堆砌关键词
    
    ## 2. 元描述(Meta Description)
    - 长度 150-160 字符
    - 包含行动号召(CTA)
    - 自然融入主关键词
    
    ## 3. 标题层级(H2/H3)
    - 清晰的层级结构
    - 2-3 个标题中包含目标关键词
    - 避免跳级(如 H2 直接接 H4)
    
    ...
  • L3(资源层) :仅当需要细则时,再 read references/seo-guidelines.md(关键词研究、内容质量、技术 SEO 等),把 token 花在「真的要用到附录」的那一刻。

    py 复制代码
    # SEO 详细指南(L3 资源)
    
    本文件为 seo-checklist 技能的补充参考,仅在需要更细致规则时加载。
    
    ## 关键词研究
    - 使用工具(如 Google Keyword Planner)识别搜索量适中、竞争度可控的词
    - 长尾关键词通常更容易排名
    - 关注用户意图:信息型、交易型、导航型
    
    ## 内容质量
    - E-E-A-T:Experience, Expertise, Authoritativeness, Trustworthiness
    - 原创、深入、对用户有价值
    - 定期更新以保持时效性
    
    ...

AgentScope 多智能体文档 也用「Skills (Progressive Disclosure)」一词强调:不要把所有技能全文一次性塞进系统提示 。OpenClaw 的实现与此一致:默认注入的是 XML 形态的 <available_skills> 列表 ,并明确提示模型用 read 加载技能文件(见 §8.3「formatSkillsForPrompt」)。

💡 理解要点 :Skills 的性能与成本之间的关系,本质是 「摘要进提示、正文走工具」;若你把 20 个技能的正文都手工贴进 system prompt,就退化成「巨型静态提示词工程」,失去了扩展平面的意义。

4.1 Skills 在 Router → Brain 流程中的实际使用(OpenClaw 视角)

渐进式披露不是静态概念,而是在 Router → Brain → 回复 的完整数据流中分阶段按需触发的。以下结构图展示了 Skills 各部分内容在何时、如何被使用:
Brain 层:LLM 推理
L3: Resources(资源层)
L2: Instructions(指令层)
L1: Catalog(目录层)
磁盘层:Skill 包结构
我需要 weather skill
需要调用 API
需要查阅文档
返回结果
skills/weather/
**SKILL.md**

YAML frontmatter + Markdown body
scripts/

query_api.py
references/

api-docs.md
只读 **frontmatter**

name + description + version
技能目录

<available_skills>
读取 **SKILL.md body**

去掉 frontmatter 的正文
注入 System Prompt

作为技能上下文
读取 references/
执行 scripts/
LLM 推理
输出工具调用

read_skill / exec_script

三阶段渐进式使用(与 Router/Brain 流程对应)

阶段 使用到的 Skill 部分 触发时机 目的 Token 控制
L1 Catalog SKILL.mdfrontmatter(YAML 头部) 每次请求初始化(Brain 组装 Prompt 时) 让 LLM 知道"有哪些技能可用" 极小(仅 name + description,约 500 tokens)
L2 Instructions SKILL.mdbody(正文,去掉 frontmatter) LLM 决定使用该 Skill 时 (输出 read_skill 工具调用) 告诉 LLM"如何使用这个技能" 中等(技能说明书,约 2000 tokens)
L3 Resources references/ 文档、scripts/ 脚本 执行阶段按需加载exec_script 或再次 read 提供额外的参考资料或可执行代码 按需(引用文件大小,或脚本返回结果)

实际工作流程示例(用户输入:"查北京天气,如果下雨提醒我今晚关窗"):

复制代码
Step 1: Router 接收消息
├─ 判定:需要 Brain 处理(非命令、非简单问候)
↓
Step 2: Brain 初始化(L1 - Catalog)
├─ 扫描 skills/ 目录,读取每个 SKILL.md 的 frontmatter
├─ 生成 <available_skills> XML(仅含 name + description)
└─ 注入 System Prompt(Token:~500)
↓
Step 3: LLM 第一轮推理
├─ 输入:System Prompt + <available_skills> + 用户消息
├─ LLM 决策:"我需要 weather-query 和 reminder 两个 skill"
└─ 输出工具调用:{ tool: "read_skill", args: { skill: "weather-query" } }
↓
Step 4: 加载 Skill 详情(L2 - Instructions)
├─ 读取 weather-query/SKILL.md 的 body(完整说明书)
└─ 注入到当前上下文(Token:~2000)
↓
Step 5: LLM 第二轮推理
├─ 现在 LLM 知道 "怎么查天气"
└─ 输出工具调用:{ tool: "exec_script", args: { script: "query_api.py", params: "北京" } }
↓
Step 6: 执行与加载资源(L3 - Resources)
├─ 执行 scripts/query_api.py 北京
├─ 返回结果:{ "city": "北京", "weather": "rain", "temp": "18°C" }
└─ 结果返回给 LLM
↓
Step 7-8: 设置 reminder(重复 L2/L3 流程)→ 生成最终回复

📎 与第4篇 Router 的关联 :Router 决定"消息是否进 Brain";一旦进入 Brain,上述 L1/L2/L3 流程自动触发。disable-model-invocation: true 的 Skills 不会出现在 L1 的 <available_skills> 中,只能通过用户显式命令 (如 /weather 北京)触发,详见 §8.3。


5 技能从哪些目录来?(官方优先级)

以下与 D1 文档一致,便于你在磁盘上「按图索骥」(高优先级在前):

<workspace>/skills<workspace>/.agents/skills~/.agents/skills~/.openclaw/skills(托管/共享)→ 捆绑技能skills.load.extraDirs(最低)

多 Agent 时,每个 Agent 有自己的 workspace ,因此 <workspace>/skills 天然是 按 Agent 隔离 的;共享机器级技能则落在 ~/.openclaw/skills 或通过 extraDirs 挂载公共包。详见 docs/tools/skills.md 的 Locations and precedence


6 ClawHub 与技能生命周期(安装 / 更新 / 移除)

6.1 ClawHub 是什么?

ClawHub 是 OpenClaw 的公开技能注册表 (另有组织仓库 openclaw/clawhub)。可以把它想成 「面向 Agent 技能的 npm」 :社区上传技能包,你用命令行或对话里的指令 搜索、安装、升级、卸载,而不必自己维护下载链接与解压脚本。

官方说明见仓库 D1:Skills 文档 · ClawHub 一节(含 openclaw skills ... 与独立 clawhub CLI 的分工)。

6.2 安装:对话里怎么敲、终端里怎么敲?

常见入口有两类(不同客户端可能只暴露其中一种,以你当前 UI/文档为准):

  1. 对话(slash)里安装------适合「正在聊天、顺手装一个」:
text 复制代码
/skills install @author/skill-name
  1. 本机 CLI(官方文档主推,适合脚本与 CI):
bash 复制代码
openclaw skills install <skill-slug>

安装完成后,磁盘上通常会发生什么?

  • 当前 Agent 的 workspace 下出现 <workspace>/skills/<slug>/(或等价布局) 的技能目录,内含 SKILL.md(及脚本、参考文档等附件)。
  • 若该技能需要 API Key、开关、渠道白名单 等配置,产品侧往往还会在 openclaw.json (默认路径多为 ~/.openclaw/openclaw.json,可用 $OPENCLAW_CONFIG_PATH 覆盖)里登记一项,便于 启用/禁用注入环境变量。示例(结构与官方叙述一致,字段以你本机生成结果为准):
json 复制代码
{
  "skills": {
    "@niceperson/brave-web-search": {
      "enabled": true,
      "config": {
        "apiKey": "${BRAVE_API_KEY}"
      }
    }
  }
}

💡 理解要点SKILL.md 教模型「怎么用」openclaw.json 里那一段教运行时「给不给你用、密钥从哪来」------两条线经常同时出现,但职责不同。

6.3 更新:单包与「一键全更」

对话里:

text 复制代码
/skills update @author/skill-name
/skills update --all

终端里 (与 D1 一致):openclaw skills update ...openclaw skills update --all 等。

语义上可以记三条:

  • 单技能更新 :从注册表(或上游源)拉取 该 slug 的最新包 ,覆盖 <workspace>/skills/<slug>/ 下文件;openclaw.json 里该项的 enabledconfig 等通常保留(除非你手动改过同步策略)。
  • 全部更新 :对锁文件/已安装集合里登记的多个技能 逐个重复上述过程
  • 更新前 :尽量阅读 changelog / 权限说明;技能升级可能改行为、加网络依赖或破坏既有工作流。

6.4 移除

对话里:

text 复制代码
/skills remove @author/skill-name

语义上:删除该技能目录 ,并在配置里 去掉对应 skills 条目(具体以你客户端生成的 diff 为准),避免模型继续看到已卸载能力。

6.5 一次操作会动到哪些地方?(小结表)

下面把 「用户操作 → 落盘 → 模型何时看见」 压成一张表。第三列刻意 不写 旧叙述里常见的聚合文件 SKILLS.md :在 openclaw/openclaw 当前 main 技能管线 中,模型侧可见性 来自 扫描各 SKILL.md + 拼 <available_skills> + 会话 skillsSnapshot (见 §8.3~§8.5);安装器写入的 .clawhub/lock.json 则服务 版本与复现

操作 对工作区(skills/ 等) openclaw.json(若使用) 对「下一轮模型是否看见」
install 新增 <workspace>/skills/<slug>/(含 SKILL.md 常新增 skills 子项(enabledconfig...) 文件一落地 ,watcher 会 bump 快照版本;同会话 下一用户轮 起可重建 skillsSnapshot(§8.5)
update 覆盖该 slug 目录内容 多数情况下保留config / enabled 同上:内容变 → 版本 bump → 快照可刷新
remove 删除目录 删除对应配置项 目录消失 → 版本 bump → 新快照里不再列出该技能

6.6 源码侧:安装路径、锁文件与 SKILL.md 变体

src/agents/skills-clawhub.tsClawHub 安装目录 规范到 <workspace>/skills/<slug>/ ,解压后调用 ensureSkillRoot :为兼容历史包名,会依次探测 SKILL.md / skill.md / skills.md / SKILL.MD ;若都不存在则抛错 downloaded archive is missing SKILL.md ------规范文件名仍是 SKILL.md ,其余仅为兼容。(节选,commit 0dd4958bc8a78d26b3b526b1f2e63b15110c64a2

ts 复制代码
// openclaw/src/agents/skills-clawhub.ts --- ensureSkillRoot
async function ensureSkillRoot(rootDir: string): Promise<void> {
  for (const candidate of ["SKILL.md", "skill.md", "skills.md", "SKILL.MD"]) {
    if (await fileExists(path.join(rootDir, candidate))) {
      return;
    }
  }
  throw new Error("downloaded archive is missing SKILL.md");
}

锁文件与来源元数据<workspace>/.clawhub/lock.json 记录已安装 slug 与版本时间;各技能目录下可有 .clawhub/origin.json (与 skills-clawhub.ts 前半部类型一致),用于 供应链审计与「从哪一版 ClawHub 来」 。这与运行时 formatSkillsForPrompt 拼出来的 <available_skills> 、以及会话里缓存的 skillsSnapshot.prompt 仍是 两条线 :前者回答 「装了什么、能不能复现」 ,后者回答 「这一轮提示里贴哪张菜单」


7 LangGraph 式 Skills 案例演示与 OpenClaw 对照

在讲 OpenClaw 源码(§8)之前,本节把 「Agent Skills 规范 + 发现 → 选择 → 加载 → 使用」 落到一个 可下载、可运行 的工程里:技能全部是 「子目录 + SKILL.md ;Python 里用 skill_loader.py 拆成四个函数,main.py 按固定顺序调用;需要 LLM 时走 use_skill_with_llm (LangChain ChatPromptTemplateChatOpenAIStrOutputParser)。这与 LangGraph 系列里「先把数据流讲清楚,再决定要不要画成 StateGraph」 的教法一致------本示例甚至没有强制引入图编排 :流水线本身就是一张 有向无环图(DAG),便于你对照 OpenClaw 里「谁负责发现、谁负责注入提示、谁负责 read」。

示例源代码LangGraph Skills 示例。解压后请以包内 demo_codes/README.md 为准对照目录结构;下文文件名与职责与该包一致。

💡 理解要点 :本节的定位是 教学脚手架 ------一个技能根目录 + 一个 loader + 一个入口脚本不是 OpenClaw Gateway 的产品形态。读完 §7.9 再进入 §8,可避免把 SKILLS.md 等旧叙述误当成内核契约。

7.1 架构总览(Mermaid)与每个节点在干什么

下面这张图与示例代码中的 数据流 一一对应:从左到右,磁盘上的技能包 → 四次纯函数/链式调用 → 终端上的自然语言输出
skills_library/
discover_skills
select_skill_for_task
load_skill
use_skill_with_llm
输出结果

各节点含义(读图时请 从左到右 跟随数据形态变化):

节点 名称(代码中) 输入 / 输出(直觉) 职责一句话
A skills_library/ 输入:无(磁盘根);输出:若干子目录 技能包仓库 :每个子文件夹 必须SKILL.md (YAML frontmatter + Markdown 正文);可从 SkillMD、Anthropic anthropics/skills 等来源解压放入。
B discover_skills 入:skills_root(可选);出:List[Dict] 元数据列表 发现(L1) :只读每个 SKILL.mdfrontmatter ,得到 namedescription 等;不读正文,控制 token,与渐进披露第一层对齐。
C select_skill_for_task 入:用户任务字符串 + 上一步列表;出:一个 skill_nameNone 选择 :在「已发现的技能名集合」里做 路由 。示例用 关键词表 (如「总结」→ summarize);生产可换成 LLM 分类器embedding 相似度
D load_skill 入:skill_name;出:(full_content, body) 加载(L2) :读取 完整 SKILL.mdbody去掉 frontmatter 后的正文,准备塞进 LLM。若目录不存在则返回空串,由入口逻辑报错退出。
E use_skill_with_llm 入:skill_nameskill_instructions(即 body)、user_input 等;出:模型生成的字符串 使用 :把 「技能说明书 + 用户任务」 填进 system / user 模板,调用 ChatOpenAI 执行;API Key 等可由 config_parser.env 注入。
F (标准输出) 入:模型返回;出:人眼可读答案 呈现main.py 或 notebook 打印;无状态,不持久化会话。

与 OpenClaw 的粗粒度映射 (细节以 §8 为准):A≈多根 skills/ 目录 ;B≈**local-loader + workspace 拼 catalog**;C≈模型自行决定 read 哪个 SKILL.md (外加 agents.*.skills allowlist);D≈read 工具打开路径 ;E≈Brain 里那一轮 tool/LLM 循环 ;F≈通道回包

7.2 技能库节点:demo_codes/skills_library/

  • 路径 :资源包内 demo_codes/skills_library/
  • 约定一级子目录 = 一个技能 ;每个子目录下 SKILL.md 为硬入口文件名(与 Agent Skills 一致)。
  • 准备技能包 :按包内 skills_library/README.md ,从 SkillMD、Anthropic 官方仓库等下载 ZIP 或复制子文件夹到上述目录(例如 summarize/pdf/)。

7.3 发现节点:discover_skills(只抬「菜单」,不上菜)

作用 :遍历技能根下子目录,仅解析 frontmatter ,跳过无 SKILL.md 或解析失败的目录;返回的列表即 「当前机器上可选技能索引」

python 复制代码
def discover_skills(skills_root: Optional[Path] = None) -> List[Dict[str, str]]:
    root = Path(skills_root) if skills_root else DEFAULT_SKILLS_LIBRARY
    if not root.is_dir():
        return []
    result: List[Dict[str, str]] = []
    for path in sorted(root.iterdir()):
        if not path.is_dir():
            continue
        skill_md = path / SKILL_FILENAME  # 通常为 SKILL.md
        if not skill_md.is_file():
            continue
        raw = skill_md.read_text(encoding="utf-8", errors="replace")
        meta = _parse_frontmatter(raw)
        if meta.get("name"):
            result.append(meta)
    return result

典型输出形态 (节选):打印多行 - name: description 前 60 字...,便于人眼确认 L1 元数据是否加载成功。

7.4 选择节点:select_skill_for_task(把自然语言路由到技能名)

作用 :给定用户任务文本,在 已发现 的技能里挑出 一个 name 。示例实现刻意 简单、可预测 :先 小写 + strip ,再按 关键词 → 目标技能名 扫描;命中则返回该名;否则 退回列表第一个 (教学上避免「无技能可选」卡死,生产应显式处理 None)。

python 复制代码
keywords: List[Tuple[List[str], str]] = [
    (["总结", "摘要", "summarize", "压缩", "汇总"], "summarize"),
    (["pdf", "文档"], "pdf"),
    (["doc", "word", "docx"], "docx"),
    (["协作", "coauthor", "文档协作"], "doc-coauthoring"),
]
# ... 命中则返回对应 discovered 项中的 name;否则返回 discovered[0]

🔍 实际例子 :任务写「请总结下面这段话...」→ 命中「总结」类关键词 → 若库中存在 summarize 技能则选中它;若你库里只有 doc-coauthoring,则可能落到 默认第一个 ------这正是 关键词路由的局限 ,也解释为何生产常改用 LLM 选择

7.5 加载节点:load_skill(按需读全文,拆出正文)

作用 :在 已选定 skill_name 之后,才打开 skills_library/<skill_name>/SKILL.md 全文;返回 (full, body) ,其中 body 去掉 YAML 头,供下游 只把「可执行说明」塞进 prompt,减少无关节点污染 system 槽位。

python 复制代码
def load_skill(skill_name: str, skills_root: Optional[Path] = None) -> Tuple[str, str]:
    root = Path(skills_root) if skills_root else DEFAULT_SKILLS_LIBRARY
    skill_md = (root / skill_name) / SKILL_FILENAME
    if not skill_md.is_file():
        return "", ""
    try:
        full = skill_md.read_text(encoding="utf-8", errors="replace")
    except OSError:
        return "", ""
    body = full
    m = FRONTMATTER_PATTERN.match(full)
    if m:
        body = full[m.end() :].strip()
    return full, body

若文件不存在或读失败,示例返回空串并由 入口脚本 raise SystemExit(...) 提示用户检查路径------这是 「加载失败 = 硬错误」 的显式风格,便于初学者排障。

7.6 使用节点:use_skill_with_llm(LangChain 把「说明书 + 用户话」交给模型)

作用 :把 load_skill 得到的 body 作为 技能说明 ,与用户任务一起交给 ChatOpenAI ;依赖 langchain-openai / langchain-core (未安装则返回友好提示字符串)。API Key / BASE_URL / MODEL 可由 config_parser.skills_config.env 读取,与主示例其它篇一致。

python 复制代码
prompt = ChatPromptTemplate.from_messages([
    ("system", "你正在使用名为「{skill_name}」的 Agent Skill。请严格按照以下技能说明执行用户请求。\n\n技能说明:\n{skill_instructions}"),
    ("user", "{user_input}"),
])
chain = prompt | llm | StrOutputParser()
return (chain.invoke({...}) or "").strip()

💡 理解要点 :这一步等价于 OpenClaw Brain 里「把 read 到的 SKILL 正文并入上下文再让模型推理」 ------差别在于:这里是 单次链式调用 ;OpenClaw 里往往还有 工具循环、沙箱、多轮会话

7.7 入口节点 main.py 与配套文件

文件 节点角色 说明
main.py 编排器(Orchestrator) 严格顺序discover_skillsselect_skill_for_taskload_skilluse_skill_with_llm;支持 python main.py "任务..." 从命令行传入任务。
skill_loader.py B~E 的实现载体 含发现、选择、加载、使用的全部 Python 逻辑;可与单元测试直接对打。
config_parser.py 配置适配 .env 读取 OPENAI_API_KEY / DASHSCOPE_API_KEYBASE_URLMODEL,供节点 E 使用。
requirements.txt 依赖清单 langchain-openailangchain-corepython-dotenv 等。
main.ipynb 交互式走读 逐步打印「已发现技能」「选中技能」「正文长度」等,适合课堂演示。
README.md 人类文档 CSDN 包 内说明互补,以本地解压版本为准。

7.8 如何运行(与示例 README 一致)

  1. 进入 demo_codes ,创建虚拟环境并 pip install -r requirements.txt
  2. 配置 .env (至少一种模型 Key;可选 BASE_URLMODEL)。
  3. §7.2skills_library/ 下放入至少一个合规技能目录。
  4. 执行 python main.pypython main.py "你的任务描述" ;或打开 main.ipynb 逐步运行节点 B~E。

7.9 与 OpenClaw 运行时对照:一张表纠正常见误读

下面左列概括 本教学脚手架 的设定,右列对齐 OpenClaw 当前主线的真实行为 (以 openclaw/openclawsrc/ + D1 docs/tools/skills.md 为准)。其中关于 SKILLS.md 的一行,专门回应一些旧对照表 里的写法:它们常把 OpenClaw 的「能力可见性」说成 「安装器维护一个总表 SKILLS.md ------这与 当前内核实现 并不一致,读源码时应以 「扫描各 SKILL.md + 拼 <available_skills> +(可选)会话 skillsSnapshot 为准(详见 §8)。

维度 LangGraph 式小演示(「四门功课」脚手架) OpenClaw 运行时
技能形态 文件夹 + SKILL.md(YAML frontmatter + 正文) 主流一致;部分技能包还可带 manifest / 脚本等(以具体技能与 D1 为准)
技能根目录 示例里常固定为单一 skills_library/ 多根路径 + 显式优先级<workspace>/skills<workspace>/.agents/skills~/.agents/skills~/.openclaw/skills、捆绑包、skills.load.extraDirs 等(§5)
注册 / 配置 往往 没有 与内核同级的全局 JSON;「有没有子目录」≈「装没装」 openclaw.json 中的 skills/agents.defaults.skills/agents.list[].skills 等,管 启用、密钥、按 Agent 的 allowlist(§6)
能力可见性 discover_skills() 的返回列表(打印或内存),不承诺落盘成某个总表文件 运行时拼接 formatSkillsForPrompt / formatSkillsCompact<available_skills> XML ,并写入会话 skillsSnapshot.prompt 缓存;不应 简单映射为「必须存在根目录聚合 SKILLS.md
发现→选择→加载→使用 四函数显式调用,顺序由 main.py 写死 同思路、不同挂载点 :发现/过滤在 workspace.ts + local-loader.ts ;加载由模型 read;Gateway 把「目录摘要」提前注入提示(§8)
安装 / 更新 / 移除 多为 手工拷贝/删除 子目录 /skills ...openclaw skills ... + ClawHub ;并写 .clawhub/lock.json / origin.json(§6)
热更新 通常需 重新跑脚本 或重启进程才再 discover ensureSkillsWatcher + bumpSkillsSnapshotVersion ,让会话快照在 后续用户轮 可失效重建(§8.5)

8 源码:从「磁盘上的 SKILL.md」到「会话里的技能快照 Snapshot」

8.1 只监听技能入口文件

src/agents/skills/refresh.ts 里,resolveWatchTargets 明确注释:技能由 SKILL.md 定义,只监听这些文件,避免监听无关大目录耗尽文件描述符:

ts 复制代码
// openclaw/src/agents/skills/refresh.ts --- resolveWatchTargets(节选)
function resolveWatchTargets(workspaceDir: string, config?: OpenClawConfig): string[] {
  // Skills are defined by SKILL.md; watch only those files to avoid traversing
  // or watching unrelated large trees (e.g. datasets) that can exhaust FDs.
  const targets = new Set<string>();
  for (const root of resolveWatchPaths(workspaceDir, config)) {
    const globRoot = toWatchGlobRoot(root);
    targets.add(`${globRoot}/SKILL.md`);
    targets.add(`${globRoot}/*/SKILL.md`);
  }
  return Array.from(targets).toSorted();
}

resolveWatchPaths 则把 skills/.agents/skills/~/.openclaw/skills~/.agents/skillsskills.load.extraDirs、插件技能目录 等拼成监听根集合(同文件 resolveWatchPaths)。

💡 理解要点 :热更新不是「监听整个 workspace」,而是 围绕技能入口文件的稀疏监听

8.2 本地加载:openVerifiedFileSync + frontmatter + 最小必填字段

src/agents/skills/local-loader.ts 在读取 SKILL.md 时使用 openVerifiedFileSync ,并校验 真实路径落在技能根内 ;随后解析 frontmatter,并要求 namedescription 非空,否则该目录不算有效技能:

ts 复制代码
// openclaw/src/agents/skills/local-loader.ts --- loadSingleSkillDirectory(节选)
function loadSingleSkillDirectory(params: {
  skillDir: string;
  source: string;
  rootRealPath: string;
  maxBytes?: number;
}): Skill | null {
  const skillFilePath = path.join(params.skillDir, "SKILL.md");
  const raw = readSkillFileSync({
    rootRealPath: params.rootRealPath,
    filePath: skillFilePath,
    maxBytes: params.maxBytes,
  });
  if (!raw) {
    return null;
  }
  let frontmatter: Record<string, string>;
  try {
    frontmatter = parseFrontmatter(raw);
  } catch {
    return null;
  }
  const fallbackName = path.basename(params.skillDir).trim();
  const name = frontmatter.name?.trim() || fallbackName;
  const description = frontmatter.description?.trim();
  if (!name || !description) {
    return null;
  }
  // ... 后续组装 Skill 对象

这与 Agent Skills 规范里对 元数据可发现性 的要求同向:没有描述,就不该出现在自动发现列表里,否则模型只能瞎猜何时加载。

8.3 拼进模型上下文的 XML:formatSkillsForPrompt

src/agents/skills/skill-contract.ts 中的 formatSkillsForPrompt 负责把技能数组渲染成 与上游 Agent Skills 格式化器字节对齐 的 XML(文件头注释写明意图),并再次强调 用 read 工具加载相对路径相对于技能目录解析

ts 复制代码
// openclaw/src/agents/skills/skill-contract.ts --- formatSkillsForPrompt(节选)
export function formatSkillsForPrompt(skills: Skill[]): string {
  if (skills.length === 0) {
    return "";
  }
  const lines = [
    "\n\nThe following skills provide specialized instructions for specific tasks.",
    "Use the read tool to load a skill's file when the task matches its description.",
    "When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
    "",
    "<available_skills>",
  ];
  for (const skill of skills) {
    lines.push("  <skill>");
    lines.push(`    <name>${escapeXml(skill.name)}</name>`);
    lines.push(`    <description>${escapeXml(skill.description)}</description>`);
    lines.push(`    <location>${escapeXml(skill.filePath)}</location>`);
    lines.push("  </skill>");
  }
  lines.push("</available_skills>");
  return lines.join("\n");
}

同目录下的 compact-format.test.ts@mariozechner/pi-coding-agent 的上游实现做对齐测试------说明 OpenClaw 把自己定位在 「兼容生态的事实标准」 上,而不是另起一套提示语文案。

8.4 Token 预算:全量 → 紧凑(仅 name+location)→ 截断

当技能很多时,src/agents/skills/workspace.tsapplySkillsPromptLimits 会依次尝试:

  • 保留 完整 formatSkillsForPrompt
  • 超长则降级为 formatSkillsCompact(去掉 description)
  • 仍超长则 二分前缀 找到能塞进预算的最大子集,并附加 openclaw skills check 审计提示

这保证了 「先丢描述、再丢条目」 的降级顺序,与渐进式披露理念一致:宁可少给摘要,也不要静默把技能整个消失而不留痕迹

下列摘自 OpenClaw 仓库 src/agents/skills/workspace.ts (与篇首 pin 的 commit 0dd4958bc8a78d26b3b526b1f2e63b15110c64a2 一致):先看 formatSkillsCompact (仅保留 name + location),再看 applySkillsPromptLimits 如何在「全量 XML 放不下 → 紧凑仍放不下 → 二分截断前缀」之间切换;最后在 resolveWorkspaceSkillPromptState 里拼接 openclaw skills check 提示行。

ts 复制代码
// formatSkillsCompact --- 无 description,先保住「还有哪些技能」
export function formatSkillsCompact(skills: Skill[]): string {
  if (skills.length === 0) return "";
  const lines = [
    "\n\nThe following skills provide specialized instructions for specific tasks.",
    "Use the read tool to load a skill's file when the task matches its name.",
    "When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
    "",
    "<available_skills>",
  ];
  for (const skill of skills) {
    lines.push("  <skill>");
    lines.push(`    <name>${escapeXml(skill.name)}</name>`);
    lines.push(`    <location>${escapeXml(skill.filePath)}</location>`);
    lines.push("  </skill>");
  }
  lines.push("</available_skills>");
  return lines.join("\n");
}

const COMPACT_WARNING_OVERHEAD = 150;

function applySkillsPromptLimits(params: { skills: Skill[]; config?: OpenClawConfig }): {
  skillsForPrompt: Skill[];
  truncated: boolean;
  compact: boolean;
} {
  const limits = resolveSkillsLimits(params.config);
  const total = params.skills.length;
  const byCount = params.skills.slice(0, Math.max(0, limits.maxSkillsInPrompt));

  let skillsForPrompt = byCount;
  let truncated = total > byCount.length;
  let compact = false;

  const fitsFull = (skills: Skill[]): boolean =>
    formatSkillsForPrompt(skills).length <= limits.maxSkillsPromptChars;

  const compactBudget = limits.maxSkillsPromptChars - COMPACT_WARNING_OVERHEAD;
  const fitsCompact = (skills: Skill[]): boolean =>
    formatSkillsCompact(skills).length <= compactBudget;

  if (!fitsFull(skillsForPrompt)) {
    if (fitsCompact(skillsForPrompt)) {
      compact = true;
    } else {
      compact = true;
      let lo = 0;
      let hi = skillsForPrompt.length;
      while (lo < hi) {
        const mid = Math.ceil((lo + hi) / 2);
        if (fitsCompact(skillsForPrompt.slice(0, mid))) {
          lo = mid;
        } else {
          hi = mid - 1;
        }
      }
      skillsForPrompt = skillsForPrompt.slice(0, lo);
      truncated = true;
    }
  }

  return { skillsForPrompt, truncated, compact };
}
ts 复制代码
// resolveWorkspaceSkillPromptState --- 截断/紧凑时追加审计提示,再选择 formatter
const truncationNote = truncated
  ? `⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}${compact ? " (compact format, descriptions omitted)" : ""}. Run \`openclaw skills check\` to audit.`
  : compact
    ? `⚠️ Skills catalog using compact format (descriptions omitted). Run \`openclaw skills check\` to audit.`
    : "";
const prompt = [
  remoteNote,
  truncationNote,
  compact ? formatSkillsCompact(skillsForPrompt) : formatSkillsForPrompt(skillsForPrompt),
]
  .filter(Boolean)
  .join("\n");

8.5 会话首包与快照版本:session-updates.ts

这一节回答一个朴素问题:技能目录里的 SKILL.md 被人改了、或新装了一个技能之后,正在聊天里的 Agent 什么时候会「看见」新目录? 若每一轮对话都重新扫描整个 skills/ 树并重新拼一大段 XML,磁盘 I/O 和 CPU 会浪费 ;若只在进程启动时扫一次,热更新又没了 。OpenClaw 的做法是:把「当时拼好的技能提示块」缓存在会话条目里,叫 skillsSnapshot;再用一个单调递增的「技能快照版本号」判断缓存是否过期

8.5.1 三个名词,先对齐
名词 你可以把它想成... 在代码里做什么
Session(会话) 一条「和用户连着聊」的上下文线,对应会话存储里的一条记录 SessionEntry 里除了 sessionId、消息指针等,还可以挂 skillsSnapshot
skillsSnapshot 一张已经算好的「技能菜单贴纸」 :里面有拼进系统侧的 prompt 字符串 (即 §8.4 那套 XML/紧凑格式 + 可能的截断提示)、技能名列表、以及生成时的 version buildWorkspaceSkillSnapshot 算出来,写回会话条目,后续轮次能 复用
快照 version 菜单贴纸左上角印的批次号;磁盘上任意技能相关变更会把「批次号」加大 getSkillsSnapshotVersion(workspaceDir) ;变更时由 watcher 等路径调用 bumpSkillsSnapshotVersion (见 src/agents/skills/refresh-state.ts

💡 理解要点skillsSnapshot 不是 SKILL.md 原文缓存 ,而是「已经格式化、可能已截断、准备塞进模型上下文的那一段说明文字 」的快照;模型真要读某技能细节,仍然走 read 打开文件(§8.3)。

8.5.2 ensureSkillsWatcher:谁在「盯着」技能文件?

src/agents/skills/refresh.ts 里的 ensureSkillsWatcher 会在 每个 workspace 上挂一层 稀疏文件监听 (主要看各技能根下的 SKILL.md / */SKILL.md,见 §8.1)。当文件有增删改,监听逻辑会触发 bumpSkillsSnapshotVersion({ workspaceDir, ... }) ------等价于通知全进程:「这个工作区的技能集合变了,批次号 +1」

src/auto-reply/reply/session-updates.ts 导出的 ensureSkillSnapshot (注意名字里带 Skill)在每一轮需要技能快照时 先调用 ensureSkillsWatcher,保证:你改文件的那一刻起,版本号已经准备好;下一轮会话逻辑就能发现「贴纸过期」

8.5.3 何时重算快照?shouldRefreshSnapshotForVersion + 过滤器

核心判断在 ensureSkillSnapshot 里(节选如下;逻辑以你 pin 的 commit 为准):

ts 复制代码
// src/auto-reply/reply/session-updates.ts --- ensureSkillSnapshot(节选)
const snapshotVersion = getSkillsSnapshotVersion(workspaceDir);
const existingSnapshot = nextEntry?.skillsSnapshot;
ensureSkillsWatcher({ workspaceDir, config: cfg });
const shouldRefreshSnapshot =
  shouldRefreshSnapshotForVersion(existingSnapshot?.version, snapshotVersion) ||
  !matchesSkillFilter(existingSnapshot?.skillFilter, skillFilter);
const buildSnapshot = () =>
  buildWorkspaceSkillSnapshot(workspaceDir, {
    config: cfg,
    agentId: sessionAgentId,
    skillFilter,
    eligibility: { remote: remoteEligibility },
    snapshotVersion,
  });
  • shouldRefreshSnapshotForVersion(缓存版, 当前版)refresh-state.ts):若 当前版 > 缓存版 ,说明自上次生成快照以来 至少发生过一次「技能变更事件」 ,必须 重扫并重拼 prompt。特殊地,若当前版为 0 而缓存版已大于 0,也会刷新------避免「版本号被重置」后仍误用旧贴纸。
  • matchesSkillFilter :若本轮对话携带的 skillFilter (例如按通道只允许部分技能)与快照里记录的不一致,即使磁盘没变 也要重建------否则 Agent 会看到 不该暴露 的技能菜单。
8.5.4 「首轮对话」和「后续轮次对话」分别发生什么?

仍看 ensureSkillSnapshot 的分支(可与上节节选连读):

  1. isFirstTurnInSession === true(本会话的第一轮用户消息)

    若会话条目里还没有 skillsSnapshot,或 shouldRefreshSnapshot 为真,就 buildSnapshot() ,并把结果连同 systemSent: true 等写回 会话存储persistSessionEntryUpdate)。直观理解:新会话的「第一声铃」要把系统侧该准备的东西(含技能菜单贴纸)一次性备齐

  2. 后续轮次

    若发现 已有 skillsSnapshot不需要 shouldRefreshSnapshot,就 继续用旧贴纸 ,避免每轮扫盘。

    需要刷新 (版本号变了或 filter 变了),即便不是首轮,也会在 !isFirstTurnInSession 分支里 再次 persistSessionEntryUpdate ,把新快照写回会话------于是 下一轮模型读到的会话元数据里已经是新菜单

  3. 测试快速路径

    若环境变量 OPENCLAW_TEST_FAST=1 ,该函数会直接返回、不写会话、不挂 watcher ------单元测试里另有用例覆盖快照行为,正文读者只需知道:生产路径才会走完整逻辑

8.5.5 和 §8.4 的关系(把拼图合上)
  • §8.4 解决的是:「贴纸」上能写多少字maxSkillsPromptChars、紧凑、截断)。
  • §8.5 解决的是:「贴纸」何时重印(版本号 + 会话持久化 + 首轮/后续轮写回策略)。

合起来:技能文件一改 → watcher bump 版本 → 下轮 ensureSkillSnapshot 发现版本落后 → 调用 buildWorkspaceSkillSnapshot 重算(内部再走 §8.4 预算逻辑)→ 新 prompt 写入 skillsSnapshot 。因此 一般不必重启 Gateway 才能看到新技能;除非你的客户端始终不触发会话更新路径(那属于集成问题,而非 Skills 子系统设计目标)。

🔍 实际例子 :你在 <workspace>/skills/ 下新增一个 my-corp/SKILL.md 并保存;chokidar 触发 bump;同一 Agent 同一会话里 下一笔用户消息 到来时,shouldRefreshSnapshotForVersion 为真,会话文件里的 skillsSnapshot.prompt 被换成带新技能条目的 XML------用户感知为「下一轮它就认识了新技能」。
会话层
§8.5 版本层(何时重印)
§8.4 预算层(贴纸内容)
bump
当前版 > 缓存版

或 filter 变化
版本未变
内部调用
生成
是首轮
后续轮次
无需刷新
需要刷新
maxSkillsPromptChars

紧凑格式 / 截断逻辑
chokidar watcher

监听 skills/ 目录
全局版本号

snapshotVersion
shouldRefreshSnapshotForVersion

对比缓存版 vs 当前版
isFirstTurnInSession?

首轮对话
skillsSnapshot.prompt

XML 技能菜单贴纸
persistSessionEntryUpdate

持久化到会话存储
用户修改 SKILL.md

或新增技能文件
下一笔用户消息到来
ensureSkillSnapshot
调用 buildWorkspaceSkillSnapshot
继续用旧贴纸
新的 skillsSnapshot.prompt
buildSnapshot()

systemSent: true
已有快照?

需要刷新?
再次 persistSessionEntryUpdate

写回新快照
模型读取 XML 菜单

感知新技能


9 安全与治理:把第三方技能当不可信代码

官方 Skills 文档 Security notes 强调:第三方技能应视为不可信代码 ;工作区发现路径需落在配置根内;Gateway 背书的依赖安装路径会跑 危险代码扫描critical 默认拦截等)。这与前文 openVerifiedFileSync + realpath 守卫 形成纵深:读文件跑安装脚本两道门槛。

🔍 实际例子 :从 ClawHub 装技能前,先在仓库网页或解压目录里读一遍 SKILL.md 与脚本;生产环境优先沙箱执行、最小化 API Key 暴露面------Skills 再方便,也不改变「供应链 = 攻击面」这一事实。


10 和本篇相关的后续篇目

  • 第5篇 Brain :工具循环里 read 如何真正打开 SKILL.md、多轮上下文如何增长。
  • 第6篇 Hands:沙箱与执行面如何承接技能文档里描述的命令。
  • 第10篇 多 Agentagents.list[].skills 替换式 allowlist 与「默认不限制」的差异(见 D1 文档示例)。
  • 第14篇 安全与成本:供应链、密钥与按 Agent 选模型。

11 本篇小结

  • 概念上 :Skills = Agent Skills 兼容目录 + 渐进式披露;与 Built-in、Plugins 分层清晰。
  • 对照上 :§7 以 Mermaid 架构图 + 节点表 走读 skills_library/ → discover → select → load → use**(示例源码见参考文献第 8 条),§7.9 专门纠正旧对照里 **SKILLS.md` = OpenClaw 可见性 的简化说法。
  • 产品上 :ClawHub 负责 安装/版本/锁文件 ;原生 CLI openclaw skills ... 与 UI 技能页共享同一套后端能力叙述(以文档为准)。
  • 源码上 (§8 起):稀疏监听 SKILL.md → 安全读取与 frontmatter → XML 目录注入 → 预算降级 → 会话快照 构成主链路;本地加载器以 SKILL.md 为硬文件名,与 ClawHub 解压兼容层(skills.md 等)略有分工。
  • 澄清 :在当前 openclaw/openclaw 仓库的 src/ 实现中,并未出现名为 SKILLS.md 的聚合清单文件生成逻辑 ;「技能对模型可见」主要来自 运行时生成的提示文本 / skillsSnapshot.prompt 。若你在其它工具链里看到 SKILLS.md 叙述,请 以本仓库 S0+D1 为准 做概念对齐,避免混用两套机制。

下一篇(第3篇)Gateway ------长驻控制面、单端口多协议、单实例锁与启动链;见 03_OpenClaw_Gateway.md


12 参考文献与链接

  1. OpenClaw 主仓库:https://github.com/openclaw/openclaw
  2. OpenClaw 文档(D1,Skills):https://github.com/openclaw/openclaw/blob/0dd4958bc8a78d26b3b526b1f2e63b15110c64a2/docs/tools/skills.md
  3. Agent Skills 规范(D3,社区规范):https://agentskills.io/specification
  4. Agent Skills 概念页(渐进式披露,D3):https://agentskills.io/what-are-skills
  5. 如何把 Skills 接进自有 Agent(D3):https://agentskills.io/integrate-skills
  6. AgentScope 文档中的 Skills / Progressive Disclosure(D3,类比阅读):https://java.agentscope.io/en/multi-agent/skills.html
  7. ClawHub:https://clawhub.ai;仓库:https://github.com/openclaw/clawhub
  8. 与本篇 §7 配套的 Skills 四门功课 可运行示例(ZIP,CSDN):https://download.csdn.net/download/zyctimes/92728873
  9. Skills 系列博客1:LangGraph 7 · 技能 Skills
  10. Skills 系列博客2:LangGraph 17 · 三级加载与 Token 优化
  11. Skills 系列博客3:LangGraph 18 · Skill 四种形态
  12. Skills 系列博客4:LangGraph 19 · SkillToolset
  13. Skills 系列博客5:LangGraph 20 · Meta Skills
  14. Skills 系列博客6:LangGraph 21 · Skills 6 · 从意图到可上线技能
相关推荐
空中湖2 小时前
光计算:用“光“代替“电“,AI算力的下一场革命
人工智能
小江的记录本2 小时前
【 AI工程化】AI工程化:MLOps、大模型全生命周期管理、大模型安全(幻觉、Prompt注入、数据泄露、合规)
java·人工智能·后端·python·机器学习·ai·架构
quetalangtaosha2 小时前
Anomaly Detection系列(CVPR2025 TailedCore论文解读)
人工智能·计算机视觉
Flying pigs~~2 小时前
企业级模块化RAG项目(mysql➕redis➕milvus➕模型微调➕bm25➕fastapi➕ollama➕Prompt➕多策略选择)
人工智能·redis·mysql·docker·prompt·milvus·rag
小oo呆2 小时前
【自然语言处理与大模型】Harness Engineering是什么?和提示词工程、上下文工程有什么关系?
人工智能·自然语言处理
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年4月16日
人工智能·python·信息可视化·自然语言处理·ai编程
颯沓如流星2 小时前
从Prompt Engineering到Harness Engineering:游戏服务器开发的AI工程范式
人工智能·游戏·prompt
志栋智能2 小时前
超自动化运维如何重塑IT组织的核心竞争力?
运维·服务器·网络·数据库·人工智能·自动化
视觉&物联智能2 小时前
【杂谈】-筑牢企业防线:抵御恶意人工智能代理的攻击
人工智能·网络安全·ai·企业安全·agi