-
Búsqueda con LLM mediante Core Spotlight
Mejora la búsqueda básica y conviértela en un sistema de búsqueda asistida utilizando SpotlightSearchTool y LanguageModelSession. Descubre la integración de Core Spotlight, los patrones de hidratación basados en delegados y cómo la calidad de los metadatos influye en tus resultados de búsqueda. Descubre cómo utilizar etapas personalizadas de PipelineStages para tareas como el análisis de opiniones. Descubre las mejores prácticas para indexar y crear experiencias de búsqueda flexibles y llenas de contexto en tu app.
Capítulos
- 0:00 - Introducción
- 1:41 - Cómo vincular respuestas con la llamada a herramientas de Spotlight
- 4:00 - Configura y agrega SpotlightSearchTool
- 6:44 - Visualización de resultados y respuestas parciales
- 6:46 - Proporciona los elementos completos mediante un delegado de índice
- 8:12 - Personalización con perfiles de orientación
- 11:02 - Resolución de referencias con una resolución de contactos
- 11:24 - Etapas personalizadas del canal
- 12:47 - Evaluación de la calidad de las respuestas
- 15:53 - Próximos pasos
Recursos
-
Buscar este video…
-
-
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") }
-