블로그 • Foundation Models

Apple Intelligence 2026: Foundation Models와 App Intents 사이에서 교환하는 JSON

2026년, 같은 앱에는 입구가 둘이다. 기기의 LanguageModelSession은 타입에 맞춰 값을 디코드하고, 시스템은 App Intent의 perform()을 호출한다. 남길 것은 그 둘의 원본이 아니라 직접 정의한 교환 봉투다.

제품 이야기는 iPhone 18이 하드웨어 그 이상인 이유. Siri가 Agent가 되는지는 Siri AI는 AI Agent가 될까. 이 글은 봉투만 다룬다. 어떤 필드를 남기는지, 언제 실행하면 안 되는지.

먼저 이것만: 프레임워크는 Generable을 schema로 만들고 값을 디코드한다. 그건 모델 세션 안의 일이다. 저장하고 맞춘 뒤 실행할지 정하려고 남기는 것이 봉투다. 아래는 계약 둘, 필드, JSON 하나, 멈춰야 하는 네 가지 순이다.

계약 둘은 한 번의 호출이 아니다

기기의 모델이 타입에 맞춰 값을 채우는 방식은 Generable. 타입은 schema가 되고, 모델이 채우며, 프레임워크가 디코드한다. 정적 타입은 @Generable을 쓴다. 정적 타입이 없으면 세션은 GenerationSchema로 GeneratedContent를 반환할 수 있다. 봉투에 쓸 때는 둘 다 content다.

App Intent는 다른 호출 경로다. 파라미터는 @Parameter에 둔다. 설명은 App Intent에 파라미터 추가하기. 시스템이 perform()을 호출할 때는 LanguageModelSession이 없고, Generable의 content도 없다.

  1. 1
    세션이 내용을 낸다

    respond(to:generating:)는 Response를 반환한다. 생성이 끝나기 전에는 PartiallyGenerated이고, 필드는 아직 비어 있을 수 있다. 실행할 파라미터는 아직 없다.

  2. 2
    파라미터를 투영한다

    Intent가 받는 필드만 parameters로 옮긴다. 모델이 더한 rationale은 content에 남긴다. 이름은 같아도 같은 객체는 아니다.

  3. 3
    실행 전에 봉투를 쓴다

    source, sessionId, schemaVersion, complete, content, intent, parameters를 JSON 하나로 남긴다. complete가 true가 아니면 perform()을 호출하지 않는다.

봉투의 필드

아래 키는 자신의 봉투에 속한다. Apple이 디코드한 값의 필드가 아니다.

필드누가 쓰나역할
source당신의 앱foundation-models 또는 app-intent. 값이 어느 입구에서 왔는지 나타낸다.
sessionId당신의 앱모델 세션에만 있다. 예: lm_01. 시스템 호출이면 null.
schemaVersion당신의 앱Generable 필드 표의 세대. 이 예는 3이다. 필드가 바뀌면 올리고, 옛 봉투를 새 Intent에 그대로 다시 실행하지 않는다.
generable당신의 앱타입 이름. 예: OrderDraft. 시스템이 직접 호출하면 null.
complete생성 상태에서 옮긴다false는 아직 생성 중이라 필드가 바뀔 수 있다. true가 된 뒤에 투영하고 실행을 검토한다.
content모델이 디코드한 결과모델이 디코드한 객체. 동작보다 필드가 많아도 된다. 예: rationale. 시스템 호출이면 null.
intent당신의 앱호출할, 또는 시스템이 호출 중인 App Intent 식별자. 예: PlaceOrderIntent.
parameters당신의 투영, 또는 시스템 인자perform()에 넘길 객체. 미완이면 null. 시스템 호출이면 시스템이 이미 넘긴 인자.

content와 parameters는 필드 이름이 같아도 서로 다른 객체다. content를 그대로 perform()에 넘기면 모델이 더한 rationale이 동작에 들어간다.

저장할 교환 JSON

