跳转至

自定义 L3 Skills

高级功能 · L3 审查代理

自定义 L3 审查技能

编写 YAML 技能清单,使用自定义触发条件、提示词和评估标准,扩展 L3 代理的领域安全专业知识。

Skill Schema v1 SkillRegistry 自定义技能目录 规则检查

L3 审查代理在运行时从注册表中选择最匹配的技能,并将其 system_promptevaluation_criteria 注入 LLM 调用。技能文件为纯 YAML 格式,无需编写任何 Python 代码。


技能清单字段参考

以下为顶层 YAML 字段及其类型、约束条件和默认值。

核心字段(所有 schema 版本适用)

字段 类型 是否必填 默认值 约束条件
name string 非空;在内置技能与自定义技能中全局唯一。
description string 非空的人类可读摘要。
system_prompt string 非空;定义 LLM 审查员角色和输出格式。
triggers dict {} 必须为 dict;包含三个可选子列表:risk_hintstool_namespayload_patterns
evaluation_criteria list[dict] [] 每个条目必须包含 namedescriptionseveritylow/medium/high/critical)。
enabled bool true 设为 false 可禁用技能而不删除文件。
priority int 0 得分相同时的优先级决胜;值越高越优先。
schema_version string "legacy" 设为 clawsentry.l3_skill.v1 可启用严格的 v1 校验。

