본문으로 건너뛰기

신호

추가된 항목: @mastra/core@1.39.0

:::실험적

이 기능은 베타 버전입니다. API가 안정될 때까지 주요 버전 변경 없이 주요 변경 사항이 발생할 수 있습니다.

:::

신호는 스레드를 통해 Agent와 상호작용하는 방법입니다. 모든 상호작용을 agent.stream()으로 시작하는 대신 스레드를 구독하고 메시지나 신호를 보내세요. Mastra는 스레드가 유휴 상태이면 Agent를 깨우고, Agent 루프가 실행 중이면 입력을 루프에 삽입하거나, 다음 턴을 위해 입력을 대기열에 추가합니다. 사용자가 작성한 입력에는 메시지 API를 사용하세요. 백그라운드 작업 알림, 정책 알림 또는 프로세서가 생성한 컨텍스트 같은 하위 수준의 시스템 컨텍스트에는 sendSignal()을 사용하세요. :::tip[📹 보기]

신호가 장기 실행 Agent를 깨우고 조정하는 방법은 Mastra 신호 개요에서 확인하세요. :::

신호를 사용해야 하는 경우
신호를 사용해야 하는 경우에 대한 직접 링크

Agent 스레드에 원래 stream() 호출 외부의 새로운 입력이나 컨텍스트가 필요할 때 신호를 사용하세요. 사용자가 실행 중 후속 메시지를 보내거나, 백그라운드 시스템이 스레드에 컨텍스트를 추가해야 하거나, 외부 이벤트가 Agent를 깨우거나 업데이트하거나 알림을 보내야 할 때 신호가 유용합니다. 사용자가 작성한 입력에는 sendMessage()queueMessage()를 사용하세요. 하위 수준의 시스템 컨텍스트에는 sendSignal()을 사용하세요. 지속형 상태 레인에는 sendStateSignal()을 사용하고, 외부 이벤트가 영속 알림 수신함 레코드를 생성해야 할 때는 sendNotificationSignal()을 사용하세요.

빠른 시작
빠른 시작에 대한 직접 링크

Agent를 만들고 스레드를 구독한 다음 해당 스레드에 메시지를 보냅니다. 메시지가 Agent를 깨우거나 실행 중인 루프에 들어갈 때 구독은 활성 스트림을 수신합니다.

src/mastra/signals.ts
import { Agent } from '@mastra/core/agent'

const agent = new Agent({
id: 'support-agent',
name: 'Support Agent',
instructions: 'Help the user compare options.',
model: 'openai/gpt-5.6-sol',
})

const thread = {
resourceId: 'user_123',
threadId: 'thread_456',
}

const subscription = await agent.subscribeToThread(thread)

await agent.sendMessage('Compare that with the previous option.', thread)

for await (const chunk of subscription.stream) {
console.log(chunk)
}

스레드에 실행 중인 Agent 스트림이 있으면 sendMessage()가 해당 Agent 루프 내부의 새 입력이 됩니다. 스레드가 유휴 상태이면 Mastra가 메시지를 첫 번째 입력으로 사용하여 스트림을 시작합니다.

메시지 입력
메시지 입력에 대한 직접 링크

지금 메시지 보내기
지금 메시지 보내기에 대한 직접 링크

사용자가 활성 Agent에서 메시지를 즉시 확인하기를 기대할 때 sendMessage()를 사용하세요.

src/mastra/signals.ts
agent.sendMessage(
{
contents: 'Use the latest customer note too.',
attributes: { name: 'Jane', sentFrom: 'slack' },
},
{
resourceId: 'user_123',
threadId: 'thread_456',
},
)

Model은 XML로 래핑된 사용자 입력으로 특성 메시지를 받습니다.

<user name="Jane" sentFrom="slack">Use the latest customer note too.</user>

속성이 없는 메시지는 일반 사용자 입력으로 전송됩니다.

