Skip to main content

Крючок после использования инструмента

Используйте onPostToolUse крюк для трансформации результатов инструмента, логирования выполнения инструмента и добавления контекста после запуска инструмента в Второй пилот SDK.

Кто может использовать эту функцию?

GitHub Copilot SDK Доступна со всеми Copilot тарифными планами.

Примечание.

Второй пилот SDK в настоящее время находится в Technical Preview. Функциональность и доступность могут меняться.

          `onPostToolUse` Крюк **вызывается после** выполнения инструмента. Он используется для следующих задач:
  • Результаты инструмента преобразования или фильтрации
  • Выполнение инструментов логирования для аудита
  • Добавьте контекст на основе результатов
  • Подавление результатов разговора

Сигнатура крюка

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

Для сигнатур хуков в Python, Go и .NET см. репозиторийgithub/copilot-sdk.

Ввод

ПолеТипОписание
timestampчисло/номерВременная метка Unix, когда срабатывал крюк
cwdструнаТекущий рабочий справочник
toolNameструнаНазвание инструмента, который был назван
toolArgsобъектАргументы, которые передавались инструменту
toolResultобъектРезультат, возвращаемый инструментом

Выходные данные

Вернуть null или undefined пройти через результат без изменений. В противном случае верните объект с любым из следующих полей.

ПолеТипОписание
modifiedResultобъектИзменённый результат для использования вместо оригинального
additionalContextструнаДополнительный контекст в разговор
suppressOutputбулевыйЕсли это правда, результат не появится в разговоре

Примеры

Записывать все результаты инструментов

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input, invocation) => {
      console.log(
        `[${invocation.sessionId}] `
        + `Tool: ${input.toolName}`
      );
      console.log(
        `  Args: ${JSON.stringify(input.toolArgs)}`
      );
      console.log(
        `  Result: `
        + `${JSON.stringify(input.toolResult)}`
      );
      return null; // Pass through unchanged
    },
  },
});

Примеры в Python, Go и .NET см. репозиторийgithub/copilot-sdk.

Редактировать чувствительные данные

const SENSITIVE_PATTERNS = [
  /api[_-]?key["\s:=]+["']?[\w-]+["']?/gi,
  /password["\s:=]+["']?[\w-]+["']?/gi,
  /secret["\s:=]+["']?[\w-]+["']?/gi,
];

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      if (typeof input.toolResult === "string") {
        let redacted = input.toolResult;
        for (const pattern of SENSITIVE_PATTERNS) {
          redacted = redacted.replace(
            pattern, "[REDACTED]"
          );
        }

        if (redacted !== input.toolResult) {
          return { modifiedResult: redacted };
        }
      }
      return null;
    },
  },
});

Сократить большие результаты

const MAX_RESULT_LENGTH = 10000;

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      const resultStr =
        JSON.stringify(input.toolResult);

      if (resultStr.length > MAX_RESULT_LENGTH) {
        return {
          modifiedResult: {
            truncated: true,
            originalLength: resultStr.length,
            content:
              resultStr.substring(
                0, MAX_RESULT_LENGTH
              ) + "...",
          },
          additionalContext:
            `Note: Result was truncated from `
            + `${resultStr.length} to `
            + `${MAX_RESULT_LENGTH} characters.`,
        };
      }
      return null;
    },
  },
});

Добавьте контекст на основе результатов

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      // If a file read returned an error,
      // add helpful context
      if (
        input.toolName === "read_file"
        && input.toolResult?.error
      ) {
        return {
          additionalContext:
            "Tip: If the file doesn't exist, "
            + "consider creating it or "
            + "checking the path.",
        };
      }

      // If shell command failed,
      // add debugging hint
      if (
        input.toolName === "shell"
        && input.toolResult?.exitCode !== 0
      ) {
        return {
          additionalContext:
            "The command failed. Check if "
            + "required dependencies are installed.",
        };
      }

      return null;
    },
  },
});

Трассы стека ошибок фильтра

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      if (input.toolResult?.error && input.toolResult?.stack) {
        // Remove internal stack trace details
        return {
          modifiedResult: {
            error: input.toolResult.error,
            // Keep only first 3 lines of stack
            stack: input.toolResult.stack.split("\n").slice(0, 3).join("\n"),
          },
        };
      }
      return null;
    },
  },
});

Аудитский след на соответствие

interface AuditEntry {
  timestamp: number;
  sessionId: string;
  toolName: string;
  args: unknown;
  result: unknown;
  success: boolean;
}

const auditLog: AuditEntry[] = [];

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input, invocation) => {
      auditLog.push({
        timestamp: input.timestamp,
        sessionId: invocation.sessionId,
        toolName: input.toolName,
        args: input.toolArgs,
        result: input.toolResult,
        success: !input.toolResult?.error,
      });

      // Optionally persist to database/file
      await saveAuditLog(auditLog);

      return null;
    },
  },
});

Подавление шумных результатов

const NOISY_TOOLS = ["list_directory", "search_codebase"];

const session = await client.createSession({
  hooks: {
    onPostToolUse: async (input) => {
      if (NOISY_TOOLS.includes(input.toolName)) {
        // Summarize instead of showing full result
        const items = Array.isArray(input.toolResult) 
          ? input.toolResult 
          : input.toolResult?.items || [];
        
        return {
          modifiedResult: {
            summary: `Found ${items.length} items`,
            firstFew: items.slice(0, 5),
          },
        };
      }
      return null;
    },
  },
});

Лучшие практики

  •         **Возвращайтесь `null` , когда изменения не нужны.** Это эффективнее, чем возврат пустого объекта или того же результата.
    
  •         **Будьте осторожны с изменением результата.** Изменение результатов может повлиять на то, как модель интерпретирует результаты инструмента. Модифицируйте только при необходимости.
    
  •         **Используйте `additionalContext` для подсказок.** Вместо изменения результатов добавьте контекст, чтобы модель могла их интерпретировать.
    
  •         **Учитывайте конфиденциальность при ведении логов.** Результаты инструмента могут содержать чувствительные данные. Примените редактирование перед логированием.
    
  •         **Держите крючки быстрыми.** Пост-инструментальные крючки работают синхронно. Интенсивная обработка должна выполняться асинхронно или пакетно.
    

Дополнительные материалы

  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/quickstart)
    
  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/pre-tool-use)
    
  •         [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/error-handling)