D2-基于本地Ollama模型的多輪問答系統

本程序是一個基于 Gradio 和 Ollama API 構建的支持多輪對話的寫作助手。相較于上一版本,本版本新增了對話歷史記錄、Token 計數、參數調節和清空對話功能,顯著提升了用戶體驗和交互靈活性。

程序通過抽象基類 LLMAgent 實現模塊化設計,當前使用 OllamaAgent 作為具體實現,調用本地部署的 Ollama 大語言模型(如 qwen3:8b)生成寫作建議,并提供一個交互式的 Web 界面供用戶操作。

設計支持未來擴展到其他 LLM 平臺(如 OpenAI、HuggingFace),只需實現新的 LLMAgent 子類即可。


環境配置

依賴安裝

需要以下 Python 庫:

  • gradio:用于創建交互式 Web 界面。
  • requests:向 Ollama API 發送 HTTP 請求。
  • json:解析 API 響應數據(Python 內置)。
  • logging:記錄運行日志(Python 內置)。
  • abc:定義抽象基類(Python 內置)。
  • tiktoken:精確計算 Token 數量以管理輸入和歷史長度。

安裝命令:

pip install gradio requests tiktoken

建議使用 Python 3.8 或更高版本。


Ollama 服務配置

  1. 安裝 Ollama
    從 https://ollama.ai/ 下載并安裝。

  2. 啟動 Ollama 服務

    ollama serve
    
    • 默認監聽地址:http://localhost:11434
  3. 下載模型

    ollama pull qwen3:8b
    
  4. 驗證模型

    ollama list
    

