DeepSeek-R1-Distill-Qwen-1.5B实战教程:添加对话历史搜索功能支持关键词回溯

1. 项目概述

DeepSeek-R1-Distill-Qwen-1.5B是一个超轻量级的智能对话模型,专门为本地化部署设计。这个模型结合了DeepSeek优秀的逻辑推理能力和Qwen成熟的架构优势,经过蒸馏优化后,在保持强大性能的同时大幅降低了计算资源需求。

现在,我们要为这个已经很强大的对话助手添加一个实用功能:对话历史搜索。想象一下,当你和AI进行了长时间的对话后,想要找回之前讨论过的某个重要观点或代码示例,却需要手动翻阅所有聊天记录,这多么不方便。对话历史搜索功能就是为了解决这个问题而生的。

2. 为什么需要对话历史搜索

在实际使用中,我发现几个常见的痛点:

  • 信息回溯困难:长时间的对话后,很难快速找到之前讨论过的特定内容
  • 关键词记忆模糊:只记得大概的关键词,不记得具体在哪个对话中
  • 效率低下:需要手动滚动查看所有历史消息,浪费时间和精力

添加搜索功能后,你可以:

  • 输入关键词快速定位相关对话
  • 查看包含关键词的所有历史消息
  • 继续基于找到的历史内容进行深入讨论

3. 环境准备与快速部署

3.1 基础环境要求

确保你的环境满足以下要求:

# Python版本要求
Python 3.8+
# 主要依赖库
torch >= 2.0
transformers >= 4.30
streamlit >= 1.24
sentence-transformers >= 2.2

3.2 一键安装依赖

pip install torch transformers streamlit sentence-transformers

3.3 模型准备

如果你还没有下载模型,可以从魔塔平台获取:

# 模型下载(如果尚未下载)
# 通常模型会放置在 /root/ds_1.5b 目录

4. 实现对话历史搜索功能

4.1 搜索功能设计思路

我们要实现两种搜索方式:

  1. 关键词匹配搜索:基于文本内容直接匹配
  2. 语义相似度搜索:使用嵌入模型找到语义相关的内容

4.2 核心代码实现

首先,我们需要扩展对话历史管理功能:

import re
from sentence_transformers import SentenceTransformer
import numpy as np

class ChatHistoryManager:
    def __init__(self):
        self.history = []
        self.embedding_model = None
        self.history_embeddings = []
        
    def initialize_embedding_model(self):
        """初始化语义搜索模型"""
        try:
            self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
        except:
            print("语义搜索模型加载失败,将使用关键词搜索")
    
    def add_to_history(self, role, content):
        """添加对话到历史记录"""
        message = {
            'role': role,
            'content': content,
            'timestamp': len(self.history)
        }
        self.history.append(message)
        
        # 如果启用了语义搜索,同时生成嵌入
        if self.embedding_model and content:
            embedding = self.embedding_model.encode(content)
            self.history_embeddings.append(embedding)
    
    def keyword_search(self, query, max_results=5):
        """关键词搜索"""
        results = []
        query_lower = query.lower()
        
        for i, message in enumerate(self.history):
            if query_lower in message['content'].lower():
                results.append({
                    'index': i,
                    'role': message['role'],
                    'content': message['content'],
                    'score': 1.0  # 关键词匹配得分为1
                })
        
        # 按时间倒序排列
        results.sort(key=lambda x: x['index'], reverse=True)
        return results[:max_results]
    
    def semantic_search(self, query, max_results=5):
        """语义相似度搜索"""
        if not self.embedding_model or not self.history_embeddings:
            return self.keyword_search(query, max_results)
        
        try:
            # 生成查询嵌入
            query_embedding = self.embedding_model.encode(query)
            
            # 计算相似度
            similarities = []
            for i, emb in enumerate(self.history_embeddings):
                similarity = np.dot(query_embedding, emb) / (
                    np.linalg.norm(query_embedding) * np.linalg.norm(emb)
                )
                similarities.append((i, similarity))
            
            # 按相似度排序
            similarities.sort(key=lambda x: x[1], reverse=True)
            
            # 返回前N个结果
            results = []
            for idx, score in similarities[:max_results]:
                message = self.history[idx]
                results.append({
                    'index': idx,
                    'role': message['role'],
                    'content': message['content'],
                    'score': float(score)
                })
            
            return results
            
        except Exception as e:
            print(f"语义搜索出错: {e}")
            return self.keyword_search(query, max_results)

