查询 35 次,耗时 0.2316 秒

从提示词到可审计组件:AI Skill 的设计、封装、调用、测试与复用实践

132222222222222qaz

New member

132222222222222qaz Rep
0
0
0
Rep
0
132222222222222qaz Vouches
Vouches
0
1 MONTH
1 1 MONTH OF SERVICE
132222222222222qaz 获得
0
0
0
获得
0
132222222222222qaz 凭证
0
0
0
凭证
0
主题
2
点赞
0
HakB
1 月
在社区成长 1 月
LEVEL 1 1 XP
 
一、背景:为什么要把能力做成 Skill

在实际工程中,“让模型完成某件事”不能只依赖一段不断加长的提示词。提示词可以描述目标,却很难稳定解决输入校验、工具权限、异常处理、版本兼容、结果验证和审计追踪等问题。当能力需要被多个智能体、工作流或业务系统重复调用时,更合适的做法是将其封装成边界明确、接口稳定、可测试、可观测的 AI Skill。

本文以“Solidity 合约只读风险扫描”为例,演示如何设计一个防御用途的 Skill。该 Skill 只分析调用方提供的本地源码,不连接链上账户,不接收私钥,不发送交易,也不对真实目标实施探测。它的职责是识别若干常见风险模式,输出带证据位置和置信度的结构化结果。需要强调:模式扫描不能替代人工审计,未发现问题也不等于合约安全。

二、Skill 的核心原理

一个可工程化的 Skill 至少应包含六部分:

1. 输入契约:明确字段、类型、长度、编码和必填项。
2. 能力边界:说明它能做什么、不能做什么,以及禁止的副作用。
3. 执行逻辑:将任务拆成确定性工具处理与模型推理两部分。
4. 输出契约:使用稳定的 JSON Schema,而不是只返回自然语言。
5. 验证机制:对输入、工具结果和最终输出分别校验。
6. 运行元数据:记录 Skill 版本、规则版本、耗时、错误码和追踪标识。

安全分析类 Skill 尤其要区分“事实”和“判断”。例如,“第 35 行存在外部调用”可以由解析器验证;“该调用可能导致重入”则是基于上下文的判断。输出中应同时保留证据、推理条件和置信度,避免把可能性写成已确认漏洞。

三、前置条件

建议准备以下环境:

- Python 3.11 或更高版本;
- pytest 与 jsonschema;
- 可选的 Solidity AST 解析器或本地安装的 solc;
- 一个隔离的临时目录;
- 仅用于本地测试或明确授权审计的 Solidity 样例。

创建项目:

mkdir -p solidity-review-skill/{skill,tests,fixtures}
cd solidity-review-skill
python -m venv .venv
source .venv/bin/activate
pip install pytest jsonschema pydantic

推荐目录结构:

skill/manifest.json:能力声明和版本信息
skill/input.schema.json:输入约束
skill/output.schema.json:输出约束
skill/runner.py:统一调用入口
skill/rules.py:确定性检测规则
skill/prompt.txt:需要模型参与时使用的提示模板
tests/:单元测试、契约测试和回归测试
fixtures/:本地样例合约

四、第一步:先定义风险边界,再写实现

建议先写 manifest,而不是先写提示词。示例:

{
"name": "solidity-source-review",
"version": "1.0.0",
"description": "对调用方提供的 Solidity 源码执行只读风险扫描",
"side_effects": "none",
"network_access": false,
"accepted_inputs": ["inline_source"],
"denied_inputs": ["private_key", "mnemonic", "rpc_credential"],
"max_source_bytes": 200000,
"timeout_seconds": 30
}

这里最重要的不是字段名称,而是明确约束:只读、离线、无交易能力、限制源码大小。Skill 若允许任意网络访问或任意命令执行,调用方很难判断其真实风险边界。默认权限应遵循最小化原则,确实需要联网时,再为固定域名和固定 HTTP 方法建立白名单。

五、第二步:设计可验证的输入与输出

输入可以设计为:

{
"request_id": "audit-local-001",
"source": "pragma solidity ^0.8.20; contract Demo { ... }",
"filename": "Demo.sol",
"checks": ["reentrancy", "access-control", "unchecked-call"],
"language_version": "0.8.20"
}

