技能系统
路由与激活的工作原理,以及完整的可用技能目录
技能是 Minara Agent 的能力单元。每个技能包含一段提示词片段、一份工具白名单,以及一组告诉路由器该技能何时相关的元数据。技能是发现与 playbook 的入口:激活它会把该领域的工具暴露出来,并拉入使用这些工具的指引(何时该用哪个工具、某个工作流背后的方法论、它所依托的领域理论)。它不是工具授权门。普通会话里每个已注册工具都可直接调用;加载技能只是帮模型找到合适的工具并读到 playbook。可以把技能理解为 Agent 按需加载的插件。
为何不用 MCP? Minara 的核心能力使用自有技能与工具注册表(而非 MCP),原因是金融工具需要逐次调用的权限等级、类型化 schema 以及 MCP 规范未覆盖的涉资金确认门。外部服务商仍通过 MCP 服务器集成(见 自定义 MCP)。
本页涵盖技能机制(路由与激活)及 Agent 可激活的完整技能目录。
实际案例:功能 页面各自标注了所激活的技能。安装技能 展示了外部技能 vendoring 的用户侧操作。
三层结构
上图展示路由管道:确定性的 L0 路由器、非确定性的 LLM 激活步骤,以及确定性的工具调用层。
L0 路由器是确定性的:相同输入产生相同目录。LLM 的 activate_skills 调用是唯一的非确定性步骤。工具调用层(逐次调用的权限等级与涉资金确认)再次回归确定性,且它在工具被调用时运行,而非在技能被激活时。因此管道两端无需 LLM 即可测试,这是有意为之的设计选择。
L0 路由:确定性预过滤
apps/agent/src/skills/router.ts 中的 buildRoutedCatalog() 接收 TurnRoutingContext,并对每个已注册技能评分:
| 信号 | 得分 |
|---|---|
| 用户消息中关键词命中 | 每次 +30,上限 +60 |
| 生命周期阶段匹配 | +20 |
| 资产类别匹配 | +15 |
来自其他命中技能的 co_activate | +10 |
| 信号来源匹配(cron 轮次) | +25 |
| 兜底(优先级决胜) | 100 - priority |
| 负关键词命中 | −∞ |
conditions 未通过 | −∞ |
任何评分为 -∞ 的技能将从目录中彻底移除,LLM 不会看到它。conditions 是优雅降级的调节旋钮:
conditions: {
requires_env: ["GLASSNODE_API_KEY"], // skill hidden without key
requires_tools: ["glassnode_metric"], // skill hidden without tool
requires_toolsets: ["documents"],
fallback_for_tools:["web_extract"], // only surface when web_extract absent
platforms: ["darwin", "linux"], // hide on Windows
}输出的 RoutedCatalogEntry[] 携带技能得分的原因(如 ["kw:buy", "stage:decide"]);注册表的 buildCatalogFor() 将其渲染为目录块中的内联提示,供 LLM 参考。
激活:由主 LLM 决定
基础系统提示词始终暴露一个元工具:
activate_skills(ids: string[], confirmed?: boolean)LLM 读取路由后的目录,选择请求所匹配的技能,并调用 activate_skills(["minara.core", "memory-personal"])。工具处理器调用 SkillSession.activate(),后者把这些技能的 playbook 与工具提示加载进提示词。激活从不询问用户,也不运行任何风险门控;所有安全约束都在工具调用层(见下文)。
从机制上看,已加载集合就是最近一次 activate_skills 调用所传入的内容,所以新的调用会替换上一组。这让提示词体积保持有界。它不是工具门:一组基线能力(activate_skills、get_price、search_tokens、memory_*、todo 等,即 apps/agent/src/skills/session.ts 中的 DEFAULT_ALWAYS_INCLUDE_TOOLS 集合)在没有任何技能激活时每轮都可调用,主会话中任何其他已注册工具也可经 tool_invoke 直接触达。激活关乎发现与 playbook 文字,而非授权。
没有任何技能是常驻激活的。身份、安全不变量、语言策略、markdown 线协议都在缓存的系统提示词骨架(apps/agent/src/core/system-prompt-skeleton.ts)中,烘焙进每一轮的前缀。
安全门到底在哪
技能激活从不做任何确认。涉资金与高风险门位于工具调用层,在 apps/agent/src/tools/_security/tier-gate.ts 中,按每个工具的 permissionTier 区分:
| 等级 | 行为 |
|---|---|
READ_ONLY | 立即执行 |
CONFIRM_ONCE | 首次使用确认,之后本会话内记住 |
ALWAYS_CONFIRM | 每次调用都确认;自主调用需要已存储的授权 |
MANUAL_ONLY | 需要显式批准;自主调用被拒绝 |
由于该门控以工具而非技能为键,无论工具如何被发现都同样生效。被篡改的工具输出回显 activate_skills 调用也无法转移资金:激活只加载 playbook 文字,真正的交易工具仍会带着完整参数上下文撞上 tier 门。Agent Loop 页面详细介绍了 hook 链(见 Agent 循环)。
生命周期阶段
路由器从用户消息推断生命周期阶段:
| 阶段 | 触发词 |
|---|---|
discover | "what's trending", "show me", "price of" |
evaluate | "should I", "analyze", "compare", "risk" |
decide | "buy", "sell", "long", "short", "swap" |
manage | "close", "stop loss", "my positions" |
阶段用于路由(阶段匹配会提升技能的目录得分),并为工具调用层提供上下文(discover 轮次上的自主来源会收紧 tier 门将执行的内容)。阶段不再钳制哪些技能可以激活;交易边界由工具 tier 把守,所以将交易调用藏入看似无害的问题中,仍会在执行时撞上确认门。
信号:cron 与 webhook 轮次
cron 触发或 webhook 推送会构建一个 SignalContext:
{
source: "cron",
signal_id: "btc_drop_5pct",
asset: { symbol: "BTC", asset_class: "crypto_major" },
severity: "warn",
preload_skills: ["market.watch", "memory.alerts"],
suggested_stages: ["evaluate"],
trace_id: "t_abc123",
}preload_skills 是给路由器的提示,指示其直接激活对应 id 而无需等待 LLM 调用 activate_skills。预加载只加载 playbook 文字,不绕过工具调用层的 tier 门,所以自主的 cron 或 webhook 轮次在执行时仍受约束:从 cron 来源调用 MANUAL_ONLY 工具会被直接拒绝。会话运行时 placement(Preferences computer.backend)固定该聊天的 shell / 文件 / execute_code;模型没有按次 environment 覆盖。trace_id 贯穿 WorkflowInstance、SkillAuditRecord 及工具执行日志,因此"14:03 的 BTC 提醒做了什么"只需一条 SQL 查询即可追溯。
编写优质技能
以下是从内置技能中积累的具体建议:
- 提示词片段控制在 800 token(约 3 KB)以内。 过长的提示词会撑大可缓存的目录块,拖慢每次轮次。
description要具体(不超过 80 个字符)。LLM 依据 description 选择技能;激活后才会看到提示词。"Perps: open, close, monitor positions on Hyperliquid" 是好示例,"Trading stuff" 则不是。- 没有任何技能是常驻激活的。 若某些内容应在每轮运行(身份、安全、markdown 协议),它属于
core/system-prompt-skeleton.ts,而非某个技能。 - 准确设置每个工具的
permissionTier,而非技能。 风险落在工具上,而非技能。不要为了省一次确认而把涉资金工具标成低等级;审查会发现。 - 为每个外部服务商声明
conditions.requires_env。 注册表会在缺少密钥的开发机器上静默隐藏技能,这是正确行为。 - 保持
tool_names精简。 只列出技能实际使用的工具。拥有 20 个工具的技能,通常是两个技能硬撑成一个。
参考实现(SKILL.md 包):
- 工具密集型技能:
minara-perps/ - 提示词密集型技能:
deep-research/ - 方法论透镜技能:
analysis/
外部技能
apps/agent/src/skills/external/ 存放 vendored 的第三方 SKILL.md 包。通过 minara skills add <git-url> 添加,流程如下:
- 将仓库克隆至
apps/agent/src/skills/external/<id>/。 - 检测许可证并记录在
.minara-skill.json中。 - 拒绝添加专有许可证下的内容(历史案例:Anthropic 的 office skills)。
- 启动时通过
buildExternalDomainSkills()从 SKILL.md frontmatter 构建DomainSkill。
外部技能与内置技能同等地位,经过相同的注册表与路由器,其工具撞上同一套工具调用 tier 门,没有单独的代码路径。
技能目录
每个内置技能和外部技能的完整目录都放在参考文档中。它由每个技能的 SKILL.md 自动生成,因此永远不会与代码脱节:
当一种能力两种形式都有时,优先选择内置技能。它随二进制文件一起发布, 在 CI 中做类型检查,并直接调用内部工具注册表。当内置技能没有覆盖你需要 的数据源,或你希望获得上游 SKILL.md 更新而不必等待发版时,再选择外部技能。
技能系统是 Minara Agent 资金安全模型最直观的体现。如果要添加涉及资金转移的技能,在写任何代码之前,请先完整走一遍流程(L0 评分、LLM 激活意图,以及运行确认流程的工具调用 tier 门)。在设计阶段发现问题,远比在错误交易发生后打补丁代价要小得多。