跳到主内容
创建时间:最后更新:作者:白川

通过事件改变智能体的执行行为

用工具调用前后的事件实现可检查的规则,理解阻止、修改结果和结束时机。

通过事件改变智能体的执行行为

工具本身已经检查路径,为什么还需要事件?业务函数保证这次检查的输入边界,事件让宿主在执行前后应用统一策略,例如为多个工具增加审批、审计或结果整理。二者不能互相替代。

这篇在调用前拒绝无效路径,在结果失败后提示排查方向。沿着一次 lesson_inspect 调用看清哪个阶段还来得及改变执行。

本文示例固定使用 @earendil-works/pi-coding-agent@1.5.0,源码位于仓库 columns/coding-agent/examples/extension-development/。示例是独立包,先在该目录安装依赖;不需要修改专栏站点依赖。下面的命令均从这个示例目录运行。

把规则放在执行前

ts
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"
import { allowedFiles } from "../src/inspect.js"
import { registerInspection } from "../src/register-inspection.js"

export default function (pi: ExtensionAPI) {
  registerInspection(pi)
  pi.on("tool_call", (event) => {
    if (event.toolName !== "lesson_inspect") {
      return
    }
    const file = event.input.file
    if (!allowedFiles.some((allowed) => allowed === file)) {
      return { block: true, reason: "教学检查只接受两个 fixtures 文件" }
    }
  })
  pi.on("tool_result", (event, ctx) => {
    if (event.toolName === "lesson_inspect" && event.isError && ctx.hasUI) {
      ctx.ui.notify("检查失败:先核对文件路径与大小,再决定是否重试", "warning")
    }
  })
}

tool_call 中返回 block: true,宿主会阻止这次调用,并把原因交给后续处理。这里比较确切的工具名与文件名;没有用字符串包含判断去推测任意 shell 命令是否安全。

bash
npx pi --extension ./extensions/events.ts

合法调用传入 {"file":"fixtures/sample.ts"};无效调用传入 {"file":"../secret.ts"}。后者应得到拒绝原因,并且业务函数不应读该文件。可以先用测试上下文直接调用 handler 检查分支,再在真实宿主中核对执行轨迹;直接调用 handler 不等于完整事件派发已验证。

不同阶段能改变什么

阶段合适的用途容易产生的误解
before_agent_start
本轮提示与上下文准备
每次都无限追加历史
tool_call
校验、审批、阻止本次工具调用
能拦住扩展自己发起的所有系统操作
tool_result
整理结果、记录失败
工具造成的副作用可以被撤回
agent_end
一次执行阶段的观察
一定是本次交互最终结束
agent_before_settle
在收束边界提出后续动作
与只读通知事件相同
agent_settled
确认收束后的通知
仍可返回动作继续执行

agent_end 后宿主可能重试或继续。需要“最终完成”信号时,应核对该版本的 settle 语义,而不是见到 end 就发完成通知。也不要在观察回调里无条件追加新消息,制造无法结束的循环。

修改参数和修改结果要保留契约

1.5 的 tool_call 返回值主要表达阻止;需要调整参数时按事件约定原地修改 event.input,随后仍应验证新值。不要返回一个自创的 arguments 字段,期待宿主自动采用。

tool_result 可以返回新的内容或结构化结果。只替换 content 而未一并返回 structuredContent,会丢弃旧结构化数据,防止两份结果互相矛盾。想保留机器可读结果时必须明确处理两份数据。

本例不改结果,只根据 isError 提示检查路径和大小。错误提示只用来帮助定位,不能把失败改为成功。需要审计时记录工具名、调用 ID、耗时和错误类型即可,避免默认写入源码、凭据或完整提示词。

规则为什么仍要写进业务函数

手动命令可能直接调用 inspect,扩展代码也可以直接使用文件系统。这些操作不会自动转成模型的 tool_call。因此,事件用于协调调用,业务校验放在真正执行的边界;沙箱由进程权限、容器或其他运行环境提供。

多个扩展同时监听事件时还要检查处理顺序和结果组合。某个扩展允许继续,不能成为忽略另一条拒绝规则的理由。接入现有宿主前先枚举已有监听器,并用同一条调用测试组合后的结果。

依据与版本

代码以已发布的 1.5.0 包导出、类型声明和加载器为准。在线主分支会继续变化,遇到接口差异时先核对版本。