Loading...
Loading...
Compare original and translation side by side
axiom-foundation-models-diagaxiom-foundation-models-refaxiom-foundation-models-diagaxiom-foundation-models-ref// ❌ BAD - Asking for world knowledge
let session = LanguageModelSession()
let response = try await session.respond(to: "What's the capital of France?")// ❌ BAD - Asking for world knowledge
let session = LanguageModelSession()
let response = try await session.respond(to: "What's the capital of France?")session.respond()async// ❌ BAD - Blocking main thread
Button("Generate") {
let response = try await session.respond(to: prompt) // UI frozen!
}// ✅ GOOD - Async on background
Button("Generate") {
Task {
let response = try await session.respond(to: prompt)
// Update UI with response
}
}session.respond()// ❌ BAD - Blocking main thread
Button("Generate") {
let response = try await session.respond(to: prompt) // UI frozen!
}// ✅ GOOD - Async on background
Button("Generate") {
Task {
let response = try await session.respond(to: prompt)
// Update UI with response
}
}// ❌ BAD - Manual JSON parsing
let prompt = "Generate a person with name and age as JSON"
let response = try await session.respond(to: prompt)
let data = response.content.data(using: .utf8)!
let person = try JSONDecoder().decode(Person.self, from: data) // CRASHES!{firstName: "John"}{name: "John"}// ✅ GOOD - @Generable guarantees structure
@Generable
struct Person {
let name: String
let age: Int
}
let response = try await session.respond(
to: "Generate a person",
generating: Person.self
)
// response.content is type-safe Person instance// ❌ BAD - Manual JSON parsing
let prompt = "Generate a person with name and age as JSON"
let response = try await session.respond(to: prompt)
let data = response.content.data(using: .utf8)!
let person = try JSONDecoder().decode(Person.self, from: data) // CRASHES!{firstName: "John"}{name: "John"}// ✅ GOOD - @Generable guarantees structure
@Generable
struct Person {
let name: String
let age: Int
}
let response = try await session.respond(
to: "Generate a person",
generating: Person.self
)
// response.content is type-safe Person instance// ❌ BAD - No availability check
let session = LanguageModelSession() // Might fail!// ✅ GOOD - Check first
switch SystemLanguageModel.default.availability {
case .available:
let session = LanguageModelSession()
// proceed
case .unavailable(let reason):
// Show graceful UI: "AI features require Apple Intelligence"
}// ❌ BAD - No availability check
let session = LanguageModelSession() // Might fail!// ✅ GOOD - Check first
switch SystemLanguageModel.default.availability {
case .available:
let session = LanguageModelSession()
// proceed
case .unavailable(let reason):
// Show graceful UI: "AI features require Apple Intelligence"
}// ❌ BAD - Everything in one prompt
let prompt = """
Generate a 7-day itinerary for Tokyo including hotels, restaurants,
activities for each day, transportation details, budget breakdown...
"""
// Exceeds context, poor quality// ❌ BAD - Everything in one prompt
let prompt = """
Generate a 7-day itinerary for Tokyo including hotels, restaurants,
activities for each day, transportation details, budget breakdown...
"""
// Exceeds context, poor quality// ✅ GOOD - Handle overflow
do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// Condense transcript and create new session
session = condensedSession(from: session)
}// ✅ GOOD - Handle overflow
do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// Condense transcript and create new session
session = condensedSession(from: session)
}// ✅ GOOD - Handle guardrails
do {
let response = try await session.respond(to: userInput)
} catch LanguageModelSession.GenerationError.guardrailViolation {
// Show message: "I can't help with that request"
}// ✅ GOOD - Handle guardrails
do {
let response = try await session.respond(to: userInput)
} catch LanguageModelSession.GenerationError.guardrailViolation {
// Show message: "I can't help with that request"
}// ✅ GOOD - Check supported languages
let supported = SystemLanguageModel.default.supportedLanguages
guard supported.contains(Locale.current.language) else {
// Show disclaimer
return
}// ✅ GOOD - Check supported languages
let supported = SystemLanguageModel.default.supportedLanguages
guard supported.contains(Locale.current.language) else {
// Show disclaimer
return
}switch SystemLanguageModel.default.availability {
case .available:
// Proceed with implementation
print("✅ Foundation Models available")
case .unavailable(let reason):
// Handle gracefully - show UI message
print("❌ Unavailable: \(reason)")
}switch SystemLanguageModel.default.availability {
case .available:
// Proceed with implementation
print("✅ Foundation Models available")
case .unavailable(let reason):
// Handle gracefully - show UI message
print("❌ Unavailable: \(reason)")
}| Use Case | Foundation Models? | Alternative |
|---|---|---|
| Summarization | ✅ YES | |
| Extraction (key info from text) | ✅ YES | |
| Classification (categorize content) | ✅ YES | |
| Content tagging | ✅ YES (built-in adapter!) | |
| World knowledge | ❌ NO | ChatGPT, Claude, Gemini |
| Complex reasoning | ❌ NO | Server LLMs |
| Mathematical computation | ❌ NO | Calculator, symbolic math |
| 使用场景 | 是否适合Foundation Models? | 替代方案 |
|---|---|---|
| 文本摘要 | ✅ 是 | |
| 文本提取(从文本中提取关键信息) | ✅ 是 | |
| 文本分类(对内容进行分类) | ✅ 是 | |
| 内容打标签 | ✅ 是(内置适配器!) | |
| 通用知识 | ❌ 否 | ChatGPT、Claude、Gemini |
| 复杂推理 | ❌ 否 | 云端大语言模型 |
| 数学计算 | ❌ 否 | 计算器、符号数学工具 |
@Generable
struct SearchSuggestions {
@Guide(description: "Suggested search terms", .count(4))
var searchTerms: [String]
}@Generable
struct SearchSuggestions {
@Guide(description: "Suggested search terms", .count(4))
var searchTerms: [String]
}let stream = session.streamResponse(
to: prompt,
generating: Itinerary.self
)
for try await partial in stream {
// Update UI incrementally
self.itinerary = partial
}let stream = session.streamResponse(
to: prompt,
generating: Itinerary.self
)
for try await partial in stream {
// Update UI incrementally
self.itinerary = partial
}Need on-device AI?
│
├─ World knowledge/reasoning?
│ └─ ❌ NOT Foundation Models
│ → Use ChatGPT, Claude, Gemini, etc.
│ → Reason: 3B parameter model, not trained for encyclopedic knowledge
│
├─ Summarization?
│ └─ ✅ YES → Pattern 1 (Basic Session)
│ → Example: Summarize article, condense email
│ → Time: 10-15 minutes
│
├─ Structured extraction?
│ └─ ✅ YES → Pattern 2 (@Generable)
│ → Example: Extract name, date, amount from invoice
│ → Time: 15-20 minutes
│
├─ Content tagging?
│ └─ ✅ YES → Pattern 3 (contentTagging use case)
│ → Example: Tag article topics, extract entities
│ → Time: 10 minutes
│
├─ Need external data?
│ └─ ✅ YES → Pattern 4 (Tool calling)
│ → Example: Fetch weather, query contacts, get locations
│ → Time: 20-30 minutes
│
├─ Long generation?
│ └─ ✅ YES → Pattern 5 (Streaming)
│ → Example: Generate itinerary, create story
│ → Time: 15-20 minutes
│
└─ Dynamic schemas (runtime-defined structure)?
└─ ✅ YES → Pattern 6 (DynamicGenerationSchema)
→ Example: Level creator, user-defined forms
→ Time: 30-40 minutes需要端侧AI?
│
├─ 需要通用知识/复杂推理?
│ └─ ❌ 不适合Foundation Models
│ → 使用ChatGPT、Claude、Gemini等
│ → 原因:30亿参数模型,并非为百科知识训练
│
├─ 文本摘要?
│ └─ ✅ 是 → 模式1(基础会话)
│ → 示例:文章摘要、邮件压缩
│ → 耗时:10-15分钟
│
├─ 结构化提取?
│ └─ ✅ 是 → 模式2(@Generable)
│ → 示例:从发票中提取姓名、日期、金额
│ → 耗时:15-20分钟
│
├─ 内容打标签?
│ └─ ✅ 是 → 模式3(contentTagging场景)
│ → 示例:为文章主题打标签、提取实体
│ → 耗时:10分钟
│
├─ 需要外部数据?
│ └─ ✅ 是 → 模式4(工具调用)
│ → 示例:获取天气、查询联系人、获取地点
│ → 耗时:20-30分钟
│
├─ 长生成任务?
│ └─ ✅ 是 → 模式5(流式传输)
│ → 示例:生成行程、创建故事
│ → 耗时:15-20分钟
│
└─ 动态架构(运行时定义结构)?
└─ ✅ 是 → 模式6(DynamicGenerationSchema)
→ 示例:关卡创建器、用户定义表单
→ 耗时:30-40分钟import FoundationModels
func respond(userInput: String) async throws -> String {
let session = LanguageModelSession(instructions: """
You are a friendly barista in a pixel art coffee shop.
Respond to the player's question concisely.
"""
)
let response = try await session.respond(to: userInput)
return response.content
}import FoundationModels
func respond(userInput: String) async throws -> String {
let session = LanguageModelSession(instructions: """
You are a friendly barista in a pixel art coffee shop.
Respond to the player's question concisely.
"""
)
let response = try await session.respond(to: userInput)
return response.content
}let session = LanguageModelSession()
// First turn
let first = try await session.respond(to: "Write a haiku about fishing")
print(first.content)
// "Silent waters gleam,
// Casting lines in morning mist—
// Hope in every cast."
// Second turn - model remembers context
let second = try await session.respond(to: "Do another one about golf")
print(second.content)
// "Silent morning dew,
// Caddies guide with gentle words—
// Paths of patience tread."
// Inspect full transcript
print(session.transcript)let session = LanguageModelSession()
// First turn
let first = try await session.respond(to: "Write a haiku about fishing")
print(first.content)
// "Silent waters gleam,
// Casting lines in morning mist—
// Hope in every cast."
// Second turn - model remembers context
let second = try await session.respond(to: "Do another one about golf")
print(second.content)
// "Silent morning dew,
// Caddies guide with gentle words—
// Paths of patience tread."
// Inspect full transcript
print(session.transcript)let transcript = session.transcript
// Use for:
// - Debugging generation issues
// - Showing conversation history in UI
// - Exporting chat logslet transcript = session.transcript
// 可用于:
// - 调试生成问题
// - 在UI中显示对话历史
// - 导出聊天日志do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.guardrailViolation {
// Content policy triggered
print("Cannot generate that content")
} catch LanguageModelSession.GenerationError.unsupportedLanguageOrLocale {
// Language not supported
print("Please use English or another supported language")
}do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.guardrailViolation {
// Content policy triggered
print("Cannot generate that content")
} catch LanguageModelSession.GenerationError.unsupportedLanguageOrLocale {
// Language not supported
print("Please use English or another supported language")
}// ❌ BAD - Unreliable
let prompt = "Generate a person with name and age as JSON"
let response = try await session.respond(to: prompt)
// Might get: {"firstName": "John"} when you expect {"name": "John"}
// Might get invalid JSON entirely
// Must parse manually, prone to crashes// ❌ BAD - Unreliable
let prompt = "Generate a person with name and age as JSON"
let response = try await session.respond(to: prompt)
// Might get: {"firstName": "John"} when you expect {"name": "John"}
// Might get invalid JSON entirely
// Must parse manually, prone to crashes@Generable
struct Person {
let name: String
let age: Int
}
let session = LanguageModelSession()
let response = try await session.respond(
to: "Generate a person",
generating: Person.self
)
let person = response.content // Type-safe Person instance!@Generable
struct Person {
let name: String
let age: Int
}
let session = LanguageModelSession()
let response = try await session.respond(
to: "Generate a person",
generating: Person.self
)
let person = response.content // Type-safe Person instance!@Generable@GenerableStringIntFloatDoubleBool@Generable
struct SearchSuggestions {
var searchTerms: [String]
}@Generable
struct Itinerary {
var destination: String
var days: [DayPlan] // Composed type
}
@Generable
struct DayPlan {
var activities: [String]
}@Generable
struct NPC {
let name: String
let encounter: Encounter
@Generable
enum Encounter {
case orderCoffee(String)
case wantToTalkToManager(complaint: String)
}
}@Generable
struct Itinerary {
var destination: String
var relatedItineraries: [Itinerary] // Recursive!
}StringIntFloatDoubleBool@Generable
struct SearchSuggestions {
var searchTerms: [String]
}@Generable
struct Itinerary {
var destination: String
var days: [DayPlan] // Composed type
}
@Generable
struct DayPlan {
var activities: [String]
}@Generable
struct NPC {
let name: String
let encounter: Encounter
@Generable
enum Encounter {
case orderCoffee(String)
case wantToTalkToManager(complaint: String)
}
}@Generable
struct Itinerary {
var destination: String
var relatedItineraries: [Itinerary] // Recursive!
}@Generable
struct NPC {
@Guide(description: "A full name with first and last")
let name: String
}@Generable
struct Character {
@Guide(.range(1...10))
let level: Int
}@Generable
struct Suggestions {
@Guide(description: "Suggested search terms", .count(4))
var searchTerms: [String]
}@Generable
struct Result {
@Guide(.maximumCount(3))
let topics: [String]
}@Generable
struct NPC {
@Guide(Regex {
Capture {
ChoiceOf {
"Mr"
"Mrs"
}
}
". "
OneOrMore(.word)
})
let name: String
}
// Output: {name: "Mrs. Brewster"}@Generable
struct NPC {
@Guide(description: "A full name with first and last")
let name: String
}@Generable
struct Character {
@Guide(.range(1...10))
let level: Int
}@Generable
struct Suggestions {
@Guide(description: "Suggested search terms", .count(4))
var searchTerms: [String]
}@Generable
struct Result {
@Guide(.maximumCount(3))
let topics: [String]
}@Generable
struct NPC {
@Guide(Regex {
Capture {
ChoiceOf {
"Mr"
"Mrs"
}
}
". "
OneOrMore(.word)
})
let name: String
}
// Output: {name: "Mrs. Brewster"}@Generable
struct Itinerary {
var destination: String // Generated first
var days: [DayPlan] // Generated second
var summary: String // Generated last
}@Generable
struct Itinerary {
var destination: String // 首先生成
var days: [DayPlan] // 其次生成
var summary: String // 最后生成
}// User waits 3-5 seconds seeing nothing
let response = try await session.respond(to: prompt, generating: Itinerary.self)
// Then entire result appears at once// User waits 3-5 seconds seeing nothing
let response = try await session.respond(to: prompt, generating: Itinerary.self)
// Then entire result appears at once@Generable
struct Itinerary {
var name: String
var days: [DayPlan]
}
let stream = session.streamResponse(
to: "Generate a 3-day itinerary to Mt. Fuji",
generating: Itinerary.self
)
for try await partial in stream {
print(partial) // Incrementally updated
}@Generable
struct Itinerary {
var name: String
var days: [DayPlan]
}
let stream = session.streamResponse(
to: "Generate a 3-day itinerary to Mt. Fuji",
generating: Itinerary.self
)
for try await partial in stream {
print(partial) // 增量更新
}@GenerablePartiallyGenerated// Compiler generates:
extension Itinerary {
struct PartiallyGenerated {
var name: String? // All properties optional!
var days: [DayPlan]?
}
}@GenerablePartiallyGenerated// Compiler generates:
extension Itinerary {
struct PartiallyGenerated {
var name: String? // 所有属性都是可选的!
var days: [DayPlan]?
}
}struct ItineraryView: View {
let session: LanguageModelSession
@State private var itinerary: Itinerary.PartiallyGenerated?
var body: some View {
VStack {
if let name = itinerary?.name {
Text(name)
.font(.title)
}
if let days = itinerary?.days {
ForEach(days, id: \.self) { day in
DayView(day: day)
}
}
Button("Generate") {
Task {
let stream = session.streamResponse(
to: "Generate 3-day itinerary to Tokyo",
generating: Itinerary.self
)
for try await partial in stream {
self.itinerary = partial
}
}
}
}
}
}struct ItineraryView: View {
let session: LanguageModelSession
@State private var itinerary: Itinerary.PartiallyGenerated?
var body: some View {
VStack {
if let name = itinerary?.name {
Text(name)
.font(.title)
}
if let days = itinerary?.days {
ForEach(days, id: \.self) { day in
DayView(day: day)
}
}
Button("Generate") {
Task {
let stream = session.streamResponse(
to: "Generate 3-day itinerary to Tokyo",
generating: Itinerary.self
)
for try await partial in stream {
self.itinerary = partial
}
}
}
}
}
}if let name = itinerary?.name {
Text(name)
.transition(.opacity)
}
if let days = itinerary?.days {
ForEach(days, id: \.self) { day in
DayView(day: day)
.transition(.slide)
}
}if let name = itinerary?.name {
Text(name)
.transition(.opacity)
}
if let days = itinerary?.days {
ForEach(days, id: \.self) { day in
DayView(day: day)
.transition(.slide)
}
}// ✅ GOOD - Stable identity
ForEach(days, id: \.id) { day in
DayView(day: day)
}
// ❌ BAD - Identity changes, animations break
ForEach(days.indices, id: \.self) { index in
DayView(day: days[index])
}// ✅ GOOD - Stable identity
ForEach(days, id: \.id) { day in
DayView(day: day)
}
// ❌ BAD - Identity changes, animations break
ForEach(days.indices, id: \.self) { index in
DayView(day: days[index])
}// ✅ GOOD - Title appears first, summary last
@Generable
struct Itinerary {
var name: String // Shows first
var days: [DayPlan] // Shows second
var summary: String // Shows last (can reference days)
}
// ❌ BAD - Summary before content
@Generable
struct Itinerary {
var summary: String // Doesn't make sense before days!
var days: [DayPlan]
}// ✅ GOOD - 标题先显示,摘要最后显示
@Generable
struct Itinerary {
var name: String // 首先显示
var days: [DayPlan] // 其次显示
var summary: String // 最后显示(可以引用days)
}
// ❌ BAD - 摘要在内容之前
@Generable
struct Itinerary {
var summary: String // 在days之前显示毫无意义!
var days: [DayPlan]
}// ❌ BAD - Model will hallucinate
let response = try await session.respond(
to: "What's the temperature in Cupertino?"
)
// Output: "It's about 72°F" (completely made up!)// ❌ BAD - Model will hallucinate
let response = try await session.respond(
to: "What's the temperature in Cupertino?"
)
// Output: "It's about 72°F" (completely made up!)import FoundationModels
import WeatherKit
import CoreLocation
struct GetWeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve latest weather for a city"
@Generable
struct Arguments {
@Guide(description: "The city to fetch weather for")
var city: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
let places = try await CLGeocoder().geocodeAddressString(arguments.city)
let weather = try await WeatherService.shared.weather(for: places.first!.location!)
let temp = weather.currentWeather.temperature.value
return ToolOutput("\(arguments.city)'s temperature is \(temp) degrees.")
}
}import FoundationModels
import WeatherKit
import CoreLocation
struct GetWeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve latest weather for a city"
@Generable
struct Arguments {
@Guide(description: "The city to fetch weather for")
var city: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
let places = try await CLGeocoder().geocodeAddressString(arguments.city)
let weather = try await WeatherService.shared.weather(for: places.first!.location!)
let temp = weather.currentWeather.temperature.value
return ToolOutput("\(arguments.city)'s temperature is \(temp) degrees.")
}
}let session = LanguageModelSession(
tools: [GetWeatherTool()],
instructions: "Help user with weather forecasts."
)
let response = try await session.respond(
to: "What's the temperature in Cupertino?"
)
print(response.content)
// "It's 71°F in Cupertino!"GetWeatherToollet session = LanguageModelSession(
tools: [GetWeatherTool()],
instructions: "Help user with weather forecasts."
)
let response = try await session.respond(
to: "What's the temperature in Cupertino?"
)
print(response.content)
// "It's 71°F in Cupertino!"GetWeatherToolprotocol Tool {
var name: String { get }
var description: String { get }
associatedtype Arguments: Generable
func call(arguments: Arguments) async throws -> ToolOutput
}getWeatherfindContact@Generableprotocol Tool {
var name: String { get }
var description: String { get }
associatedtype Arguments: Generable
func call(arguments: Arguments) async throws -> ToolOutput
}getWeatherfindContact@Generablereturn ToolOutput("Temperature is 71°F")let content = GeneratedContent(properties: ["temperature": 71])
return ToolOutput(content)return ToolOutput("Temperature is 71°F")let content = GeneratedContent(properties: ["temperature": 71])
return ToolOutput(content)let session = LanguageModelSession(
tools: [
GetWeatherTool(),
FindRestaurantTool(),
FindHotelTool()
],
instructions: "Plan travel itineraries."
)
let response = try await session.respond(
to: "Create a 2-day plan for Tokyo"
)
// Model autonomously decides:
// - Calls FindRestaurantTool for dining
// - Calls FindHotelTool for accommodation
// - Calls GetWeatherTool to suggest activitieslet session = LanguageModelSession(
tools: [
GetWeatherTool(),
FindRestaurantTool(),
FindHotelTool()
],
instructions: "Plan travel itineraries."
)
let response = try await session.respond(
to: "Create a 2-day plan for Tokyo"
)
// Model autonomously decides:
// - Calls FindRestaurantTool for dining
// - Calls FindHotelTool for accommodation
// - Calls GetWeatherTool to suggest activitiesclass FindContactTool: Tool {
let name = "findContact"
let description = "Find contact from age generation"
var pickedContacts = Set<String>() // State!
@Generable
struct Arguments {
let generation: Generation
@Generable
enum Generation {
case babyBoomers
case genX
case millennial
case genZ
}
}
func call(arguments: Arguments) async throws -> ToolOutput {
// Use Contacts API
var contacts = fetchContacts(for: arguments.generation)
// Remove already picked
contacts.removeAll(where: { pickedContacts.contains($0.name) })
guard let picked = contacts.randomElement() else {
return ToolOutput("No more contacts")
}
pickedContacts.insert(picked.name) // Update state
return ToolOutput(picked.name)
}
}callclass FindContactTool: Tool {
let name = "findContact"
let description = "Find contact from age generation"
var pickedContacts = Set<String>() // State!
@Generable
struct Arguments {
let generation: Generation
@Generable
enum Generation {
case babyBoomers
case genX
case millennial
case genZ
}
}
func call(arguments: Arguments) async throws -> ToolOutput {
// Use Contacts API
var contacts = fetchContacts(for: arguments.generation)
// Remove already picked
contacts.removeAll(where: { pickedContacts.contains($0.name) })
guard let picked = contacts.randomElement() else {
return ToolOutput("No more contacts")
}
pickedContacts.insert(picked.name) // Update state
return ToolOutput(picked.name)
}
}call1. Session initialized with tools
2. User prompt: "What's Tokyo's weather?"
3. Model analyzes: "Need weather data"
4. Model generates tool call: getWeather(city: "Tokyo")
5. Framework calls your tool's call() method
6. Your tool fetches real data from API
7. Tool output inserted into transcript
8. Model generates final response using tool output1. 使用工具初始化会话
2. 用户提示:"东京的天气如何?"
3. 模型分析:"需要天气数据"
4. 模型生成工具调用:getWeather(city: "Tokyo")
5. 框架调用你的工具的call()方法
6. 你的工具从API获取真实数据
7. 工具输出被插入到对话记录中
8. 模型使用工具输出生成最终响应struct FindPointsOfInterestTool: Tool {
let name = "findPointsOfInterest"
let description = "Find restaurants, museums, parks near a landmark"
let landmark: String
@Generable
struct Arguments {
let category: Category
@Generable
enum Category {
case restaurant
case museum
case park
case marina
}
}
func call(arguments: Arguments) async throws -> ToolOutput {
// Use MapKit
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "\(arguments.category) near \(landmark)"
let search = MKLocalSearch(request: request)
let response = try await search.start()
let names = response.mapItems.prefix(5).map { $0.name ?? "" }
return ToolOutput(names.joined(separator: ", "))
}
}struct FindPointsOfInterestTool: Tool {
let name = "findPointsOfInterest"
let description = "Find restaurants, museums, parks near a landmark"
let landmark: String
@Generable
struct Arguments {
let category: Category
@Generable
enum Category {
case restaurant
case museum
case park
case marina
}
}
func call(arguments: Arguments) async throws -> ToolOutput {
// Use MapKit
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "\(arguments.category) near \(landmark)"
let search = MKLocalSearch(request: request)
let response = try await search.start()
let names = response.mapItems.prefix(5).map { $0.name ?? "" }
return ToolOutput(names.joined(separator: ", "))
}
}// Long conversation...
for i in 1...100 {
let response = try await session.respond(to: "Question \(i)")
// Eventually...
// Error: exceededContextWindowSize
}// Long conversation...
for i in 1...100 {
let response = try await session.respond(to: "Question \(i)")
// Eventually...
// Error: exceededContextWindowSize
}var session = LanguageModelSession()
do {
let response = try await session.respond(to: prompt)
print(response.content)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// New session, no history
session = LanguageModelSession()
}var session = LanguageModelSession()
do {
let response = try await session.respond(to: prompt)
print(response.content)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// New session, no history
session = LanguageModelSession()
}var session = LanguageModelSession()
do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// New session with condensed history
session = condensedSession(from: session)
}
func condensedSession(from previous: LanguageModelSession) -> LanguageModelSession {
let allEntries = previous.transcript.entries
var condensedEntries = [Transcript.Entry]()
// Always include first entry (instructions)
if let first = allEntries.first {
condensedEntries.append(first)
// Include last entry (most recent context)
if allEntries.count > 1, let last = allEntries.last {
condensedEntries.append(last)
}
}
let condensedTranscript = Transcript(entries: condensedEntries)
return LanguageModelSession(transcript: condensedTranscript)
}var session = LanguageModelSession()
do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// New session with condensed history
session = condensedSession(from: session)
}
func condensedSession(from previous: LanguageModelSession) -> LanguageModelSession {
let allEntries = previous.transcript.entries
var condensedEntries = [Transcript.Entry]()
// Always include first entry (instructions)
if let first = allEntries.first {
condensedEntries.append(first)
// Include last entry (most recent context)
if allEntries.count > 1, let last = allEntries.last {
condensedEntries.append(last)
}
}
let condensedTranscript = Transcript(entries: condensedEntries)
return LanguageModelSession(transcript: condensedTranscript)
}func condensedSession(from previous: LanguageModelSession) -> LanguageModelSession {
let entries = previous.transcript.entries
guard entries.count > 3 else {
return LanguageModelSession(transcript: previous.transcript)
}
// Keep first (instructions) and last (recent)
var condensedEntries = [entries.first!]
// Summarize middle entries
let middleEntries = Array(entries[1..<entries.count-1])
let summaryPrompt = """
Summarize this conversation in 2-3 sentences:
\(middleEntries.map { $0.content }.joined(separator: "\n"))
"""
// Use Foundation Models itself to summarize!
let summarySession = LanguageModelSession()
let summary = try await summarySession.respond(to: summaryPrompt)
condensedEntries.append(Transcript.Entry(content: summary.content))
condensedEntries.append(entries.last!)
return LanguageModelSession(transcript: Transcript(entries: condensedEntries))
}func condensedSession(from previous: LanguageModelSession) -> LanguageModelSession {
let entries = previous.transcript.entries
guard entries.count > 3 else {
return LanguageModelSession(transcript: previous.transcript)
}
// Keep first (instructions) and last (recent)
var condensedEntries = [entries.first!]
// Summarize middle entries
let middleEntries = Array(entries[1..<entries.count-1])
let summaryPrompt = """
Summarize this conversation in 2-3 sentences:
\(middleEntries.map { $0.content }.joined(separator: "\n"))
"""
// Use Foundation Models itself to summarize!
let summarySession = LanguageModelSession()
let summary = try await summarySession.respond(to: summaryPrompt)
condensedEntries.append(Transcript.Entry(content: summary.content))
condensedEntries.append(entries.last!)
return LanguageModelSession(transcript: Transcript(entries: condensedEntries))
}// ❌ BAD
let prompt = """
I want you to generate a comprehensive detailed analysis of this article
with multiple sections including summary, key points, sentiment analysis,
main arguments, counter arguments, logical fallacies, and conclusions...
"""
// ✅ GOOD
let prompt = "Summarize this article's key points"// ❌ BAD - One massive generation
let response = try await session.respond(
to: "Create 7-day itinerary with hotels, restaurants, activities..."
)
// ✅ GOOD - Multiple smaller generations
let overview = try await session.respond(to: "Create high-level 7-day plan")
for day in 1...7 {
let details = try await session.respond(to: "Detail activities for day \(day)")
}// ❌ BAD
let prompt = """
I want you to generate a comprehensive detailed analysis of this article
with multiple sections including summary, key points, sentiment analysis,
main arguments, counter arguments, logical fallacies, and conclusions...
"""
// ✅ GOOD
let prompt = "Summarize this article's key points"// ❌ BAD - One massive generation
let response = try await session.respond(
to: "Create 7-day itinerary with hotels, restaurants, activities..."
)
// ✅ GOOD - Multiple smaller generations
let overview = try await session.respond(to: "Create high-level 7-day plan")
for day in 1...7 {
let details = try await session.respond(to: "Detail activities for day \(day)")
}let response = try await session.respond(
to: prompt,
options: GenerationOptions(sampling: .greedy)
)let response = try await session.respond(
to: prompt,
options: GenerationOptions(sampling: .greedy)
)let response = try await session.respond(
to: prompt,
options: GenerationOptions(temperature: 0.5)
)let response = try await session.respond(
to: prompt,
options: GenerationOptions(temperature: 2.0)
)0.1-0.51.01.5-2.0let response = try await session.respond(
to: prompt,
options: GenerationOptions(temperature: 0.5)
)let response = try await session.respond(
to: prompt,
options: GenerationOptions(temperature: 2.0)
)0.1-0.51.01.5-2.0"I understand ChatGPT delivers great results for certain tasks. However,
for this feature, Foundation Models is the right choice for three critical reasons:
1. **Privacy**: This feature processes [medical notes/financial data/personal content].
Users expect this data stays on-device. Sending to external API violates that trust
and may have compliance issues.
2. **Cost**: At scale, ChatGPT API calls cost $X per 1000 requests. Foundation Models
is free. For Y million users, that's $Z annually we can avoid.
3. **Offline capability**: Foundation Models works without internet. Users in airplane
mode or with poor signal still get full functionality.
**When to use ChatGPT**: If this feature required world knowledge or complex reasoning,
ChatGPT would be the right choice. But this is [summarization/extraction/classification],
which is exactly what Foundation Models is optimized for.
**Time estimate**: Foundation Models implementation: 15-20 minutes.
Privacy compliance review for ChatGPT: 2-4 weeks.""我理解ChatGPT在某些任务上表现出色。然而,对于这个功能,Foundation Models是更合适的选择,主要有三个关键原因:
1. **隐私**:该功能处理[医疗记录/财务数据/个人内容]。用户期望这些数据保留在设备上。发送到外部API会违背这种信任,还可能引发合规问题。
2. **成本**:大规模使用时,ChatGPT API每1000次请求需要花费$X。而Foundation Models是免费的。对于Y百万用户,我们可以每年节省$Z的成本。
3. **离线能力**:Foundation Models无需互联网即可工作。处于飞行模式或信号差的用户仍然可以使用完整功能。
**何时使用ChatGPT**:如果该功能需要通用知识或复杂推理,ChatGPT会是合适的选择。但当前功能是[摘要/提取/分类],这正是Foundation Models优化的场景。
**时间估算**:Foundation Models实现需要15-20分钟。而ChatGPT的隐私合规审查需要2-4周。"{firstName: "John"}{name: "John"}keyNotFoundHere's the person: {name: "John", age: 30}// ❌ BAD - Will fail
let prompt = "Generate a person with name and age as JSON"
let response = try await session.respond(to: prompt)
// Model outputs: {"firstName": "John Smith", "years": 30}
// Your code expects: {"name": ..., "age": ...}
// CRASH: keyNotFound(name)// ✅ GOOD - 15 minutes, guaranteed to work
@Generable
struct Person {
let name: String
let age: Int
}
let response = try await session.respond(
to: "Generate a person",
generating: Person.self
)
// response.content is type-safe Person, always valid"I understand JSON parsing feels familiar, but for LLM output, @Generable is objectively
better for three technical reasons:
1. **Constrained decoding guarantees structure**: Model can ONLY generate valid Person
instances. Impossible to get wrong keys, invalid JSON, or missing fields.
2. **No parsing code needed**: Framework handles parsing automatically. Zero chance of
parsing bugs.
3. **Compile-time safety**: If we change Person struct, compiler catches all issues.
Manual JSON parsing = runtime crashes.
**Real cost**: Manual JSON approach will hit edge cases. Debugging 'keyNotFound' crashes
takes 2-4 hours. @Generable implementation takes 15 minutes and has zero parsing bugs.
**Analogy**: This is like choosing Swift over Objective-C for new code. Both work, but
Swift's type safety prevents entire categories of bugs."{firstName: "John"}{name: "John"}keyNotFoundHere's the person: {name: "John", age: 30}// ❌ BAD - Will fail
let prompt = "Generate a person with name and age as JSON"
let response = try await session.respond(to: prompt)
// Model outputs: {"firstName": "John Smith", "years": 30}
// Your code expects: {"name": ..., "age": ...}
// CRASH: keyNotFound(name)// ✅ GOOD - 15 minutes, guaranteed to work
@Generable
struct Person {
let name: String
let age: Int
}
let response = try await session.respond(
to: "Generate a person",
generating: Person.self
)
// response.content is type-safe Person, always valid"我理解JSON解析感觉很熟悉,但对于大语言模型的输出,@Generable在技术上更优,主要有三个原因:
1. **约束解码保证结构**:模型只能生成有效的Person实例。不可能出现错误的键、无效JSON或缺失字段。
2. **无需解析代码**:框架会自动处理解析。完全没有解析错误的可能。
3. **编译时安全**:如果我们修改Person结构体,编译器会捕获所有问题。手动JSON解析会导致运行时崩溃。
**真实成本**:手动JSON方法会遇到边缘案例。调试'keyNotFound'崩溃需要2-4小时。而@Generable实现只需15分钟,且没有解析错误。
**类比**:这就像为新代码选择Swift而非Objective-C。两者都能工作,但Swift的类型安全可以避免一整类错误。"// ❌ BAD - One massive prompt
let prompt = """
Extract from this invoice:
- Vendor name
- Invoice date
- Total amount
- Line items (description, quantity, price each)
- Payment terms
- Due date
- Tax amount
...
"""
// 4 seconds, poor quality, might exceed context
// ✅ GOOD - Structured extraction with focused prompts
@Generable
struct InvoiceBasics {
let vendor: String
let date: String
let amount: Double
}
let basics = try await session.respond(
to: "Extract vendor, date, and amount",
generating: InvoiceBasics.self
) // 0.5 seconds, axiom-high quality
@Generable
struct LineItem {
let description: String
let quantity: Int
let price: Double
}
let items = try await session.respond(
to: "Extract line items",
generating: [LineItem].self
) // 1 second, axiom-high quality
// Total: 1.5 seconds, better quality, graceful partial failures"I understand the appeal of one simple API call. However, this specific task requires
a different approach:
1. **Context limits**: Invoice + complex extraction prompt will likely exceed 4096 token
limit. Multiple focused prompts stay well under limit.
2. **Better quality**: Model performs better with focused tasks. 'Extract vendor name'
gets 95%+ accuracy. 'Extract everything' gets 60-70%.
3. **Faster perceived performance**: Multiple prompts with streaming show progressive
results. Users see vendor name in 0.5s, not waiting 5s for everything.
4. **Graceful degradation**: If line items fail, we still have basics. All-or-nothing
approach means total failure.
**Implementation**: Breaking into 3-4 focused extractions takes 30 minutes. One big
prompt takes 2-3 hours debugging why it hits context limit and produces poor results."// ❌ BAD - One massive prompt
let prompt = """
Extract from this invoice:
- Vendor name
- Invoice date
- Total amount
- Line items (description, quantity, price each)
- Payment terms
- Due date
- Tax amount
...
"""
// 4 seconds, poor quality, might exceed context
// ✅ GOOD - Structured extraction with focused prompts
@Generable
struct InvoiceBasics {
let vendor: String
let date: String
let amount: Double
}
let basics = try await session.respond(
to: "Extract vendor, date, and amount",
generating: InvoiceBasics.self
) // 0.5 seconds, 高质量
@Generable
struct LineItem {
let description: String
let quantity: Int
let price: Double
}
let items = try await session.respond(
to: "Extract line items",
generating: [LineItem].self
) // 1 second, 高质量
// Total: 1.5 seconds, better quality, graceful partial failures"我理解一个简单API调用的吸引力。然而,这个特定任务需要不同的方法:
1. **上下文限制**:发票 + 复杂提取提示词很可能会超过4096token限制。多个聚焦的提示词会保持在限制内。
2. **更好的质量**:模型在处理聚焦任务时表现更好。'提取供应商名称'的准确率可达95%以上。而'提取所有信息'的准确率只有60-70%。
3. **更快的感知性能**:多个提示词结合流式传输可以逐步显示结果。用户会在0.5秒内看到供应商名称,而不是等待5秒才能看到所有内容。
4. **优雅降级**:如果行项目提取失败,我们仍然可以获取基础信息。全有或全无的方法会导致完全失败。
**实现**:拆分为3-4个聚焦的提取任务需要30分钟。而一个大提示词需要2-3小时调试,解决上下文限制和结果质量差的问题。"class ViewModel: ObservableObject {
private var session: LanguageModelSession?
init() {
// Prewarm on init, not when user taps button
Task {
self.session = LanguageModelSession(instructions: "...")
}
}
func generate(prompt: String) async throws -> String {
let response = try await session!.respond(to: prompt)
return response.content
}
}class ViewModel: ObservableObject {
private var session: LanguageModelSession?
init() {
// 在初始化时预启动,而不是用户点击按钮时
Task {
self.session = LanguageModelSession(instructions: "...")
}
}
func generate(prompt: String) async throws -> String {
let response = try await session!.respond(to: prompt)
return response.content
}
}let firstResponse = try await session.respond(
to: "Generate first person",
generating: Person.self
// Schema inserted automatically
)
// Subsequent requests with SAME schema
let secondResponse = try await session.respond(
to: "Generate another person",
generating: Person.self,
options: GenerationOptions(includeSchemaInPrompt: false)
)let firstResponse = try await session.respond(
to: "Generate first person",
generating: Person.self
// 架构会自动插入
)
// 后续使用相同架构的请求
let secondResponse = try await session.respond(
to: "Generate another person",
generating: Person.self,
options: GenerationOptions(includeSchemaInPrompt: false)
)// ✅ GOOD - Title shows immediately
@Generable
struct Article {
var title: String // Shows in 0.2s
var summary: String // Shows in 0.8s
var fullText: String // Shows in 2.5s
}
// ❌ BAD - Wait for everything
@Generable
struct Article {
var fullText: String // User waits 2.5s
var title: String
var summary: String
}// ✅ GOOD - Title shows immediately
@Generable
struct Article {
var title: String // 0.2秒内显示
var summary: String // 0.8秒内显示
var fullText: String // 2.5秒内显示
}
// ❌ BAD - Wait for everything
@Generable
struct Article {
var fullText: String // 用户需要等待2.5秒
var title: String
var summary: String
}exceededContextWindowSizeguardrailViolationunsupportedLanguageOrLocaleTask {}exceededContextWindowSizeguardrailViolationunsupportedLanguageOrLocaleTask {}