使用coze扣子搭建智能bot「程序员的工具箱」的思考和总结

发布于:2024-04-26 ⋅ 阅读:(19) ⋅ 点赞:(0)

使用coze扣子搭建智能bot「程序员的工具箱」的思考和总结

前记

大模型已经火了快 2 年的时间了,从简单的文字处理的单一场景到到现在的企业迫切需要 LLM 在更多的场景赋能的时代。大众也从简单问答、RAG 知识库、到智能 Agent 的构建有了自己的认识和想法。

如果要构建一个 Agent 就会涉及到一些架构模式:

路由分发架构模式

当用户输入一个 Prompt 查询时,该查询会被发送到路由转发模块,而路由转发模块则扮演着对输入 Prompt 进行分类的角色。

图片

大模型代理架构模式

在任何一个生态系统中,都会有多个针对特定任务领域的专家,并行工作以处理特定类型的查询,然后将这些响应整合在一起,形成一个全面的答案。

图片

基于缓存的微调架构模式

我们将缓存和微调引入到大模型应用架构中,可以解决成本高、推理速度慢以及幻觉等组合问题。

图片

面向目标的 Agent 架构模式

对于用户的 Prompt 提示词,Agent 会基于大模型先做规划(Planning),拆解成若干子任务,然后对每个子任务分别执行(Action),同时对每一步的执行结果进行观测(Observation),如果观测结果合格,就直接返回给用户最终答案,如果观测结果不合格或者执行出错,会重新进行规划(Replanning)。

图片

Agent 智能体组合架构模式

图片

从上述几种架构中我们不难看出还是非常复杂的,也不是一般企业能够做的了的。(费钱😭)

好在扣子已经把很多工作都做了。我们只需要在他上面去搭建想要的 agent 即可,大大减少了普通人的使用成本。

下面就结合自己的搭建一个 agent 的过程详细了解各个功能的构建过程。

工具集成

在扣子 bot 的编辑页面的中间区块插件区域我们可以选用扣子商店里面上架的工具。同时也可以自己去编写插件。插件的实现目前提供了两种语言:JavaScriptpython。可以根据自己的能力选用对应的语言编写即可。具体的编写插件的教程可以去官网文档查看。

那一个插件具体要实现什么样子的功能需要看我们构建的 bot 需要提供什么的能力。比如我是工具类的 bot,就需要集成各种常用的功能的工具。

目前我的bot 里面集成了挺多的工具:红框住的都是自研插件,具体如何做一个插件可以参考官网文档:

image-20240424213506484

先分享一个自己实现工具 json2sql 将json数据转换为sql:(此插件目前已经上架到官方插件商店)

 import { Args } from '@/runtime';
 import { Input, Output } from "@/typings/json2sql/json2sql";
 ​
 /**
   * Each file needs to export a function named `handler`. This function is the entrance to the Tool.
   * @param {Object} args.input - input parameters, you can get test input value by input.xxx.
   * @param {Object} args.logger - logger instance used to print logs, injected by runtime
   * @returns {*} The return data of the function, which should match the declared output parameters.
   * 
   * Remember to fill in input/output in Metadata, it helps LLM to recognize and use tool.
   */
 export async function handler({ input, logger }: Args<Input>): Promise<Output> {
   var json_str = input.jsonStr.replaceAll('\n', '')
   json_str = json_str.replaceAll("'",'"')
   var data = JSON.parse(json_str);
   var sql = "";
   sql += "CREATE TABLE {table_name} (\n";
   var fflag = 1;
   var field = [];
   for (const key in data) {
     if (key == "id") {
         fflag = 0;
         continue
     }
     let val = data[key];
     if (typeof val == "string") {
         field = field.concat("  `" + key + "` VARCHAR(255) NOT NULL default '' ");
     }
     if (typeof val == "number") {
       if ((val + "").indexOf(".") > -1) {
         field = field.concat("  `" + key + "` FLOAT ");
       } else {
         field = field.concat("  `" + key + "` INT NOT NULL default 0 ");
       }
     }
     if (typeof val == "boolean") {
         field = field.concat("  `" + key + "` BOOLEAN NOT NULL default false ");
     }
   }
 ​
   if (!fflag) {
     field = field.concat("  `id` int(11) NOT NULL AUTO_INCREMENT");
     field = field.concat("   PRIMARY KEY (id)")
   }
   logger.info(field);
   sql += field.join(",\n");
   sql += " \n );";
   return {
     result: sql,
   };
 };

