使用python实现与本地ollama部署的deepseek对话

专栏总目录

        按照ollama官方doc的example操作,没有成功与本地ollama上的deepseek-r1:1.5b通讯后,发现vscode可以调用本地ollama上的deepseek模型。

        为了实现与ollama上的deepseek模型通讯,我使用wireshark对本地回环地址进行侦听后,发现了具体方法。

一、研究过程

        vscode上提问,给了如下回答

        如下图所示,上传的内容及参数,关键字在相关内容的的位置

二、对话脚本

        使用python脚本编辑后如下所示

import socket
import json
import json

def get_content(str_data):
    #print(str_data)
    content_str = ''
    for i in str_data.splitlines():
        if 'think' in i:
            continue
        elif '"content"' in i:
            # 给定的JSON字符串
            json_string = i
            # 解析JSON字符串
            data = json.loads(json_string)
            
            # 提取content的值
            content = data['message']['content']

            content_str = content_str + content.replace('/n', '')
            
            stripped_string = content_str.lstrip('\n')
    return stripped_string


def send_post_request(host, port, path, headers, body):
    # 创建一个socket对象
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        # 连接到服务器
        s.connect((host, port))
        # 构造HTTP请求
        request = f"POST {path} HTTP/1.1\r\n"
        for header, value in headers.items():
            request += f"{header}: {value}\r\n"
        request += "\r\n"  # 空行表示头部结束
        request += body
        # 发送HTTP请求
        s.sendall(request.encode('utf-8'))
        # 接收响应
        response = b''
        while True:
            part = s.recv(1024)
            if not part:
                break
            response += part
        return response

def main():
    # 服务器的地址和端口
    host = '127.0.0.1'
    port = 11434
    path = "/api/chat"
    while True:
        question = input('>>>')
        # 构造请求数据
        data = {
            "model": "deepseek-r1:1.5b",
            "messages": [{"role": "user", "content": question}],
            "options": {
                "num_predict": 4096,
                "stop": ["<...begin...of...sentence...>", "<...end...of...sentence...>", "<...User...>", "<...Assistant...>"],
                "num_ctx": 8096
            },
            "keep_alive": 1800
        }
        body = json.dumps(data)

        # 构造HTTP头部
        headers = {
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate, br",
            "Authorization": "Bearer undefined",
            "Content-Length": str(len(body)),
            "Content-Type": "application/json",
            "User-Agent": "node-fetch",
            "Host": "127.0.0.1:11434",
            "Connection": "close"
        }

        # 发送POST请求并获取响应
        response = send_post_request(host, port, path, headers, body)
        print("【deepseek】: ",end='')
        #print(response.decode('utf-8'))
        mess = get_content(response.decode('utf-8'))
        print(mess)

if __name__ == '__main__':
    main()

        具体切片、美化步骤再此省略。

