Language

AgentLoop

AgentLoop(范式 / PARADIGM)是 Agent 的主循环范式:用一份 YAML 声明每一轮循环在哪些阶段(stage)注入什么内容、执行什么检查,从而定制它的思考与行动流程。范式支持会话级热切换,且切换前后系统提示前缀保持一致,不破坏 KV Cache。 AgentLoop (a paradigm) is the agent's main loop. A single YAML file declares what each stage injects and checks every round, shaping how the agent thinks and acts. Paradigms hot-swap per session, and the system-prompt prefix is kept stable so the KV cache survives the switch.

  • 每轮循环依次触发若干阶段,每个阶段可挂多个 hook,按声明顺序执行。 Each round fires several stages in order; a stage can carry multiple hooks that run in declaration order.
  • hook 的三种作用:注入内容到系统提示/上下文、执行检查决定循环能否结束、产生反馈(通过 / 失败提示词)。 Hooks do three things: inject into the prompt or context, check whether the loop may end, and feed back a pass or fail prompt.
  • on_loop_end 的检查未通过且未达 loop_end_max_retry 上限时,本轮回打重试。 When an on_loop_end check fails below the loop_end_max_retry ceiling, the round is replayed.
name: strict-finish
description: 强制 Agent 调用 finish_task 并提供 summary 才允许结束
description-en: Force the agent to call finish_task with a summary before ending.
path: agent_vm                # 执行环境标识,保持默认
loop_end_max_retry: 3         # on_loop_end 未通过时的最大重试轮数
trigger:                      # 定时触发声明(可选)
  - cron:
      time: "08:08"
      injection: "【Demo】闹钟响了"
hooks:                        # 阶段 -> hook 列表
  on_build_system_prompt:
    - injection: { content: "你是一个严谨的助手。" }
  on_input_received:
    - keyword_check: { keyword: "周报", type: user, successed_prompt: "用户要写周报:先列出数据来源与周期,再按模板生成。", failed_prompt: "本次输入与周报无关,不要生成周报。" }
  on_loop_start:
    - skill_info: { skills: ["my-skill"], failed_prompt: "缺少技能,请先获取。" }
  on_loop_epoch:
    - injection: { content: "当前进度:{{@TASKSPACE}}", interval: 3 }
  on_loop_end:
    - tool_use_check:
        name: "finish_task"
        parameter_check: "summary"
        successed_prompt: "任务已完成,可以结束。"
        failed_prompt: "尚未调用 finish_task 或缺少 summary 参数,请补齐后重试。"

顶层字段Top-level fields

字段Field 必填Required 说明Notes
name 是Yes 安装标识,与文件名一致Install id, identical to the file name
description 是Yes 中文描述(市场展示)Chinese description for the market
description-en 是Yes 英文描述English description
path 是Yes 执行环境,保持 agent_vmExecution environment, keep agent_vm
loop_end_max_retry 否No on_loop_end 检查未通过时的最大重试轮数Max retry rounds when an on_loop_end check fails
trigger 否No 定时触发声明:{ "cron": { "time": "HH:MM", "injection": "文本" } },到点向当前会话注入一条消息。Timed trigger: { "cron": { "time": "HH:MM", "injection": "text" } } injects a message into the current session at the given time.
hooks 是Yes 阶段到 hook 列表的映射Map of stage to hook list

阶段(stage)Stages

hooks 的键即阶段名,全部可选、按需声明。 The keys of hooks are the stage names; all are optional.

