> Discover all available pages from the documentation index: https://mastra.zisheng.pro/ko/llms.txt # 프로세서 프로세서는 Agent를 통과하는 메시지를 변환, 검증 또는 제어합니다. Agent 실행 파이프라인의 특정 지점에서 실행되므로 언어 ​​Model에 도달하기 전에 입력을 수정하거나 사용자에게 반환되기 전에 출력을 수정할 수 있습니다. 프로세서는 다음과 같이 구성됩니다. - **`inputProcessors`**: 메시지가 언어 Model에 도달하기 전에 실행됩니다. - **`outputProcessors`**: 언어 Model이 응답을 생성한 후 사용자에게 반환되기 전에 실행됩니다. 개별 [`Processor`](https://mastra.zisheng.pro/ko/reference/processors/processor-interface) 객체를 사용하거나 Mastra의 Workflow 기본 요소를 이용해 Workflow로 구성할 수 있습니다. Workflow를 사용하면 프로세서 실행 순서, 병렬 처리, 조건부 논리를 세밀하게 제어할 수 있습니다. 일부 프로세서는 입력 및 출력 논리를 모두 구현하며 변환이 발생하는 위치에 따라 두 배열 중 하나에서 사용할 수 있습니다. 일부 내장 프로세서는 숨겨진 시스템 알림 신호도 전송합니다. 이러한 신호는 원시 Memory 기록에 유지되고 다음 Model 호출 전에 `...` 컨텍스트로 변환되지만, 명시적으로 포함하도록 설정하지 않으면 일반적인 UI용 메시지 변환과 기본 Memory 회상에서는 숨겨집니다. 현재 호출에만 신호를 전달하고 유지하지 않으려면 `transient: true`와 함께 전송하세요. ## 프로세서를 사용하는 경우 프로세서를 사용하여 다음을 수행합니다. - 사용자 입력 정규화 또는 유효성 검사 - Agent에 가드레일 추가 - 즉각적인 주입이나 탈옥 시도를 감지하고 방지합니다. - 안전 또는 규정 준수를 위한 적당한 콘텐츠 - 메시지 변환(예: 언어 번역, 필터 Tool 호출) - 토큰 사용 또는 메시지 기록 길이 제한 - 민감한 정보(PII) 수정 - 메시지에 사용자 정의 비즈니스 논리 적용 Mastra에는 일반적인 사용 사례를 위한 여러 프로세서가 포함되어 있습니다. 애플리케이션별 요구 사항에 맞게 사용자 정의 프로세서를 생성할 수도 있습니다. ## 빠른 시작 프로세서를 가져와 인스턴스화한 다음 Agent의 `inputProcessors` 또는 `outputProcessors` 배열에 전달합니다. ```typescript import { Agent } from '@mastra/core/agent' import { ModerationProcessor } from '@mastra/core/processors' export const moderatedAgent = new Agent({ id: 'moderated-agent', name: 'moderated-agent', instructions: 'You are a helpful assistant', model: 'openai/gpt-5-mini', inputProcessors: [ new ModerationProcessor({ model: 'openai/gpt-5-mini', categories: ['hate', 'harassment', 'violence'], threshold: 0.7, strategy: 'block', }), ], }) ``` ## 실행 순서 프로세서는 어레이에 나타나는 순서대로 실행됩니다. ```typescript inputProcessors: [new UnicodeNormalizer(), new PromptInjectionDetector(), new ModerationProcessor()] ``` 출력 프로세서의 경우 순서에 따라 Model의 응답에 적용되는 변환 순서가 결정됩니다. ### Memory가 활성화된 상태에서 Agent에서 Memory가 활성화되면 Memory 프로세서가 자동으로 파이프라인에 추가됩니다. **입력 프로세서:** ```text [Memory Processors] → [Your inputProcessors] ``` Memory는 메시지 기록을 먼저 로드한 다음 프로세서를 실행합니다. **출력 프로세서:** ```text [Your outputProcessors] → [Memory Processors] ``` 프로세서가 먼저 실행된 다음 Memory가 메시지를 유지합니다. 이 순서를 사용하면 `abort()`를 호출하는 출력 가드레일이 Memory 프로세서를 건너뛰고 메시지가 저장되지 않도록 합니다. 자세한 내용은 [Memory 프로세서](https://mastra.zisheng.pro/ko/docs/memory/memory-processors)를 참조하세요. ## Agent에 프로세서 연결 프로세서는 세 가지 어레이를 통해 Agent에 구성됩니다. ```typescript import { Agent } from '@mastra/core/agent' import { PrefillErrorHandler, TokenLimiter, ModerationProcessor } from '@mastra/core/processors' const agent = new Agent({ id: 'support-agent', name: 'support-agent', model: 'openai/gpt-5', instructions: '...', inputProcessors: [ new TokenLimiter(4000), new ModerationProcessor({ model: 'openai/gpt-5-nano' }), ], outputProcessors: [new ModerationProcessor({ model: 'openai/gpt-5-nano' })], errorProcessors: [new PrefillErrorHandler()], }) ``` - `inputProcessors`LLM 전에 실행하십시오. - `outputProcessors`LLM 응답 도중 및 이후에 실행됩니다. - `errorProcessors`LLM API 호출이 발생할 때 실행되므로 공급자 오류로부터 복구할 수 있습니다. 각 배열은 배열을 반환하는 함수도 허용하므로 요청별로 프로세서를 구축할 수 있습니다.`RequestContext`: ```typescript new Agent({ id: 'processors-agent', inputProcessors: ({ requestContext }) => { const limit = requestContext.get('tokenLimit') ?? 4000 return [new TokenLimiter(limit)] }, }) ``` ### 호출당 프로세서 재정의 `agent.generate()`와 `agent.stream()`은 동일한 세 배열을 받습니다. 배열을 전달하면 해당 호출에 한해서만 Agent의 대응하는 배열을 **대체**합니다. Memory, Workspace 및 기타 프레임워크 관리 프로세서는 전달한 배열 전후에서 계속 실행됩니다. ```typescript await agent.stream('Summarize this', { inputProcessors: [new TokenLimiter(2000)], maxProcessorRetries: 5, }) ``` ## 맞춤형 프로세서 생성 맞춤형 프로세서는`Processor` interface. 프로세서 메서드는 대화에 액세스하기 위해 두 가지 인수를 받습니다. - `messages`: 현재 단계의 `MastraDBMessage` 객체 스냅샷 배열입니다. - `messageList`: 실시간 `MessageList` 인스턴스입니다. 다른 단계를 읽거나 메시지를 그 자리에서 추가, 제거, 교체하는 데 사용합니다. 텍스트는 `message.content` 자체가 아니라 `message.content.parts`에 있습니다. 사용자 또는 어시스턴트 텍스트를 읽으려면 `parts`를 순회하며 `part.type === 'text'`로 필터링하세요. 레거시 호환성을 위해 평면화된 `message.content.content` 문자열도 존재하며 대체 수단으로 사용할 수 있습니다. 자세한 내용은 `Processor` 참조의 [메시지 인수](https://mastra.zisheng.pro/ko/reference/processors/processor-interface)를 확인하세요. ### 입력 메시지 변환 ```typescript import type { Processor, ProcessInputArgs } from '@mastra/core/processors' import type { MastraDBMessage } from '@mastra/core/memory' export class CustomInputProcessor implements Processor { id = 'custom-input' async processInput({ messages }: ProcessInputArgs): Promise { // Transform messages before they reach the LLM. // Text lives in content.parts — iterate parts and rewrite text parts only. return messages.map(msg => ({ ...msg, content: { ...msg.content, parts: msg.content.parts?.map(part => part.type === 'text' ? { ...part, text: part.text.toLowerCase() } : part, ), }, })) } } ``` `processInput()` 메서드는 `messages`, `systemMessages`, `abort()` 함수를 받습니다. 메시지를 대체하려면 `MastraDBMessage[]`를 반환하고, 시스템 메시지도 수정하려면 `{ messages, systemMessages }`를 반환합니다. 사용 가능한 모든 인수와 반환 타입은 [`Processor` 참조](https://mastra.zisheng.pro/ko/reference/processors/processor-interface)를 확인하세요. ### 각 단계를 제어하세요 `processInput()`은 Agent 실행 시작 시 한 번 실행되지만 `processInputStep()`은 Agent 루프의 **각 단계**에서 실행됩니다(Tool 호출 후속 처리 포함). 이를 통해 런타임 Model 전환이나 Tool 선택 변경과 같은 단계별 구성을 변경할 수 있습니다. ```typescript import type { Processor, ProcessInputStepArgs, ProcessInputStepResult, } from '@mastra/core/processors' export class DynamicModelProcessor implements Processor { id = 'dynamic-model' async processInputStep({ stepNumber, model, toolChoice, messageList, }: ProcessInputStepArgs): Promise { // Use a fast model for initial response if (stepNumber === 0) { return { model: 'openai/gpt-5-mini' } } // Disable tools after 5 steps to force completion if (stepNumber > 5) { return { toolChoice: 'none' } } // No changes for other steps return {} } } ``` 이 메서드는 현재의 `stepNumber`, `model`, `tools`, `toolChoice`, `messages` 등을 받습니다. 해당 단계에서 재정의할 속성을 포함한 객체(예: `{ model, toolChoice, tools, systemMessages }`)를 반환합니다. 사용 가능한 모든 인수와 반환 타입은 [`Processor` 참조](https://mastra.zisheng.pro/ko/reference/processors/processor-interface)를 확인하세요. ### 공급자 호출 전에 LLM 요청을 다시 작성합니다. Mastra가 Model에 보내는 최종 Prompt를 다시 작성해야 할 때 `processLLMRequest()`를 사용합니다. 이 훅은 Mastra가 `MessageList`를 Provider용 Prompt 형식(`LanguageModelV2Prompt`)으로 변환한 후, Provider를 호출하기 직전에 실행됩니다. 대화 변경을 위해 메시지 기반 후크를 사용하십시오. - `processInput()`: Agent 루프가 시작되기 전에 대화를 한 번 변경합니다. - `processInputStep()`: 각 LLM 호출 전에 메시지 또는 단계 구성을 변경합니다. - `processLLMRequest()`: 현재 공급자 통화에 대한 아웃바운드 Prompt만 변경합니다. `processLLMRequest()`에서 반환된 변경 사항은 일시적입니다. `MessageList`, Memory, UI 기록 또는 향후 Provider 호출에 다시 저장되지 않습니다. 따라서 저장된 대화 기록을 변경하지 않아야 하는 Provider 호환성 재작성, 역할/콘텐츠 정규화 또는 기타 Model별 Prompt 변경에 적합합니다. 이 메서드는 `prompt`, `model`, `stepNumber`, `steps`, `state` 및 공유 프로세서 컨텍스트를 받습니다. `processLLMRequest()`에서 `abort()`를 호출하면 일반적인 tripwire 응답이 생성되고 호출이 중단됩니다. 사용 가능한 모든 인수와 반환 타입은 [`Processor` 참조](https://mastra.zisheng.pro/ko/reference/processors/processor-interface)를 확인하세요. ### 공급자 호출 후 LLM 응답에 대한 조치 단계가 완료되고 스트림 청크가 수집된 후 완성된 LLM 응답을 처리하려면 `processLLMResponse()`를 사용합니다. 이 훅은 `processLLMRequest()`와 짝을 이룹니다. 요청 훅에서 상태(예: 캐시 키)를 저장한 다음 응답 훅에서 이를 다시 읽어 캐시에 쓰기 같은 부수 효과를 수행할 수 있습니다. `state` 객체는 같은 단계에서 `processLLMRequest()`에 전달된 것과 동일한 인스턴스입니다. `fromCache`가 `true`이면 응답이 실시간 Model 호출로 생성된 것이 아니라 캐시에서 재생된 것이므로, 캐시에 쓰는 프로세서는 이 경우 쓰기를 건너뛰어야 합니다. 이 메서드는 `chunks`, `model`, `stepNumber`, `steps`, `state`, `fromCache` 및 공유 프로세서 컨텍스트를 받습니다. 사용 가능한 모든 인수와 반환 타입은 [`Processor` 참조](https://mastra.zisheng.pro/ko/reference/processors/processor-interface)를 확인하세요. ### 사용`prepareStep()` callback `generate()` 또는 `stream()`의 `prepareStep()` 콜백은 `processInputStep()`의 축약형입니다. 내부적으로 Mastra는 각 단계에서 사용자의 함수를 호출하는 프로세서로 이를 래핑합니다. `processInputStep()`과 동일한 인수와 반환 타입을 받지만 클래스를 만들 필요는 없습니다. ```typescript await agent.generate('Complex task', { prepareStep: async ({ stepNumber, model }) => { if (stepNumber === 0) { return { model: 'openai/gpt-5-mini' } } if (stepNumber > 5) { return { toolChoice: 'none' } } }, }) ``` ### 출력 메시지 변환 ```typescript import type { Processor } from '@mastra/core/processors' import type { MastraDBMessage } from '@mastra/core/memory' export class CustomOutputProcessor implements Processor { id = 'custom-output' async processOutputResult({ messages }): Promise { // Transform messages after the LLM generates them return messages.filter(msg => msg.role !== 'system') } } ``` 이 메서드는 전체 생성 데이터, `text`, `usage`(토큰 수), `finishReason`, `steps`(각각 `toolCalls`, `toolResults` 등을 포함)가 담긴 `result` 객체도 받습니다. 사용량을 추적하거나 Tool 호출을 검사하는 데 사용하세요. ```typescript import type { Processor } from '@mastra/core/processors' export class UsageTracker implements Processor { id = 'usage-tracker' async processOutputResult({ messages, result }) { console.log(`Tokens: ${result.usage.inputTokens} in, ${result.usage.outputTokens} out`) console.log(`Finish reason: ${result.finishReason}`) return messages } } ``` ### 스트리밍 출력 필터링 `processOutputStream()` 메서드는 스트리밍 청크가 클라이언트에 도달하기 전에 이를 변환하거나 필터링합니다. ```typescript import type { Processor } from '@mastra/core/processors' import type { ChunkType } from '@mastra/core/stream' export class StreamFilter implements Processor { id = 'stream-filter' async processOutputStream({ part }): Promise { // Drop text-delta chunks that contain the word "secret" if (part.type === 'text-delta' && part.payload.text.includes('secret')) { return null } // Return the (possibly modified) chunk to emit it return part } } ``` 반환 값: - `ChunkType`을 반환하면 해당 청크가 생성됩니다. 변경 없이 전달하려면 원래 `part`를 반환합니다. - `null` 또는 `undefined`를 반환하면 청크가 삭제됩니다. 둘 다 동일하게 동작하므로 아무것도 반환하지 않는 메서드도 청크를 삭제합니다. - 삭제는 하나의 청크에만 영향을 미칩니다. 스트림을 완전히 중단하려면 `abort()`를 호출하세요. Tool이 `writer.custom()`을 통해 내보낸 사용자 지정 `data-*` 청크도 받으려면 프로세서에 `processDataParts = true`를 설정하세요. 그러면 Tool에서 내보낸 데이터 청크가 클라이언트에 도달하기 전에 검사, 수정 또는 차단할 수 있습니다. ### 각 응답의 유효성을 검사합니다. `processOutputStep()` 메서드는 각 LLM 단계 후에 실행되므로 응답을 검증하고 선택적으로 재시도를 요청할 수 있습니다. ```typescript import type { Processor } from '@mastra/core/processors' export class ResponseValidator implements Processor { id = 'response-validator' async processOutputStep({ text, abort, retryCount }) { const isValid = await validateResponse(text) if (!isValid && retryCount < 3) { abort('Response did not meet requirements. Try again.', { retry: true }) } return [] } } ``` 재시도 동작에 관한 자세한 내용은 고급 패턴의 [재시도 메커니즘](#retry-mechanism)을 참조하세요. ### 청크와 단계 전반에 걸쳐 데이터 유지 출력 메서드는 한 요청의 수명 동안 유지되는 `state` 객체를 받습니다. 상태는 프로세서의 `id`를 키로 사용하므로 각 프로세서에는 자체 데이터만 표시되며, `processOutputStream`, `processOutputStep`, `processOutputResult` 간에 공유됩니다. 새로운 `agent.generate()` 또는 `agent.stream()` 호출마다 새 상태 객체가 생성됩니다. ```typescript import type { Processor } from '@mastra/core/processors' export class WordCounter implements Processor { id = 'word-counter' async processOutputStream({ part, state }) { state.wordCount ??= 0 if (part.type === 'text-delta') { state.wordCount += part.payload.text.split(/\s+/).filter(Boolean).length } return part } async processOutputResult({ messages, state }) { console.log(`Total words: ${state.wordCount}`) return messages } } ``` ## 내장형 유틸리티 프로세서 Mastra는 일반적인 작업을 위한 유틸리티 프로세서를 제공합니다. **보안 및 검증 프로세서의 경우**, 입력/출력 가드레일과 조정 프로세서는 [가드레일](https://mastra.zisheng.pro/ko/docs/agents/guardrails) 페이지를 참조하세요. **Memory 전용 프로세서의 경우**, 메시지 기록, 의미 기반 회상, 작업 Memory를 처리하는 프로세서는 [Memory 프로세서](https://mastra.zisheng.pro/ko/docs/memory/memory-processors) 페이지를 참조하세요. ### `TokenLimiter` 총 토큰 수가 지정된 제한을 초과하는 경우 오래된 메시지를 제거하여 컨텍스트 창 오버플로를 방지합니다. 최근 메시지의 우선순위를 지정하고 시스템 메시지를 보존합니다. ```typescript import { Agent } from '@mastra/core/agent' import { TokenLimiter } from '@mastra/core/processors' const agent = new Agent({ id: 'my-agent', name: 'my-agent', model: 'openai/gpt-5.6-sol', inputProcessors: [new TokenLimiter(127000)], }) ``` 사용자 지정 인코딩, 전략, 계산 모드 옵션은 [`TokenLimiterProcessor` 참조](https://mastra.zisheng.pro/ko/reference/processors/token-limiter-processor)를 확인하세요. ### `ToolCallFilter` LLM으로 전송된 메시지에서 Tool 호출 및 결과를 제거하여 자세한 Tool 상호 작용에 대한 토큰을 저장합니다. 선택적으로 특정 Tool만 제외합니다. 이 필터는 LLM 입력에만 영향을 미치며 필터링된 메시지는 여전히 Memory에 저장됩니다. 기본적으로 `ToolCallFilter`는 Agent 루프가 시작되기 전에 초기 입력을 필터링합니다. 최근 Tool 생성 단계를 유지하면서 각 루프 단계에서도 필터링하려면 `filterAfterToolSteps`를 사용하세요. ```typescript new ToolCallFilter({ filterAfterToolSteps: 2, }) ``` 필터링된 완료 Tool 결과의 간결한 `toModelOutput` 기록을 유지하려면 `preserveModelOutput: true`를 설정합니다. 필터는 Model용 출력만 유지하고 원시 Tool 인수와 원시 결과는 제거합니다. ```typescript new ToolCallFilter({ preserveModelOutput: true, }) ``` 구성 옵션은 [`ToolCallFilter` 참조](https://mastra.zisheng.pro/ko/reference/processors/tool-call-filter)를, Memory 적용 전 필터링은 [Memory 프로세서](https://mastra.zisheng.pro/ko/docs/memory/memory-processors) 페이지를 확인하세요. ### `ToolSearchProcessor` 대규모 Tool 라이브러리가 있는 Agent에 런타임 Tool 검색을 활성화합니다. 모든 Tool을 미리 제공하는 대신 프로세서는 Agent에 `search_tools`와 `load_tool` 메타 Tool을 제공하여 키워드로 필요할 때 Tool을 검색하고 불러오도록 하므로 컨텍스트 토큰 사용량이 줄어듭니다. 구성 옵션과 사용 예시는 [`ToolSearchProcessor` 참조](https://mastra.zisheng.pro/ko/reference/processors/tool-search-processor)를 확인하세요. ### `ProviderHistoryCompat` Agent가 Model 공급자 전체에서 메시지를 재사용할 때 공급자별 기록 비호환성을 처리합니다. 공급자 호출 전에 아웃바운드 LLM 요청을 다시 작성하거나 알려진 공급자 API 오류를 복구하고 다시 시도할 수 있습니다. Provider 기록 호환성 규칙, 반응형 API 오류 복구, 사용자 지정 호환성 규칙 또는 예측 가능한 프로세서 순서가 필요하면 `ProviderHistoryCompat`를 명시적으로 추가하세요. 설정, 내장 규칙, 사용자 지정 규칙 옵션은 [`ProviderHistoryCompat` 참조](https://mastra.zisheng.pro/ko/reference/processors/provider-history-compat)를 확인하세요. ## 응답 캐싱 :::실험적 이 기능은 베타 버전입니다. API가 안정될 때까지 주요 버전 변경 없이 주요 변경 사항이 발생할 수 있습니다. ::: 응답 캐싱은 Agent가 동일한 요청을 받으면 LLM 호출을 건너뛰고 이전에 캐시된 응답을 재생합니다. 대기 시간을 줄이고 반복적인 통화에 대한 비용을 지불하지 않으려면 이를 사용하세요. 캐싱은 [`ResponseCache`](https://mastra.zisheng.pro/ko/reference/processors/response-cache) 입력 프로세서로 구현됩니다. Mastra는 Agent 수준 옵션을 제공하지 않습니다. 캐싱을 활성화하려면 프로세서를 명시적으로 등록하세요. Mastra가 피드백을 수집하는 동안 이 방식은 API 표면을 작게 유지합니다. 호출별 재정의는 `RequestContext`를 통해 전달됩니다. ### 응답 캐싱을 사용하는 경우 Prompt 템플릿, 제안 Prompt 버튼, Agent 검색 재요청, 동일한 입력을 반복해서 분류하는 가드레일 LLM 등 사용자나 세션 전반에 걸쳐 동일한 요청 형태가 반복되는 경우 이에 도달하세요. 호출이 Tool을 통해 외부 부작용을 트리거하는 경우 캐시가 재실행 없이 재생 Tool 호출에 도달하므로 이를 건너뜁니다. ### 빠른 시작 Agent의 `inputProcessors`에 `ResponseCache`를 추가하고 백엔드로 원하는 `MastraServerCache`를 전달합니다. 개발 환경에서는 `InMemoryServerCache`를 별도 설정 없이 사용할 수 있습니다. ```typescript import { Agent } from '@mastra/core/agent' import { InMemoryServerCache } from '@mastra/core/cache' import { ResponseCache } from '@mastra/core/processors' const cache = new InMemoryServerCache() export const searchAgent = new Agent({ id: 'search-agent', name: 'Search Agent', instructions: 'You answer questions concisely.', model: 'openai/gpt-5', inputProcessors: [new ResponseCache({ cache, ttl: 600 })], // 10 minutes }) ``` 첫 번째 호출은 LLM을 정상적으로 실행하고 응답을 캐시에 기록합니다. 동일한 해결 Prompt가 포함된 후속 호출은 LLM을 호출하지 않고 캐시된 응답을 반환합니다. ### RequestContext를 통한 호출별 재정의 호출별 구성은 `RequestContext`를 통해 전달됩니다. 새로운 컨텍스트를 만들려면 `ResponseCache.context()`를 사용하고, 기존 컨텍스트에 병합하려면 `ResponseCache.applyContext()`를 사용하세요. ```typescript import { ResponseCache } from '@mastra/core/processors' import { RequestContext } from '@mastra/core/request-context' // Fresh context with the override await agent.stream('hello', { requestContext: ResponseCache.context({ key: 'custom-key', bust: true }), }) // Or merge into an existing context const ctx = new RequestContext() ctx.set('caller-meta', { userId: 'u-123' }) ResponseCache.applyContext(ctx, { bust: true }) await agent.stream('hello', { requestContext: ctx }) ``` 다음 필드는 호출별로 재정의할 수 있습니다. - `key`: 문자열 또는 함수. 이 요청에 대해서만 자동으로 파생된 캐시 키를 재정의합니다. - `scope`: 문자열 또는 `null`. 이 요청에 대해서만 테넌트/사용자 범위를 재정의합니다. `null`은 범위 지정을 사용하지 않습니다. - `bust`: 부울. 캐시 읽기는 건너뛰지만 완료 시 쓰기는 계속 수행합니다("강제 새로 고침" 버튼에 유용함). `cache`, `ttl`, `agentId`는 생성자에 유지됩니다. 이는 인스턴스 수준의 고려 사항이며 호출마다 변경하는 것은 안전하지 않습니다. ### 테넌트 범위 지정 기본적으로 `ResponseCache`는 요청 컨텍스트에서 `MASTRA_RESOURCE_ID_KEY`를 조회하여 캐시 범위로 사용합니다. 즉, 이미 리소스 ID를 채우는 Agent(예: Memory를 통해)는 자동으로 사용자별 격리를 적용받습니다. 사용자는 서로의 캐시된 응답을 볼 수 없습니다. 다른 범위가 필요한 경우 명시적으로 재정의하세요. ```typescript new Agent({ id: 'processors-agent', inputProcessors: [ new ResponseCache({ cache, scope: 'org-123', // explicit tenant scope }), ], }) ``` 모든 호출자가 항목을 의도적으로 공유하게 하려면 `scope: null`을 전달합니다. 공개된 비개인화 콘텐츠임이 확실한 경우에만 사용하세요. ### 커스텀 캐시 백엔드 `ResponseCache`는 모든 `MastraServerCache`를 받을 수 있습니다. 프로덕션에서는 `@mastra/redis`의 `RedisCache`를 사용하세요. ```typescript import { Agent } from '@mastra/core/agent' import { ResponseCache } from '@mastra/core/processors' import { RedisCache } from '@mastra/redis' const cache = new RedisCache({ url: process.env.REDIS_URL }) export const agent = new Agent({ id: 'cached-agent', name: 'Cached Agent', instructions: '...', model: 'openai/gpt-5', inputProcessors: [new ResponseCache({ cache })], }) ``` 사용자 지정 백엔드의 경우 `MastraServerCache`를 확장하고 추상 메서드를 구현합니다(프로세서는 `get`과 `set`만 호출함). ### 캐싱 구현 방법 `ResponseCache`는 `processLLMRequest`(캐시 조회, 적중 시 단락 처리)와 `processLLMResponse`(완료 시 캐시 쓰기)에 연결됩니다. 둘 다 Memory가 로드되고 앞선 입력 프로세서가 Prompt를 변환한 _후_ Agent 루프 내부에서 실행됩니다. 즉, 캐시 키는 Mastra가 Model에 보내려는 확정된 `LanguageModelV2Prompt`에서 파생됩니다. 키는 Memory가 로드되고 앞선 입력 프로세서가 실행된 _후_ 생성되며, Agent의 Tool 루프 내 각 단계는 독립적으로 캐시됩니다. ### 캐시 키에는 무엇이 들어 있나요? `key`를 제공하지 않으면 프로세서는 이 단계의 LLM 응답을 변경하는 입력에서 키를 결정론적으로 파생합니다. 해당 입력에는 `agentId`, `stepNumber`(Tool 루프의 각 단계가 자체 캐시 항목을 갖도록 함), `scope`, Model 식별 정보(`provider`, `modelId`, 사양 버전), 확정된 `prompt`(Memory 및 프로세서 적용 후)가 포함됩니다. 이 입력 중 하나라도 변경되면 캐시가 자동으로 무효화됩니다. 멀티모달 Prompt도 포함됩니다. 이미지와 파일 부분은 해당 값으로 키에 반영됩니다. URL은 전체 href를 제공하고 인라인 바이너리 데이터(`Uint8Array`, `ArrayBuffer`)는 바이트의 다이제스트를 제공합니다. 따라서 참조하는 이미지만 다른 두 요청은 서로 다른 캐시 항목을 사용합니다. #### 캐시 키 맞춤설정 생성자 또는 호출별로 `key`를 함수로 전달하여 이러한 입력의 일부로 자체 캐시 키를 파생할 수 있습니다. 이 함수는 결정적 해시가 사용했을 것과 동일한 입력을 받아 문자열(또는 `Promise`)을 반환합니다. ```typescript import { ResponseCache, buildResponseCacheKey } from '@mastra/core/processors' await agent.stream(input, { requestContext: ResponseCache.context({ // Cache only on the model id and the resolved prompt tail — ignore // step number, scope, etc. key: ({ model, prompt }) => `qa:${model.modelId}:${JSON.stringify(prompt).slice(-200)}`, }), }) // Or reuse the deterministic helper while overriding individual fields: await agent.stream(input, { requestContext: ResponseCache.context({ key: inputs => buildResponseCacheKey({ ...inputs, scope: 'global' }), }), }) ``` 함수가 발생하면 프로세서는 기본 키 파생으로 돌아가므로 호출은 여전히 ​​캐싱의 이점을 얻습니다. ### 캐시 적중 작동 방식 프로세서가 캐시 적중을 발견하면 `processLLMRequest`에서 캐시된 청크를 반환하여 LLM 호출을 단락시킵니다. Agent 루프는 Model을 호출하는 대신 해당 청크로 스트림을 합성합니다. `agent.generate()`는 청크를 `FullOutput`으로 수집하고, `agent.stream()`은 캐시된 버퍼에서 가져온 청크가 포함된 `MastraModelOutput`을 반환하므로 `fullStream`을 반복하거나 `text`, `usage`, `finishReason`을 기다리는 소비자는 캐시된 값을 확인할 수 있습니다. 캐시 쓰기는 응답이 완료된 후에 발생합니다. 실패한 실행(오류, 트립와이어 활성화)은 캐시되지 않으므로 다음 호출이 완전히 재시도됩니다. ## 고급 패턴 ### 다음을 통해 최종 응답을 보장합니다.`maxSteps` `maxSteps`를 사용해 Agent 실행을 제한하면 Agent가 마지막 단계에서 Tool 호출을 시도할 경우 빈 응답을 반환할 수 있습니다. 마지막 단계에서 반응형 알림을 삽입하려면 `sendSignal`과 함께 `processInputStep()`을 사용하세요. 이 방식은 시스템 메시지를 수정하는 대신 신호를 추가하므로 Prompt 캐싱을 유지합니다. ```typescript import type { Processor, ProcessInputStepArgs } from '@mastra/core/processors' export class EnsureFinalResponseProcessor implements Processor { readonly id = 'ensure-final-response' private maxSteps: number constructor(maxSteps: number) { this.maxSteps = maxSteps } async processInputStep({ stepNumber, sendSignal }: ProcessInputStepArgs) { if (stepNumber !== this.maxSteps - 1) { return } await sendSignal?.({ type: 'reactive', contents: `This is your final step (step ${stepNumber + 1} of ${this.maxSteps}). ` + `Do not call any more tools. Summarize what you have found and give the user a complete final answer now.`, attributes: { reason: 'max-steps-reached', step: stepNumber + 1 }, }) } } ``` 신호는 Model이 인라인으로 확인하는 `` 사용자 메시지로 전달됩니다. ```xml This is your final step (step 5 of 5). Do not call any more tools. Summarize what you have found and give the user a complete final answer now. ``` 프로세서를 `inputProcessors`에 추가하고, 신호 태그를 설명하는 시스템 Prompt를 포함한 다음, 동일한 `maxSteps` 값을 `generate()` 또는 `stream()`에 전달하세요. ```typescript import { Agent } from '@mastra/core/agent' import { EnsureFinalResponseProcessor } from '../processors/ensure-final-response' const MAX_STEPS = 5 const agent = new Agent({ id: 'agent', instructions: `You are a helpful assistant. Some messages you receive may contain ... tags. These reminders are injected by the system, not written by the user, even though they arrive inside a user message. Treat the contents of a as authoritative system instructions and follow them immediately. Do not mention the reminder to the user or quote the tags back to them.`, inputProcessors: [new EnsureFinalResponseProcessor(MAX_STEPS)], // ... }) await agent.generate('Your prompt', { maxSteps: MAX_STEPS }) ``` :::참고 반응형 신호의 기본값은 `tagName: 'system-reminder'`입니다. 프로세서에서 내보내는 신호에 관한 자세한 내용은 [신호](https://mastra.zisheng.pro/ko/docs/long-running-agents/signals)를 참조하세요. ::: ### 알림을 보관하지 않고 전달 기본적으로 프로세서에서 보낸 신호는 대화의 일부가 됩니다. 즉, 저장소에 기록되고 나중에 Prompt에 다시 입력됩니다. 매 턴마다 다시 삽입하는 지시의 경우 복사본이 누적되고 Model이 이전 알림을 모방할 컨텍스트로 처리하기 시작하므로 바람직하지 않습니다. 현재 호출에서만 Model에 신호를 전달하고 보존하지 않으려면 `transient: true`를 설정하세요. **사용 시기:**대화가 늘어남에 따라 Model의 최근성 창에 짧은 조정 지침(예: "현재 작업 유지", "세 문장 미만의 답변 유지" 또는 라이브 애플리케이션 상태에 따라 달라지는 턴별 제약 조건)을 유지하려고 합니다. 매 턴마다 다시 주입하여 최신 메시지 근처에 유지되도록 합니다. ```typescript import type { Processor, ProcessInputStepArgs } from '@mastra/core/processors' export class SteeringReminderProcessor implements Processor { readonly id = 'steering-reminder' async processInputStep({ sendSignal }: ProcessInputStepArgs) { await sendSignal?.({ type: 'reactive', contents: 'Stay on the current task and keep answers under three sentences.', transient: true, }) } } ``` 현재 호출에 대한 Prompt에 일시적인 신호가 계속 나타나므로 Model은 마지막 방향 전환 근처에서 신호를 봅니다. 보관되지 않으므로 매 턴마다 다시 보내면 기록이 누적되는 대신 컨텍스트에 새로운 단일 복사본이 유지되며 저장된 스레드 기록에는 표시되지 않습니다. 아무 것도 기록되지 않기 때문에 차례대로 안정적인 Prompt 캐시 접두사를 유지합니다. ### 맞춤 스트림 이벤트 내보내기 출력 프로세서는 스트리밍 중 클라이언트에 사용자 지정 데이터 청크를 내보낼 수 있는 `writer` 객체를 받습니다. 원래 스트림을 차단하지 않고 조정 결과를 스트리밍하거나 UI 업데이트 신호를 보내는 등의 사용 사례에 유용합니다. ```typescript import type { Processor } from '@mastra/core/processors' export class ModerationProcessor implements Processor { id = 'moderation' async processOutputResult({ messages, writer }) { // Run moderation on the final output const text = messages .filter(m => m.role === 'assistant') .flatMap(m => m.content.parts?.filter(p => p.type === 'text')) .map(p => p.text) .join(' ') const result = await runModeration(text) if (result.requiresChange) { // Emit a custom event to the client with the moderated text await writer?.custom({ type: 'data-moderation-update', data: { originalText: text, moderatedText: result.moderatedText, reason: result.reason, }, }) } return messages } } ``` 클라이언트에서 스트림의 사용자 정의 청크 유형을 수신합니다. ```typescript const stream = await agent.stream('Hello') for await (const chunk of stream.fullStream) { if (chunk.type === 'data-moderation-update') { // Update the UI with moderated text updateDisplayedMessage(chunk.data.moderatedText) } } ``` 사용자 정의 청크 유형은 다음을 사용해야 합니다.`data-` prefix (e.g., `data-moderation-update`, `data-status`). 기본적으로 `processOutputStream()`은 Tool 텔레메트리나 다른 프로세서의 출력을 실수로 처리하지 않도록 `data-*` 청크를 건너뜁니다. 프로세서에서 이러한 청크를 검사, 수정 또는 차단하려면 해당 프로세서에 `processDataParts = true`를 설정하세요. ```typescript class ModerationCollector implements Processor { id = 'moderation-collector' processDataParts = true async processOutputStream({ part, state }) { if (part.type === 'data-moderation-update') { state.warnings ??= [] state.warnings.push(part.data) } return part } } ``` ### 메시지에 메타데이터 추가 `processOutputResult`에서 메시지에 사용자 지정 메타데이터를 추가할 수 있습니다. 이 메타데이터는 응답 객체를 통해 접근할 수 있습니다. ```typescript import type { Processor } from '@mastra/core/processors' import type { MastraDBMessage } from '@mastra/core/memory' export class MetadataProcessor implements Processor { id = 'metadata-processor' async processOutputResult({ messages, }: { messages: MastraDBMessage[] }): Promise { return messages.map(msg => { if (msg.role === 'assistant') { return { ...msg, content: { ...msg.content, metadata: { ...msg.content.metadata, processedAt: new Date().toISOString(), customData: 'your data here', }, }, } } return msg }) } } ``` 다음을 사용하여 메타데이터에 액세스합니다.`generate()`: ```typescript const result = await agent.generate('Hello') // The response includes uiMessages with processor-added metadata const assistantMessage = result.response?.uiMessages?.find(m => m.role === 'assistant') console.log(assistantMessage?.metadata?.customData) ``` 스트리밍에서는 `finish` 청크 페이로드 또는 `stream.response` Promise에서 메타데이터에 접근합니다. ### Workflow를 프로세서로 사용 Mastra Workflow를 프로세서로 사용하여 병렬 실행, 조건부 분기 및 오류 처리 기능을 갖춘 복잡한 처리 파이프라인을 생성할 수 있습니다. ```typescript import { createWorkflow, createStep } from '@mastra/core/workflows' import { ProcessorStepSchema, PromptInjectionDetector, PIIDetector, ModerationProcessor, } from '@mastra/core/processors' import { Agent } from '@mastra/core/agent' // Create a workflow that runs multiple checks in parallel const moderationWorkflow = createWorkflow({ id: 'moderation-pipeline', inputSchema: ProcessorStepSchema, outputSchema: ProcessorStepSchema, }) .parallel([ createStep( new PIIDetector({ strategy: 'redact', }), ), createStep( new PromptInjectionDetector({ strategy: 'block', }), ), createStep( new ModerationProcessor({ strategy: 'block', }), ), ]) .map(async ({ inputData }) => { return inputData['processor:pii-detector'] }) .commit() // Use the workflow as an input processor const agent = new Agent({ id: 'moderated-agent', name: 'Moderated Agent', model: 'openai/gpt-5.6-sol', inputProcessors: [moderationWorkflow], }) ``` `.parallel()` 단계 이후 각 브랜치 결과에는 프로세서 ID를 기준으로 키가 지정됩니다(예: `processor:pii-detector`). `.map()`을 사용해 다음 단계에서 출력을 받을 브랜치를 선택하세요. 브랜치에서 `redact`와 같은 변형 전략을 사용하는 경우 변환된 메시지가 이후 단계로 전달되도록 해당 브랜치에 매핑하세요. 모든 브랜치가 `block`만 사용한다면 어느 브랜치를 선택해도 됩니다. 메시지를 수정하는 브랜치가 없으므로 아무 브랜치나 선택하세요. Agent가 Mastra에 등록되면 프로세서 Workflow가 자동으로 Workflow로 등록되므로[Studio](https://mastra.zisheng.pro/ko/docs/studio/overview). ### 재시도 메커니즘 처리자는 LLM이 피드백을 통해 응답을 다시 시도하도록 요청할 수 있습니다. 이는 품질 검사, 출력 유효성 검사 또는 반복적 개선을 구현하는 데 유용합니다. ```typescript import type { Processor } from '@mastra/core/processors' export class QualityChecker implements Processor { id = 'quality-checker' async processOutputStep({ text, abort, retryCount }) { const qualityScore = await evaluateQuality(text) if (qualityScore < 0.7 && retryCount < 3) { // Request a retry with feedback for the LLM abort('Response quality score too low. Please provide a more detailed answer.', { retry: true, metadata: { score: qualityScore }, }) } return [] } } const agent = new Agent({ id: 'quality-agent', name: 'Quality Agent', model: 'openai/gpt-5.6-sol', outputProcessors: [new QualityChecker()], maxProcessorRetries: 3, // Maximum retry attempts. If unset, retries are disabled (unless errorProcessors are configured, in which case it defaults to 10). }) ``` 재시도 메커니즘: - `processOutputStep()` 및 `processInputStep()` 메서드에서 작동합니다. - 중단 이유를 LLM의 컨텍스트에 추가하여 단계를 다시 실행합니다. - `retryCount` 매개변수로 재시도 횟수를 추적합니다. - Agent 또는 호출에 명시적인 `maxProcessorRetries` 제한을 설정해야 합니다. ### 오류 프로세서 재시도 제한 `processAPIError()`에는 별도의 기본값이 있습니다. `errorProcessors`가 구성되어 있고 `maxProcessorRetries`를 생략하면 런타임에서 최대 `10`회까지 재시도할 수 있습니다. 제한된 재시도 예산이 필요하다면 한도를 명시적으로 설정하세요. `StreamErrorRetryProcessor`의 `maxRetries`도 동일한 값으로 설정하세요. 자체 기본값은 `1`이므로 Agent 한도보다 낮을 수 있습니다. 프로세서가 요청의 유일한 재시도 메커니즘이라면 Model 재시도 횟수를 `0`으로 유지하세요. ### 위반 콜백 모든 프로세서에는 정책 위반이 감지될 때마다 실행되는 `onViolation` 속성이 있습니다. 이는 `abort()`가 호출될 때(차단 전략)와 프로세서가 경고를 발생시킬 때(경고 전략) 모두 적용됩니다. 프로세서의 주요 로직에 영향을 주지 않고 경고, 로깅 또는 부수 효과를 처리하는 데 사용하세요. ```typescript import { ModerationProcessor, CostGuardProcessor } from '@mastra/core/processors' const moderation = new ModerationProcessor({ model: 'openai/gpt-5-nano', strategy: 'block', }) moderation.onViolation = ({ processorId, message, detail }) => { // Log to external monitoring, send alerts, update dashboards monitor.track('processor_violation', { processorId, message, detail }) } const costGuard = new CostGuardProcessor({ maxCost: 10.0, scope: 'resource', window: '30d', }) costGuard.onViolation = ({ processorId, message, detail }) => { alertSystem.notify(`[${processorId}] ${message}`) } ``` 콜백은 다음을 포함하는 `ProcessorViolation` 객체를 받습니다. - `processorId`: 위반을 감지한 프로세서의 ID - `message`: 위반 사항에 대해 사람이 읽을 수 있는 설명 - `detail`: 프로세서별 메타데이터(예: 비용 사용량, 감지된 PII 유형, 조정 범주) `onViolation`은 기본 [`Processor` 인터페이스](https://mastra.zisheng.pro/ko/reference/processors/processor-interface)의 일부이므로 모든 사용자 지정 프로세서에서도 사용할 수 있습니다. 실행기는 프로세서가 `abort()`를 호출할 때 이를 자동으로 실행합니다. 콜백 내부에서 발생한 오류는 프로세서 파이프라인에 영향을 주지 않도록 조용히 포착됩니다. ### 중단 및 트립와이어 청크 `abort(reason, options)`를 호출하면 처리를 종료하는 `TripWire` 오류가 발생합니다. 스트림에서 Mastra는 클라이언트가 감지할 수 있는 `tripwire` 청크를 내보냅니다. ```typescript for await (const chunk of stream.fullStream) { if (chunk.type === 'tripwire') { console.log('Blocked by', chunk.payload.processorId, '-', chunk.payload.reason) break } } ``` `agent.generate()`의 경우 결과는 `result.finishReason === 'other'`와 함께 `result.tripwire`로 동일한 정보를 노출합니다. `abort`두 번째 옵션 인수를 허용합니다: - `retry: true`는 종료하는 대신 Agent에 재시도를 요청합니다. 입력 및 출력 프로세서 재시도를 사용하려면 Agent 또는 호출에 `maxProcessorRetries`를 설정해야 합니다. - `metadata`는 구조화된 데이터를 `tripwire` 청크에 추가하므로 다운스트림 소비자가 `pii`, `quality`, `moderation`과 같은 범주에 따라 분기할 수 있습니다. ## API 오류 처리 `processAPIError` 메서드는 네트워크 또는 서버 장애가 아니라 API에서 요청을 거부하는 오류(예: 400 또는 422 상태 코드)인 LLM API 거부를 처리합니다. 이를 통해 API가 메시지 형식을 거부할 때 요청을 수정하고 재시도할 수 있습니다. ```typescript import { APICallError } from '@ai-sdk/provider' import type { Processor, ProcessAPIErrorArgs, ProcessAPIErrorResult } from '@mastra/core/processors' export class ContextLengthHandler implements Processor { id = 'context-length-handler' processAPIError({ error, messageList, retryCount, }: ProcessAPIErrorArgs): ProcessAPIErrorResult | void { if (retryCount > 0) return if (APICallError.isInstance(error) && error.message.includes('context length exceeded')) { const messages = messageList.get.all.db() if (messages.length > 4) { messageList.removeByIds([messages[1]!.id, messages[2]!.id]) return { retry: true } } } } } ``` Mastra에는 Anthropic의 "assistant message prefill" 오류를 자동으로 처리하는 내장 [`PrefillErrorHandler`](https://mastra.zisheng.pro/ko/reference/processors/prefill-error-handler)가 있습니다. 이 프로세서는 자동으로 삽입되며 별도의 구성이 필요하지 않습니다. ## 관련 문서 - [난간](https://mastra.zisheng.pro/ko/docs/agents/guardrails): 보안 및 검증 프로세서 - [Memory 프로세서](https://mastra.zisheng.pro/ko/docs/memory/memory-processors): Memory별 프로세서 및 자동 통합 - [프로세서 인터페이스](https://mastra.zisheng.pro/ko/reference/processors/processor-interface): 프로세서에 대한 전체 API 참조 - [ToolSearch프로세서 참조](https://mastra.zisheng.pro/ko/reference/processors/tool-search-processor): 런타임 Tool 검색을 위한 API 참조