QwenLM / Qwen-Agent

Agent framework and applications built upon Qwen>=2.0, featuring Function Calling, Code Interpreter, RAG, and Chrome extension.
https://pypi.org/project/qwen-agent/
Other
3.24k stars 316 forks source link

from qwen_agent.llm import get_chat_model多次请求无法召回 #254

Open 812781385 opened 3 months ago

812781385 commented 3 months ago

我使用from qwen_agent.llm import get_chat_model时候,调用模型,如果messages里有历史,则无法命中functioncall,这是为啥? 使用如下:

同样的数据发送会频繁出现不同的结果:

代码片段: client = get_chat_model({ 'model': 'qwen:32b', 'model_server': 'http://127.0.0.1:11434/v1' }) messages = [ {"role":"system","content":"### 你好!有什么我可以帮助你的吗?"}, {"role":"user","content":"帮我查下一下这首船的信息:477795600"}, {"role":"assistant","content":"这艘船的信息如下:\n\n- 船舶ID: 477795600\n- MMSI 号码: 477795600\n- IMO 编号: 9620293\n\n请注意,这些数据可能会有延迟。如果有其他具体问题或者需要更多的帮助,请随时告诉我。"}, {"role":"user","content":"帮我查下一下这首船的信息:227745780"} ]

res_stream = [] for res_stream in client.chat( messages=messages, functions=tools, stream=True ): print(res_stream)

第一次结果: [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get', 'arguments': ''}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"m'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mms'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi":'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "2'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "22'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "227'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "2277'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "22774'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "227745'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "2277457'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "22774578'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "227745780'}}] [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "227745780"}'}}]

第二次结果: [{'role': 'assistant', 'content': '我'}] [{'role': 'assistant', 'content': '我需要'}] [{'role': 'assistant', 'content': '我需要更多信息'}] [{'role': 'assistant', 'content': '我需要更多信息才能'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如M'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMS'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、IMO'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、IMO编号'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、IMO编号或者'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、IMO编号或者具体的'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、IMO编号或者具体的船'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、IMO编号或者具体的船名'}] [{'role': 'assistant', 'content': '我需要更多信息才能查询船舶的详细资料,如MMSI号码、IMO编号或者具体的船名。'}]

JianxinMa commented 3 months ago

这是因为受到对话历史的干扰了,您现在给的历史是:

messages = [
{"role":"system","content":"### 你好!有什么我可以帮助你的吗?"},
{"role":"user","content":"帮我查下一下这首船的信息:477795600"},
{"role":"assistant","content":"这艘船的信息如下:\n\n- 船舶ID: 477795600\n- MMSI 号码: 477795600\n- IMO 编号: 9620293\n\n请注意,这些数据可能会有延迟。如果有其他具体问题或者需要更多的帮助,请随时告诉我。"},
{"role":"user","content":"帮我查下一下这首船的信息:227745780"}
]

这个历史的里的用户问题是明显需要调用工具的,但是历史却没有function call的messages,直接就给了Assistant的回复。这会让模型认为“历史里我就是不调用工具回答的,那我回答新问题自然也不用调用工具”。

解决方法一:确保历史里该调用工具的历史都有对应的function call messages,比如

messages = [
{"role":"system","content":"### 你好!有什么我可以帮助你的吗?"},
{"role":"user","content":"帮我查下一下这首船的信息:477795600"},
{"role":"assistant","content": "", "function_call": ...},  ##  工具调用!!!
{"role":"function","content": ...},   ## 工具结果!!!
{"role":"assistant","content":"这艘船的信息如下:\n\n- 船舶ID: 477795600\n- MMSI 号码: 477795600\n- IMO 编号: 9620293\n\n请注意,这些数据可能会有延迟。如果有其他具体问题或者需要更多的帮助,请随时告诉我。"},
{"role":"user","content":"帮我查下一下这首船的信息:227745780"}
]

解决方法二(不一定稳定):使用function_choice(又名tool_choice)功能向模型强调要调用工具:https://github.com/QwenLM/Qwen-Agent/blob/main/examples/function_calling.py#L76

812781385 commented 3 months ago

这是因为受到对话历史的干扰了,您现在给的历史是:

