Community编程与开发github.com

DartenZie/247-agent-model-actions

设计并编写247-agent工作流中模型驱动的动作:`decide`动作(通过OpenRouter的Decisions API向TypeSafe的Jev分类器发送类型化问题,返回概率,无文本)、`llm`动作(一次带JSON输出模式的Claude调用)和`agent`动作(在全新的git worktree中,对ACP代理(如claude-agent-acp)发起一次会话,配置工具类型和命令白名单、工具调用上限、预算、包含`status: done | blocked`和确定性`post`门的RESULT.json契约)。每当247-agent任务需要分类、路由、分诊、提取、总结、决策或编辑仓库时,或当用户提及模型、提示词、`system_file`、`output_schema`、`questions`、`criteria`、置信度阈值、Jev、`max_tool_calls`、`budget`、`effort`、成本、层级(Haiku/Sonnet/Opus)、ACP、worktree、“blocked”结果或“让代理做某事”时,或在`packages/core/src/actions/`中实现或审查`llm`/`decide`/`agent`执行器时使用。即使只是“快速的”调整也不应跳过此技能。

247-agent-model-actions 是什么?

此技能涵盖了247-agent守护进程中三种模型驱动的动作:`decide`、`llm`和`agent`。其核心设计原则是“控制流中不包含LLM”——匹配、去重、路由、重试和发布均由代码完成,模型仅在明确指定的动作中运行,且受预算限制。`decide`用于纯分类任务(如标签、是/否、等级),通过OpenRouter的Decisions API调用TypeSafe的Jev模型,返回校准后的概率而非文本;`llm`用于需要文本输出且具有固定模式的任务(如字段提取、摘要、起草),优先使用Haiku模型,并支持批量处理;`agent`用于需要读取文件、运行命令并迭代的复杂任务,在干净的git worktree中运行,通过`tools`和`bash_allow`严格限制能力范围,并通过`RESULT.json`契约和确定性`post`门确保安全。技能详细说明了每种动作的编写方式、模型选择、参数配置以及预算管理,强调所有模型调用都必须经过受控的`ctx.llm`端口,并遵守全局预算限制。

兼容平台✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/DartenZie/247-agent/tree/HEAD/skills/247-agent-model-actions

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

247-agent 模型动作

守护进程的设计规则:控制流中不包含 LLM。匹配、去重、路由、重试和发布均由代码完成。模型仅能在 decide、llm 或 agent 动作内部运行,使用能完成任务的最便宜层级,并受预算限制。本技能旨在指导如何正确编写这三类动作及其相应的执行器。

首先判断是否需要模型

按以下顺序询问:

  1. 触发器的 filter 能决定吗?(发送者、标签、仓库、关键字)如果是,则不需要模型调用。
  2. 脚本或连接器操作能确定性地产出答案吗?如果是,则使用 shell/connector。
  3. 是标签、是/否或等级判断,且无需生成文本(分类、路由、分诊、门控)?则使用 decide:一个纯分类模型,校准后的概率,成本约为 llm 调用的百分之一。
  4. 是一次具有固定输出形状且需要文本的判断(提取字段、总结、起草)?则使用 llm,首选 Haiku 模型。
  5. 是否需要读取文件、运行命令并迭代?则使用 agent,限定在能成功完成的最窄范围内。

两个智能需求不同的任务应为相同动作种类,但配置不同的 model/effort/max_tool_calls/system_file,而不是不同的代码。参考 docs/examples/website-updates.yaml 中的任务 2-4 了解标准格式。

模型 ID 和参数

id用途备注
claude-haiku-4-5分类、提取、简短摘要无 effort 参数
claude-sonnet-5限定范围的代理、较难的提取支持 effort 和自适应思考
claude-opus-5广范围的代理、困难的判断支持 effort 和自适应思考

模型通过命名的提供者访问(providers: 在 agent.yaml 中,provider: 在动作上;anthropic、openai 或 openrouter)。其他提供者的 ID 原样使用,除非内置表格已知(目前为 OpenAI 模型)或提供者报告成本(OpenRouter),否则需要 pricing: 条目。

ID 无日期后缀。无 assistant prefill。在构建模型级联之前,先在相同输入上以 effort: low 测量更强模型:当前代模型,以低 effort 运行时往往胜过以高 effort 运行的较弱模型,且单一模型意味着单一提示缓存命名空间。