다음 차례를 위해 메시지 대기열에 넣기
다음 차례를 위해 메시지 대기열에 넣기에 대한 직접 링크

사용자가 후속 메시지를 보내지만 활성 Model 호출이 먼저 완료되어야 한다면 queueMessage()를 사용하세요. Mastra는 활성 실행이 완료될 때까지 기다린 다음 동일한 스레드에서 새 실행을 시작합니다.

src/mastra/signals.ts
agent.queueMessage('Also check whether the tests need updates.', {
resourceId: 'user_123',
threadId: 'thread_456',
})

스레드가 유휴 상태이면 queueMessage()가 즉시 실행을 시작합니다. 스레드가 활성 상태이면 현재 실행이 완료된 후 새 실행을 시작하여 턴 순서를 유지합니다.

신호 컨텍스트
신호 컨텍스트에 대한 직접 링크

낮은 수준의 신호 동작 제어
낮은 수준의 신호 동작 제어에 대한 직접 링크

사용자가 작성한 입력 대신 시스템이 생성한 컨텍스트를 보내야 할 때 sendSignal()을 사용하세요. 외부 이벤트에는 type: 'notification'을 사용합니다. 기본적으로 Mastra는 활성 실행에 신호를 전달하고 유휴 스레드를 깨웁니다. 이 동작을 변경하려면 ifActive.behaviorifIdle.behavior를 사용하세요.

src/mastra/signals.ts
const result = agent.sendSignal(
{
type: 'notification',
contents: 'GitHub CI failed on PR #123: 3 tests failed.',
},
{
resourceId: 'user_123',
threadId: 'thread_456',
ifIdle: {
behavior: 'persist',
},
},
)

await result.persisted

유휴 상태를 깨우는 스트림에 Model 설정, Tool 또는 런타임 컨텍스트 같은 옵션이 필요하면 ifIdle.streamOptions를 전달하세요. ifActive, ifIdle, 분기 속성 및 streamOptionsAgent.sendSignal() 레퍼런스를 참조하세요.

알림 컨텍스트 보내기
알림 컨텍스트 보내기에 대한 직접 링크

신호에는 의미를 나타내는 type과 LLM에 표시되는 tagName이 있습니다. 신호 범주를 설명하려면 type을 사용하세요. Model에 표시되는 XML 태그를 제어하려면 tagName을 사용하세요. 외부 이벤트에는 type: 'notification'을 사용하세요. 반응형 신호는 정책 지침, 백그라운드 작업 결과 및 자동으로 로드된 지침처럼 프로세서 또는 런타임이 생성한 컨텍스트용으로 예약되어 있습니다.

src/mastra/signals.ts
agent.sendSignal(
{
type: 'notification',
contents: 'PR #123 has a new review comment from User X about the API surface.',
attributes: {
source: 'github',
pr: '123',
},
},
{
resourceId: 'user_123',
threadId: 'thread_456',
},
)

Model은 다음과 같은 컨텍스트로 신호를 수신합니다.

<notification source="github" pr="123">PR #123 has a new review comment from User X about the API surface.</notification>

XML에 안전한 tagName과 속성 이름을 사용하세요. 문자, 숫자, 콜론, 마침표 및 하이픈을 포함할 수 있으며, 문자 또는 밑줄로 시작해야 합니다.

스토리지 지원
스토리지 지원에 대한 직접 링크

풍부한 Memory와 신호 Workflow를 지원하는 스토리지 어댑터인 libSQL, PostgreSQL, MongoDB에서 알림 수신함 스토리지를 사용할 수 있습니다. 이러한 어댑터는 getStore('notifications')를 통해 알림 레코드를 노출합니다.

프로세서 컨텍스트 보내기
프로세서 컨텍스트 보내기에 대한 직접 링크

프로세서는 실행 중에 반응 신호를 보낼 수 있습니다. 처리자는 채팅 기록을 검사하고 특정 트리거에 반응하며 동일한 컨텍스트를 두 번 이상 전송하지 않아야 합니다.