messages = [
{"role":"system","content":"### 你好!有什么我可以帮助你的吗?"},
{"role":"user","content":"帮我查下一下这首船的信息:477795600"},
{"role":"assistant","content":"这艘船的信息如下:\n\n- 船舶ID: 477795600\n- MMSI 号码: 477795600\n- IMO 编号: 9620293\n\n请注意,这些数据可能会有延迟。如果有其他具体问题或者需要更多的帮助,请随时告诉我。"},
{"role":"user","content":"帮我查下一下这首船的信息:227745780"}
]

这个历史里的用户问题在于,历史其实并没有提供任何功能调用,直接就给了我们模型的想法。这让模型认为“历史里我就是不会自动回答的,那我回答新问题自然也就不用调用了”。

解决方法一:确保该历史调用函数都可用,比如

messages = [
{"role":"system","content":"### 你好!有什么我可以帮助你的吗?"},
{"role":"user","content":"帮我查下一下这首船的信息:477795600"},
{"role":"assistant","content": "", "function_call": ...},  ##  工具调用!!!
{"role":"function","content": ...},   ## 工具结果!!!
{"role":"assistant","content":"这艘船的信息如下:\n\n- 船舶ID: 477795600\n- MMSI 号码: 477795600\n- IMO 编号: 9620293\n\n请注意,这些数据可能会有延迟。如果有其他具体问题或者需要更多的帮助,请随时告诉我。"},
{"role":"user","content":"帮我查下一下这首船的信息:227745780"}
]

解决方法二(不一定稳定):使用function_choice(又名tool_choice)功能向模型强调要调用的工具:https://github.com/QwenLM/Qwen-Agent/blob/main/examples/function_calling.py#L76

你好,我把多轮记录组合进去。当拿到接口数据后再次调用模型的时候,他又返回function_call。

我的代码:

from qwen_agent.llm import get_chat_model
import requests, json
import os

client = get_chat_model({
    'model': 'qwen:32b',
    'model_server': 'http://127.0.0.1:11434/v1',
    'generate_cfg': {
        'top_p': 0.5
    }
})

def get_ship(args):
  return str({'ShipID': 636016918, 'mmsi': 636016918, 'imo': 9254513, 'nationality': '利比里亚', 'name': 'BAO LAI', 'callsign': 'D5IJ8', 'shiptype': 70, 'length': 1900, 'width': 320, 'left': 190, 'trail': 280, 'draught': 9700, 'dest': 'LIANYUNGANG,CN', 'dest_std': '', 'destcode': '', 'eta': '07-01 19:00', 'eta_std': '2024-07-01 19:00:00', 'navistat': 0, 'lon': 119393350, 'lat': 34748566, 'hdg': 28300, 'cog': 15250, 'sog': 0, 'rot': 0, 'lasttime': 1720402545})

tools = [
    {
      "name": "get_ship",
      "description": "获取船舶详情信息",
      "parameters": {
        "type": "object",
        "properties": {
          "mmsi": {
            "type": "string",
            "description": "船舶mmsi, " "例如: 13324313",
          }
        },
        "required": ["mmsi"],
      },
    },
]

