如何查看Qwen推理日志?Flask中间件部署教程
1. 项目背景与目标
随着大模型轻量化趋势的加速,越来越多开发者希望在资源受限的环境中部署具备基础对话能力的AI服务。Qwen1.5-0.5B-Chat作为通义千问系列中参数量最小但性能高效的模型之一,特别适合用于边缘设备、本地开发环境或低成本云实例上的智能对话应用。
本项目基于ModelScope(魔塔社区)生态构建,旨在通过轻量级技术栈实现模型的快速部署与调试,并重点解决一个常见痛点:如何有效查看和分析模型推理过程中的日志信息。我们将使用 Flask 搭建 Web 服务中间层,在提供用户友好交互界面的同时,完整记录请求响应流程中的关键日志,便于后续优化与问题排查。
本文将详细介绍从环境搭建到服务上线的全流程,并深入讲解如何通过 Flask 中间件机制捕获并输出 Qwen 的推理日志,帮助开发者掌握可监控、可调试的本地化大模型部署方案。
2. 核心架构设计
2.1 系统整体结构
整个系统采用分层架构设计,主要包括以下四个层级:
- 模型层:加载
qwen/Qwen1.5-0.5B-Chat模型权重,运行于 CPU 上的 PyTorch 推理引擎。 - 推理层:基于 Hugging Face Transformers 库封装生成逻辑,支持流式输出和上下文管理。
- Web 服务层:使用 Flask 提供 RESTful API 和前端页面访问入口,处理 HTTP 请求。
- 日志监控层:通过自定义 Flask 中间件拦截请求/响应周期,记录完整的推理行为日志。
该架构确保了系统的模块化与可扩展性,同时为日志追踪提供了清晰的数据通道。
2.2 日志采集的关键挑战
在无 GPU 支持的 CPU 环境下部署大模型时,推理延迟较高,因此对每一次请求的执行状态进行细粒度监控尤为重要。主要挑战包括:
- 如何在不干扰主推理流程的前提下收集输入输出数据?
- 如何准确记录每轮对话的时间消耗(含预处理、推理、后处理)?
- 如何区分正常响应与异常中断(如超时、OOM)?
我们通过引入 WSGI 中间件的方式,在请求进入视图函数前和响应返回客户端前插入日志钩子,实现了非侵入式的日志采集机制。
3. 部署实践步骤
3.1 环境准备
首先创建独立的 Conda 虚拟环境以隔离依赖:
conda create -n qwen_env python=3.9 conda activate qwen_env安装必要的 Python 包:
pip install torch==2.1.0 transformers==4.36.0 flask==2.3.3 modelscope==1.13.0注意:建议使用较新版本的
modelscopeSDK 以兼容 Qwen1.5 系列模型。
3.2 模型加载与推理封装
使用 ModelScope 提供的接口直接加载模型:
from modelscope.pipelines import pipeline from modelscope.utils.constant import Tasks # 初始化对话管道 inference_pipeline = pipeline( task=Tasks.text_generation, model='qwen/Qwen1.5-0.5B-Chat', device='cpu' # 明确指定使用 CPU )定义推理函数,支持基本上下文维护:
def generate_response(prompt, history=None): if history is None: history = [] inputs = { "text": prompt, "history": history } try: result = inference_pipeline(inputs) response_text = result["text"] return response_text, 200 except Exception as e: return str(e), 5003.3 Flask Web 服务搭建
创建app.py文件,初始化 Flask 应用并注册路由:
from flask import Flask, request, jsonify, render_template_string import time import logging app = Flask(__name__) # 基础 HTML 页面(简化版) HTML_TEMPLATE = ''' <!DOCTYPE html> <html> <head><title>Qwen Chat</title></head> <body> <h2>Qwen1.5-0.5B-Chat 对话界面</h2> <div id="chat"></div> <input type="text" id="userInput" placeholder="请输入消息..." onkeydown="handleKey(event)"> <script> function send(msg) { fetch('/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({prompt: msg}) }) .then(r => r.json()).then(data => { document.getElementById('chat').innerHTML += '<p><strong>你:</strong> ' + msg + '</p>'; document.getElementById('chat').innerHTML += '<p><strong>AI:</strong> ' + data.response + '</p>'; }); } function handleKey(e) { if (e.key === 'Enter') { send(e.target.value); e.target.value=''; } } </script> </body> </html> ''' @app.route('/') def index(): return render_template_string(HTML_TEMPLATE) @app.route('/chat', methods=['POST']) def chat(): data = request.get_json() prompt = data.get('prompt', '') start_time = time.time() response, status_code = generate_response(prompt) duration = time.time() - start_time if status_code == 200: return jsonify({"response": response, "time": f"{duration:.2f}s"}) else: return jsonify({"error": response}), status_code3.4 自定义日志中间件实现
为了实现全面的日志追踪,我们编写一个 WSGI 中间件类,用于记录每个请求的详细信息:
class LoggingMiddleware: def __init__(self, app, log_file="qwen_inference.log"): self.app = app self.log_file = log_file logging.basicConfig( filename=log_file, level=logging.INFO, format='%(asctime)s | %(method)s | %(url)s | %(status)d | %(duration).2f s | %(body)s' ) def __call__(self, environ, start_response): # 记录开始时间 start_time = time.time() # 获取请求方法和路径 method = environ['REQUEST_METHOD'] path = environ['PATH_INFO'] # 读取请求体(仅支持 application/json) content_length = int(environ.get('CONTENT_LENGTH', 0)) body = '' if content_length > 0: input_stream = environ['wsgi.input'] body_bytes = input_stream.read(content_length) body = body_bytes.decode('utf-8') # 将已读取的内容重新置入流中,保证后续可读 environ['wsgi.input'] = BytesIO(body_bytes) # 临时存储响应状态码 status_code = [0] def custom_start_response(status, headers, *args): status_code[0] = int(status.split()[0]) return start_response(status, headers, *args) # 执行原始应用 response = self.app(environ, custom_start_response) duration = time.time() - start_time # 写入日志 logging.info("", extra={ 'method': method, 'url': path, 'status': status_code[0], 'duration': duration, 'body': body }) return response⚠️ 注意:需导入
from io import BytesIO以支持流重置。
最后,在主程序中启用中间件:
if __name__ == '__main__': from io import BytesIO app.wsgi_app = LoggingMiddleware(app.wsgi_app) app.run(host='0.0.0.0', port=8080, threaded=True)3.5 启动服务与访问测试
运行命令启动服务:
python app.py服务启动后,点击界面上的HTTP (8080端口)访问入口,即可进入聊天界面。发送几条测试消息后,检查当前目录下的qwen_inference.log文件内容示例:
2025-04-05 10:23:45,123 | POST | /chat | 200 | 8.76 s | {"prompt": "你好"} 2025-04-05 10:24:01,456 | POST | /chat | 200 | 12.34 s | {"prompt": "你能做什么?"}每条日志包含: - 时间戳 - 请求方法 - 接口路径 - 返回状态码 - 推理耗时(秒) - 原始请求体(可用于回放测试)
4. 性能优化与调试建议
4.1 减少 CPU 推理延迟的策略
尽管 Qwen1.5-0.5B-Chat 已经非常轻量,但在纯 CPU 环境下仍可能出现明显延迟。以下是几种有效的优化手段:
- 启用半精度计算(可选):若平台支持
bfloat16或float16,可在加载模型时设置torch_dtype=torch.bfloat16以提升速度。 - 限制最大生成长度:通过
max_new_tokens=128参数防止过长输出拖慢响应。 - 缓存历史上下文:避免重复传输全部对话历史,仅传递增量部分。
4.2 日志级别的精细化控制
当前中间件默认记录所有请求。在生产环境中,可根据需要增加日志级别控制:
import os log_level = os.getenv('LOG_LEVEL', 'INFO') logging.getLogger().setLevel(getattr(logging, log_level))并通过环境变量灵活调整:
export LOG_LEVEL=DEBUG4.3 异常情况的捕获与告警
建议结合外部工具(如 Sentry 或 Prometheus)对日志文件进行监控。例如,当单次推理时间超过 30 秒时触发告警,提示可能存在内存溢出或死循环风险。
5. 总结
5.1 核心价值回顾
本文围绕Qwen1.5-0.5B-Chat模型的本地部署需求,提出了一套完整的 Flask 中间件解决方案,重点解决了推理日志可视化这一工程难题。通过 WSGI 层的日志中间件设计,实现了对每次请求的全链路追踪,涵盖请求内容、响应状态、执行耗时等关键指标。
该方案具备以下优势:
- 非侵入式集成:日志逻辑与业务代码解耦,不影响核心推理流程。
- 高兼容性:适用于任何基于 Flask 的大模型服务部署场景。
- 低成本可落地:完全运行于 CPU 环境,内存占用低于 2GB,适配大多数通用服务器配置。
5.2 实践建议
- 定期归档日志文件:避免长期运行导致磁盘空间耗尽,建议按天切割日志。
- 脱敏敏感信息:若涉及用户隐私输入,应在写入日志前做内容过滤。
- 结合前端埋点:除了服务端日志,也可在 WebUI 中添加用户行为统计,形成端到端分析闭环。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。