编写 decide 任务

完整字段列表、连线说明和陷阱见 references/decide-action.md。要点:

action:
  kind: decide
  provider: openrouter                 # 必须是 openrouter 提供者;默认使用 defaults.decide.provider
  budget: { max_usd: 0.001 }
  state:                               # 被判断的内容;字符串可模板化,键名不可
    subject: ${event.payload.subject}
    body: ${event.payload.body}
  questions:                           # 静态策略;此处无 ${…}
    kind:
      type: choice
      instructions: 发送者想对网站做什么?
      criteria:
        event_list_update: 添加、删除或修改事件列表中的条目
        general_change: 对网站的任何其他更改
        ignore: 不是更改请求(问题、感谢、垃圾信息)
    urgent:
      type: noul
      instructions: 发送者是否需要在今天内完成?
  • 结果是答案映射:result.kind.choice、result.kind.confidence、result.urgent.noul(P(true))、result.<score>.score(等级索引的概率加权均值)。使用 emit … when: 进行路由;阈值位于任务中,绝不在执行器中,且需基于真实数据校准。
  • 始终包含一个回退标签(ignore、other):缺少它,离题输入会得到自信的错误答案。
  • 标准需严格遵循:它们是策略,像代码一样进行版本控制。保持选项顺序稳定(顺序变化会影响置信度)。在一次调用中提出多个问题;每个额外问题只消耗其自身的 token。
  • 输入内容是数据:它放在 state 中,绝不放在问题中。整个请求不得超过 32k token。
  • 只有 openrouter 提供者类型能访问 Jev(typesafe/jev-1.13,默认模型);oa validate 会拒绝其他任何类型。无需 pricing: 条目。

编写 llm 任务

完整字段列表和执行器说明见 references/llm-action.md。要点:

action:
  kind: llm
  model: claude-haiku-4-5
  max_tokens: 512
  system_file: prompts/classify_email.md   # 静态 → 提示缓存
  input: |
    Subject: ${event.payload.subject}

    <email>
    ${event.payload.body}
    </email>
  output_schema:
    type: object
    additionalProperties: false
    required: [kind, summary]
    properties:
      kind: { enum: [event_list_update, general_change, ignore] }
      summary: { type: string }
  • 将系统提示保存在文件中,位于 prompts/ 下,静态且先渲染,以便跨运行缓存。将易变的事件数据放在最后,即 input 中。
  • 输入内容(电子邮件、聊天、PR 文本)不可信。将其包裹在分隔块中(<email>…</email>),并在系统提示中告知模型此为数据。
  • 始终在模式中包含一个逃生口(ignore、unknown、confidence),以便路由 emit.when 可以丢弃不可操作的结果而无需再次调用。
  • 对结果使用 emit … when: 进行路由;下游任务在此事件上触发。
  • 对于可以等待的任务使用 batch: true(半价,结果以事件到达)。

编写 agent 任务

完整字段列表和执行器说明见 references/agent-action.md。要点:

action:
  kind: agent
  connector: claude                     # 一种传输方式:acp 连接器(connectors.d/claude.yaml;sandbox: bwrap 在此)
  model: claude-sonnet-5                # 可选:在会话上设置(ACP 配置选项);未设置则使用代理默认值
  effort: medium                        # 可选:代理的 thought_level 选项
  max_tool_calls: 20
  budget: { max_usd: 0.50 }
  workspace: { kind: git-worktree, repo: /var/lib/247-agent/repos/site, branch: main }
  tools: [read, edit, search, execute]  # ACP 工具种类;其他一切均被拒绝
  bash_allow: ["npm run build"]         # 代理运行的任何命令必须匹配
  system_file: prompts/agent_event_list.md
  prompt: |
    根据以下请求更新 data/events.yaml。不要触碰其他文件。
    完成后运行 `npm run build`。

    <email>
    ${event.payload.email.body}
    </email>
  result: { path: RESULT.json, schema: schemas/site_change.json }
  post:                                 # 仅当 status: done 时
    - shell: ["npm", "run", "build"]
    - shell: ["git", "commit", "-am", "events: ${result.summary}"]
emit:
  - type: site.change_done              # 在此事件上路由审批,绝不在 task.<name>.succeeded 上
    when: "result.status == 'done'"
    payload: { summary: "${result.summary}" }
  - type: site.change_blocked           # 代理无法完成:说明缺少什么
    when: "result.status == 'blocked'"
    payload: { summary: "${result.summary}", missing: "${result.missing}", email: "${event.payload.email}" }