運行程序

  1. 將代碼保存為 writing_assistant.py
  2. 確保 Ollama 服務正在運行。
  3. 執行程序:
    python writing_assistant.py
    
  4. 打開瀏覽器訪問界面(通常為 http://127.0.0.1:7860)。
  5. 輸入寫作提示,調整參數后點擊“獲取寫作建議”,查看結果和對話歷史。

代碼說明

1. 依賴導入

import gradio as gr
import requests
import json
import logging
from abc import ABC, abstractmethod
import tiktoken
  • tiktoken:精確計算 Token 數量,優化輸入控制。

2. 日志配置

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
  • 配置日志級別為 INFO,記錄 API 調用和錯誤信息,便于調試。

3. 抽象基類:LLMAgent

class LLMAgent(ABC):@abstractmethoddef generate_response(self, prompt):pass
  • 定義通用 LLM 代理接口,要求實現 generate_response 方法。
  • 支持未來擴展到其他 LLM 平臺(如 OpenAI、Anthropic)。

4. 具體實現:OllamaAgent

class OllamaAgent(LLMAgent):def __init__(self, config): ...def set_max_history_length(self, max_rounds): ...def set_parameters(self, max_tokens, temperature): ...def generate_response(self, prompt): ...def clear_history(self): ...
  • 對話歷史:維護 history 列表,支持多輪對話。
  • 參數調節:動態調整 max_tokenstemperature
  • Token 管理:自動截斷歷史記錄,防止超出模型上下文限制。
  • 錯誤處理:捕獲網絡請求失敗和 JSON 解析錯誤,返回用戶友好的提示。

5. Token 計數函數

def calculate_tokens(text): ...
def calculate_history_tokens(history): ...
  • 使用 tiktoken 精確估算 Token 數量,提升輸入長度控制能力。

6. 歷史格式化

def format_history_for_chatbot(history): ...
  • 將內部 history 結構轉換為 Gradio 的 Chatbot 格式 [user_msg, assistant_msg]

7. 核心邏輯:generate_assistance

def generate_assistance(prompt, agent, max_rounds, max_tokens, temperature): ...
  • 設置最大對話輪數和生成參數。
  • 調用 agent.generate_response 獲取響應。
  • 返回格式化的對話歷史、最新回復和 Token 計數。

8. 輔助函數

def update_token_count(prompt): ...
def clear_conversation(agent): ...
  • 實時更新輸入 Token 數量。
  • 清空對話歷史并重置狀態。

9. 主函數:main

def main():config = { ... }agent = OllamaAgent(config)with gr.Blocks(...) as demo:...demo.launch()
  • 增強的 UI:包含輸入框、Token 顯示、參數調節滑塊和清空按鈕。
  • Gradio 事件綁定
    • prompt_input.change():動態更新 Token 計數。
    • submit_button.click():觸發寫作建議生成。
    • clear_button.click():重置對話歷史。

運行流程圖

graph TDA[用戶輸入提示] --> B[點擊 submit_button]B --> C[調用 generate_assistance(prompt, agent, 參數)]C --> D[調用 agent.set_* 設置參數]D --> E[調用 agent.generate_response(prompt)]E --> F[向 Ollama API 發送 POST 請求]F --> G[接收并解析 JSON 響應]G --> H[更新聊天歷史和輸出結果]

注意事項

  • Ollama 服務:確保服務運行并監聽在 http://localhost:11434/v1
  • 模型可用性:確認 qwen3:8b 已下載。
  • Token 上限:注意模型的最大上下文長度(如 4096 Tokens),避免歷史過長導致超限。
  • 參數影響
    • temperature:控制生成隨機性(較低值更確定,較高值更具創造性)。
    • max_tokens:限制輸出長度。
  • 調試信息:查看終端日志,確認 API 響應是否正常或是否有錯誤。

未來改進建議

  • 多模型支持:添加 OpenAIAgent 等子類,通過下拉菜單切換模型。
  • 配置文件化:將硬編碼配置移至 JSON/YAML 文件。
  • 異步請求:使用 aiohttp 替換 requests,提升并發性能。
  • 對話持久化:將歷史對話保存到本地文件或數據庫。
  • 用戶認證:區分不同用戶的對話記錄。
  • 移動端適配:優化界面布局以適配手機端。

示例使用

  1. 啟動程序后訪問 http://127.0.0.1:7860
  2. 輸入提示:“幫我寫一段關于環保的文章。”
  3. 調整參數(如 max_tokens=1000temperature=0.2)。
  4. 點擊“獲取寫作建議”,查看類似以下輸出:

在這里插入圖片描述

代碼

import gradio as gr
import requests
import json
import logging
from abc import ABC, abstractmethod
import tiktoken# 設置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)# 抽象基類:定義通用的 LLM Agent 接口
class LLMAgent(ABC):@abstractmethoddef generate_response(self, prompt):pass# Ollama 特定的實現
class OllamaAgent(LLMAgent):def __init__(self, config):self.model = config["model"]self.base_url = config["base_url"]self.api_key = config["api_key"]self.max_tokens = config["max_tokens"]self.temperature = config["temperature"]self.history = []self.max_history_length = 10def set_max_history_length(self, max_rounds):self.max_history_length = int(max_rounds * 2)if len(self.history) > self.max_history_length:self.history = self.history[-self.max_history_length:]def set_parameters(self, max_tokens, temperature):self.max_tokens = int(max_tokens)self.temperature = float(temperature)def generate_response(self, prompt):self.history.append({"role": "user", "content": prompt})if len(self.history) > self.max_history_length:self.history = self.history[-self.max_history_length:]url = f"{self.base_url}/chat/completions"headers = {"Authorization": f"Bearer {self.api_key}","Content-Type": "application/json"}payload = {"model": self.model,"messages": self.history,"max_tokens": self.max_tokens,"temperature": self.temperature}try:response = requests.post(url, headers=headers, json=payload)response.raise_for_status()result = response.json()content = result['choices'][0]['message']['content']logger.info(f"API 響應: {content}")self.history.append({"role": "assistant", "content": content})return contentexcept requests.exceptions.RequestException as e:logger.error(f"API 請求失敗: {str(e)}")return f"錯誤:無法連接到 Ollama API: {str(e)}"except KeyError as e:logger.error(f"解析響應失敗: {str(e)}")return f"錯誤:解析響應失敗: {str(e)}"def clear_history(self):self.history = []def calculate_tokens(text):if not text:return 0cleaned_text = text.strip().replace('\n', '')try:encoding = tiktoken.get_encoding("cl100k_base")tokens = encoding.encode(cleaned_text)return len(tokens)except Exception as e:logger.error(f"Token 計算失敗: {str(e)}")return len(cleaned_text)def calculate_history_tokens(history):total_tokens = 0try:encoding = tiktoken.get_encoding("cl100k_base")for message in history:content = message["content"].strip()tokens = encoding.encode(content)total_tokens += len(tokens)return total_tokensexcept Exception as e:logger.error(f"歷史 Token 計算失敗: {str(e)}")return sum(len(msg["content"].strip()) for msg in history)def format_history_for_chatbot(history):"""將 agent.history 轉換為 gr.Chatbot 所需格式:List[List[str, str]]"""messages = []for i in range(0, len(history) - 1, 2):if history[i]["role"] == "user" and history[i+1]["role"] == "assistant":messages.append([history[i]["content"], history[i+1]["content"]])return messagesdef generate_assistance(prompt, agent, max_rounds, max_tokens, temperature):agent.set_max_history_length(max_rounds)agent.set_parameters(max_tokens, temperature)response = agent.generate_response(prompt)history_tokens = calculate_history_tokens(agent.history)chatbot_format_history = format_history_for_chatbot(agent.history)return chatbot_format_history, response, f"歷史總 token 數(估算):{history_tokens}"def update_token_count(prompt):return f"當前輸入 token 數(精確):{calculate_tokens(prompt)}"def clear_conversation(agent):agent.clear_history()return [], "對話已清空", "歷史總 token 數(估算):0"def main():config = {"api_type": "ollama","model": "qwen3:8b","base_url": "http://localhost:11434/v1","api_key": "ollama","max_tokens": 1000,"temperature": 0.2}agent = OllamaAgent(config)with gr.Blocks(title="寫作助手") as demo:gr.Markdown("# 寫作助手(支持多輪對話)")gr.Markdown("輸入您的寫作提示,獲取建議和指導!支持連續對話,調整對話輪數、max_tokens 和 temperature,或點擊“清空對話”重置。")with gr.Row():with gr.Column():prompt_input = gr.Textbox(label="請輸入您的提示",placeholder="例如:幫我寫一段關于環保的文章",lines=3)token_count = gr.Textbox(label="輸入 token 數",value="當前輸入 token 數(精確):0",interactive=False)history_token_count = gr.Textbox(label="歷史 token 數",value="歷史總 token 數(估算):0",interactive=False)max_rounds = gr.Slider(minimum=1,maximum=10,value=5,step=1,label="最大對話輪數",info="設置保留的對話輪數(每輪包含用戶和模型消息)")max_tokens = gr.Slider(minimum=100,maximum=2000,value=1000,step=100,label="最大生成 token 數",info="控制單次生成的最大 token 數")temperature = gr.Slider(minimum=0.0,maximum=1.0,value=0.2,step=0.1,label="Temperature",info="控制生成隨機性,0.0 為確定性,1.0 為較隨機")submit_button = gr.Button("獲取寫作建議")clear_button = gr.Button("清空對話")with gr.Column():chatbot = gr.Chatbot(label="對話歷史")output = gr.Textbox(label="最新生成結果", lines=5)prompt_input.change(fn=update_token_count,inputs=prompt_input,outputs=token_count)submit_button.click(fn=generate_assistance,inputs=[prompt_input, gr.State(value=agent), max_rounds, max_tokens, temperature],outputs=[chatbot, output, history_token_count])clear_button.click(fn=clear_conversation,inputs=gr.State(value=agent),outputs=[chatbot, output, history_token_count])demo.launch()if __name__ == "__main__":main()

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/83741.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/83741.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/83741.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

傳統業務對接AI-AI編程框架-Rasa的業務應用實戰(2)--選定Python環境 安裝rasa并初始化工程

此篇接續上一篇 傳統業務對接AI-AI編程框架-Rasa的業務應用實戰(1)--項目背景即學習初衷 1、Python 環境版本的選擇 我主機上默認的Python環境是3.12.3 (我喜歡保持使用最新版本的工具或框架,當初裝python時最新的穩定版本就是…

Ubuntu22.04安裝MinkowskiEngine

MinkowskiEngine簡介 Minkowski引擎是一個用于稀疏張量的自動微分庫。它支持所有標準神經網絡層,例如對稀疏張量的卷積、池化和廣播操作。 MinkowskiEngine安裝 官方源碼鏈接:GitHub - NVIDIA/MinkowskiEngine: Minkowski Engine is an auto-diff neu…

高等數學基礎(矩陣基本操作轉置和逆矩陣)

矩陣是否相等 若 A A A和 B B B為同型矩陣且對應位置的各個元素相同, 則稱矩陣 A A A和 B B B相等 在Numpy中, 可以根據np.allclose()來判斷 import numpy as npA np.random.rand(4, 4) # 生成一個隨機 n x n 矩陣B A A.Tprint("矩陣是否相等:", np…

網絡爬蟲一課一得

網頁爬蟲(Web Crawler)是一種自動化程序,通過模擬人類瀏覽行為,從互聯網上抓取、解析和存儲網頁數據。其核心作用是高效獲取并結構化網絡信息,為后續分析和應用提供數據基礎。以下是其詳細作用和用途方向: …

MATLAB實現井字棋

一、智能決策系統與博弈游戲概述 (一)智能決策系統核心概念 智能決策系統(Intelligent Decision System, IDS)是通過數據驅動和算法模型模擬人類決策過程的計算機系統,核心目標是在復雜環境中自動生成最優策略&#…

解決el-select選擇框右側下拉箭頭遮擋文字問題

如圖所示&#xff1a; el-select長度較短的時候&#xff0c;選擇框右側下拉箭頭會遮擋選中的數據 選中數據被遮擋 解決辦法&#xff1a; 組件如下&#xff1a; <td class"fmtd" :colspan"col.ptproCupNum" v-for"col in row" :key"…

【Linux】pthread多線程同步

參考文章&#xff1a;https://blog.csdn.net/Alkaid2000/article/details/128121066 一、線程同步 線程的主要優勢在于&#xff0c;能夠通過全局變量來共享信息。不過&#xff0c;這種便攜的共享是有代價的&#xff1b;必須確保多個線程不會同時修改同一變量&#xff0c;或者某…

Spring框架學習day7--SpringWeb學習(概念與搭建配置)

SpringWeb1.SpringWeb特點2.SpringWeb運行流程3.SpringWeb組件4.搭建項目結構圖&#xff1a;4.1導入jar包4.2在Web.xml配置**4.2.1配置統一攔截分發器 DispatcherServlet**4.2.2開啟SpringWeb注解&#xff08;spring.xml&#xff09; 5.處理類的搭建6.SpringWeb請求流程(自己理…

業務到解決方案構想

解決方案構想的核心理解 解決方案構想是連接業務需求與技術實現的關鍵橋梁&#xff0c;從您描述的內容和我的理解&#xff0c;這個階段的核心點包括&#xff1a; 核心要點解讀 轉化視角&#xff1a;將業務視角的需求轉變為解決方案視角 業務能力探索階段識別了"做什么&q…

jvm學習第1day jvm簡介,棧溢出、堆溢出

jvm學習第1day jvm簡介&#xff0c;棧溢出、堆溢出 jvm簡介棧線程安全棧溢出線程運行診斷堆堆溢出 方法區方法區內存溢出常量池和運行時常量池 jvm簡介 jvm 是編譯后的字節碼文件運行的環境&#xff0c; 因此各個平臺有了jvm可以運行java.class文件&#xff0c;這是Java跨平臺…

關于神經網絡中的激活函數

這篇博客主要介紹一下神經網絡中的激活函數以及為什么要存在激活函數。 首先&#xff0c;我先做一個簡單的類比&#xff1a;激活函數的作用就像給神經網絡里的 “數字信號” 加了一個 “智能閥門”&#xff0c;讓機器能學會像人類一樣思考復雜問題。 沒有激活i函數的神經網絡…

免費無限使用GPT Plus、Claude Pro、Grok Super、Deepseek滿血版

滲透智能-ShirtAI&#xff0c;可以免費無限使用GPT Plus、Claude Pro、Grok Super、Deepseek滿血版、除此之外還能免費使用AI搜索、Gemini AI、AI照片修復、AI橡皮擦、AI去背景、AI智能摳圖、AI證件照、OCR識別、在線思維導圖、在線繪圖工具、PDF工具箱、PDF翻譯。 傳送入口&a…

阿里云 Linux 搭建郵件系統全流程及常見問題解決

阿里云 Linux 搭建 [conkl.com]郵件系統全流程及常見問題解決 目錄 阿里云 Linux 搭建 [conkl.com]郵件系統全流程及常見問題解決一、前期準備&#xff08;關鍵配置需重點檢查&#xff09;1.1 服務器與域名準備1.2 系統初始化&#xff08;必做操作&#xff09; 二、核心組件安裝…

python版若依框架開發:項目結構解析

python版若依框架開發 從0起步&#xff0c;揚帆起航。 python版若依部署代碼生成指南&#xff0c;迅速落地CURD&#xff01;項目結構解析 文章目錄 python版若依框架開發前端后端 前端 后端

RabbitMQ 的異步化、解耦和流量削峰三大核心機制

RabbitMQ 的異步化、解耦和流量削峰三大核心機制 RabbitMQ 是解決數據庫高并發問題的利器&#xff0c;通過異步化、解耦和流量削峰三大核心機制保護數據庫。下面從設計思想到具體實現&#xff0c;深入剖析 RabbitMQ 應對高并發的完整方案&#xff1a; 一、數據庫高并發核心痛點…

前端沒有“秦始皇“,但可以做跨端的王[特殊字符]

前端各領域的 “百家爭鳴” 框架之爭&#xff1a;有 React、Vue、Angular 等多種框架。它們各有優缺點&#xff0c;開發者之間還存在鄙視鏈&#xff0c;比如 Vue 嫌 React 難用&#xff0c;React 嫌 Vue 不夠靈活。樣式處理&#xff1a; CSS 預處理器&#xff1a;像 Sass、Les…

Spring Boot-面試題(52)

摘要&#xff1a; 1、通俗易懂&#xff0c;適合小白 2、僅做面試復習用&#xff0c;部分來源網絡&#xff0c;博文免費&#xff0c;知識無價&#xff0c;侵權請聯系&#xff01; 1. 什么是 Spring Boot 框架&#xff1f; Spring Boot 是基于 Spring 框架的快速開發框架&#…

JVM——JVM中的字節碼:解碼Java跨平臺的核心引擎

引入 在Java的技術版圖中&#xff0c;字節碼&#xff08;Bytecode&#xff09;是連接源代碼與機器世界的黃金橋梁。當開發者寫下第一行public class HelloWorld時&#xff0c;編譯器便開始了一場精密的翻譯工程——將人類可讀的Java代碼轉化為JVM能夠理解的字節碼指令。這些由…

Java中的JSONObject詳解:從基礎到高級應用

Java中的JSONObject詳解&#xff1a;從基礎到高級應用 在當今前后端分離的架構中&#xff0c;JSONObject已成為Java開發者處理JSON數據的瑞士軍刀。本文將深入解析JSONObject的核心機制與實戰技巧。 一、JSONObject的本質與實現庫 1.1 核心定位 JSONObject是Java中表示JSON對…

在 SpringBoot+Tomcat 環境中 線程安全問題的根本原因以及哪些變量會存在線程安全的問題。

文章目錄 前言Tomcat SpringBoot單例加載結果分析多例加載&#xff1a;結果分析&#xff1a; 哪些變量存在線程安全的問題&#xff1f;線程不安全線程安全 總結 前言 本文帶你去深入理解為什么在web環境中(Tomcat SpringBoot)會存在多線程的問題以及哪些變量會存在線程安全的…