다음 예시는 Tool 호출이 AGENTS.md 파일을 읽은 후 AGENTS.md 지침을 삽입하는 프로세서를 보여 줍니다.

src/mastra/processors/agents-md-reminder.ts
import type { Processor, ProcessInputStepArgs } from '@mastra/core/processors'

export const agentsMdReminderProcessor: Processor = {
id: 'agents-md-reminder',
async processInputStep({ messageList, sendSignal }: ProcessInputStepArgs) {
const messages = messageList.get.all.db()
const agentsMdPath = findAgentsMdPathFromToolCalls(messages)

if (!agentsMdPath || hasAlreadySentAgentsMdReminder(messages, agentsMdPath)) {
return messageList
}

await sendSignal?.({
type: 'reactive',
contents: readAgentsMdInstructions(agentsMdPath),
attributes: {
type: 'dynamic-agents-md',
path: agentsMdPath,
},
metadata: {
path: agentsMdPath,
},
})

return messageList
},
}

반응형 신호의 기본값은 tagName: 'system-reminder'이므로 Model은 이 컨텍스트를 다음과 같이 수신합니다.

<system-reminder type="dynamic-agents-md" path="packages/ui/AGENTS.md">
$agentsMdFileContents
</system-reminder>

구독 중인 스레드가 활성 상태일 때 sendSignal()을 await하면 스트림 에코 순서가 유지됩니다.

조건부 속성
조건부 속성에 대한 직접 링크

전달 시점에 Agent가 활성 상태인지 유휴 상태인지에 따라 달라지는 컨텍스트로 입력에 태그를 지정하려면 ifActive.attributesifIdle.attributes를 사용하세요. 최상위 attributes는 항상 적용되며, Mastra는 입력이 수락될 때 선택한 분기의 attributes를 여기에 병합합니다. 분기별 속성은 Agent.sendMessage() 레퍼런스Agent.sendSignal() 레퍼런스를 참조하세요.

상태 및 알림 신호
상태 및 알림 신호에 대한 직접 링크

상태 신호
상태 신호에 대한 직접 링크

상태 신호는 명명된 스레드 범위 컨텍스트 레인을 노출합니다. 브라우저 상태, 편집기 상태 또는 백그라운드 감시자 결과와 같이 시간이 지남에 따라 변경되는 지속 가능한 컨텍스트에 사용하세요.

외부 생성자가 상태 변경을 감지할 때 sendStateSignal()을 사용하세요. 각 상태 신호는 상태 레인, 생성자가 소유한 캐시 키, 업데이트가 스냅샷인지 델타인지를 식별합니다.

src/mastra/browser-watcher.ts
await agent.sendStateSignal(
{
id: 'browser',
mode: 'snapshot',
cacheKey: 'browser:https://example.com:3-tabs',
contents: 'Browser is open. Active tab URL: https://example.com. 3 open tabs.',
value: {
activeUrl: 'https://example.com',
tabCount: 3,
open: true,
},
},
{
resourceId: 'user_123',
threadId: 'thread_456',
},
)

Mastra는 상태 신호를 수락하면 스레드에 압축 추적 메타데이터를 저장합니다. 해당 상태가 여전히 최신인 동안 생성자가 동일한 cacheKey와 모드를 다시 보내면 Mastra는 중복 항목을 건너뜁니다. 프로세서가 상태 레인을 소유할 때 computeStateSignal()을 사용하세요. Mastra는 각 Model 입력 단계에서 processInputStep() 이후 이 메서드를 한 번 호출합니다. 상태 신호 필드와 반환 값은 Agent.sendStateSignal() 레퍼런스를 참조하세요.

src/mastra/processors/browser-state.ts
import type { ComputeStateSignalArgs, Processor } from '@mastra/core/processors'

export const browserStateProcessor: Processor = {
id: 'browser-state',
stateId: 'browser',
computeStateSignal(args: ComputeStateSignalArgs) {
const browser = readCurrentBrowserState()
const previous = readMostRecentBrowserState(args.activeStateSignals)
const changed = previous ? diffBrowserState(previous, browser) : browser
const shouldRefreshSnapshot = Boolean(args.lastSnapshot && !args.contextWindow.hasSnapshot)

if (previous && Object.keys(changed).length === 0 && !shouldRefreshSnapshot) {
return
}

const isDelta = Boolean(previous && !shouldRefreshSnapshot)

return {
mode: isDelta ? 'delta' : 'snapshot',
cacheKey: stableBrowserStateCacheKey(browser),
contents: isDelta ? describeBrowserDelta(changed) : describeBrowserSnapshot(browser),
value: browser,
...(isDelta ? { delta: changed } : {}),
}
},
}

Mastra는 lastSnapshotdeltasSinceSnapshotcomputeStateSignal()에 전달합니다. 현재 메시지 목록에 최신 스냅샷이 없으면 메시지 기록에서 이를 확인합니다. 병합 및 diff 로직은 여전히 프로세서가 담당합니다. contextWindow.hasSnapshot은 활성 메시지 창에 이 상태 레인의 스냅샷이 이미 포함되어 있는지를 프로세서에 알려 줍니다. false라면 이전 상태 메시지가 컨텍스트 창에서 제거된 후에도 Model이 현재 상태를 볼 수 있도록 새로운 snapshot을 반환하세요. 내장 브라우저 컨텍스트 프로세서는 스냅샷 및 델타 모드와 함께 browser ID로 상태를 내보냅니다.

알림 신호
알림 신호에 대한 직접 링크

알림 신호는 GitHub 활동, 이메일, Slack 멘션, CI 상태, 인시던트, 녹음 또는 다이렉트 메시지와 같은 외부 이벤트를 나타냅니다. 이벤트가 지속성 있는 받은 편지함 레코드를 생성해야 할 때는 agent.sendNotificationSignal()을 사용하세요. 알림 전달은 두 단계로 이루어집니다. 수신 단계에서 agent.sendNotificationSignal()은 알림 레코드를 저장하고 Agent의 전달 정책을 결정합니다. 발송 단계에서 Mastra는 기한이 된 레코드를 처리하고 전체 알림 또는 요약 신호를 내보냅니다. 기본 전달 정책은 우선순위를 고려합니다. 긴급 알림은 즉시 전달되지만, 우선순위가 낮은 알림은 요약으로 일괄 처리되거나 스레드가 유휴 상태가 될 때까지 대기할 수 있습니다. 알림 필드는 Agent.sendNotificationSignal() 참고 문서, notifications.deliveryPolicy 구성은 Agent 생성자 참고 문서, 받은 편지함 Tool 작업은 createNotificationInboxTool() 참고 문서를 확인하세요.

src/mastra/notifications.ts
await agent.sendNotificationSignal(
{
source: 'github',
kind: 'ci-status',
priority: 'high',
summary: 'CI failed on main: 3 tests failed.',
payload: {
repository: 'acme/app',
branch: 'main',
},
dedupeKey: 'github:acme/app:main:ci',
},
{
resourceId: 'user_123',
threadId: 'thread_456',
},
)

Model은 컨텍스트로 전체 알림을 받습니다.

<notification source="github" type="ci-status" priority="high" status="delivered">CI failed on main: 3 tests failed.</notification>

알림 요약은 받은 편지함 기록이 대기 중임을 Model에 알려줍니다.

<notification-summary pending="10">github: 3, email: 5, slack: 2</notification-summary>