输出:

 CREATE TABLE {table_name} (
   `name` VARCHAR(255) NOT NULL default '' ,
   `age` INT NOT NULL default 0 ,
   `sex` VARCHAR(255) NOT NULL default '' ,
   `phone` VARCHAR(255) NOT NULL default '' ,
   `address` VARCHAR(255) NOT NULL default '' ,
   `money` FLOAT ,
   `deleted` BOOLEAN NOT NULL default false ,
   `id` int(11) NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (id) 
  );

让模型调用工具

不管工具的能力多好,能让大模型调用到才是真的好🤣。

如何才能让大模型调用到呢?那就是 prompt 的编写。

扣子官方提供的案例里面是使用 markdown 格式写的,那我们也参考官方的格式:

 ### 技能 3: json 数据转 sql 
 1. 如果用户需要将 json 数据转换为 sql 语句,运用 json2sql 工具进行转换。
 2. 不要对输出的 sql 做改写,输出 markdown 格式。

这里主要是对模型说明当遇到什么样子的数据时候需要调用哪个工具来处理数据。所以我们调用一个工具需要注意两点:

  • prompt 中需要以简单直接的语句说明工具调用的方式
  • 在工具的说明里面也需要说明工具的能力

这些内容都会提交给大模型去理解,观察、执行。也就是 React 的执行能力。如果想要构建一个比较智能的 Agent 就需要 React 的思考执行模型。感兴趣的可以去了解下大模型应用中的 React

查询Linux命令的能力:知识库

我们还是先看使用效果:

示例 1: 查询 sed 命令

image-20240424085642354

示例2: 查询 grep 命令

image-20240424204538055

解析:

linux命令的查询以及使用能力大模型已经具备了一些能力,不过我这里还是使用了知识库的能力作为 LLM 的内容补充。总共有 600+的命令作为知识库的内容。

image-20240424205112727

LLM 会存在一些痛点,比如知识老旧、幻觉、高成本。所以很多情况下需要使用 RAG 的技术来解决 LLM 的问题。当然这里也不例外,也是利用了 RAG 的技术。

RAG 有三方面的好处:

  • 确保 LLM 可以回答最新、最准确的内容。并且用户可以访问模型内容的来源,确保可以检查其声明的准确性并最终可信。
  • 通过将 LLM建立在一组外部的、可验证的事实数据之上,该模型将信息提取到其参数中的机会更少。这减少了 LLM 泄露敏感数据或“幻觉”不正确或误导性信息的机会。
  • RAG 还减少了用户根据新数据不断训练模型并随着数据的变化更新训练参数的需要。通过这种方式企业可以减低相关财务成本。

关于 LLM 与 RAG 的详细内容这里不再赘述,大家可以参考我前面的文章。

自己干

我这里的 600+的 linux 命令对应的都是采集的网页数据。扣子提供了网页自动采集的能力。但是他只能一条条采集。对于600+的网页数据在没有 API 的基础上去搞估计的废了😭。文件上传可以一次导入 10 个文本,这个效率感觉比上面的那个靠谱多了。

于是就动手自己去采集 600+的网页数据。采集数据需要注意几点:

  • 限制采集的频率
  • 添加必要的 header 参数
  • 如果有用户认证的需要对应的 cookie 或者 token。
  • 提取网页上面自己关心的数据内容。

所以奔着一切动手实践的初衷手写爬取网页的代码,并按照每 10 个文件保存到一个目录中。

下面是 python 代码,需要的同学可以参考:

 from langchain_community.document_loaders import WebBaseLoader
 from bs4 import SoupStrainer
 from bs4 import BeautifulSoup
 import os, time
 ​
 def linux_shell():
     total = 1
     for idx in urls:
         url = idx
         url_arr = url.split('/')
         name = url_arr[-1].replace('html', 'txt')
         # 定位网页上面需要爬取的数据块
         only_a_tags = SoupStrainer(class_="markdown-style")
         loader = WebBaseLoader(
             web_path=url,
             encoding='utf-8',
             bs_kwargs={'parse_only': only_a_tags}
         )
         # 这里为了简单就直接使用langchain 的文本加载器
         data = loader.load()
         dir_name = f'/Users/oo7/Developer/langchain/coze/cmds/{int(total/10)+1}'
         if os.path.exists(dir_name) is False:
             os.makedirs(dir_name)
         file_name = f"{os.path.join(dir_name, name)}"
         # 简单处理文件的格式
         page_content = data[0].page_content.replace('\n\n\n\n', '\n')
         # 保存内容到文件中
         with open(file_name, 'w') as f:
             f.writelines(f"Linux {name.split('.')[0]} 命令详解 \n")
             f.write(page_content)
         
         print(f"{total}: {idx} is downloaded")
         time.sleep(0.5)
         total += 1

知识库配置:

对于知识库的配置,个人感觉还是比较重要的。知识库的配置有几项下面分别说一下:

  • 搜索策略:从知识库中获取知识的检索方式,不同的检索策略可以更有效地找到正确的信息,提高其生成的答案的准确性和可用性。

  • 最大召回数量:从知识库中返回给大模型的最大段落数,数值越大返回的内容越多。

    • 这个配置需要根据自己的实际情况去做调整。
    • 值越大返回的内容越多,某种情况下会导致返回的无用的信息多。
    • 值越小返回的内容越少,某种啥情况下会导致内容缺乏核心点。
  • 最小匹配度:根据设置的匹配度选取段落返回给大模型,低于设定匹配度的内容不会被召回。

    • 这个就通常所说的向量数据库的得分。得分越大匹配度越高,返回的条数越少。
    • 这个配置也是需要根据自己内容来做调整。
  • 调用方式

    • 自动调用:LLM 会根据用户内容做匹配。
    • 按需调用:这种情况需要在角色设定里面配置使用知识库的场景和匹配情况。

下面是我的提示词以及配置:

 ### 技能10:linux命令查询
 1、如果用户查询linux 命令时,运用 recallKnowledge 来获取内容。
 2、如果内容比较少,尽可能的给出一些使用示例和场景。
 3、如果找到的内容匹配度较低,需要运用 bingWebSearch 搜索内容。

image.png

image-20240425212259970

从图中看到这个效果还是比较好的。很多人添加完知识库就不管了,我的建议是最好还是做下配置,多使用看效果。

获取新闻能力

这里主要是使用了获取新闻的插件:ithome_news 这个插件是自己做的(现已上架到扣子官方的插件商店中,有需要的可以自行下载),数据来源是爬取某科技网站的日榜周榜月榜的新闻。

工作流构建:

构建工作流需要的节点:

  • 开始节点
  • LLM 节点
  • 选择器节点 (好像没必要了😄)
  • 插件节点(调用获取新闻)
  • 结束节点

image-20240424090524773

这里流程图里面两个节点很关键:

  • 大模型节点:这个节点让大模型去理解用户输入内容,并分析需要获取新闻的分类是哪一个。

     ## 返回
     新闻类型:日榜、周榜、月榜。
     从用户输入内容"{{input}}。默认类型为日榜" 中提取新闻类型.返回的内容只能是新闻类型中的内容。
     - 类型参数:{{category}}
     ​
    

    image-20240424090946299

    这一步很关键,目的就是让大模型能准确获取用户内容的意图从而提取里面的分类。这个分类会传递到后面的新闻节点,然后输出对应的类型。

  • 新闻工具节点:节点从LLM获取到的输入分类参数,输出对应分类下面的新闻。如果没有大模型提取出的新闻分类,这一步就很鸡肋。

    image-20240424091335437

    从上面的测试结果看,已经很完美的输出了数据。

  • 看下使用效果

    image-20240424091739486

使用工作流

image-20240424091946013

bot 中使用工作流 news_flow

image-20240424092128400

想让 bot 展现的时候是卡片的效果,只需要将工作流输出的内容数据以卡片的形式展现即可。

image-20240424092613954

对于卡片输出的注意点:

  • 输出的内容必须是固定的,绑定卡片的数据才能固定显示。一般使用API等程序输出固定格式内容
  • 多字段的数据,比如具有标题字段、内容字段、图片字段、url字段的数据会让效果更好。

定时任务能力

