-
Busca com LLM usando o Core Spotlight
Transforme buscas básicas em um sistema aprimorado por recuperação de informações utilizando SpotlightSearchTool e LanguageModelSession. Explore a integração do Core Spotlight, padrões de hidratação baseados em delegados e o impacto da qualidade dos metadados nos resultados de busca. Aprenda a utilizar PipelineStages personalizados para tarefas como análise de sentimentos. Confira as práticas recomendadas para indexação e criação de experiências de busca flexíveis e contextuais no app.
Capítulos
- 0:00 - Introdução
- 1:41 - Fundamentação das respostas com chamadas de ferramentas via Spotlight
- 4:00 - Configure e adicione o SpotlightSearchTool
- 6:44 - Exibição de resultados e respostas parciais
- 6:46 - Forneça itens completos com um delegado de indexação
- 8:12 - Personalização com perfis de diretrizes
- 11:02 - Resolução de referência com um resolvedor de contatos
- 11:24 - Estágios de pipeline personalizados
- 12:47 - Avaliação da qualidade da resposta
- 15:53 - Próximas etapas
Recursos
-
Buscar neste vídeo...
-
-
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") }
-