fastAPI接口(普通流式响应和大模型流式响应)

1. 流式输出和非流失输出:

大模型的流式输出(Streaming Output)和非流式输出(Non-streaming Output)是指在生成文本或其他输出时,如何将结果返回给用户或下游系统。

流式输出 (Streaming Output):流式输出意味着模型生成的内容是逐步、逐块返回的,而不是等到整个生成过程完成后再一次性返回所有内容。
  优点:
   用户可以更早地开始看到部分结果,提高用户体验。
   对于长时间生成任务,可以减少内存占用,因为不需要一次性存储完整的输出。
   更适合实时应用,如在线聊天机器人,其中即时反馈是非常重要的。
  应用场景:适用于需要即时响应的场景,比如对话系统、直播翻译等。

非流式输出 (Non-streaming Output):非流式输出是指模型在完成整个生成过程后,一次性返回全部生成的结果。
  **优点:
   实现简单,易于处理和调试。
   在某些情况下,可能更适合那些需要对完整输出进行后续处理的应用。
  应用场景:适用于那些不依赖即时反馈、或者需要对整个输出进行整体处理的场景,例如批量文本生成、文档摘要等。

2. 流式输出、非流失输出和vllm的同步、异步关系

对于vllm同步:无论是流式还是非流式输出,vllm的LLM函数创建的模型对象通常以同步的方式工作,处理多并发情况时只能以队列形式一个个输出。对于非流式输出,它会阻塞直到生成完成并返回结果;对于流式输出,它也可以逐步返回数据给前端,但这是假流式,因为后端以及把所有的文本都输出了,然后我们又把文本一个个传给前端。

对vllm异步:异步引擎同样可以支持流式和非流式输出,但它允许你以非阻塞的方式处理这些输出。你可以启动一个生成任务而不等待它完成,然后根据需要逐步获取流式输出,或者在任务完成后一次性获取非流式输出(也是并发状态)。这为高并发环境下的应用提供了更好的性能和灵活性。

总结:流式输出和非流式输出关注的是输出的传输方式,而AsyncLLMEngine和LLM则更多地涉及到执行模式(同步 vs 异步)。两者可以组合使用,例如,你可以使用AsyncLLMEngine来异步地处理流式输出,从而在高并发环境中获得最佳性能和用户体验。

3. fastapi流式响应代码

from fastapi import FastAPI
import uvicorn
import os
import json
import time
from starlette.responses import StreamingResponse
# from fastapi.responses import StreamingResponse


# 创建一个FastAPI应用程序实例
app = FastAPI()

@app.post("/api")
def aaa():
    # StreamingResponse是一个提供的用于包装流式响应的类,必须以json字符串进行数据传递
    # starlette.responses和fastapi.responses中的StreamingResponse对象实现方式基本类似。
    return StreamingResponse(handle_post_request())

def handle_post_request():
    for i in range(5):
        print(i)
        time.sleep(1)
        yield json.dumps({'type': i}) + '\n'


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("API_PORT", 7777)), workers=1)

开启上述api后,使用以下代码发送请求:

import json
import requests

# url_api = "http://10.4.0.141:800h1/cat/stream"
url_api = "http://localhost:7777/api"

# 此调用方法为推荐方法。也可直接request.post发送请求,见https://editor.csdn.net/md/?articleId=144560081
with requests.post(url_api, stream=True) as r:
    r.raise_for_status()  # 检查请求是否成功
    print(r.iter_lines())
    for line in r.iter_lines():
        if line:  # 过滤掉保持连接的空行
            print(json.loads(line.decode('utf-8')))

4. LLM流式输出代码(非接口型,仅后端)

vllm的chat和generate的具体区别可以看:https://docs.vllm.ai/en/latest/models/generative_models.html官方文档,里面解释的很详细

普通后端的流式输出使用TextStreamer(是同步的,不适合做api的流式响应)

from modelscope import AutoModelForCausalLM, AutoTokenizer
from transformers import TextStreamer
model_name = "地址"

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",
    device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)

