LC1332 / Chat-Haruhi-Suzumiya

Chat凉宫春日, An open sourced Role-Playing chatbot Cheng Li, Ziang Leng, and others.
Apache License 2.0
1.84k stars 164 forks source link

如何使用流式输出 ? #15

Open KissMyLady opened 1 year ago

KissMyLady commented 1 year ago

一, 推理后一次性返回数据

当前项目下语言模型的调用(适当修改后), 可以做到推理后全量返回.

但是也有些许问题, 使用本地模型时, 会有很多轮对话导致等待时间过长.

# if not "OPENAI_API_KEY" in os.environ.keys():
# # 使用第三方的语言模型
# if JinaChat_Key:
#     # chat = ChatOpenAI(temperature=0,
#     #                   openai_api_key=self.api_key,
#     #                   model_kwargs={"stop": ["\n", "」"]})
#     chat = JinaChat(temperature=0, jinachat_api_key=JinaChat_Key, model_kwargs={"stop": ["\n", "」"]})
# else:
#     # chat = ChatOpenAI(temperature=0, model_kwargs={"stop": ["\n", "」"]})
#     chat = JinaChat(temperature=0, jinachat_api_key=JinaChat_Key, model_kwargs={"stop": ["\n", "」"]})
# return_msg = chat(messages)
# response = return_msg.content + "」"

if glm26b_model is not None:
    chat_prompt = ChatPromptTemplate.from_messages(messages)
    chain = LLMChain(prompt=chat_prompt, 
                     llm=glm26b_model, 
                     verbose=False
                    )
    response = chain({})
    # response, history = glm26b_model.chat(tokenizer, "%s" % messages, history=[])
else:
    response = "未实例化语言模型"

print("")
print("推理返回内容: %s" % response)

一个问题是: 很大概率的出现多轮对话, 台词连续生成

汤师爷:「哎呀,你是张牧之啊。我可认得你,你是那个有名的狗肉将军。」

张牧之:「哼,你这个老狐狸,我还真以为你是个正人君子呢。」

汤师爷:「哎呀,别提了,都是命。认得我这个师爷的人,有几个不是想找上门来的?」

张牧之:「我可不是来找你的,我是来找张(拍拍汤的肩):听说你有个师爷朋友?」

....

汤师爷:「哦,是啊,这可是个厉害的师爷。不过我得提醒你,他是个买官县长,可别上当。」

黄四郎:「上当?你以为他真的是个正人君子?」

汤师爷:「哎,别这么说,你们这些年轻人,真是太天真了。我告诉你,这个师爷背后的靠山可是很厉害的人,你们可得小心点。」

黄四郎:「靠山?他背后的靠山是什么人?」

一个请求, 在ChatGLM2-6B上会有多伦对话输出

二, 尝试使用流式输出

# src_reform/ChatGPT.py
class ChatGPT:

    ...

    def get_response(self, user_message, chat_history_tuple, glm26b_model=None):
        messages = self.get_message(chat_history_tuple=chat_history_tuple,
                                    user_message=user_message)
        chat_prompt = ChatPromptTemplate.from_messages(messages)

        if glm26b_model is None:
            return None

        async def chat_iterator(chat_prompt: object) -> AsyncIterable[str]:
            callback = AsyncIteratorCallbackHandler()
            chain = LLMChain(prompt=chat_prompt,
                             llm=glm26b_model,
                             verbose=False)
            # Begin a task that runs in the background.
            task = asyncio.create_task(
                wrap_done(chain.acall({}),
                          callback.done
                          ),
            )
            async for token in callback.aiter():
                # Use server-sent-events to stream the response
                print("token: %s" % token)
                yield token
            await task

        return StreamingResponse(chat_iterator(chat_prompt), media_type="text/event-stream")

前端接口

@app.post("/stream")
async def chat_stream(request: Request):
    user_message = "你好啊"
    chat_history = []
    character = "凉宫春日"

    input_message = character + ':「' + user_message + '」'

    return chatSystem.get_response(input_message, chat_history, character)

效果: 只能在控制台打印, 无法返回数据到postman 2023-08-14_162350

三, 期望/解决问题

  1. 希望能够增加本地模型的调用

  2. 本地模型能够使用流式输出

LC1332 commented 1 year ago

我觉得这是一个很好的问题,我最近在给这个项目写arxiv和重构成ChatHaruhi2.0的代码,流式输出可以在2.0版本里面考虑去一同实现。

LC1332 commented 1 year ago

你去知乎上加一下我微信吧 https://www.zhihu.com/people/cheng-li-47

KissMyLady commented 1 year ago

好的, 感谢大佬关注

Zhuqln commented 1 year ago
yield f"data: {output}\n\n"

        return StreamingResponse(chat_iterator(), media_type="text/event-stream")

使用postman这类测试流输出,可能需要对输出进行包装

Bald0Wang commented 1 year ago

鲁鲁我再问个相关的,如果是语音后面会考虑流式优化吗?

LC1332 commented 1 year ago

如果开公司可以考虑这些问题 但是llm的流式输出可以先整一个。

KissMyLady commented 1 year ago

实现流式输出

在后续测试中, 能够实现流式输出, 但是是调用的别人家的API实现的, 本地chatGLM模型未实现流式输出.

实现代码

@app.get('/stream')
@app.post('/stream')
async def conversations(request: Request):
    predictGenerator = chat_by_Characterglm_api(prompt=response_raw, dictData=dictData)
    return EventSourceResponse(predictGenerator)

api接口调用

async def chat_by_Characterglm_api(prompt=None, meta=None, dictData=None):
    if meta is None:
        meta = getRoleMeta()
        pass

    logger.info("Characterglm 发起请求, meta: %s" % meta)
    logger.info("Characterglm 发起请求, prompt: %s" % prompt)

    """
    角色聊天 api
    """
    zhipuai.api_key = get_zhipu_api()
    response = zhipuai.model_api.sse_invoke(
        model="characterglm",
        meta=meta,
        prompt=prompt,
        temperature=0.95,
        top_p=0.7,
        incremental=True
    )
    t1 = time.time()
    stringBuilder = ""
    for event in response.events():
        if event.event == "add":
            # print("add: ", event.data)
            stringBuilder += event.data
            yield event.data
        elif event.event == "error":
            print("error: ", event.data)
            stringBuilder += event.data
            yield event.data
        elif event.event == "interrupted":
            print("interrupted: ", event.data)
            stringBuilder += event.data
            yield event.data
        elif event.event == "finish":
            print("finish: ", event.data)
            stringBuilder += event.data
            yield event.data
        else:
            print(event.data)
            stringBuilder += event.data
            yield event.data
    yield stringBuilder + "--finish--"
LC1332 commented 1 year ago

我看有几家的API本身也支持流式输出,等我集成一下BGE,然后把RoleLLM的角色放进来试试看之后再来试一下流式。