Mastra가 요약을 내보내면 요약된 각 레코드에 summaryAt을 설정하고 summarySignalId를 지정합니다. 레코드는 대기 상태로 유지되며 계속 읽을 수 있습니다. Mastra가 전체 알림을 내보내면 deliveredSignalId를 설정하고 레코드를 delivered로 표시합니다. 받은 편지함 Tool이 알림을 먼저 읽으면 전체 알림 신호를 주입하고 레코드를 seen으로 표시할 수 있으며, 이를 통해 전체 알림이 중복 전달되는 것을 방지합니다. 일부 알림이 다른 발송 창이나 요약 롤업을 기다려야 한다면 Agent의 전달 정책을 구성하세요. 지연된 알림과 요약 롤업을 자동으로 전달해야 한다면 Mastra 수준에서 예약 발송을 활성화하세요. notifications.deliveryPolicyAgent 생성자 참고 문서, 런타임 알림 발송 구성은 Mastra 클래스 참고 문서를 확인하세요.

알림 받은 편지함 Tool
알림 받은 편지함 Tool에 대한 직접 링크

여러 CRUD Tool 대신 하나의 Tool로 Agent가 받은 편지함 작업을 수행하도록 하려면 createNotificationInboxTool()을 사용하세요. <notification-summary> 신호를 받은 후 Agent가 요약의 기반이 된 전체 레코드를 확인해야 할 때는 read를 사용하세요. 알림 내용은 일반적인 Tool 출력이 아니라 신호로 전달됩니다. 설정 예제, 입력 스키마 및 작업 동작은 createNotificationInboxTool() 참고 문서를 확인하세요. sendNotificationSignal()에는 notifications를 지원하는 스토리지 도메인이 필요합니다. 받은 편지함 스토리지를 우회해야 하는 저수준 알림 형태의 컨텍스트에만 sendSignal({ type: 'notification' })을 사용하세요.

분산 및 서버리스 배포
분산 및 서버리스 배포에 대한 직접 링크

신호 조정은 게시/구독 백엔드를 통해 이루어집니다. LeaseProvider를 구현하는 백엔드에 신호가 도착하면 Mastra는 대상 스레드의 임대를 획득하여 한 번에 하나의 프로세스만 대화를 소유하도록 한 다음, Agent를 깨우거나 입력을 실행 중인 루프로 라우팅합니다. 임대를 지원하지 않는 백엔드는 항상 소유권을 부여하는 no-op 방식으로 대체됩니다. 단일 프로세스에서는 문제가 없지만 여러 인스턴스에서는 적합하지 않습니다. 기본 Memory 내 게시/구독은 인스턴스 경계를 ​​넘을 수 없습니다. Vercel과 같은 서버리스 플랫폼이나 다중 인스턴스 배포에서는 후속 신호가 Agent를 실행하는 인스턴스가 아닌 다른 인스턴스로 라우팅될 수 있습니다.

공유 게시/구독이 없으면 해당 인스턴스는 활성 실행에 도달할 수 없으며 자체적으로 시작되므로 원래 실행은 그대로 유지되고 스레드는 두 번 처리됩니다.

여러 인스턴스에서 임대와 신호를 조정할 수 있도록 Redis Streams 기반의 공유 게시/구독을 Mastra 인스턴스에 구성하세요.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { RedisStreamsPubSub } from '@mastra/redis-streams'

export const mastra = new Mastra({
agents: { agent },
pubsub: new RedisStreamsPubSub({
url: process.env.REDIS_URL,
keyPrefix: 'mastra:my-app',
}),
})

RedisStreamsPubSub은 이벤트 전달 계약과 분산 임대를 모두 구현하므로 단일 백엔드가 인스턴스 간 신호 전달과 임대 소유권을 처리합니다. Vercel의 관리형 Redis 통합과 Upstash Redis를 모두 사용할 수 있습니다. 분산 게시/구독이 필요한 경우에 대한 자세한 내용은 PubSub 가이드RedisStreamsPubSub 참고 문서를 확인하세요.

호환성 및 API
호환성 및 API에 대한 직접 링크

