Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

访问api返回”Role must be user or assistant and Content length must be greater than 0“ #11

Open
ChenXu233 opened this issue Jun 16, 2024 · 6 comments

Comments

@ChenXu233
Copy link

ChenXu233 commented Jun 16, 2024

问题

经过一段时间的聊天,Bot 开始回复消息”有错误,自己看日志,可能过期了“。
观察日志发现:

  • 06-16 17:45:41 [CRITICAL] nonebot_plugin_with_ai_agents | {"code":"InvalidParameter","message":"Role must be user or assistant and Content length must be greater than 0","request_id":"3719f5d1-4de0-9998-b7fe-f34226b4611a"}'output'

插件版本

0.1.14

Nonebot版本

2.3.0

python 版本

3.12.3

复现过程

只要启动 Bot ,聊一段时间就会出现这样的问题。

日志上下文:

截图

image

文字

06-16 17:52:10 [SUCCESS] nonebot | OneBot V11 278133507 | [message.group.normal]: Message -2147483089 from 1964324406@[ 群:795946478] '[at:qq=278133507] 你好'
06-16 17:52:10 [DEBUG] nonebot | Checking for matchers in priority 1...
06-16 17:52:10 [DEBUG] nonebot | Checking for matchers in priority 11...
06-16 17:52:10 [DEBUG] nonebot | Checking for matchers in priority 12...
06-16 17:52:11 [DEBUG] nonebot | Checking for matchers in priority 13...
06-16 17:52:11 [DEBUG] nonebot | Checking for matchers in priority 999...
06-16 17:52:11 [INFO] nonebot | Event will be handled by Matcher(type='message', module=nonebot_plugin_with_ai_agents.handler, lineno=9)
06-16 17:52:11 [DEBUG] nonebot | Running Matcher(type='message', module=nonebot_plugin_with_ai_agents.handler, lineno=9)
06-16 17:52:11 [DEBUG] nonebot | Running handler Dependent(call=agent_handler)
06-16 17:52:12 [INFO] nonebot_plugin_with_ai_agents | 智脑分类结果:[6]
06-16 17:52:12 [INFO] nonebot_plugin_with_ai_agents | assemble_prompt
06-16 17:52:13 [CRITICAL] nonebot_plugin_with_ai_agents | {"code":"InvalidParameter","message":"Role must be user or assistant and Content length must be greater than 0","request_id":"eb5256d2-08c3-9b7e-8361-09796f4d3f32"}'output'
06-16 17:52:13 [DEBUG] nonebot | OneBot V11 | Calling API send_msg
06-16 17:52:13 [INFO] nonebot | Matcher(type='message', module=nonebot_plugin_with_ai_agents.handler, lineno=9) running complete
06-16 17:52:13 [DEBUG] nonebot | Stop event propagation
06-16 17:52:13 [DEBUG] nonebot | Checking for matchers completed

排查

已确定不是由网络问题引发的

async with httpx.AsyncClient() as client:
try:
r = await client.post(url, headers=headers, json=body, timeout=self.timeout)
ans = r.json()["output"]["text"]
except httpx.ReadTimeout as e:
logger.critical(f"访问大模型超时, {e}")
ans = "访问大模型超时"
except Exception as e:
logger.critical(r.text + str(e))
ans = "有错误,自己看日志,可能过期了"
return ans

通过观察源码发现这个错误是直接通过错误基类被捕获的,然后以 str 的形式与 r.text 拼接,在这里这个异常的str是'output',然后 bot 回复 ”有错误,自己看日志,可能过期了“。这是不是有点太草率了。

  • ( 我认为应该是日志输出内容,然后 bot 回复(或许可以加加一个额外选项让不让 bot 在错误的情况下回复和自定义回复),再后面再用 raise e 引发异常,因为都报出超出预期的异常了,那么最好还是再引发异常比较好,这样还能有调用栈打印出来)

把 body 的内容打印出来以后,发现 role 的内容也是正常的。

@ChenXu233
Copy link
Author

深入阅读代码后我还有个疑问:

async with httpx.AsyncClient() as client:
try:
r = await client.post(url, headers=headers, json=body, timeout=self.timeout)
ans = r.json()["output"]["text"]
except httpx.ReadTimeout as e:
logger.critical(f"访问大模型超时, {e}")
ans = "访问大模型超时"
except Exception as e:
logger.critical(r.text + str(e))
ans = "有错误,自己看日志,可能过期了"
return ans

async with httpx.AsyncClient() as client:
try:
r = await client.post(url, headers=headers, json=body, timeout=self.timeout)
ans = r.json()["choices"][0]["message"]["content"]
except httpx.ReadTimeout as e:
logger.critical(f"访问大模型超时, {e}")
ans = "访问大模型超时"
except Exception as e:
logger.critical(r.text + str(e))
ans = "有错误,自己看日志,可能过期了"
return ans