阶段Stage 时机When 典型 hookTypical hooks
on_build_system_prompt 构建系统提示时While building the system prompt injection · memo_injection
on_input_received 收到用户输入时(携带本批 received_messages)On user input (carries this batch's received_messages) keyword_check · injection · search
on_loop_start 一轮循环开始时When a round begins injection · skill_info · file_operation
on_loop_epoch 循环内每一 epoch(每次模型调用)前Before each epoch inside the round (per model call) injection · file_operation · command_run
on_tool_calling 即将调用工具时Just before a tool call tool_use_check · injection
on_loop_end 一轮循环结束判定时When deciding whether the round ends tool_use_check · file_operation

keyword_check(in检查)只检查「本批收到的输入」,因此仅在带 received_messages 的 on_input_received 阶段有意义;它不拦截输入,唯一产出就是提示词——两个提示词都留空则等于什么都没做。 keyword_check only inspects the current batch of input, so it is meaningful only in an on_input_received stage. It never blocks input: its only output is the prompt, and with both prompts left empty it does nothing at all.

hook 类型Hook types

每个 hook 是一个「单键对象」:<action> 为键,值为该 action 的参数对象。 Each hook is a single-key object: the key is the <action>, the value is its parameter object.

actionAction 参数Parameters 说明Notes
injection content 直接注入提示文本;content 支持 @引用Injects prompt text directly; content supports @ references
memo_injection type · count 注入记忆:type 取 full / light / 自定义 key,count 为条目数Injects memory: type is full / light / a custom key, count is the number of entries
skill_info skills · failed_prompt 拼装指定技能的描述提示;缺失时给 failed_promptAssembles descriptions of the named skills; falls back to failed_prompt when missing
search query · route 调检索工具并注入结果;route ∈ skill / mcp / memory / local / web,默认 localRuns retrieval and injects the result; route ∈ skill / mcp / memory / local / web, default local
tool_use_check name · parameter_check · successed_prompt · failed_prompt 检查本轮是否调用了工具 name(可要求特定参数);据此放行或回打并附提示Checks whether tool name was called (optionally with a required parameter); passes or replays with a prompt
keyword_check keyword · type · successed_prompt · failed_prompt in检查:看本批输入里有没有 keyword,type 只检查该类型的消息(留空不限);只做提示注入、不拦截输入(见下方「in检查的语义」)The in-check: looks for keyword in the current batch of input; type restricts which message types are inspected (empty means any). It only injects prompts and never blocks input (see “The in-check” below)
file_operation action · path · content · failed_prompt 文件检查 / 操作,action ∈ read / exist_check / write_in / add_in / deleteFile check or operation; action ∈ read / exist_check / write_in / add_in / delete
command_run command · return_log · failed_prompt 执行命令并按结果判定,常用于 on_loop_epochRuns a command and judges by its result; common in on_loop_epoch

通用参数:delay(仅在第 N 个 epoch 执行)、interval(每 N 个 epoch 执行一次)、expect(on_loop_end 中 "fail" 表示「未满足才算通过」,用于反向结束条件)。 Shared parameters: delay (run on the Nth epoch only), interval (run every N epochs), and expect ("fail" in on_loop_end means the loop passes only when the check is unmet, for inverted end conditions).

检查类动作的通用语义(提示词才是它的产出)Check actions in general: the prompt is the payload

检查类动作(tool_use_check、keyword_check、file_operation 的 read / exist_check / delete、command_run、skill_info)只做两件事:判断通过 / 不通过,并把 successed_prompt / failed_prompt 以 role=user 的提示消息注入回历史给模型看。 Check actions (tool_use_check, keyword_check, file_operation with read / exist_check / delete, command_run, skill_info) do exactly two things: decide pass / fail, and inject successed_prompt / failed_prompt back into history as a role=user hint for the model to read.

因此不写提示词的检查等于白写:条件不通过时既没有回打理由,也没有任何内容注入给模型。除「纯放行的闸门」外,都应写出 failed_prompt。 So a check without prompts is a no-op: when it fails there is no replay reason and nothing injected for the model. Unless it is a pure gate, always write failed_prompt.

阶段Stage 不通过时的后果What a failure does 提示词效果Effect of the prompt
on_tool_calling 拦下本次工具调用的后续流程、继续循环The current tool call's follow-up is held back and the loop continues 注入 failed_prompt,模型据此重来Injects failed_prompt so the model can redo it
on_loop_end 未达 loop_end_max_retry 上限时整轮打回重试(系统另附一条通用打回提示)The whole round is replayed until loop_end_max_retry is reached (a generic replay hint is added too) 注入 failed_prompt;通过时 successed_prompt 不生效(结束即静默放行)Injects failed_prompt; on pass successed_prompt has no effect (ending is silent)
on_input_received 不拦不挡,只做提示(不决定循环能否结束)Blocks nothing, only hints (it cannot end the loop) 命中注入 successed_prompt,未命中注入 failed_promptA hit injects successed_prompt; a miss injects failed_prompt

结论:挂在 on_input_received 上的检查价值 100% 体现在提示词上,只写检查条件、两个提示词都留空,是最典型的“写了但没用”的写法。 Bottom line: for a check on on_input_received, the prompts are 100% of its value; declaring the condition with both prompts empty is the classic “wrote it, useless” case.

in检查(keyword_check)的语义The in-check (keyword_check)

  • 它检查的是输入侧:本批以 role=user 注入历史的消息里有没有 keyword——不检查模型输出,也不检查文件。It inspects the input side: whether the current batch of role=user history messages contains keyword — not model output, not files.
  • 「输入」不只是用户真实提问:钩子自己注入的提示消息(含 on_loop_end 的打回提示)同样算输入,想只针对真实用户提问时用 type: user 收窄;type 留空则不限制消息类型。“Input” is not just a real user question: hook-injected hints (including on_loop_end replay hints) count too. Narrow to real user messages with type: user; leaving type empty means any message type.
  • 命中即通过、未命中即不通过,但它不拦截输入、不阻塞流程、不影响循环能否结束,唯一作用是往历史里追加一条提示,用来做输入分诊。A hit passes, a miss fails, but it never blocks input or the flow and cannot end the loop; its only job is to append a hint into history — input triage.
  • 只在 on_input_received 上有意义:其他阶段拿不到本批输入,会恒定判为「未命中」并一直注入 failed_prompt。Only meaningful in on_input_received: other stages get no input batch, so they always fail and keep injecting failed_prompt.
on_input_received:
  # 输入分诊:用户提到「周报」才引导周报流程;没提到也要明确告诉模型别乱走流程
  - keyword_check:
      keyword: "周报"
      type: user
      successed_prompt: "用户要写周报:先列出数据来源与周期,再按模板生成。"
      failed_prompt: "本次输入与周报无关,不要生成周报,按用户真实诉求处理。"

@引用 语法@ reference syntax

在 injection.content 内可直接使用: Usable directly inside injection.content:

引用Reference 含义Meaning
@RULES系统规则System rules
@INFO环境信息Environment info
@SOUL人格 / 灵魂设定Persona / soul
@MEMORY记忆Memory
@SYS系统提示System prompt
@USERQUERY本轮用户输入This round's user input
@TASKSPACE任务空间Task space

接口Interfaces

  • 列出 / 读取 / 保存:GET /api/paradigms、GET/POST /api/paradigms/{name}。 List, read, save: GET /api/paradigms and GET/POST /api/paradigms/{name}.
  • 会话热切换:POST /api/sessions/{session_id}/paradigm(切换后 KV Cache 前缀保持,冷启动更快)。 Hot swap: POST /api/sessions/{session_id}/paradigm keeps the KV-cache prefix, so cold starts are faster.
  • 默认范式:~/.purrcat/paradigms/PARADIGM.yaml;用户未提供时回退到内置 src/agent/system_rules/PARADIGM.yaml。 Default paradigm: ~/.purrcat/paradigms/PARADIGM.yaml, falling back to the built-in src/agent/system_rules/PARADIGM.yaml.
  • 落地目录:~/.purrcat/paradigms/;市场仓库 PurrPod/agentloops。 Runtime directory: ~/.purrcat/paradigms/; market repository PurrPod/agentloops.