prompt = "你是谁"
messages = [
    {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
    {"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)

# 非流输出
# generated_ids = model.generate(
#     **model_inputs,
#     max_new_tokens=1024
# )

# generated_ids = [
#     output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
# ]


# response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
# print(response)

# 流输出,普通后端的流式输出使用TextStreamer,这是同步的,不适合于api交互做流式响应,必须使用TextIteratorStreamer,它是异步的
streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
generated_ids = model.generate(
    **model_inputs,
    max_new_tokens=512,
    streamer=streamer,
)

5. LLM流式响应代码(接口型FastAPI)

API的流式输出使用TextIteratorStreamer(是异步的,适合做api的流式响应)

from fastapi import FastAPI
import uvicorn
import os
import json
from starlette.responses import StreamingResponse
# from fastapi.responses import StreamingResponse
from transformers import TextIteratorStreamer
from threading import Thread
from modelscope import AutoModelForCausalLM, AutoTokenizer

# 创建一个FastAPI应用程序实例
app = FastAPI()

model_name = "地址"

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",
    device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)

prompt = "写一篇800字的作文"
messages = [
    {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
    {"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)

@app.post("/api")
def aaa():
    # StreamingResponse包装的是可迭代对象
    return StreamingResponse(handle_post_request())

def handle_post_request():
    # TextIteratorStreamer为异步。skip_prompt=True, skip_special_tokens=True可以去除输出中的|im_start|等标记
    streamer = TextIteratorStreamer(tokenizer, timeout=60.0, skip_prompt=True, skip_special_tokens=True)
    generation_kwargs = {
        "max_new_tokens": 1024,  # 或者任何其他生成参数
        "streamer": streamer,
    }
    thread = Thread(target=model.generate, kwargs={**model_inputs, **generation_kwargs})
    thread.start()
    
    answer = ''
    for new_text in streamer:
        answer += new_text
        print(answer)
        yield json.dumps({'content': answer}) + '\n'

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("API_PORT", 7777)), workers=1)

前端的调用为

import json
import requests

# url_api = "http://10.4.0.141:800h1/cat/stream"
url_api = "http://localhost:7777/api"


with requests.post(url_api, stream=True) as r:
    r.raise_for_status()  # 检查请求是否成功
    print(r.iter_lines())
    for line in r.iter_lines():
        if line:  # 过滤掉保持连接的空行
            print(json.loads(line.decode('utf-8')))

6. 注意

非流式响应:fastapi之间的通信一般都是json对象的形式,发送请求时请求体为{},后端返回请求时的响应体也为{}。

流式响应:fastapi之间的通信一般都是json对象字符串格式的形式。使用json.dumps()将 str或者python对象(basemodel或者dict)转为json字符串格式。且流式响应一定要使用StreamingResponse对象包装

### 如何在 Flask 或 FastAPI 中实现大模型流式响应 #### 使用 FastAPI 实现流式响应 FastAPI 提供了强大的支持用于构建高效、异步的应用程序接口,非常适合处理复杂的多模态数据请求。为了实现实时的数据传输提高用户体验,在处理大型模型推断结果时采用流式响应是一种有效的方法。 当涉及到大模型推理时,由于计算量庞大可能导致长时间等待,因此可以考虑分批次返回部分结果给客户端,而不是等到整个过程结束才一次性给出答复。这不仅提升了用户的即时感知度,也减轻了内存压力[^1]。 下面是一个简单的例子来说明如何利用 Python 的 `async`/`await` 特性 FastAPI 来创建一个流式的 API: ```python from fastapi import FastAPI, Request import asyncio app = FastAPI() @app.get("/streaming_inference/") async def streaming_inference(request: Request): async def event_generator(): for i in range(5): # 假设这是五次逐步得到的结果片段 await asyncio.sleep(1) # 模拟耗时操作 yield f"data:{i}\n\n" if await request.is_disconnected(): # 如果连接被中断则停止生成器 break return EventSourceResponse(event_generator()) ``` 这段代码展示了怎样定义一个 GET 请求处理器 `/streaming_inference/` ,它会每隔一秒向客户端推送一条消息直到完成全部五个阶段的消息发送或是检测到客户端已断开连接为止。这里使用到了 SSE (Server-Sent Events),这是一种允许服务器主动向浏览器或其他 HTTP 客户端推送更新的技术[^4]。 对于更复杂的情况比如图像识别或自然语言处理任务,则可以根据具体需求调整上述逻辑中的模拟延迟部分为实际的大规模预训练模型调用,并确保这些调用是非阻塞的方执行以便维持良好的性能表现。 #### 使用 Flask 实现流式响应 虽然 Flask 不像 FastAPI 那样原生地强调异步特性,但它仍然可以通过一些技巧达到相似的效果。例如,借助于第三方库如 gevent 或者直接运用 Werkzeug 自带的支持来进行长期运行的任务处理并实时反馈进度给用户。 以下是基于 Flask 构建的一个简单示例,该实例同样实现了类似于前面提到的功能——即周期性地向客户端传递信息直至所有步骤完成: ```python from flask import Flask, Response import time app = Flask(__name__) def generate_data(): for i in range(5): time.sleep(1) yield 'data: {}\n\n'.format(i) @app.route('/stream') def stream(): return Response(generate_data(), mimetype='text/event-stream') if __name__ == '__main__': app.run(debug=True, threaded=True) ``` 此段脚本里我们定义了一个名为 `/stream` 的路由地址,访问这个 URL 后将会启动一个持续性的事件源通信通道,每秒钟发出一次新的数值作为回应的一部分内容。需要注意的是为了让这种模正常运作起来,必须保证 Web 服务器配置正确并且能够处理长轮询类型的请求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值