跳至主要内容

工具使用前 Hook

使用 onPreToolUse 钩子来控制工具执行、修改参数,并在 Copilot SDK 中工具运行前添加上下文。

谁可以使用此功能?

GitHub Copilot SDK 在所有 Copilot 计划中均可使用。

注意

Copilot SDK 目前处于公开预览阶段。功能和可用性可能会更改。

在工具执行 之前 会调用 onPreToolUse 钩子。使用它可以

  • 批准或拒绝工具执行
  • 修改工具参数
  • 为工具添加上下文
  • 在对话中抑制工具输出

钩子签名

import type { PreToolUseHookInput, HookInvocation, PreToolUseHookOutput } from "@github/copilot-sdk";
type PreToolUseHandler = (
  input: PreToolUseHookInput,
  invocation: HookInvocation
) => Promise<PreToolUseHookOutput | null | undefined>;

有关 Python、Go 和 .NET 中的钩子签名,请参阅 github/copilot-sdk 仓库。对于 Java,请参阅 github/copilot-sdk-java 仓库

输入

字段类型描述
时间戳number钩子触发时的 Unix 时间戳
cwdstring当前工作目录
toolNamestring被调用工具的名称
toolArgsobject传递给工具的参数

输出

返回 nullundefined 以允许工具在不做更改的情况下执行。否则,返回包含以下任意字段的对象。

字段类型描述
permissionDecision"allow" | "deny" | "ask"是否允许调用该工具
permissionDecisionReasonstring向用户显示的解释(用于 deny/ask)
modifiedArgsobject传递给工具的修改后参数
additionalContextstring注入对话的额外上下文
suppressOutputboolean如果为 true,工具输出将不会出现在对话中

权限决定

决定行为
"allow"工具正常执行
"deny"工具被阻止,向用户显示原因
"ask"提示用户批准(交互模式)

示例

允许所有工具(仅记录)

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input, invocation) => {
      console.log(
        `[${invocation.sessionId}] `
        + `Calling ${input.toolName}`
      );
      console.log(
        `  Args: ${JSON.stringify(input.toolArgs)}`
      );
      return { permissionDecision: "allow" };
    },
  },
});

有关 Python、Go 和 .NET 示例,请参阅 github/copilot-sdk 仓库。对于 Java,请参阅 github/copilot-sdk-java 仓库

阻止特定工具

const BLOCKED_TOOLS = [
  "shell", "bash", "write_file", "delete_file",
];

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      if (BLOCKED_TOOLS.includes(input.toolName)) {
        return {
          permissionDecision: "deny",
          permissionDecisionReason:
            `Tool '${input.toolName}' `
            + `is not permitted in this environment`,
        };
      }
      return { permissionDecision: "allow" };
    },
  },
});

修改工具参数

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      // Add a default timeout to all shell commands
      if (
        input.toolName === "shell" && input.toolArgs
      ) {
        const args = input.toolArgs as {
          command: string;
          timeout?: number;
        };
        return {
          permissionDecision: "allow",
          modifiedArgs: {
            ...args,
            timeout: args.timeout ?? 30000,
          },
        };
      }
      return { permissionDecision: "allow" };
    },
  },
});

将文件访问限制到特定目录

const ALLOWED_DIRECTORIES = [
  "/home/user/projects", "/tmp",
];

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      if (
        input.toolName === "read_file"
        || input.toolName === "write_file"
      ) {
        const args = input.toolArgs as {
          path: string;
        };
        const isAllowed =
          ALLOWED_DIRECTORIES.some((dir) =>
            args.path.startsWith(dir)
          );

        if (!isAllowed) {
          return {
            permissionDecision: "deny",
            permissionDecisionReason:
              `Access to '${args.path}' `
              + `is not permitted. `
              + `Allowed directories: `
              + ALLOWED_DIRECTORIES.join(", "),
          };
        }
      }
      return { permissionDecision: "allow" };
    },
  },
});

抑制冗长的工具输出

const VERBOSE_TOOLS = ["list_directory", "search_files"];

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      return {
        permissionDecision: "allow",
        suppressOutput: VERBOSE_TOOLS.includes(input.toolName),
      };
    },
  },
});

基于工具添加上下文

const session = await client.createSession({
  hooks: {
    onPreToolUse: async (input) => {
      if (input.toolName === "query_database") {
        return {
          permissionDecision: "allow",
          additionalContext:
            "Remember: This database uses "
            + "PostgreSQL syntax. "
            + "Always use parameterized queries.",
        };
      }
      return { permissionDecision: "allow" };
    },
  },
});

最佳实践

  • 始终返回一个决定。 返回 null 允许工具执行,但使用明确的 { permissionDecision: "allow" } 更清晰。
  • 提供有帮助的拒绝原因。 拒绝时解释原因,以便用户了解发生了什么。
  • 修改参数时要小心。 确保修改后的参数符合工具的预期模式。
  • 考虑性能。 预工具钩子在每次工具调用前同步运行。保持其快速。
  • 谨慎使用 suppressOutput 抑制输出意味着模型看不到结果,这可能会影响对话质量。
  • 注意敏感数据。 工具参数和结果可能包含机密、文件路径或个人身份信息。避免在生产环境中记录或暴露这些数据。

延伸阅读

© . This site is unofficial and not affiliated with GitHub, Inc.