호환성
호환성에 대한 직접 링크

Mastra는 type: 'user-message'type: 'system-reminder'와 같은 레거시 신호 페이로드도 계속 허용합니다. 내부적으로는 이를 새로운 카테고리 및 태그 형식으로 정규화합니다.

  • type: 'user-message': type: 'user'tagName: 'user'로 정규화
  • type: 'system-reminder': type: 'reactive'tagName: 'system-reminder'로 정규화 기존에 저장된 신호 행과 이전 클라이언트는 호환성 계층을 통해 계속 로드됩니다. 새 클라이언트는 서버가 지원하는 경우 메시지 경로를 호출합니다. React의 스레드 신호 경로는 이전 서버를 감지하면 레거시 /signals 경로로 대체됩니다. 전체 메시지, 신호 및 구독 유형은 Agent 신호 참고 문서를 확인하세요.

Tool 호출 승인
Tool 호출 승인에 대한 직접 링크

Tool 승인을 위해 구독된 실행이 일시 중지되면 구독 네이티브 메서드를 사용하여 Tool 호출을 승인하거나 거부하세요. 재개된 청크는 기존 스레드 구독을 통해 도착합니다. 요청 및 응답 형식은 client.getAgent().sendToolApproval() 참고 문서서버 Agent 경로를 확인하세요.

HTTP 경로 사용
HTTP 경로 사용에 대한 직접 링크

HTTP를 통해 Mastra를 직접 호출한다면 즉시 메시지에는 POST /api/agents/:agentId/send-message, 다음 턴 메시지에는 POST /api/agents/:agentId/queue-message를 사용하세요. 구독 네이티브 Tool 승인에는 POST /api/agents/:agentId/send-tool-approval을 사용하세요. 요청 및 응답 스키마는 서버 경로 참고 문서를 확인하세요.

클라이언트 SDK 사용
클라이언트 SDK 사용에 대한 직접 링크

JavaScript 클라이언트는 스레드 신호 API를 노출합니다.

입력을 받거나 입력에 응답하여 깨어나는 스트림을 클라이언트가 렌더링할 수 있도록, 스레드 입력을 보내기 전에 subscribeToThread()를 사용하세요.

src/app/chat.ts
const agent = client.getAgent('supportAgent')

const subscription = await agent.subscribeToThread({
resourceId: 'user_123',
threadId: 'thread_456',
})

await agent.sendMessage({
message: 'Show the shorter version.',
resourceId: 'user_123',
threadId: 'thread_456',
})

await subscription.processDataStream({
onChunk: chunk => {
console.log(chunk)
},
reconnect: true,
})

장기 구독에는 reconnect: true를 사용하세요. 재연결 옵션은 client.getAgent().subscribeToThread() 참고 문서를 확인하세요.

맞춤형 SSE 구독을 유지하세요
맞춤형 SSE 구독을 유지하세요에 대한 직접 링크

스레드 구독을 위해 자체 SSE(서버 전송 이벤트) 엔드포인트를 노출하는 경우 스트림이 유휴 상태인 동안 주기적인 하트비트 프레임을 보냅니다. 이렇게 하면 다음 신호나 Model 청크가 도착하기 전에 브라우저, 프록시 및 로드 밸런서가 연결을 닫는 것을 방지할 수 있습니다.

다음 예에서는 25초마다 SSE 주석을 보냅니다.

src/api/subscribe.ts
const heartbeat = setInterval(() => {
controller.enqueue(encoder.encode(': keep-alive\n\n'))
}, 25_000)

request.signal.addEventListener('abort', () => {
clearInterval(heartbeat)
})

클라이언트 측 재연결 논리와 함께 하트비트를 사용합니다. 하트비트는 유휴 연결 끊김을 줄이는 반면 네트워크 또는 런타임이 여전히 스트림을 닫을 때 다시 연결을 복구합니다.