使用 flask + qwen 实现 txt2sql 流式输出

前言

一般的大模型提供的 api 都是在提问之后过很久才会返回对话内容,可能要耗时在 3 秒以上了,如果是复杂的问题,大模型在理解和推理的耗时会更长,这种展示结果的方式对于用户体验是很差的。

其实大模型也是可以进行流式输出,也就是像 chatgpt 一个字一个字往出崩,这样用户可以一直追踪输出的内容,而不是枯燥的没有止境的等待,本文以我的 txt2sql 实际项目为例,简单介绍使用通义千问 api + flask 框架搭建出一个可以流式输出结果的服务。

txt2sql 任务

我的 txt2sql 任务是基于我的业务数据库内容,用户会提出相关的业务问题,我会让大模型在理解数据库内容的情况下,输出对于问题的理解和思考过程,并最终返回正确的 sql 。

准备

  • 需要阿里云 开通DashScope并创建API-KEY,正常情况下通义千问系列的每个大模型有 100 万的免费 token 可以白嫖。
  • 在实际 python 开发的时候需要安装通义千问特有的库 DashScope ,并将 api-key 设置为 环境变量
  • 熟悉通义千问的流式输出模式
  • 需要安装并熟悉 flask 框架

服务

这里的代码虽然很长,但是内容不多,这里需要关心的点有以下几个:

  1. flask 的路由函数 getAnwser 正常写即可,但是最后的返回为了支持流输出,需要另外封装定义一个函数 getStream,并在 getAnwser 最后使用下面方式调用 getStream 进行流式输出:
ini 复制代码
Response(stream_with_context(getStream()), content_type='text/event-stream')
  1. 很多关于大模型的 tools 回调、 rag 框架细节、prompt 模板都被我封装了,剩下的就是使用 get_llm_prompt 获取最终的 prompt ,然后喂给通义千问最强模型 qwen-max-longcontext,设置到参数 stream=True 和 incremental_output=True,让通义千问进行流式输出,将获得的 responses 结果进行处理即可,结果要用 yield 生成输出流数据。
  2. 其他的代码是日志管理和异常处理。
python 复制代码
import logging
from http import HTTPStatus

import dashscope
from flask import request, Flask, Response, stream_with_context
from config import config
from llm import MyCustomLLM
from tools_imp import get_llm_prompt
from my_util import get_question_sql

app = Flask(__name__)
model = MyCustomLLM(config.DB_HOST, config.DB_PORT, config.DB_NAME, config.DB_USER, config.DB_PASS)
logging.basicConfig(level=logging.INFO, encoding="utf-8",
                    filename=config.LOG_PATH, filemode='a',
                    format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s')
question_sql = get_question_sql()

@app.route('/getAnwser', methods=["POST"])
def getAnwser():
    def getStream():
        data = request.get_json()
        if 'question' not in data or not data['question']:
            yield "无法理解,请重新输入问题"
        question = data['question']
        try:
            prompt = get_llm_prompt(model, question, question_sql)
            dashscope.api_key = config.API_KEY
            llm_response = ""
            responses = dashscope.Generation.call(model="qwen-max-longcontext", messages=prompt, result_format='message', stream=True, incremental_output=True )
            r = None
            for r in responses:
                if r.status_code == HTTPStatus.OK:
                    info = r['output']['choices'][0]['message']['content']
                    llm_response += info
                    yield info
                else:
                    raise Exception("大模型执行报错")
            logging.info(f"llm_response: {llm_response}")
            logging.info(f"input_tokens: {r['usage']['input_tokens']}, output_tokens: {r['usage']['output_tokens']}")
        except BaseException as e:
            logging.error(f'question:{question}, Error: {e}')
            yield f"Error: {str(e)}\n\n".encode()

    return Response(stream_with_context(getStream()), content_type='text/event-stream')



if __name__ == '__main__':
    app.run(config.FLASK_HOST, config.FLASK_PORT, debug=True)

测试

另外写一个访问 post 请求的测试代码,请求我的服务接口,结果会持续地一点一点打印完整。

ini 复制代码
import requests

url = 'http://localhost:9001/getAnwser'
payload = {"question": "沈塘桥地铁站的信息"}
response = requests.post(url, json=payload, stream=True)
if response.status_code == 200:
    try:
        for chunk in response.iter_content(chunk_size=1024):
            if chunk:
                print(chunk.decode('utf-8'), end="")   
    except Exception as e:
        print(f"流处理过程中出现错误: {e}")

控制台中会一点点持续输出以下内容,就是流式输出样式,但是我没法使用 gif 动态展示,只能直接显示最后的整体内容:

sql 复制代码
您的问题是:沈塘桥地铁站的信息

思考过程:
- 用户想了解关于"沈塘桥地铁站"的具体信息。
- 关键点在于定位到名为"沈塘桥"的地铁站,这涉及到模糊匹配站名。
- 需要从dtzpt表中查询,因为该表存储了地铁站点的详细信息。
- 查询时,需确保返回所有字段信息,以便提供完整详情。

```sql
SELECT * FROM dtzpt WHERE name LIKE '%沈塘桥%'```
相关推荐
小鸡吃米…24 分钟前
机器学习 - K - 中心聚类
人工智能·机器学习·聚类
好奇龙猫1 小时前
【AI学习-comfyUI学习-第三十节-第三十一节-FLUX-SD放大工作流+FLUX图生图工作流-各个部分学习】
人工智能·学习
沈浩(种子思维作者)1 小时前
真的能精准医疗吗?癌症能提前发现吗?
人工智能·python·网络安全·健康医疗·量子计算
minhuan1 小时前
大模型应用:大模型越大越好?模型参数量与效果的边际效益分析.51
人工智能·大模型参数评估·边际效益分析·大模型参数选择
Cherry的跨界思维1 小时前
28、AI测试环境搭建与全栈工具实战:从本地到云平台的完整指南
java·人工智能·vue3·ai测试·ai全栈·测试全栈·ai测试全栈
MM_MS1 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
ASF1231415sd2 小时前
【基于YOLOv10n-CSP-PTB的大豆花朵检测与识别系统详解】
人工智能·yolo·目标跟踪
水如烟2 小时前
孤能子视角:“意识“的阶段性回顾,“感质“假说
人工智能
Carl_奕然2 小时前
【数据挖掘】数据挖掘必会技能之:A/B测试
人工智能·python·数据挖掘·数据分析
旅途中的宽~2 小时前
《European Radiology》:2024血管瘤分割—基于MRI T1序列的分割算法
人工智能·计算机视觉·mri·sci一区top·血管瘤·t1