-
Recherche de LLM avec Core Spotlight
Transformez la recherche de base en un système augmenté par récupération à l'aide de SpotlightSearchTool et LanguageModelSession. Explorez l'intégration de Core Spotlight, les modèles d'hydratation basés sur la délégation et l'impact de la qualité des métadonnées sur vos résultats de recherche. Découvrez comment utiliser des PipelineStages personnalisés pour des tâches telles que l'analyse des sentiments. Découvrez les bonnes pratiques pour indexer et créer des expériences de recherche flexibles et riches en données de contexte dans votre app.
Chapitres
- 0:00 - Introduction
- 1:41 - Ancrage des réponses par appel de l’outil Spotlight
- 4:00 - Configurer et ajouter SpotlightSearchTool
- 6:44 - Affichage des résultats et des réponses partielles
- 6:46 - Fournir des éléments complets avec un délégué d’index
- 8:12 - Personnalisation à l’aide de profils de guidage
- 11:02 - Résolution de référence avec un résolveur de contacts
- 11:24 - Étapes de pipeline personnalisées
- 12:47 - Évaluer la qualité des réponses
- 15:53 - Étapes suivantes
Ressources
-
Rechercher dans cette vidéo…
-
-
0:59 - Ask the model with a Foundation Models session
let response = try await session.respond(to: "What are some nice hikes near water?") -
4:20 - Set up SpotlightSearchTool
// Set up SpotlightSearchTool import CoreSpotlight import FoundationModels // In one line, the tool is ready to search your app's Core Spotlight index let tool = SpotlightSearchTool() // Or provide a custom configuration — e.g. search file paths in your app's sandbox let fileTool = SpotlightSearchTool( configuration: .init( sources: [ .files ] ) ) -
4:50 - Add SpotlightSearchTool to a session
// Add SpotlightSearchTool to a session import CoreSpotlight import FoundationModels let tool = SpotlightSearchTool() let session = LanguageModelSession(model: model, tools: [tool], instructions: instructions) let response = try await session.respond(to: "What hikes have I gone on?") -
6:24 - Implement an index delegate
// Implement an index delegate import CoreSpotlight class IndexDelegate: NSObject, CSSearchableIndexDelegate { // Called when the index requests searchable items for the provided identifiers func searchableItems(forIdentifiers identifiers: [String]) async -> [CSSearchableItem] { let entries = await mystore.fetchEntries(ids: identifiers) return entries.map { makeSearchableItem(from: $0) } } } -
7:37 - Track the query token for refresh
// Track the query token for refresh import CoreSpotlight import FoundationModels let tool = SpotlightSearchTool() for await reply in tool.searchResults { if reply.queryToken != currentToken { // New query — start a new display section currentToken = reply.queryToken } switch reply.content { case .items(let searchItems): } } -
8:42 - Set a dynamic guidance profile
// Set a dynamic guidance profile import CoreSpotlight import FoundationModels let profile = SpotlightSearchTool.GuidanceProfile( textMatch: true, dates: true, people: false, attributes: [.title, .altitude, .completionDate] ) let tool = SpotlightSearchTool( configuration: .init( guide: .init(level: .dynamic(profile)) ) ) // On-device models have smaller context — prefer focused guidance let focusedTool = SpotlightSearchTool( configuration: .init( guide: .init(level: .focused(.items)) ) ) -
9:32 - Implement a ContactResolver
// Implement a ContactResolver import CoreSpotlight import FoundationModels struct MyContactResolver: ContactResolver { func userIdentity() -> ResolvedContact { // Pull from whatever identity source your app has — // account profile, Contacts framework, sign-in session, etc. var contact = ResolvedContact(displayName: "Jane Doe") contact.emailAddresses = ["jane@example.com", "jdoe@work.com"] contact.names = ["Jane", "JD"] return contact } } tool.contactResolver = MyContactResolver() -
11:34 - Define a custom stage
// Define a custom stage import CoreSpotlight import FoundationModels @Generable struct HappinessStage: CustomStage { static var name = "happiness" static var description = "Scores hike by how happy the author was" static var inputTypes: [SearchPipelineDataType] = [.items] static var outputTypes: [SearchPipelineDataType] = [.scoredItems] @Guide(description: "Minimum happiness score (0.0-1.0) to include in results") var threshold: Double? func execute(on input: SearchPipelineData) async throws -> SearchPipelineData { return SearchPipelineData(payload: .scoredItems(sorted)) } } // Register the stage by adding it to the tool's configuration let tool = SpotlightSearchTool(configuration: .init( customStages: [.happinessBoost(threshold: 0.5)]) ) -
12:10 - Handle a reply data types
// Handle a reply data types import CoreSpotlight import FoundationModels for await reply in tool.searchResults { let label = reply.label case .items(let searchItems): case .scoredItems(let scored): case .groupedItems(let groups): case .count(let count): case .table(let table): case .statistic(let statistic): case .text(let text): continue } } -
13:47 - Define an evaluation dataset with ModelSampleProtocol
// Evaluations import Evaluations struct TrailRequest: ModelSampleProtocol { typealias ExpectedValue = String // sample response typealias Expectation = TrajectoryExpectation var input: ModelSampleInput var output: ModelSampleOutput<String, TrajectoryExpectation> var expectedIdentifiers: [String] } -
15:06 - Define the trajectory expectation
// Evaluations import Evaluations TrajectoryExpectation( unordered: [ ToolExpectation("searchSpotlight", arguments: [.keyOnly(argumentName: "query")]) ] ) -
15:17 - Run the evaluation test —
@Test("Trail search evaluation meets quality thresholds") func trailSearchEval() async throws { let items = try Self.loadItems() let samples = try Self.loadSamples() try await Self.indexDelegate.indexSearchableItems(items) let tool = Self.makeSearchTool() let evaluation = TrailSearchEvaluation( tool: tool, dataset: ArrayLoader(samples: samples) ) let result = try await evaluation.run() let coverageMean = result.aggregateValue(.mean(of: Metric("ResultCoverage"))) #expect(coverageMean >= 0.5, "Result coverage should be at least 50% across queries") }
-