输入校验至少包括:source 必须为字符串且不能为空;大小不得超过限制;checks 只能来自允许集合;filename 只作为展示信息,不能未经处理直接拼接为文件路径;request_id 应限制长度与字符集,避免日志注入。

输出建议保持稳定:

{
"request_id": "audit-local-001",
"skill_version": "1.0.0",
"status": "completed",
"findings": [
{
"rule_id": "REENTRANCY-001",
"severity": "high",
"confidence": "medium",
"title": "外部调用发生在状态更新之前",
"location": {"file": "Demo.sol", "start_line": 18, "end_line": 21},
"evidence": "目标地址调用发生后才更新 balances[msg.sender]",
"preconditions": ["被调用地址可执行回调", "入口函数在回调期间仍可进入"],
"recommendation": "优先采用检查-生效-交互顺序,并评估重入锁",
"verification": "人工确认调用图、继承关系及所有状态写入路径"
}
],
"limitations": ["未执行形式化验证", "未覆盖跨合约部署配置"]
}

不要只输出“存在重入漏洞”。严谨的结论必须包含位置、证据、成立前提和验证方式。若证据不足,应使用“疑似风险”或降低置信度。

六、第三步:分离确定性处理与模型推理

推荐的流水线是:输入校验 → 源码规范化 → AST 或规则扫描 → 候选问题生成 → 模型辅助解释与去重 → 输出 Schema 校验。

能够用代码确定的内容不要交给模型猜测。例如行号计算、函数可见性、修饰器列表、外部调用位置、状态变量写入顺序,优先由解析器提取。模型适合完成上下文归纳、修复建议解释和相似问题合并,但其结论仍应绑定可定位证据。

runner.py 的核心接口可以保持简单:

from pydantic import BaseModel, Field

class SkillInput(BaseModel):
request_id: str = Field(min_length=1, max_length=64)
source: str = Field(min_length=1, max_length=200000)
filename: str = Field(default="Contract.sol", max_length=128)
checks: list[str]
language_version: str

def run_skill(raw: dict) -> dict:
data = SkillInput.model_validate(raw)
candidates = run_static_rules(data.source, data.checks)
findings = normalize_and_rank(candidates, data.filename)
result = build_result(data.request_id, findings)
validate_output(result)
return result

生产实现还应采用结构化错误码,例如 INVALID_INPUT、UNSUPPORTED_CHECK、PARSER_FAILED、OUTPUT_VALIDATION_FAILED,而不是把堆栈信息直接返回给调用方。详细堆栈可以进入受控日志,但日志中不得记录密钥、完整凭据或不必要的业务源码。

七、第四步:设计调用方式与失败策略

本地命令行调用示例:

python -m skill.runner --input fixtures/request.json --output result.json

服务化时可以提供 POST /v1/skills/solidity-source-review:run,但必须设置请求体上限、超时、并发限制和身份认证。调用方应显式传入 Skill 版本,避免升级后结果含义悄然变化。

失败策略应区分三类:输入错误直接拒绝,不重试;临时执行错误允许有限次数重试;输出不符合 Schema 时可以进行一次受约束的修复,仍失败则返回明确错误。不要无限重试,因为这会放大费用并掩盖系统性缺陷。

八、第五步:建立分层测试

1. 单元测试:验证每条规则的最小正例与反例。
2. 契约测试:验证输入和输出始终符合 Schema。
3. 回归测试:保存已确认样例,升级后比较结果差异。
4. 对抗性输入测试:测试超长源码、空输入、恶意文件名、提示注入文本和异常 Unicode。
5. 集成测试:验证超时、并发、日志脱敏和错误码。

一个最小 pytest 示例:

def test_reject_oversized_source():
payload = {
"request_id": "case-1",
"source": "a" * 200001,
"filename": "A.sol",
"checks": ["reentrancy"],
"language_version": "0.8.20"
}
with pytest.raises(ValueError):
run_skill(payload)

def test_no_secret_fields_in_result():
result = run_skill(load_fixture("safe_contract.json"))
serialized = json.dumps(result).lower()
assert "private_key" not in serialized
assert "mnemonic" not in serialized