messages = [
  {'role': 'system', 'content': '### 你好!有什么我可以帮助你的吗?\n 作为你的智能伙伴,我既能写文案、想点子,又能陪你聊天、答疑解惑。'}, 
  {'role': 'user', 'content': '帮我查下一下这首船的信息:477795600'}, 
  {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "477795600"}'}}, 
  {'role': 'function', 'name': 'get_ship', 'content': "{'ShipID': 477795600, 'mmsi': 477795600, 'imo': 9620293, 'nationality': '中国香港', 'name': '坤燕', 'callsign': 'VRIS5', 'shiptype': 70, 'length': 1120, 'width': 190, 'left': 140, 'trail': 150, 'draught': 5000, 'dest': 'MY TMI', 'dest_std': '', 'destcode': '', 'eta': '07-05 20:00', 'eta_std': '2024-07-05 20:00:00', 'navistat': 1, 'lon': 111338333, 'lat': 2115000, 'hdg': None, 'cog': 30000, 'sog': 0, 'rot': 0, 'lasttime': 1720402664}"}, {'role': 'assistant', 'content': '船名:坤燕(英文名称:VRIS5)\n国籍:中国香港\nIMO号:9620293\n类型:70型(这通常表示是货船,但具体船型可能需要参照更详细的分类标准)\n长度:112米\n宽度:19米\n吃水深度:5米\n当前目的地:MY TMI(这可能是马来西亚的 Tanjung Pelepas 代码,但建议进一步确认)\n预计到达时间:07-05 20:00(这个日期格式是月/日/时,这里可能是指2024年7月5日20点)\n\n船位信息:\n- 最新经纬度:经度11.564814°E,纬度21.25°N\n- 航向(hdg):没有记录\n- 真航速(cog):0节\n- 对地航速(sog):0节\n- 旋转速率(rot):0°\n- 更新时间:1720402664秒(这个数值是 Unix 时间戳,转换成日期大约是2024年9月13日左右,具体时间需进一步转换)\n\n请注意,这些信息可能会有延迟或不准确,特别是在船舶实时位置和动态方面。如需最准确的信息,请参考官方船位追踪服务或者相关海事部门的更新。'}, 
  {'role': 'user', 'content': '这首呢:351690000'}, 
  {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "351690000"}'}}, 
  {'role': 'function', 'name': 'get_ship', 'content': "{'ShipID': 351690000, 'mmsi': 351690000, 'imo': 0, 'nationality': '巴拿马', 'name': 'PAN FREESIA', 'callsign': 'H3BC', 'shiptype': 70, 'length': 2920, 'width': 450, 'left': 100, 'trail': 500, 'draught': 0, 'dest': 'ZHOUSHAN,CN', 'dest_std': '', 'destcode': '', 'eta': '06-17 03:30', 'eta_std': '2024-06-17 03:30:00', 'navistat': 5, 'lon': 122008878, 'lat': 30158660, 'hdg': 29500, 'cog': 11860, 'sog': 51, 'rot': 0, 'lasttime': 1720402566}"}, 
  {'role': 'assistant', 'content': '船名:PAN FREESIA(呼叫信号:H3BC)\n国籍:巴拿马\nIMO号:未提供\n类型:70型(通常表示货船,但具体船型可能需要参照更详细的分类标准)\n长度:292米\n宽度:45米\n吃水深度:无数据\n当前目的地:ZHOUSHAN-CN(舟山,中国的一个海港城市)\n预计到达时间:06-17 03:30(这个日期格式是月/日/时,这里可能是指2024年6月17日3点30分)\n\n船位信息:\n- 最新经纬度:经度122.015189°E,纬度30.264433°N\n- 航向(hdg):295°\n- 对地航速(cog):约118.6节(考虑到数据的可能误差,这个值可能不准确)\n- 真航速(sog):51节\n- 旋转速率(rot):0°\n- 更新时间:1720402566秒(这个数值是 Unix 时间戳,转换成日期大约是2024年9月13日左右,具体时间需进一步转换)\n\n请注意,这些信息可能会有延迟或不准确,特别是在船舶实时位置和动态方面。如需最准确的信息,请参考官方船位追踪服务或者相关海事部门的更新。'}, 
  {'role': 'user', 'content': '明白了'}, 
  {'role': 'assistant', 'content': '很高兴能够帮到你!如果你有关于其他船只的信息查询,或者需要任何其他帮助,请随时告诉我。祝你有美好的一天!'}, 
  {'role': 'user', 'content': '636016918'}, 
]

res_stream = []
pending_function_call = None
pending_arguments = ""
for res_stream in client.chat(messages=messages, functions=tools, stream=True):

  if 'function_call' in res_stream[0]:
    if pending_function_call is None:
      pending_function_call = res_stream[0]['function_call']['name']
    pending_arguments = res_stream[0]['function_call']['arguments']

    try:
      params = json.loads(pending_arguments)
      print('------->>>>>', params)
      if 'mmsi' in params and params['mmsi'].endswith('"'):
        mmsi = params['mmsi'].replace('"', '')
        print('-- mmsi  --<<', mmsi)

    except json.JSONDecodeError:
      continue

    messages.append(res_stream[0])

    function_name = messages[-1]['function_call']['name']
    function_to_call = get_ship
    function_args = json.loads(messages[-1]['function_call']['arguments'])
    function_response = function_to_call(function_args)
    print('.....接口返回:', function_response)

    messages.append({
      'role': 'function',
      'name': function_name,
      'content': function_response,
    })
    print('......新的messages给模型', messages)
    newres_stream = []
    for newres_stream in client.chat(messages=messages, functions=tools, stream=True):
      print('.....newres_stream', newres_stream)
  else:
    print('........未进入funcitoncal', res_stream)

