Skip to main content

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

onPostToolUse Крюк вызывается после****успешного запуска инструмента. Он используется для следующих задач:

  • Результаты инструмента преобразования или фильтрации
  • Выполнение инструментов логирования для аудита
  • Добавьте контекст на основе результатов
  • Подавление результатов разговора
          **Вариант неудачи** — `onPostToolUse` выстрелы только при успешном выполнении инструментов. Чтобы наблюдать **failed** вызовы инструментов, регистрируйте `onPostToolUseFailure` (`on_post_tool_use_failure` в Python, `OnPostToolUseFailure` в Go/.NET, `on_post_tool_use_failure` в Rust). Обработчик получает `{ sessionId, toolName, toolArgs, error, timestamp, workingDirectory }` — `error` это строка, извлеченная из результата отказа инструмента — и может возвращаться `{ additionalContext: string }` , чтобы внедрить дополнительные подсказки для модели (например, подсказки по повтору). Смотрите [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/hooks-overview) для полного списка.

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

Языки кода navigation

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

Input

ПолеТипDescription
timestampТип временной метки SDKКогда срабатывал крюк
workingDirectoryструнаТекущий рабочий справочник
toolNameструнаНазвание инструмента, который был назван
toolArgsobjectАргументы, которые передавались инструменту
toolResultobjectРезультат, возвращаемый инструментом

Output

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

ПолеТипDescription
modifiedResultobjectИзменённый результат для использования вместо оригинального
additionalContextструнаДополнительный контекст в разговор
suppressOutputbooleanЕсли это правда, результат не появится в разговоре

Примеры

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

Языки кода navigation

TypeScript
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
    },
  },
});

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

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: Date;
  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;
    },
  },
});

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

  1. Возврат null , когда изменения не требуются — это эффективнее, чем возврат пустого объекта или того же результата.

  2. Будьте осторожны при модификации результатов — изменение результатов может повлиять на интерпретацию результатов инструмента моделью. Модифицируйте только при необходимости.

  3. Используйте additionalContext для подсказок — вместо изменения результатов добавьте контекст, чтобы модель могла их интерпретировать.

  4. Учитывайте конфиденциальность при логировании — результаты инструментов могут содержать конфиденциальные данные. Примените редактирование перед логированием.

  5. Держите крючки быстрыми — пост-инструментальные крючки работают синхронно. Интенсивная обработка должна выполняться асинхронно или пакетно.

См. также