-
Créez des expériences d’apps agentiques avec le framework Foundation Models
Apprenez à perfectionner vos fonctionnalités d'intelligence grâce aux primitives du framework Foundation Models adaptées au contexte dynamique et aux processus agentiques. Dans cette vidéo, nous voyons en détail comment créer un contexte partagé, définir des limites de confidentialité et gérer la mise en cache des paires clé-valeur. Découvrez comment orchestrer des transferts fluides entre les modèles locaux et serveurs.
Chapitres
- 0:00 - Introduction
- 2:47 - The example app and agents
- 3:47 - Declaring a dynamic profile
- 4:45 - Dynamic instructions
- 5:36 - Configuring models per phase
- 7:21 - Transcript management and history transforms
- 8:50 - Custom modifiers
- 9:39 - Lifecycle modifiers and session properties
- 12:52 - Orchestration: baton-pass
- 14:06 - Orchestration: phone-a-friend and skills
- 15:18 - Tool calling mode
- 17:12 - Transcript error handling
- 18:27 - Performance, accuracy, and evaluations
- 21:24 - Next steps
Ressources
-
Rechercher dans cette vidéo…
-
-
5:04 - DynamicInstructions
// DynamicInstructions struct BrainstormFacilitator: DynamicInstructions { var orchestrator: CraftOrchestrator var body: some DynamicInstructions { Instructions { "You are a warm and friendly expert crafting brainstorm facilitator." } // Tools GenerateProjectTitle() // Conditionally include Origami knowledge if orchestrator.techniques.contains(.origami) { OrigamiExpert() } } } -
6:41 - DynamicProfile
// DynamicProfile struct CraftProfile: LanguageModelSession.DynamicProfile { var orchestrator: CraftOrchestrator var body: some DynamicProfile { switch orchestrator.mode { case .brainstorming: Profile { BrainstormFacilitator(orchestrator: orchestrator) } .model(orchestrator.pccLanguageModel) .temperature(1) case .planning: Profile { TutorialAuthor(orchestrator: orchestrator) } .model(orchestrator.pccLanguageModel) .reasoningLevel(.deep) case .reviewing: Profile { CraftCoach() } .model(orchestrator.systemLanguageModel) } } } -
6:43 - Initialize your session with your dynamic profile
// Initialize your session with your dynamic profile let session = LanguageModelSession(profile: CraftProfile(orchestrator: orchestrator)) -
8:33 - Transcript management
// Transcript management struct CraftProfile: LanguageModelSession.DynamicProfile { var orchestrator: CraftOrchestrator var body: some DynamicProfile { switch orchestrator.mode { case .reviewing: Profile { CraftCoach() } .model(orchestrator.systemLanguageModel) .historyTransform { history in // Update the history for your profile guard let latestResponseIndex = lastResponseEntryIndex(history) else { return history } let filteredHistory = history[0..<latestResponseIndex].filter { entry in isToolCallsOrToolOutput(entry) } return filteredHistory + history[latestResponseIndex...] } } } } -
9:15 - Custom modifiers
// Custom modifiers struct DroppingToolCallsProfileModifier: LanguageModelSession.DynamicProfileModifier { func body(content: Content) -> some DynamicProfile { content .historyTransform { history in guard let latestResponseIndex = lastResponseEntryIndex(history) else { return history } let filteredHistory = history[0..<latestResponseIndex].filter { entry in isToolCallsOrToolOutput(entry) } return filteredHistory + history[latestResponseIndex...] } } } extension LanguageModelSession.DynamicProfile { func droppingCompletedToolCalls() -> some DynamicProfile { self.modifier(DroppingToolCallsProfileModifier()) } } -
9:27 - History management modifiers
// History management modifiers import FoundationModelsUtilities struct CraftProfile: LanguageModelSession.DynamicProfile { var orchestrator: CraftOrchestrator var body: some DynamicProfile { switch orchestrator.mode { case .reviewing: Profile { CraftCoach() } // Keep the most recent 10 entries // after dropping finished tool calls .rollingWindow(size: .entries(10)) .droppingCompletedToolCalls() } } } -
10:48 - Lifecycle modifiers
// Lifecycle modifiers struct CraftProfile: LanguageModelSession.DynamicProfile { @SessionProperty(\.history) var history var orchestrator: CraftOrchestrator var body: some DynamicProfile { switch orchestrator.mode { case .planning: Profile { TutorialAuthor(orchestrator: orchestrator) } .model(orchestrator.pccLanguageModel) .reasoningLevel(.deep) .onResponse { // Update history if history.count > 50, let responseIndex = lastResponseIndex(history) { history = history[responseIndex...] } } } } } -
11:40 - Declare a custom session property
// Session properties — declaration extension SessionPropertyValues { @SessionPropertyEntry var summary: String? } -
12:24 - Read and write session properties in a profile
// Session properties struct CraftProfile: LanguageModelSession.DynamicProfile { @SessionProperty(\.history) var history @SessionProperty(\.summary) var summary var orchestrator: CraftOrchestrator var body: some DynamicProfile { switch orchestrator.mode { case .planning: Profile { TutorialAuthor(orchestrator: orchestrator) if let summary { Instructions { "Summary: \(summary)" } } } .onResponse { if history.count > 50, let responseIndex = lastResponse(history.prefix(40)) { summary = try await summarize(history[0..<responseIndex]) history = history[responseIndex...] } } } } } -
13:02 - Orchestration: baton-pass
// Baton-pass struct CraftProfile: LanguageModelSession.DynamicProfile { var orchestrator: CraftOrchestrator var body: some DynamicProfile { switch orchestrator.mode { case .brainstorm: Profile { BrainstormInstructions() BatonPassTool() } .onToolCall { orchestrator.mode = .tutorial } .model(orchestrator.serverModel) case .tutorial: Profile { TutorialInstructions() BatonPassTool() } .onToolCall { orchestrator.mode = .brainstorm } .model(orchestrator.systemModel) } } } -
14:14 - Orchestration: phone-a-friend
// Phone-a-friend struct CraftProfile: LanguageModelSession.DynamicProfile { var body: some DynamicProfile { Profile { BrainstormInstructions() PhoneFriendTool( name: "generate_title", description: "Generate a creative project title", profile: TitleProfile() ) } } } struct PhoneFriendTool<P: LanguageModelSession.DynamicProfile>: Tool { func call(arguments: GeneratedContent) async throws -> String { let session = LanguageModelSession(profile: profile()) let response = try await session.respond(to: arguments) return response.content } } -
15:15 - The skills pattern
// The skills pattern struct CraftingSkills: LanguageModelSession.DynamicInstructions { var activations: SkillActivations var body: some DynamicInstructions { Skills(activations: activations) { Skill( name: "origami_folds", description: "Details about specific types of folds", prompt: """ Valley Fold: Paper is folded toward you, creating a V-shaped crease Mountain Fold: Paper is folded away from you, creating an inverted V ... """ ) Skill(...) Skill(...) } } } -
15:31 - Tool calling mode
// Tool calling mode public struct ToolCallingMode: Sendable { public static let allowed: ToolCallingMode public static let disallowed: ToolCallingMode public static let required: ToolCallingMode } // Pass tool calling mode as a profile modifier struct OrigamiExpert: LanguageModelSession.DynamicProfile { var body: some LanguageModelSession.DynamicProfile { Profile { Instructions("You are an origami expert") QueryOrigamiDatabaseTool() ShowDirectionsTool() } .toolCallingMode(.required) } } // Or pass it as a generation option let response = try await session.respond( to: "Write out the instructions for folding a paper crane.", options: GenerationOptions(toolCallingMode: .required) ) -
16:47 - Escaping a tool call loop
// Escaping a tool call loop struct OrigamiExpert: LanguageModelSession.DynamicProfile { let state: OrigamiAppState var body: some LanguageModelSession.DynamicProfile { Profile { Instructions("Answer questions about how to fold origami") QueryOrigamiDatabaseTool() } .toolCallingMode(state.queriedDatabase ? .disallowed : .required) .onToolCall { state.queriedDatabase = true } } } -
16:57 - Define a tool that throws an error
// Define a tool that throws an error var output: String? @Generable struct Arguments { var answer: String } func call(arguments: Arguments) async throws -> Never { output = arguments.answer throw CancellationError() } } -
17:28 - Set the transcript error handling policy
// Specify transcript behavior on a profile struct OrigamiExpert: LanguageModelSession.DynamicProfile { let state: OrigamiAppState var body: some LanguageModelSession.DynamicProfile { Profile { Instructions("Answer questions about how to fold origami") QueryOrigamiDatabaseTool() } .transcriptErrorHandlingPolicy(.preserveTranscript) } } // Or specify it on a session let session = LanguageModelSession() session.transcriptErrorHandlingPolicy = .preserveTranscript // Policy options extension LanguageModelSession { public struct TranscriptErrorHandlingPolicy: Sendable { // Roll the transcript back to its previous state public static let revertTranscript: TranscriptErrorHandlingPolicy // Keep the transcript in state following an error public static let preserveTranscript: TranscriptErrorHandlingPolicy } } -
17:51 - Transcript mutation
// Transcript mutation public final class LanguageModelSession: Sendable { public var transcriptErrorHandlingPolicy: TranscriptErrorHandlingPolicy { get set } // Transcript is now settable public var transcript: Transcript { get set } // But you must not modify it during a response! public var isResponding: Bool { get } }
-
-
- 0:00 - Introduction
Erik Hornberger and Oliver O'Neill introduce Dynamic Profiles and the problems they solve, context management and model boundaries, plus the new open-source Foundation Models framework utilities package. Agenda: dynamic profiles, orchestration patterns, performance and accuracy.
- 2:47 - The example app and agents
Introduces the Origami craft app with three phases (brainstorming, planning, reviewing) that share context but have different priorities. Each phase becomes an agent: a configuration with its own model, instructions, and tools.
- 3:47 - Declaring a dynamic profile
A DynamicProfile declares individual Profiles representing a configuration or agent. Build the brainstorming profile from an Observable orchestrator, instructions, and tools, conditionally adding capabilities for origami projects.
- 4:45 - Dynamic instructions
DynamicInstructions groups related instructions and tools into a single reusable, composable component. Nesting one inside another concatenates their instructions and tools, such as an OrigamiExpert reused wherever needed.
- 5:36 - Configuring models per phase
Assign different models and options per profile: Private Cloud Compute with temperature and deep reasoningLevel for brainstorming and planning, and SystemLanguageModel for reviewing. The profile body is re-evaluated on each prompt, swapping the session's persona by mode.
- 7:21 - Transcript management and history transforms
Trim or redact the transcript to stay within context limits, keep the model focused, or protect privacy. historyTransform applies stateless, per-request transforms over the history window (such as dropping tool calls) without mutating the session.
- 8:50 - Custom modifiers
Custom modifiers hide transform complexity — a type conforming to DynamicProfileModifier, exposed through a DynamicProfile extension for reuse across profiles, alongside the ready-made history-management modifiers in the utilities package.
- 9:39 - Lifecycle modifiers and session properties
onResponse and other lifecycle modifiers run imperative code at session boundaries to update UI, profile state, or history. Session properties (the built-in history, plus custom @SessionPropertyEntry values) share state across tools and profiles, for example storing a conversation summary.
- 12:52 - Orchestration: baton-pass
A collaboration pattern: multiple profiles share the full transcript, and a tool toggles which profile is active. The profile that receives the baton produces the final response.
- 14:06 - Orchestration: phone-a-friend and skills
A consultation pattern: a tool spawns a short-lived child session with an isolated transcript, and the parent profile always gives the final answer. Also notes the Skills pattern in the utilities package for procedural context loading.
- 15:18 - Tool calling mode
Control when tools run via allowed, disallowed, or required (as a profile modifier or generation option). When required, the model loops, so ensure an exit condition by conditionalizing the mode or using a final-answer tool that throws to break out.
- 17:12 - Transcript error handling
By default a thrown tool error or cancellation reverts the transcript; the new transcriptErrorHandlingPolicy (.revertTranscript or .preserveTranscript) keeps it. With preserve, the now-mutable transcript is yours to fix, only when isResponding is false.
- 18:27 - Performance, accuracy, and evaluations
Transcript mutations can invalidate key-value caches and raise latency; appending preserves them. Rewriting history can also confuse the model, so measure with the Foundation Models Instrument and quantify changes with the Evaluations framework.
- 21:24 - Next steps
Where to go next — try the sample app, explore the Foundation Models framework utilities, and measure performance with Private Cloud Compute and the revamped Xcode instrument.