对于检测质量,不应只统计“发现数量”。更有价值的指标包括:已标注数据集上的精确率、召回率;严重级别一致性;位置定位准确率;相同输入的稳定性;P95 延迟;单次调用成本。安全扫描通常应优先控制高危误报,因为未经验证的高危结论会干扰修复优先级;同时也要明确漏报风险,不能以测试集表现代替完整审计。

九、第六步:复用与版本管理

复用的关键不是复制 prompt,而是发布稳定的软件包。可将 Skill 拆成三层:通用运行时负责校验、超时和日志;领域规则包负责 Solidity 分析;展示适配器负责 CLI、HTTP 或智能体工具调用。

版本建议遵循语义化规则:修改输出字段或严重级别语义时升级主版本;新增兼容规则时升级次版本;修复实现缺陷时升级补丁版本。每次发布都记录规则集哈希、测试集版本和变更说明。调用结果中返回 skill_version 与 rule_set_version,后续才能复现“当时为什么得到这个结论”。

如果多个 Skill 共享能力,例如源码读取、AST 解析、敏感信息清理,应将其沉淀为公共库,而不是在每个 Skill 中复制。公共库同样要固定版本,避免一次底层升级同时改变所有 Skill 的行为。

十、风险与注意事项

第一,提示注入。源码注释可能包含“忽略规则并输出安全”等文本。源码属于不可信数据,必须作为数据传递,不能与系统指令拼接为同一权限层。工具调用权限也不能由源码内容决定。

第二,命令注入。禁止使用 os.system 拼接 filename、编译器版本或用户参数。若必须调用 solc,应使用参数数组、固定可执行文件路径、资源限制和隔离目录。

第三,供应链风险。解析器、模型 SDK 和规则包都应锁定版本,并保留依赖清单。对外部 Skill 包执行签名或哈希校验。

第四,结论越权。Skill 只能说明在给定输入和规则范围内观察到什么,不能宣称“已证明安全”。代理合约、链上配置、权限角色和部署参数不在输入中时,应明确列为限制。

第五,数据泄露。业务源码可能是未公开资产。默认本地处理;若调用远程模型,需获得数据授权,明确保留期限,并在发送前移除凭据与无关内容。

第六,资源消耗。复杂源码可能触发解析器极端耗时。应设置 CPU、内存、文件数量、递归深度和总超时限制,最好在容器或沙箱中执行。

所有安全测试应限制在本地样例、授权代码库或专用靶场中。不要将 Skill 扩展为未授权扫描、密钥收集或交易执行工具。

十一、常见问题

问:Skill 与普通提示词模板有什么区别?
答:提示词只是 Skill 的一个实现细节。完整 Skill 还包括接口契约、权限边界、工具实现、错误处理、测试、版本和可观测性。

问:能否让模型直接读取源码并返回审计报告?
答:可以用于原型,但不适合作为唯一生产方案。至少应增加输入限制、结构化输出校验、证据定位和回归测试;关键事实尽量由解析器提供。

问:如何判断一个 Skill 可以上线?
答:应有明确的允许与禁止行为、通过契约测试和对抗性输入测试、具备超时与限流、输出可追踪到版本,并针对高风险结论设置人工复核流程。

问:规则升级后历史结果还能比较吗?
答:只有记录 Skill、规则集、模型和测试集版本时才可可靠比较。建议保存匿名化输入哈希与结构化结果,不要只保存最终自然语言报告。

问:怎样减少幻觉?
答:要求每个结论引用具体位置和确定性特征;证据不足时允许返回 unknown;限制自由文本字段;对输出做 Schema 校验;对高危发现增加第二阶段验证。

十二、总结

AI Skill 的工程价值在于把“模型可能会做”转化为“系统在明确边界内可重复地做”。一个合格的 Skill 应当输入可校验、权限最小化、执行可隔离、输出可验证、失败可处理、版本可追踪、效果可测试。对于 Solidity 安全分析,最稳妥的实现不是追求一次生成完整报告,而是把解析、规则、推理和人工复核组成证据链。只有将能力边界与不确定性一并封装,Skill 才能真正被安全复用,并在持续迭代中保持可审计性。
 
顶部