4.3 集成到Streamlit界面

接下来,我们将搜索功能集成到现有的Streamlit界面中:

import streamlit as st

def setup_search_interface():
    """设置搜索界面"""
    
    # 在侧边栏添加搜索功能
    with st.sidebar:
        st.header("🔍 对话历史搜索")
        
        search_tab1, search_tab2 = st.tabs(["关键词搜索", "语义搜索"])
        
        with search_tab1:
            keyword_query = st.text_input("输入关键词", key="keyword_search")
            if keyword_query:
                results = st.session_state.history_manager.keyword_search(keyword_query)
                display_search_results(results, "关键词")
        
        with search_tab2:
            semantic_query = st.text_input("输入搜索内容", key="semantic_search")
            if semantic_query:
                results = st.session_state.history_manager.semantic_search(semantic_query)
                display_search_results(results, "语义")

def display_search_results(results, search_type):
    """显示搜索结果"""
    if not results:
        st.info("没有找到相关结果")
        return
    
    st.success(f"找到 {len(results)} 条{search_type}相关记录")
    
    for result in results:
        with st.expander(f"{result['role']}: {result['content'][:50]}..."):
            st.write(f"**角色**: {result['role']}")
            st.write(f"**相关度**: {result['score']:.3f}")
            st.write(f"**内容**: {result['content']}")
            
            # 添加继续对话按钮
            if st.button("基于此继续对话", key=f"continue_{result['index']}"):
                st.session_state.selected_context = result['content']
                st.rerun()

4.4 完整集成示例

下面是完整的集成代码:

import streamlit as st
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
from history_manager import ChatHistoryManager

# 初始化会话状态
if 'history_manager' not in st.session_state:
    st.session_state.history_manager = ChatHistoryManager()
    st.session_state.history_manager.initialize_embedding_model()

# 加载模型(使用缓存)
@st.cache_resource
def load_model():
    tokenizer = AutoTokenizer.from_pretrained("/root/ds_1.5b", trust_remote_code=True)
    model = AutoModelForCausalLM.from_pretrained(
        "/root/ds_1.5b",
        device_map="auto",
        torch_dtype="auto",
        trust_remote_code=True
    )
    return tokenizer, model

def main():
    st.title("DeepSeek-R1-Distill-Qwen-1.5B 智能对话")
    
    # 加载模型
    tokenizer, model = load_model()
    
    # 设置搜索界面
    setup_search_interface()
    
    # 显示聊天历史
    for message in st.session_state.history_manager.history:
        with st.chat_message(message['role']):
            st.markdown(message['content'])
    
    # 聊天输入
    if prompt := st.chat_input("考考 DeepSeek R1..."):
        # 添加用户消息到历史
        st.session_state.history_manager.add_to_history("user", prompt)
        
        # 显示用户消息
        with st.chat_message("user"):
            st.markdown(prompt)
        
        # 生成回复
        with st.chat_message("assistant"):
            with st.spinner("思考中..."):
                # 准备输入
                messages = [{"role": "user", "content": prompt}]
                text = tokenizer.apply_chat_template(
                    messages,
                    tokenize=False,
                    add_generation_prompt=True
                )
                
                # 生成回复
                with torch.no_grad():
                    inputs = tokenizer(text, return_tensors="pt").to(model.device)
                    outputs = model.generate(
                        **inputs,
                        max_new_tokens=2048,
                        temperature=0.6,
                        top_p=0.95,
                        do_sample=True
                    )
                    
                    response = tokenizer.decode(
                        outputs[0][inputs.input_ids.shape[1]:], 
                        skip_special_tokens=True
                    )
                
                # 格式化输出
                formatted_response = format_response(response)
                st.markdown(formatted_response)
        
        # 添加助手回复到历史
        st.session_state.history_manager.add_to_history("assistant", formatted_response)

