Sidekick消息引用系统:上下文感知的对话连贯性保障
在智能对话应用中,上下文理解和信息引用是确保对话连贯性的核心能力。Sidekick作为一款本地运行的macOS应用,通过其独特的消息引用系统,实现了对本地文件、文件夹和网站内容的精准引用与管理,让AI助手能够像人类一样自然地引用信息来源。本文将深入解析这一系统的技术实现与应用场景。
引用系统的核心架构
Sidekick的消息引用系统建立在三个关键组件之上,形成了完整的信息引用生命周期:消息数据结构定义引用规则、URL解析器处理不同类型来源、UI组件实现可视化展示。这种分层设计确保了引用功能的可靠性和扩展性。
消息数据结构设计
消息引用的基础定义位于Message.swift文件中,通过Message结构体实现对引用信息的存储和管理。该结构包含两个核心属性:
// 存储所有引用的URL数组
public var referencedURLs: [ReferencedURL] = []
// 处理消息文本与引用关系的核心方法
public mutating func update(
response: LlamaServer.CompleteResponse,
includeReferences: Bool
)
系统在处理LLM响应时,会自动从文本中提取引用信息并存储到referencedURLs数组中。特别值得注意的是,消息文本与引用信息采用分离存储的设计,既保证了对话内容的可读性,又确保了引用数据的结构化管理。
URL引用解析器
ReferencedURL.swift文件实现了引用URL的解析与管理逻辑。该结构体不仅处理基本的URL存储,还提供了丰富的辅助功能:
public struct ReferencedURL: Codable, Equatable, Hashable {
// URL存储
public var url: URL
// 根据URL类型提供不同的显示名称和图标
public var displayName: String {
if self.url.isWebURL {
return self.url.host(percentEncoded: false) ?? self.url.absoluteString
}
return self.url.lastPathComponent
}
// 提供打开和显示引用的功能
@MainActor
public func open() { ... }
// 从JSON数据解析引用URL
static func fromData(data: Data) -> [ReferencedURL]? { ... }
}
这个结构体巧妙地处理了两种类型的引用:网页URL(显示域名)和本地文件URL(显示文件名),为用户提供直观的来源识别方式。
可视化展示组件
引用信息的用户界面展示由SourceRowView.swift实现,该视图组件根据引用类型提供不同的视觉样式:
var body: some View {
VStack {
// 显示引用内容
Markdown(MarkdownContent(source.text))
.markdownTheme(.gitHub)
.markdownCodeSyntaxHighlighter(.splash(theme: self.theme))
Divider()
// 显示引用来源按钮
HStack {
Spacer()
if referencedUrl != nil {
referencedUrl?.openButton
} else {
unknownSource
}
}
}
// 样式设置...
}
这个设计确保用户不仅能看到引用的内容,还能清晰识别信息来源,并可以直接访问原始文件或网页。
引用提取与处理流程
Sidekick的引用系统实现了从消息生成到引用提取的完整流程,确保AI响应中包含的引用信息能够被准确识别和管理。
消息生成时的引用提示
在Message.swift中,系统在将用户查询发送给LLM之前,会自动添加引用格式说明:
let messageText: String = """
\(self.text)
Below is information that may or may not be relevant to my request in JSON format.
When multiple sources provide correct, but conflicting information, ALWAYS use sources from files, not websites.
If your response uses information from one or more provided sources, your response MUST be directly followed with a single exaustive LIST OF FILEPATHS AND URLS of ALL referenced sources, in the format [{"url": "/path/to/file.pdf"}, {"url": "https://website.com"}]
...
"""
这段提示文本指导LLM如何正确引用来源,为后续的引用提取奠定基础。
引用提取与解析
当LLM返回响应后,系统通过update方法提取引用信息:
public mutating func update(
response: LlamaServer.CompleteResponse,
includeReferences: Bool
) {
// 提取文本和引用
let text: String = response.text.dropSuffixIfPresent("[]")
let delimiters: [String] = ["\n[", " ["]
// 尝试从文本中提取引用
for delimiter in delimiters {
let messageText: String = text.dropFollowingSubstring(delimiter, options: .backwards)
let jsonText: String = text.dropPrecedingSubstring(delimiter, options: .backwards, includeCharacter: true)
// 解析JSON格式的引用
if includeReferences, let data: Data = try? jsonText.data() {
if let references = ReferencedURL.fromData(data: data) {
self.referencedURLs = references
self.text = messageText
}
}
}
}
这个过程智能地分离消息文本和引用信息,确保对话内容干净整洁,同时保留完整的引用数据。
引用唯一性与排序
ReferencedURL.swift中的fromData方法处理引用的去重和排序:
public static func fromData(data: Data) -> [ReferencedURL]? {
let decoder: JSONDecoder = JSONDecoder()
// 尝试解码为ReferencedURL数组
if let references = try? decoder.decode([ReferencedURL].self, from: data) {
// 去重并按显示名称排序
let uniqueReferences: [ReferencedURL] = Array(Set(references))
return uniqueReferences.sorted(by: \.displayName)
}
// 尝试解码为字符串数组
else if let referencesString = try? decoder.decode([String].self, from: data) {
// 转换为ReferencedURL并处理
...
}
return nil
}
这个处理确保用户看到的引用列表是唯一且有序的,提升了引用信息的可用性。
多场景引用应用
Sidekick的消息引用系统支持多种使用场景,为不同类型的信息引用提供一致的用户体验。
网页内容引用
当引用网页内容时,系统会显示网站图标和域名,直观区分网络来源:
这种设计让用户能够快速识别哪些信息来自网络资源,与本地文件引用形成鲜明对比。
本地文件引用
对于本地文件引用,系统会显示文件图标和文件名,并提供"在Finder中显示"的上下文菜单选项:
通过这种设计,用户可以方便地定位和访问引用的本地文件,实现了AI对话与本地文件系统的无缝连接。
多来源整合引用
当LLM同时引用多个来源时,系统会以标签形式展示所有引用,支持单独点击访问:
这种设计既保持了界面简洁,又确保用户可以访问所有引用来源,是处理复杂信息引用的理想方式。
技术亮点与用户价值
Sidekick的消息引用系统不仅仅是技术实现的展示,更为用户带来了实实在在的价值。
上下文感知的智能引用
系统能够根据当前对话上下文智能选择引用来源,当多个来源提供冲突信息时,优先使用本地文件来源:
// 在textWithSources方法中实现的来源优先级逻辑
let resourcesResults: [Source] = resourcesSearchResults.map { result in
// 如果禁用网络搜索结果上下文,直接返回来源
if !RetrievalSettings.useWebSearchResultContext {
return Source(text: result.text, source: result.sourceUrlText!)
}
// 否则处理上下文...
}
这种设计确保了本地数据的优先性,保护用户数据安全和隐私。
无缝的用户体验
从引用提取到可视化展示,整个过程对用户透明,不需要额外操作即可获得结构化的引用信息。无论是查看引用、打开来源还是管理引用,都通过直观的界面元素完成。
可扩展的引用处理
系统设计考虑了未来的扩展性,通过模块化的结构支持新的引用类型和处理方式。引用解析和展示逻辑的分离,使得添加新的引用处理功能变得简单。
总结与展望
Sidekick的消息引用系统通过精心设计的数据结构、智能的解析逻辑和直观的用户界面,解决了AI对话中的信息引用难题。这一系统不仅提升了对话的连贯性和可信度,还实现了本地文件系统与AI对话的无缝集成。
随着LLM技术的不断发展,我们可以期待引用系统在以下方面进一步完善:更智能的引用提取算法、支持更多文件类型的预览、引用内容的相似度分析等。无论如何,Sidekick已经通过其当前的实现,为AI本地应用树立了信息引用的新标准。
通过Message.swift、ReferencedURL.swift和SourceRowView.swift三个核心文件的协同工作,Sidekick实现了既专业又易用的消息引用系统,为用户提供了可信赖的AI对话体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