아래 봉투와 Schema는 자신의 계약이지 Apple의 전송 형식이 아니다. JSONNote의 JSON Schema는 타입, 열거, 필수 키를 본다. complete가 true일 때만 실행할 수 있다는 규칙은 이 Schema로 표현하지 못한다. 판단은 앱에서 한다.

생성이 끝난 뒤의 봉투. rationale은 content에만 있고 parameters에는 없다.
{
  "source": "foundation-models",
  "sessionId": "lm_01",
  "schemaVersion": 3,
  "generable": "OrderDraft",
  "complete": true,
  "content": {
    "itemId": "sku_42",
    "quantity": 1,
    "fulfillment": "pickup",
    "rationale": "User asked for pickup."
  },
  "intent": "PlaceOrderIntent",
  "parameters": {
    "itemId": "sku_42",
    "quantity": 1,
    "fulfillment": "pickup"
  }
}
첫 칸에 봉투, 둘째 칸에 이 Schema. 작동하다 를 Schema로 확인하세요 로 바꾼다.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["source", "sessionId", "schemaVersion", "generable", "complete", "content", "intent", "parameters"],
  "properties": {
    "source": { "enum": ["foundation-models", "app-intent"] },
    "sessionId": { "type": ["string", "null"] },
    "schemaVersion": { "type": "integer" },
    "generable": { "type": ["string", "null"] },
    "complete": { "type": "boolean" },
    "content": { "type": ["object", "null"] },
    "intent": { "type": "string" },
    "parameters": { "type": ["object", "null"] }
  }
}

rationale은 content에만 남긴다. 동작 파라미터의 타입 대응은 앱 동작 필드와 JSON Schema. 이 글은 그 표를 반복하지 않는다.

끝나기 전에 perform 하지 않는다

생성이 끝나지 않았다. quantity는 아직 없고 parameters는 null이다.
{
  "source": "foundation-models",
  "sessionId": "lm_01",
  "schemaVersion": 3,
  "generable": "OrderDraft",
  "complete": false,
  "content": {
    "itemId": "sku_42"
  },
  "intent": "PlaceOrderIntent",
  "parameters": null
}

이 봉투는 위 Schema를 통과한다. parameters가 null을 허용하기 때문이다. 모양이 맞아도 실행하지 않는다. PartiallyGenerated는 뒤의 토큰이 이미 있는 필드를 아직 바꿀 수 있다는 뜻이다. complete가 true가 된 뒤, 완성된 content에서 parameters를 투영한다. 끝나기 전에 parameters를 객체로 써도 Schema는 막지 않는다.

봉투왜 멈추나
complete가 false뒤의 토큰이 이미 있는 필드를 아직 바꿀 수 있다.
schemaVersion이 현재 필드 표가 아니다옛 content는 지금의 Intent와 맞지 않는다.
foundation-models인데 parameters에 content에 없는 키가 있다투영은 모델이 쓰지 않은 값을 만들어서는 안 된다.
app-intent인데 content가 null이 아니다시스템 인자는 모델 출력이 아니다. content에 다시 써서 생성 결과인 것처럼 만들지 않는다.

늘어난 키는 JSON 비교로 본다. 이 확인은 source가 foundation-models인 봉투에만 쓴다. 시스템 호출에서는 content가 null이므로, parameters의 모든 키가 content에 있었어야 한다고 요구할 수 없다.

Tool과 App Intent를 어떻게 넘기나

Foundation Models의 Tool은 자신의 세션이 호출한다. 설명은 도구 호출로 생성 확장하기. call(arguments:)가 받는 Arguments도 Generable이다. 먼저 content에 쓰고, parameters로 투영한 뒤에 perform()을 검토한다.

시스템이 Intent를 직접 호출하면 source를 app-intent로 하고, sessionId, generable, content는 null로 한다. parameters는 시스템이 이미 넘긴 값이다. sessionId를 만들어 모델 생성인 것처럼 꾸미지 않는다.