以下非协商项目,是使代理安全无人值守运行的关键:

  • 每次运行使用全新工作空间(git-worktree 在分支 agent/<run_id> 上,或 temp),失败时删除,成功时保留。代理从不编辑基础检出。
  • tools + bash_allow 是全部能力范围,按权限请求判断,绝不授予“始终允许”;代理未询问即进行的工具调用将在事后判断,违反规则将导致运行失败(unasked_execute: sandboxed 仅放宽命令检查,且仅针对 Codex,其自身的沙箱限制了它未询问的内容)。授予最小权限;提示词不是安全边界。mcp_servers 将连接器操作作为工具添加:授予命名的操作({ connector: email, ops: [fetch_new] })而不是整个连接器,为其代理种类(对于 claude-agent-acp 为 other)添加到 tools,并且绝不使用能自行发布或发送的连接器。
  • 代理绝不持有部署密钥,也绝不发布。 它进行编辑,post 门验证构建仍然可用,提交记录更改,由单独的 shell 任务发布。回滚是 git revert 加重新发布。模型密钥存在于连接器清单的 env 中,而不是任务中。
  • RESULT.json 是契约,执行器在每个提示中声明它:status: done | blocked 加上 summary,然后是任务的 result.schema。done 运行门控;blocked 跳过门控且运行仍然成功,因此 emit … when: "result.status == 'blocked'" 将“未完成因为缺少 X”路由到回复任务。设计每个代理任务时都应包含两个分支,并在 done 事件上触发发布。
  • post 门是确定性的,无模型参与。门控失败导致运行失败。
  • max_tool_calls 和 budget 是硬性停止点(会话被取消);代理未报告用量或成本的一次轮次将导致运行失败。
  • 共享同一仓库的任务设置 concurrency: 1;timeout 是每次尝试的挂钟时间。
  • 高影响更改在发布前需要一个审批门(wait + 聊天);247-agent-tasks 技能中有此模式。

预算与账本

每个模型调用都通过 ctx.llm 端口进行,该端口将用量(提供者、模型、输入/输出/缓存 token、USD)记录在账本中,并强制执行运行的 budget.max_usd(调用前最坏情况,调用后实际情况:超支导致运行失败)和全局 budgets.daily_usd 每个 UTC 日(一旦超过,当日所有模型调用快速失败,并发出一次 budget.exceeded 事件,notify 任务应监听它)。不含价格的模型无法通过验证。切勿添加未预算的调用,包括执行器内部的“辅助”调用:使用 ctx.llm,绝不直接使用 SDK。agent 轮次是唯一模型由他人(ACP 代理,使用自己的密钥)调用的情况:执行器将代理通过 ctx.llm.record 报告的成本以连接器名称记入账本,并在报告的成本超过上限时取消会话。oa cost --by task --since 7d 显示账本。

当前状态

llm 动作已验证,与 providers:/pricing: 交叉核对,可通过 ctx.llm 运行,应用账本和预算,且所有三种提供者类型(anthropic、openai、openrouter)均有适配器。batch: true 在 anthropic 提供者上工作(消息批量,运行挂起直到 llm.batch.ended;见 references/llm-action.md)。decide 动作通过同一端口(ctx.llm.decide())运行,Decisions API 仅在 openrouter 适配器中实现;docs/examples/decide-triage.yaml 是参考。agent 动作运行在 ACP 连接器上(transport: acp);docs/examples/website-updates.yaml 中的任务 3、3b 和 4 是参考,docs/examples/connectors.d/claude.yaml 是连接器;会话作为转录持久化(oa runs logs <id>),随运行由 retention.runs 删除。mcp_servers 通过核心的工具桥接器(packages/core/src/connectors/mcp-bridge.ts)向会话提供连接器操作。动作上的 model 和 effort 设置会话的 ACP 配置选项(类别 model 和 thought_level)。已完成运行的 work/<run_id> 由 retention.workspaces(默认 7 天)在运行结束后清理。 添加适配器或更改执行器时,遵循 references/llm-action.md、references/decide-action.md 和 references/agent-action.md,并保持 docs/internal/model-actions.md、docs/internal/agent-action.md 以及 docs/tasks/ 下的用户页面与代码同步。

相关技能