<think>嗯,用户之前已经问过如何在本地部署Ollama并通过终端运行,现在他们想用Python调用本地DeepSeek模型实现连续对话。我需要确保回答覆盖如何维护对话历史,因为连续对话需要上下文。用户可能希望像ChatGPT那样有记忆能力,所以必须处理消息的存储和传递。 首先,用户提到“本地API”,但之前的解决方案是使用subprocess而不是HTTP,所以可能需要确认是否依然不使用HTTP。用户可能希望保持纯本地调用,避免网络开销或配置服务器。因此,继续用subprocess的方法,但需要管理对话状态。 接下来,我需要考虑如何构造请求。Ollama的API通常通过HTTP,但用户明确要求不用HTTP,所以必须通过命令行传递输入。每次调用模型时,需要包含之前的对话历史。这可能需要将历史记录格式化为提示,或者利用模型本身的上下文窗口。 然后,Python代码的结构。需要维护一个对话历史列表,每次提问时将历史和新问题组合发送给Ollama。但要注意Ollama的run命令是否支持多行输入或是否需要转义引号。可能需要将整个对话历史作为单个字符串传递给命令行。 还要注意Ollama的命令行参数,是否有选项可以保持会话,但根据之前的回答,ollama run默认是交互式的,但非交互式下可能需要重新设计。可能需要在每次调用时包含所有历史,这样模型才能看到之前的对话。 另外,处理输入输出中的特殊字符,比如引号,可能需要转义,避免在shell命令中出现问题。此外,上下文长度限制,如果对话历史太长,可能需要截断,防止超过模型的token限制。 用户可能没有提到性能问题,但连续对话的延迟可能较高,尤其是长历史的情况下。可能需要建议优化方法,比如限制历史长度,或者使用更高效的模型。 最后,示例代码需要展示如何维护对话历史,每次调用时附加新的用户输入和模型回复。可能需要使用一个类来封装状态,使得对话历史在多次调用中保持。还要处理可能的超时或错误,确保代码健壮性。 总结:关键点是如何在每次调用时将整个对话历史传递给模型,并正确解析输出以维护上下文。同时处理命令行参数的正确转义和输出清洗,确保对话连贯。</think>以下是通过Python实现Ollama本地模型连续对话的完整方案(无需HTTP调用,直接进程通信): --- ### 一、核心实现原理 1. **上下文维护**:每次调用携带完整对话历史 2. **交互模式**:通过`subprocess`模块实现进程间通信 3. **上下文窗口**:自动管理对话长度(避免超过模型token限制) --- ### 二、基础实现代码 ```python import subprocess import re class DeepSeekChat: def __init__(self, model="deepseek-chat"): self.model = model self.history = [] self.context_window = 4096 # 根据模型调整 def _format_prompt(self, new_query): # 组合历史对话当前问题 history_str = "\n".join([f"用户:{q}\n助手:{a}" for q, a in self.history]) return f"{history_str}\n用户:{new_query}\n助手:" def _truncate_history(self): # 自动截断过长的上下文 while len(str(self.history)) > self.context_window * 3.5 and len(self.history) > 1: self.history.pop(0) def ask(self, query): self._truncate_history() full_prompt = self._format_prompt(query) cmd = f'ollama run {self.model} "{full_prompt}"' try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=120 ) response = self._clean_output(result.stdout) self.history.append((query, response)) return response except subprocess.TimeoutExpired: return "请求超时,请尝试缩短问题长度" def _clean_output(self, raw_text): # 清理输出中的控制字符和多余内容 cleaned = re.sub(r'\x1b\[[0-9;]*m', '', raw_text) # 去除ANSI颜色代码 cleaned = re.split(r'(>>>|</s>)', cleaned)[-1].strip() return cleaned.split("助手:")[-1].strip() # 使用示例 chat = DeepSeekChat() print(chat.ask("如何证明勾股定理?")) print(chat.ask("能用代数方法再解释一次吗?")) # 自动携带上文 ``` --- ### 三、高级功能扩展 #### 1. 记忆持久化 ```python import pickle class PersistentChat(DeepSeekChat): def save(self, path): with open(path, 'wb') as f: pickle.dump(self.history, f) def load(self, path): with open(path, 'rb') as f: self.history = pickle.load(f) # 使用示例 bot = PersistentChat() bot.load('conversation.pkl') # 读取历史对话 ``` #### 2. 流式输出 ```python def ask_stream(self, query): self._truncate_history() full_prompt = self._format_prompt(query) proc = subprocess.Popen( f'ollama run {self.model} "{full_prompt}"', shell=True, stdout=subprocess.PIPE, text=True ) buffer = "" while True: output = proc.stdout.read(1) if not output and proc.poll() is not None: break buffer += output if buffer.endswith("\n"): print(buffer.strip()) buffer = "" response = self._clean_output(buffer) self.history.append((query, response)) return response ``` #### 3. 参数控制 ```python def ask(self, query, temp=0.7, max_tokens=512): # 在cmd中添加参数 cmd = f'ollama run {self.model} "{full_prompt}" --temperature {temp} --num_predict {max_tokens}' # ...其余代码不变... ``` --- ### 四、关键问题处理 1. **上下文溢出**: ```python # 在_truncate_history中增加智能截断 def _truncate_history(self): token_count = sum(len(str(item)) for item in self.history) while token_count > self.context_window and len(self.history) > 1: removed = self.history.pop(0) token_count -= len(str(removed)) ``` 2. **特殊字符处理**: ```python def ask(self, query): # 转义双引号 safe_query = query.replace('"', r'\"') full_prompt = self._format_prompt(safe_query) # ...后续代码不变... ``` 3. **性能优化**: ```bash # 启动时预加载模型 OLLAMA_GPU_LAYERS=50 ollama serve & # 保持后台服务 ``` --- ### 五、典型使用场景 ```python chat = DeepSeekChat() while True: user_input = input("你:") if user_input.lower() in ['exit', 'quit']: break response = chat.ask(user_input) print(f"\n助手:{response}\n") ``` 这种实现方式: 1. 完全本地运行,无需网络连接 2. 对话上下文自动维护 3. 支持自定义参数调整 4. 兼容主流操作系统环境
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静候光阴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值