Foundation Models ToolApp Intent
누가 호출하나당신의 LanguageModelSessionSiri, 단축어, Spotlight
입력Generable인 Arguments@Parameter의 값
구현하는 메서드call(arguments:)perform()
봉투의 sourcefoundation-modelsapp-intent

다른 글과의 역할 나누기

제품과 Siri 두 글동작 필드 글이 글
답하는 것왜 하드웨어 이상인지, Siri가 Agent가 되는지어떤 필드가 이 동작에 들어갈 수 있는지모델 값과 동작 파라미터 사이에 어떤 JSON을 남기는지
여기서 찾지 말 것또 하나의 발표 요약AppEnum과 엔티티 id의 타입 대응content를 그대로 perform()의 인자로 쓰는 것

JSONNote에서 이 봉투를 맞춘다

  1. 1
    동작을 고른다

    JSON Schema를 연다. 작동하다 를 Schema로 확인하세요 로 바꾼다. 첫 칸에 봉투, 둘째 칸에 위 Schema를 붙인다. source는 foundation-models 또는 app-intent. complete는 불리언. parameters는 객체 또는 null.

  2. 2
    나쁜 데이터 셋을 부딪힌다

    source를 siri로 하거나, complete를 문자열 true로 하거나, parameters를 지운다. 이 셋은 실패한다. 미완 봉투(complete가 false, parameters가 null)는 통과한다. 통과해도 perform()은 호출하지 않는다.

  3. 3
    객체 둘을 비교한다

    JSON 비교 Diff를 연다. 첫 칸에 content, 둘째 칸에 parameters를 붙인다. rationale은 첫 칸에만 있어야 한다. 둘째 칸에만 있는 키는 투영이 쓴 것이고, 모델이 쓴 것이 아니다. 이 비교는 source가 foundation-models일 때만 쓴다.

자주 묻는 질문

Generable 출력을 그대로 perform()에 넘길 수 있나?

넘길 수 없다. 출력은 content에 쓴다. perform()은 투영한 parameters만 받는다. 봉투는 둘을 남겨 맞추는 데 쓴다.

Schema를 통과하면 실행해도 되나?

아직이다. 이 Schema는 타입, 열거, 필수만 본다. complete가 false이고 parameters가 null이어도 모양은 맞다. 실행 전에 complete가 true인지, 그리고 모델 입구에서는 parameters에 content에 없는 키가 없는지 확인한다.

Tool과 App Intent는 같은 한 번의 호출인가?

같은 호출이 아니다. Tool은 LanguageModelSession이 호출한다. App Intent는 Siri, 단축어, Spotlight가 호출한다. 같은 일을 양쪽에 둘 수 있다. 입력은 먼저 이 봉투에 남긴다.

schemaVersion이 바뀌면 옛 로그를 다시 실행할 수 있나?

그대로는 다시 실행할 수 없다. 버전은 Generable 필드 표의 세대다. 필드가 바뀌면 옛 content는 지금의 Intent와 맞지 않는다. 일단 멈추고, 명시적인 대응을 쓴 뒤에 실행할지 정한다.

동작 필드와 JSON Schema 글과 같은 글인가?

같은 글이 아니다. 그 글은 파라미터 타입을 필드 표로 맞춘다. 이 글은 모델 값과 그 표 사이에 남기는 봉투다. 진입점, 완료 상태, 버전, 그리고 content와 parameters를 나눈다.

정리

Foundation Models와 App Intents는 입구가 둘이다. 교환 봉투는 진입점, 완료 상태, schema 버전, content, parameters를 JSON 하나에 두어, perform() 전에 멈출 수 있게 한다.

결론: content는 봉투에 남긴다. perform()은 parameters만 받는다. complete가 false이면 실행하지 않는다. schemaVersion이 맞지 않으면 다시 실행하지 않는다. parameters에 content에 없는 키를 두지 않는다.

다음: 이 교환 JSON이 Schema를 통과하게 한 뒤, content와 parameters를 비교한다.

블로그로 돌아가기