本程序是一个基于 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)会存在多线程的问题以及哪些变量会存在线程安全的…