这俩段代码相似度很高,可以考虑一下整一个函数出来进行代码复用?
比如可以这样写

async def api_request(url, headers, body, timeout):
    async with httpx.AsyncClient() as client: 
        try: 
            result = await client.post(url, headers=headers, json=body, timeout=timeout) 
        except httpx.ReadTimeout as e: 
            logger.critical(f"访问大模型超时, {e}")
            result = None
        except Exception as e: 
            logger.critical(r.text + str(e)) 
            raise e
    return result
    
class GLMModel(BaseLLMModel):
    ...
    async def ask_model(
            self,
            question: str,
            system_prompt: str = None,
            message_history: list = (),
            temperature: float = 0.01
    ):
        """向 ChatGLM 提问
         - question:用户问题
         - system_prompt:系统级提示词
         - message_history: 消息历史列表
        """
        url = self.get_api_url()
        headers = self.get_headers()
        body = self.get_body_template(temperature)

        if message_history:
            body["messages"] = message_history

        if system_prompt:
            sys_msg = {"role": "system", "content": system_prompt}
            body["messages"].insert(0, sys_msg)

        user_message = {"role": "user", "content": question}
        body["messages"].append(user_message)

        if result := api_request(url, headers, body, self.timeout):
            #这下面可以依据不同的更改不同的逻辑
            ans = result.json()["choices"][0]["message"]["content"]
        return ans

没有代码复用,这里出现了一个奇怪的try:

async with httpx.AsyncClient() as client:
r = await client.post(url, headers=headers, json=body, timeout=self.timeout)
try:
ans = r.json()["choices"][0]["message"]["content"]
except httpx.ReadTimeout as e:
logger.critical(f"访问大模型超时, {e}")
ans = "访问大模型超时"
except Exception as e:
logger.critical(r.text + str(e))
ans = "有错误,自己看日志"
return ans

把访问大模型放在代码放在 try 外面,可能不是你的本意

@ChenXu233
Copy link
Author

想给你发一个 pr 了(跃跃欲试

@yejue
Copy link
Owner

yejue commented Jun 17, 2024

  1. 关于这个问题本身,有可能是历史聊天记录自动拾取的时候和新记录添加的时候出现的一些问题,目前还没细看
  2. 关于代码复用,在此前的版本中,各个平台的代码是直接复制的,因为早期无法预测不同平台间的兼容性,故初始版本不做太多的复用,留存空间
  3. try except httpx.ReadTimeout as e: 原本是要 try 访问大模型的,忘了什么原因又移出去了,try 的内容没有更改;
  4. 至于 "有错误,自己看日志,可能过期了" 也是初始版本中无法预想所有的错误,需要一个全局的 try 来保证全局不会被打断

@yejue
Copy link
Owner

yejue commented Jun 17, 2024

  1. 关于这个问题本身,有可能是历史聊天记录自动拾取的时候和新记录添加的时候出现的一些问题,目前还没细看
  2. 关于代码复用,在此前的版本中,各个平台的代码是直接复制的,因为早期无法预测不同平台间的兼容性,故初始版本不做太多的复用,留存空间
  3. try except httpx.ReadTimeout as e: 原本是要 try 访问大模型的,忘了什么原因又移出去了,try 的内容没有更改;
  4. 至于 "有错误,自己看日志,可能过期了" 也是初始版本中无法预想所有的错误,需要一个全局的 try 来保证全局不会被打断

其实说白了就是有许多问题都是临时解决方案,从这个插件发布第一版的时候就有了,细节的东西比较多需要考虑的,一口气写不过来。所以有些东西会尽量写的简单,为以后的修改留有余地。

@ChenXu233
Copy link
Author

  1. 关于这个问题本身,有可能是历史聊天记录自动拾取的时候和新记录添加的时候出现的一些问题,目前还没细看
  2. 关于代码复用,在此前的版本中,各个平台的代码是直接复制的,因为早期无法预测不同平台间的兼容性,故初始版本不做太多的复用,留存空间
  3. try except httpx.ReadTimeout as e: 原本是要 try 访问大模型的,忘了什么原因又移出去了,try 的内容没有更改;
  4. 至于 "有错误,自己看日志,可能过期了" 也是初始版本中无法预想所有的错误,需要一个全局的 try 来保证全局不会被打断

但是还是觉得直接回复“有错误,自己看日志,可能过期了“有些不太妥,用 Bot 的不止 Bot 的主人,非开发者看见了可能一脸懵。其实只要说明出错了可以了,比如说明”出现错误,请稍后在试或者联系 Bot 主人。“,希望后面能改改,自定义一下。

@yejue
Copy link
Owner

yejue commented Jun 18, 2024

这里的问题已经有部分得到解决,详见 0.1.15;标题中的主要的问题待解决

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants