0


Qwen-Agent开发课程助手实践

笔者在学了Qwen-Agent相关的内容后,觉得其未来可能会改变人们与信息化系统的交互方式,目前基本是GUI交互方式,这种交互的专业性较强,有一定的学习门槛。特别是当一个信息化系统做的越来越复杂的时候——比如to B的系统,对于用户来讲,使用起来就不太方便,可能只是想要查一个简单的数据呢。那如果是这种场景,能不能直接通过自然语言对话的方式来满足需求呢?

在看了Qwen-Agent的内容和其它一些相关课程后,笔者觉得可以试着先尝试做做,目前可能还不太成熟,但是先跟踪一下技术的前沿发展,保持不掉队。

一、Qwen-Agent自带的天气预报助手

笔者主要看了Qwen-Agent自身提供的天气预报助手assistant_weather_bot,它是一个具有查询天气和画图能力的智能体,分别由查询天气的工具amap_weather和画图工具image_gen来提供相应的能力。

当笔者提问“北京的天气怎么样?”时,它会提取“北京”这个地名,并将其作为location参数传递给工具amap_weather,此工具会将地名转换为地区编码字段adcode,随后调用高德查询天气的相应接口并传参adcode,高德接口返回weather和temperature字段,由工具拼接为自然语言后返回给assistant_weather_bot。

接下来再将上述返回的信息,作为参数传递给画图工具image_gen,由该工具生成图片,并将链接地址返回。

以上实现逻辑比较简单,源码可在github上下载——Qwen-Agent源码下载。

二、自定义开发课程助手(智能体)

笔者首先对课程助手进行定义,指定本地的大模型配置信息,定义system_prompt和调用的工具等。详细代码如下:

  1. import os
  2. from qwen_agent.agents import Assistant
  3. def init_agent_service():
  4. llm_cfg = {
  5. # 读者需要将以下代码修改为自己的本地配置
  6. 'model': '/opt/models/Qwen2-7B-Instruct',
  7. 'model_server': 'http://192.168.70.190:8000/v1', # api_base
  8. 'api_key': 'EMPTY',
  9. }
  10. system = '你叫MOMO,是AI课堂的助手。你只回答跟AI大模型有关的问题,不相关的问题不要回答。每次回答问题前,你要拆解问题并输出每一步的思考过程。'
  11. bot = Assistant(
  12. llm=llm_cfg,
  13. name='AI课堂助手',
  14. description='你是AI课堂的助手,对学生的提问进行解答。',
  15. system_message=system,
  16. function_list=["course_tool"],
  17. )
  18. return bot

读者在执行此代码前,注意先安装qwen_agent工具包。

从上述代码可以看到,笔者定义了一个course_tool的工具,当用户进行提问时,它会去从course_tool中寻找答案。以下来看看笔者的助手定义。

  1. import os
  2. from typing import Dict, Optional, Union
  3. from database import MySQLConnectionPool
  4. from qwen_agent.tools.base import BaseTool, register_tool
  5. import json
  6. from decimal import Decimal
  7. from datetime import timedelta, date
  8. @register_tool('course_tool')
  9. class CourseTool(BaseTool):
  10. description = '课程助手'
  11. parameters = [{
  12. 'name': 'sql_text',
  13. 'type': 'string',
  14. 'description': "SQL查询语句提取信息,来回答用户的问题。"
  15. "SQL语句需要基于以下数据库模式来编写:"
  16. "CREATE TABLE Courses ("
  17. "id INT AUTO_INCREMENT PRIMARY KEY,"
  18. "course_date DATE NOT NULL,"
  19. "start_time TIME NOT NULL,"
  20. "end_time TIME NOT NULL,"
  21. "course_name VARCHAR(255) NOT NULL,"
  22. "instructor_id VARCHAR(255) NOT NULL COMMENT '指导老师id') COMMENT = '课程表';"
  23. "CREATE TABLE Instructor ("
  24. "instructor_id int(11) NOT NULL COMMENT '指导老师id',"
  25. "instructor_name varchar(255) COMMENT '指导老师姓名',"
  26. ") COMMENT '指导老师信息表';"
  27. "Courses表与Instructor表通过字段instructor_id来关联;"
  28. "用户不会直接输入id值,所以不要直接用id值来查询;"
  29. "该查询应该以纯文本形式返回,而不是JSON格式。"
  30. "该查询只能包含MySQL支持的语法。",
  31. 'required': True
  32. }]

笔者定义了两张表,一张是课程表Courses,一张是指导老师信息表Instructor,并在数据库中提前创建并录入了数据(见下图中的数据)。两张表之间通过字段instructor_id来关联,笔者将这两张表的schema作为参数sql_text的描述知识写入供大模型进行学习。

在这样完成定义后,用户提的问题将被转换为Mysql语法的sql语句传递给CourseTool类的call方法,以下是call方法的实现。

  1. def call(self, params: Union[str, dict], **kwargs) -> str:
  2. params = self._verify_json_format_args(params)
  3. # 创建数据库连接
  4. conn = MySQLConnectionPool()
  5. records = conn.select_all(params['sql_text'], None)
  6. for record in records:
  7. for key, value in record.items():
  8. if isinstance(value, Decimal):
  9. record[key] = str(value)
  10. elif isinstance(value, timedelta):
  11. # 将 timedelta 对象转换为总秒数的浮点数
  12. record[key] = value.total_seconds()
  13. elif isinstance(value, date):
  14. record[key] = value.isoformat()
  15. return records

call方法的实现比较简单,主要逻辑就是将转换出来的sql语句执行,并返回执行结果,另外对无法转换为json字符串的数据提前进行格式转换。

三、自然语言交互式问答体验

agent和tool定义好后,就可以来体验自然语言交互式问答效果了。譬如笔者提了如下一个“李四老师比张三老师多上几堂课”,正确答案应该是5堂课。

  1. def test(query='李四老师比张三老师多上几堂课'):
  2. # Define the agent
  3. bot = init_agent_service()
  4. # Chat
  5. messages = [{'role': 'user', 'content': query}]
  6. for response in bot.run(messages):
  7. print('bot response:', response)
  8. if __name__ == '__main__':
  9. test()

以下是课程助手输出的内容,为便于查阅,笔者将其转换为标准json格式:

  1. [
  2. {
  3. 'role': 'assistant',
  4. 'content': '要回答这个问题,我们需要从数据库中获取李四老师(instructor_name为李四)和张三老师(instructor_name为张三)各自上课的课程数量。我们可以采取以下步骤:\n\n1.查询李四老师的所有课程。\n2.查询张三老师的所有课程。\n3.计算李四老师和张三老师上课的数量差值。\n\n首先,我们从`Instructor`表中通过`instructor_name`来关联获取`Courses`表中的数据。让我们进行这些查询。',
  5. 'name': 'AI课堂助手'
  6. },
  7. {
  8. 'role': 'assistant',
  9. 'content': '',
  10. 'name': 'AI课堂助手',
  11. 'function_call': {
  12. 'name': 'course_tool',
  13. 'arguments': '{
  14. "sql_text": "SELECT COUNT(Courses.course_name) as course_count FROM Courses INNER JOIN Instructor ON Courses.instructor_id = Instructor.instructor_id WHERE Instructor.instructor_name = \'李四\';"
  15. }'
  16. }
  17. },
  18. {
  19. 'role': 'function',
  20. 'content': '[
  21. \n{
  22. \n"course_count": 9\n
  23. }\n
  24. ]',
  25. 'name': 'course_tool'
  26. },
  27. {
  28. 'role': 'assistant',
  29. 'content': '',
  30. 'name': 'AI课堂助手',
  31. 'function_call': {
  32. 'name': 'course_tool',
  33. 'arguments': '{
  34. "sql_text": "SELECT COUNT(Courses.course_name) as course_count FROM Courses INNER JOIN Instructor ON Courses.instructor_id = Instructor.instructor_id WHERE Instructor.instructor_name = \'张三\';"
  35. }'
  36. }
  37. },
  38. {
  39. 'role': 'function',
  40. 'content': '[
  41. \n{
  42. \n"course_count": 4\n
  43. }\n
  44. ]',
  45. 'name': 'course_tool'
  46. },
  47. {
  48. 'role': 'assistant',
  49. 'content': '根据查询结果,李四老师上了9堂课,而张三老师上了4堂课。因此,李四老师比张三老师多上了\\(9-4=5\\)堂课。',
  50. 'name': 'AI课堂助手'
  51. }
  52. ]

以上可以清晰看到智能体的思考过程以及解决思路,非常符合开发人员的习惯,最重要的是,结果也是对的。

当然,同样的代码,如果笔者多运行几次,发现可靠性并不是非常高,偶尔也还是会出现转换成的sql语句中的表不存在或者解决的思路有一些小错误等。笔者测试过,如果是单表查询,准确率会更高一些,读者可以先尝试单表,再尝试多表。

期待Qwen团队把这块的稳定性提高,当然,也可能是笔者水平有限,对Qwen-Agent的开发储备知识不足,已有更好方式来实现此类的需求,若有读者有更好实现方式,欢迎留言。


本文转载自: https://blog.csdn.net/smalllongonline/article/details/141166145
版权归原作者 懂点投资的码农 所有, 如有侵权,请联系我们删除。

“Qwen-Agent开发课程助手实践”的评论:

还没有评论