def format_response(response):
    """格式化模型输出"""
    # 处理思考过程标签
    response = response.replace("<|im_start|>", "").replace("<|im_end|>", "")
    response = response.replace("reasoning", "思考过程").replace("answer", "回答")
    return response

if __name__ == "__main__":
    main()

5. 功能使用指南

5.1 基本搜索操作

  1. 打开搜索界面:在左侧侧边栏找到"对话历史搜索"板块
  2. 选择搜索方式
    • 关键词搜索:输入确切的关键词进行精确匹配
    • 语义搜索:输入相关概念,找到语义相似的内容
  3. 查看结果:搜索结果会显示相关对话片段和相似度评分
  4. 继续对话:点击"基于此继续对话"可以从选中的历史内容继续讨论

5.2 搜索技巧

  • 使用具体关键词:如"Python代码"、"数学题解"等
  • 尝试同义词:如果找不到结果,尝试使用相近的词语
  • 结合两种搜索:先用关键词搜索定位,再用语义搜索扩展

5.3 实际应用场景

# 示例对话历史:
# 用户:请帮我写一个Python函数计算斐波那契数列
# AI:提供了斐波那契数列的代码实现
# 用户:这个函数的时间复杂度是多少?

# 几天后,用户想要找回之前的代码:
# 在搜索框输入"斐波那契数列"
# 立即找到之前的对话和代码
# 可以基于此继续讨论优化方案

6. 性能优化建议

6.1 内存管理

对于大量对话历史,建议添加以下优化:

def optimize_memory_usage(self):
    """优化内存使用"""
    # 定期清理旧的嵌入向量
    if len(self.history_embeddings) > 1000:  # 保留最近1000条
        self.history_embeddings = self.history_embeddings[-1000:]
    
    # 使用更轻量的模型
    if self.embedding_model is None:
        try:
            self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
        except:
            pass

6.2 搜索效率优化

def create_search_index(self):
    """创建搜索索引以提高效率"""
    # 预处理文本内容
    self.search_index = []
    for message in self.history:
        # 简单的文本预处理
        processed = {
            'content': message['content'].lower(),
            'index': message['timestamp']
        }
        self.search_index.append(processed)

7. 常见问题解答

7.1 搜索功能相关问题

Q: 为什么有时候搜索不到结果? A: 可能是关键词不够准确,或者对话历史中没有相关内容。尝试使用更具体的关键词或语义搜索。

Q: 语义搜索和关键词搜索有什么区别? A: 关键词搜索需要精确匹配,语义搜索可以找到意思相近的内容,即使没有完全相同的词语。

Q: 搜索功能会影响对话性能吗? A: 基本不会。搜索操作只在用户主动搜索时进行,不会影响正常的对话流程。

7.2 技术实现问题

Q: 需要额外的模型下载吗? A: 语义搜索功能需要下载all-MiniLM-L6-v2模型(约90MB),但这是可选的。关键词搜索不需要额外下载。

Q: 如何扩展搜索功能? A: 你可以轻松添加新的搜索方式,比如按时间范围搜索、按角色过滤等。

8. 总结

通过为DeepSeek-R1-Distill-Qwen-1.5B添加对话历史搜索功能,我们大大提升了这个本地对话助手的实用性。现在你可以:

  • 🔍 快速回溯:轻松找到之前的对话内容
  • 💡 延续讨论:基于历史对话继续深入交流
  • 🎯 精准定位:使用关键词或语义搜索精确找到需要的信息
  • 💾 本地隐私:所有搜索都在本地完成,保障数据安全

这个功能的添加让长时间的AI对话变得更加高效和实用,特别适合需要频繁回溯之前讨论内容的用户。无论是技术讨论、学习交流还是创意发散,搜索功能都能帮你更好地管理和利用对话历史。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