运行结果: .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get', 'arguments': ''}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': ''}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"m'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mms'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi":'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "6'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "63'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "636'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "6360'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "63601'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "636016'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "6360169'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "63601691'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "636016918'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "636016918"}'}}] .....newres_stream [{'role': 'assistant', 'content': '', 'function_call': {'name': 'get_ship', 'arguments': '{"mmsi": "636016918"}'}}]

JianxinMa commented 3 months ago

这个for循环看起来写得不对。。。

请参考 https://github.com/QwenLM/Qwen-Agent/blob/main/qwen_agent/agents/fncall_agent.py#L46 或者直接使用 FnCallAgent 这类高级的抽象。llm.chat是个偏底层的抽象。

812781385 commented 3 months ago

这个for循环看起来写得不对。。。

请参考 https://github.com/QwenLM/Qwen-Agent/blob/main/qwen_agent/agents/fncall_agent.py#L46 或者直接使用 FnCallAgent 这类高级的抽象。llm.chat是个偏底层的抽象。

我的代码使用的是这个示例,for循环也是一样的啊:https://github.com/QwenLM/Qwen-Agent/blob/main/examples/function_calling.py 如果使用FnCallAgent的话,怎么调用,有示例不?

JianxinMa commented 3 months ago

这个for循环看起来写得不对。。。 请参考 https://github.com/QwenLM/Qwen-Agent/blob/main/qwen_agent/agents/fncall_agent.py#L46 或者直接使用 FnCallAgent 这类高级的抽象。llm.chat是个偏底层的抽象。

我的代码使用的是这个示例,for循环也是一样的啊:https://github.com/QwenLM/Qwen-Agent/blob/main/examples/function_calling.py 如果使用FnCallAgent的话,怎么调用,有示例不?

您的代码,有部分不该放进for循环的代码被放进for循环了。

FnCallAgent 和 Assistant 这个类的用法差不多: https://github.com/QwenLM/Qwen-Agent/blob/main/examples/assistant_add_custom_tool.py 区别是 FnCallAgent 相比 Assistant 少了 RAG 功能(Assistant支持传pdf files而FnCallAgent 不支持。图省事用 Assistant 也可以)

812781385 commented 3 months ago

模型mssages过多的时候,出现这个带花的提示是咋回事啊?:

模型mssages过多的时候,出现这个带花的提示是咋回事啊?:

{
    "role": "assistant",
    "content": ": \n✿FUNCTION✿: get_ship\n✿ARGS✿: {\"mmsi\": \"538008251\"} \n"
  }
JianxinMa commented 3 months ago

模型mssages过多的时候,出现这个带花的提示是咋回事啊?:

模型mssages过多的时候,出现这个带花的提示是咋回事啊?:

{
    "role": "assistant",
    "content": ": \n✿FUNCTION✿: get_ship\n✿ARGS✿: {\"mmsi\": \"538008251\"} \n"
  }

这个正常情况下是应该会被解析成function call的message的(用户看不到花)。有什么脚本可以供我们复现吗

812781385 commented 2 months ago

模型mssages过多的时候,出现这个带花的提示是咋回事啊?:

模型mssages过多的时候,出现这个带花的提示是咋回事啊?:

{
    "role": "assistant",
    "content": ": \n✿FUNCTION✿: get_ship\n✿ARGS✿: {\"mmsi\": \"538008251\"} \n"
  }

这个正常情况下是应该会被解析成function call的message的(用户看不到花)。有什么脚本可以供我们复现吗

就用我上面的脚本,然后多来几轮对话就会复现在。 后面我用了from qwen_agent.agents import Assistant 我用了8轮对话也很不稳定