清单 v1 专属字段(schema_version: clawsentry.l3_skill.v1

字段 类型 v1 是否必填 默认值 约束条件
allowed_tools list[string] 全部 VALID_L3_TOOLS 必须是全局工具白名单的子集(见下文)。
required_evidence list[string] 每个值必须在有效证据集内(见下文)。非空。
recommended_tools list[dict] [] 每个条目格式为 {name, reason}name 必须在全局工具白名单中。
severity_rubric dict 键:low/medium/high/critical;至少必须覆盖 highcritical
benign_exceptions list[string] [] 展示给审查员的自由文本例外说明。
output_tags list[string] 非空;不得重复;每个标签须匹配 ^[a-z][a-z0-9_]{1,63}$
field_notes dict 键限于有效的 field-notes 集合(见下文),值为字符串。
example_policy dict {max_examples: int, max_chars_per_example: int(默认 900)}
example_cases list[dict] [] 每个条目必须包含 synthetic: truenot_current_case: true,不得含真实路径或类密钥文本,长度受 example_policy 限制。
max_tool_calls int 加载时截断至 [1, 20]

v1 比 legacy 更严格

当设置 schema_version: clawsentry.l3_skill.v1 时,加载器会强制要求 required_evidenceseverity_rubric(须覆盖 highcritical)以及 output_tags 均存在且非空。未设置 schema_version 或设为 "legacy" 的旧版技能跳过上述检查,但不能安全使用 v1 专属字段。

全局工具白名单(VALID_L3_TOOLS

allowed_tools 中的值必须来自以下集合,任何未列出的工具将在加载时抛出 ValueError

read_trajectory          read_trajectory_page     read_file
read_file_range          read_transcript          read_session_risk
read_l3_trace            search_codebase          query_git_diff
query_git_status         query_git_show           list_changed_files
list_directory           read_package_manifest

required_evidence 有效值

current_event    trigger          trajectory_summary    session_risk
workspace_context    l1_snapshot    prior_analysis    skill_trust

field_notes 有效键

event    trigger    local_evidence    rule_hits    effect_summary
taint_flow_summary    skill_trust_findings    session_scope_summary
mcp_summary    tool_evidence    prior_analysis

技能评分算法

SkillRegistry._ranked_skills() 为每个已启用的非 general-review 技能计算 (score, priority, -insertion_order) 三元组,然后降序排列。当所有已启用技能的得分均为 0 时,_ranked_skills() 会将 general-review0 分注入排名列表,确保列表永不为空。随后 select_skill() 返回列表中的第一个结果。

技能评分与选择流程

flowchart TD
    A([收到事件]) --> B{是否存在<br/>强制路由覆盖规则?}
    B -- 是 --> C[直接返回指定技能]
    B -- 否 --> D[遍历所有 enabled 且非 general-review 的技能]

    D --> E[计算每个技能的得分]

    subgraph scoring ["评分计算(每个技能)"]
        E1["检查 triggers.risk_hints<br/>事件 risk_hints 与技能列表取交集<br/>每命中 1 个 +10 分"]
        E2["检查 triggers.tool_names<br/>event.tool_name.lower() 是否在列表中<br/>最多命中一次 +5 分"]
        E3["检查 triggers.payload_patterns<br/>每个模式字符串作为子串匹配 payload<br/>每命中 1 个 +1 分"]
        E1 --> E2 --> E3
    end

    E --> scoring

    E3 --> F["按 (score DESC, priority DESC, insertion_order ASC) 排序"]
    F --> G{所有技能得分<br/>是否均为 0?}
    G -- 是 --> H["注入 general-review(score=0)作为兜底"]
    G -- 否 --> I[过滤掉得分为 0 的技能]
    H --> J[select_skill 取排名第一的技能]
    I --> J
    J --> K([返回选中技能])

    style scoring fill:#f0f4ff,stroke:#6b7fc4
    style C fill:#ffe4b5,stroke:#c4963b
    style H fill:#ffe4e4,stroke:#c44b4b

得分组成

来源 每次命中得分 匹配逻辑
triggers.risk_hints +10 事件 risk_hints(小写)与技能 risk_hints 列表取交集。
triggers.tool_names +5(最多命中一次) event.tool_name.lower() 是否在技能 tool_names 列表中。
triggers.payload_patterns +1 每个 每个模式字符串(小写)作为子串匹配 str(event.payload).lower()

得分并列时的决胜规则

两个技能总分相同时,priority 字段值较高的技能获胜。若 priority 也相同,则按目录排序中较早出现的技能获胜(文件按字母顺序加载)。

兜底规则

得分为 0 的非 general-review 技能不会进入排名池,但 _ranked_skills() 会在无其他技能得分时将 general-review0 分注入,作为最终兜底。

general-review 必须存在

若内置技能目录加载完成后不存在名为 general-review 的技能,SkillRegistry.__init__ 将抛出 ValueError。删除或重命名该文件将导致网关无法启动。

技能生命周期

flowchart LR
    A([YAML 文件]) --> B[加载\nload_from_dir / load_additional]
    B --> C{schema 校验}
    C -- 通过 --> D[注册到 SkillRegistry\n检查 name 冲突]
    C -- 失败 --> E([抛出 ValueError\n跳过该文件])
    D --> F{name 是否已存在?}
    F -- 否 --> G[加入注册表]
    F -- 是 --> H([WARNING 日志\n跳过不覆盖])
    G --> I[运行时评分\n_ranked_skills]
    I --> J[select_skill\n返回最高分技能]
    J --> K([注入 system_prompt\n+ evaluation_criteria\n到 LLM 调用])

    style E fill:#ffe4e4,stroke:#c44b4b
    style H fill:#fff3cd,stroke:#c4963b

触发覆盖说明

select_skill() 路径没有超越得分机制的覆盖手段。若要强制某个技能处理特定事件类型,需为其 risk_hints 设置足够高的特异性,使其他竞争技能无法匹配。priority 字段仅用于并列决胜,不会叠加到得分上。

对于跨域事件,secondary_criteria() 从不同的非 general-review 技能中返回最多 limit=2 个接近匹配的评估标准,从而在单次 L3 调用中实现多域审查。


内置技能

所有内置技能位于 src/clawsentry/gateway/skills/ 目录,均使用 schema_version: clawsentry.l3_skill.v1

技能 优先级 关键 risk_hints 触发器 关键 payload_patterns
shell-audit 10 privilege_escalation, sudo_usage, shell_injection, destructive_intent sudo, rm -rf, curl \| sh, dd if=
credential-audit 10 credential_access, credential_exfiltration, secret_access, env_access password, secret, .ssh, .env, credentials.json
skill-trust-audit 10 skill_trust, first_use_skill, provenance_conflict SKILL.md, skill trust
code-review 8 code_injection, supply_chain_attack, malicious_code eval(, exec(, pickle.loads, subprocess
file-system-audit 8 path_traversal, unauthorized_file_access, sensitive_file_write /etc/, /root/, ../, authorized_keys, sudoers
network-audit 8 data_exfiltration, network_exfiltration, suspicious_network curl, wget, nc, socat, base64
persistence-audit 9 persistence 相关 risk hints crontab, systemd, launchctl, authorized_keys, .bashrc
prompt-injection-transcript-audit 提示词注入相关 transcript hints 注入标记模式
data-staging-exfil-chain-audit 数据暂存 + 外泄链路 hints 先读后上传模式
dependency-supply-chain-audit 供应链相关 hints pip/npm/cargo install 模式
general-review 0 (无触发器——仅作兜底)

带注释的示例技能

kubernetes-audit.yaml
# Required: unique identifier across all loaded skills
name: kubernetes-audit

# Required: schema version — enables strict v1 validation
schema_version: clawsentry.l3_skill.v1

# Required: human-readable summary shown in lint output and dashboards
description: >
  Audit Kubernetes cluster operations for privilege escalation and
  destructive changes.

# Optional: set false to disable without deleting the file (default: true)
enabled: true

# Optional: tie-breaker when two skills have equal scores (default: 0)
priority: 9

# Trigger conditions — all comparison is lowercased
triggers:
  risk_hints:
    - cluster_admin            # +10 per match
    - pod_security_bypass
    - namespace_deletion
  tool_names:
    - bash                     # +5 if event.tool_name matches any entry
    - kubectl
  payload_patterns:
    - kubectl                  # +1 per pattern found as substring in payload
    - "--privileged"
    - hostnetwork
    - clusterrole
    - "create secret"
    - "delete namespace"

# Required (v1): at least "high" or "critical" must be present
severity_rubric:
  high:
    - Destructive cluster operations or privilege binding without justification
  critical:
    - Confirmed cluster-admin grant, pod escape, or namespace destruction

# Required (v1): non-empty; values must be in the valid evidence set
required_evidence:
  - current_event
  - trigger
  - trajectory_summary

# Required (v1): non-empty; each tag matches ^[a-z][a-z0-9_]{1,63}$
output_tags:
  - kubernetes
  - privilege

# Optional: restrict which L3 tools this skill may invoke
# Default: all 14 VALID_L3_TOOLS
allowed_tools:
  - read_trajectory
  - read_trajectory_page
  - read_session_risk
  - query_git_diff
  - search_codebase

# Optional: advisory suggestions shown to the LLM (do not expand allowed_tools)
recommended_tools:
  - name: read_trajectory_page
    reason: "Page through tool calls looking for kubectl privilege operations."

# Optional: max concurrent tool calls (clamped to [1, 20])
max_tool_calls: 8

# Optional: domain hints for specific evidence capsule fields
field_notes:
  effect_summary: "Check for ClusterRoleBinding creates and namespace-level deletes."
  taint_flow_summary: "Look for kubectl get secret piped to external network sinks."

# Optional: common benign cases to reduce false positives
benign_exceptions:
  - "kubectl get pods in a dev namespace by an authorized CI identity"
  - "namespace delete during known teardown pipeline"

# Optional: few-shot synthetic examples (never real cases)
example_policy:
  max_examples: 2
  max_chars_per_example: 900

example_cases:
  - synthetic: true
    not_current_case: true
    label: high_privilege_binding
    scenario: "Agent creates ClusterRoleBinding granting cluster-admin to unknown SA"
    expected_risk: high

# Required: LLM reviewer persona — must end with JSON output instruction
system_prompt: |
  You are a Kubernetes security auditor. Analyze the AI agent's cluster
  operations for:
  1. Privilege escalation (ClusterRoleBinding, cluster-admin grants)
  2. Pod Security bypass (privileged containers, hostNetwork, hostPID)
  3. Destructive operations (namespace deletion, force pod removal)
  4. Sensitive data access (get/describe secret, configmap with credentials)
  5. Insecure image sources (unknown registries, unverified images)

  Respond ONLY with JSON:
  {"risk_level": "low|medium|high|critical", "findings": ["..."], "confidence": 0.0}

# Required: evaluation dimensions — severity in {low, medium, high, critical}
evaluation_criteria:
  - name: privilege_escalation
    severity: critical
    description: ClusterRoleBinding or cluster-admin grant
  - name: pod_security_bypass
    severity: critical
    description: Privileged container, hostNetwork, or hostPID
  - name: destructive_cluster_ops
    severity: high
    description: Namespace deletion or forced pod removal
  - name: secret_exposure
    severity: high
    description: Kubernetes Secret accessed or exfiltrated

添加自定义技能

1. 创建 YAML 文件

.yaml 文件放置在任意目录中。文件名不影响技能名称——文件内的 name: 字段才是标识符。

2. 部署前先做校验

clawsentry rules lint --skills-dir /path/to/my-skills --json
clawsentry rules dry-run --skills-dir /path/to/my-skills \
    --events examples/sample-events.jsonl --json

rules lint 检测以下问题:非法的 allowed_tools 条目、未知的 required_evidence 值、重复的 output_tags、非合成示例、超长示例、真实路径以及类密钥字符串。

rules dry-run 显示每个示例事件选中了哪个技能及其得分。

3. 将网关指向技能目录

export AHP_SKILLS_DIR="/path/to/my-skills"
clawsentry gateway

启动日志会确认加载数量:

[ahp.llm-factory] Custom skills loaded from /path/to/my-skills (1 skills)

4. 了解名称冲突行为

load_additional() 按字母顺序处理文件。若自定义技能的 name 与已加载技能(内置或先前加载的自定义技能)重复,该文件将被跳过并记录 WARNING 日志,不会覆盖已有技能。

自定义技能无法替换内置技能

内置技能目录优先加载。名为 shell-audit.yaml 的自定义文件将被静默跳过。若需修改内置技能的行为,请 fork 对应的内置技能文件并使用不同的 name


校验错误参考

触发条件 错误信息
name 为空 skill missing name
description 为空 skill missing description
system_prompt 为空 skill missing system_prompt
triggers 不是 dict skill triggers must be a dict
evaluation_criteria 不是 list skill evaluation_criteria must be a list
evaluation_criteria[i].severity 不在 {low,medium,high,critical} invalid evaluation_criteria[i]
allowed_tools 包含不在白名单的工具 skill allowed_tools contain non-whitelisted tools
required_evidence 包含未知值 skill required_evidence contains unknown values
output_tags 有重复值 skill output_tags must not contain duplicates
output_tags 值不匹配 ^[a-z][a-z0-9_]{1,63}$ skill output_tags contain invalid tags
field_notes 键不在有效集合中 skill field_notes references unknown fields
severity_rubric 键不在 {low,medium,high,critical} skill severity_rubric has invalid severity
example_cases[i] 缺少 synthetic: truenot_current_case: true skill example_cases[i] must be synthetic and not_current_case
example_cases[i] 包含真实路径或类密钥文本 skill example_cases[i] contains real path or secret-like text
example_cases 数量超过 example_policy.max_examples skill example_cases exceed example_policy.max_examples
max_tool_calls 不是整数 skill max_tool_calls must be an integer
同一目录中存在重复 name duplicate skill name
schema_version 为 v1 且 required_evidence 为空 skill required_evidence is required for clawsentry.l3_skill.v1
schema_version 为 v1 且 severity_rubric 未覆盖 highcritical skill severity_rubric must cover high or critical
schema_version 为 v1 且 output_tags 为空 skill output_tags is required for clawsentry.l3_skill.v1
schema_version 既非 "legacy" 也非 clawsentry.l3_skill.v1 unsupported skill schema_version

example_cases 不属于证据

示例案例必须包含 synthetic: truenot_current_case: true,仅用于向 LLM 展示模式样例。它们不能出现在 L2/L3 evidence_refs 或 findings 输出中。