当前版本的边界

BM25 对技术名词和明确关键词很有效,但面对“他更擅长产品落地还是算法研究”这类语义问题,单纯词项匹配可能漏掉相关片段。知识增加到博客、项目文档和代码后,也不能继续手工维护几个大段落。

混合检索路线

# 计划中的结构示意
keyword_docs = bm25_retriever.invoke(query)
semantic_docs = vector_retriever.invoke(query)

candidates = reciprocal_rank_fusion(
    keyword_docs,
    semantic_docs,
)
context = reranker.compress_documents(candidates, query)

第一步使用文本切分器将长文档切成带重叠的小块;第二步生成 Embedding 并存入向量库;第三步把 BM25 与向量结果融合;最后使用轻量重排序器保留最相关片段。

引用必须成为界面的一部分

Retriever 已经保留 source 元数据。下一版 SSE 除了文本 token,还会发送结构化引用事件:

event: source
data: {"id":"resume/project-linter-ai","title":"Linter+AI 应用平台"}

聊天框收到来源事件后,在答案底部展示可点击引用。这样访客可以区分“模型组织的表达”和“简历中真实存在的事实”。

记忆不是保存所有聊天

长期记忆只应保存明确授权的信息,例如用户偏好的回答长度。原始问题、联系方式和敏感内容不应默认持久化。对个人 Agent 来说,忘记同样是一项需要设计的能力。