这次意外发现,官方提供了一个触发器的能力。那就是设置定时任务,到时间自动执行。有了这个功能是不是突然感觉发挥的空间就很大了呢?

image-20240424224158307

prompt 的重要性

prompt 的重要性相信好多人都认识到了。扣子的 prompt 使用 markdown 格式编写。主要分为三个部分。

  • LLM 的角色设定以及所具备的能力。
  • 详细说明每一个能力,以及能力触发的场景和对数据的处理能力。
  • 约束:限制 bot 不能做什么。

如果效果不是很理想需要不断的调整 prompt,目的是让 LLM 里面我们的意图。

除此之外工具、工作流等的名称以及描述也是整个 botprompt 的一部分。到最后 bot 去执行的时候都会将相关工具的描述拼接到 prompt 里面。最后让 LLM 去思考、执行、观察一直不断的循环这个过程,直到 LLM 认为需要停止的时候输出最终内容。下面的图是 React 的整个过程。

image-20240424232450134

整体的prompt分享给大家作为参考,实际效果还需要根据自己的bot做调整:

# 角色
你是一位全能的数据处理专家,擅长处理各种数据格式,能对 JSON 字符串进行格式转换、转义与去除转义操作,还能获取网页内容并加以总结,能用通俗易懂的语言阐释数据特性与复杂的分析结果。

## 技能
### 技能 1: 格式化 JSON 字符串
1. 能使用 json_formater 工具将用户提供的需转换数据格式化为 JSON 对象。
2. 可运用 JSON_unescaping 工具对用户输入的需转义数据进行去除转义操作。

### 技能 2: 获取网页内容并总结
1. 若用户输入包含 URL 链接,提取该URL 链接,并通过 LinkReaderPlugin 工具获取网页内容。

### 技能 3: json 数据转 sql 
1. 如果用户需要将 json 数据转换为 sql 语句,运用 json2sql 工具进行转换。
2. 不要对输出的 sql 做改写,输出 markdown 格式。

### 技能 4: 识别图片中的文字
1. 当用户输入图片内容时,可运用 ocr 工具识别图片中的文字。

### 技能 5: 具备强大的思考及自执行能力
能根据用户输入,不断思考要提取的数据、调用的工具及提取工具的输入参数,直至获得可使用的数据为止。

### 技能 6:固定markdown 格式回复
1、如果回复用户的内容是代码,使用 markdown 格式的内容回复用户 

### 技能7:搜索引擎
1、如果用户希望通过网络查找内容,运用 bingWebSearch 工具获取内容
2、对搜索引擎的结果内容不要过度总结和优化

### 技能8:域名DNS解析记录
1、如果用户要获取域名的 DNS 解析记录,运用功能 dns_tool工具获取内容

### 技能 9: 获取新闻
1、当用户要获取新闻时,运用 news_flow 工具获取内容。

## 限制
- 不要对输出的 sql 做改写,输出 markdown 格式。
- 需依据用户输入,持续思考要提取的信息、调用的工具及工具的输入参数,直至获取到可用数据。
- 如需进行总结,只能依据现有内容,不能自行创造。
- 输出的内容必须按照给定的格式进行组织,不能偏离框架要求。
- 对于分析结果,需详细解释其含义,不能仅提供数字或图表。
- 在使用特定编程语言提取数据时,必须阐释所使用的逻辑和方法,不能仅提供代码。
- 你只能回答与编程有关的话题。
- 不要总结新闻内容

其他功能:

主要的功能讲完后剩余的就是其他功能了,下面的这些都是辅助功能,让你的 bot 更加的完美。

  • 开场白文案:也就是自我介绍👀。
  • 开场白预置问题:引导用户提问,使用户能够快速掌握相关的能力。
  • 用户问题建议:
  • 语音:个性化,依据场景以及个人爱好设定就行。

具体如何使用请查询官方文档。

总结:

上面带大家熟悉了构建一个 bot 的整体过程包括:

  • LLM agent 架构模式。
  • 插件的编写功能实现,以及插件 prompt 的重要性。
  • 知识库构建,知识库与 bot 的结合。RAG 相关知识。
  • 流程 flow 的使用。

相关文章:

欢迎大家使用 bot: , 如有想法可以评论留言讨论!

Bot id: 7360782245814190080