全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-05-23_「转」用 Coze(扣子) 打造 “最强“ 浏览器书签助手(上)

您的位置:首页 >> 新闻 >> 行业资讯

用 Coze(扣子) 打造 “最强“ 浏览器书签助手(上) 点击关注公众号,“技术干货”及时达! 1. 引言??一阵子没耍 「Coze(扣子)」 ,最近不太忙,刚好有新 「idea」??,倒腾个Bot耍耍??,也赶下本期扣子活动的尾班??,蹭个参与??。扣子好像更新了不少东西啊,先可快速过下??~ 1.1. 扣子更新速览1.1.1. 多Agents模式对于 「Agent是什么」 的一个常见观点: ?「让AI以类似人的工作和思考方法来完成一系列任务」,一个Agent可以是一个Bot,也可以是多个Bot的协作。 ??? 扣子的 「多Agents模式」 就是:支持 「组合多个Bot」 来实现 「功能更全面和复杂的Bot」。在Bot的 「编排」 页,可以进行 Agent模式的切换: 左侧 「配置」 和 「单Agent模式」 时基本类似,只是作用域是 「全局」 的,将适用于所有添加的 「Agent」。 右侧 「画布」 区域可以为 Bot添加 「节点」,默认自带一个 「开始节点」,可以设置新一轮对话的 「起始节点」: 「上一次回复用户的节点」:新消息继续发送给上次回复用户的节点。「开始节点」:所有消息都发送给Start节点,它将用户消息移交给适合的Agent节点。然后,支持添加 「三种类型」 的节点: ① 「Agent节点」 → 可以独立执行任务的智能实体,?? 感觉是支持 「快速灵活搭建的Bot节点 (不用发布)~」 ② 「Bot节点」 → 「已发布」 的可执行特定任务的 「单Agent Bot」 ③ 「全局跳转条件」 → 用户输入满足此节点的条件,立即跳转到Agent 优先级高于 「节点选择」,命中就直接跳,一个Bot最多添加 「5个条件节点」 ?? 随手缝合个支持多功能的Bot试试: 发下消息试试: ?? 可以,都正确调用了适合的Agent,多Agents的玩法和 「工作流」 很像,感觉是 「执行任务粒度粗细」 的 「划分~」 1.1.2. 记忆-变量?? 支持创建变量来保存一些 「临时数据」,在Bot的 「编排页」,找到 「记忆-变量」,点击+,可以对Bot中需要用到的变量进行编辑,比如我这里启用了 「sys_uuid」 用于获取的唯一ID,并添加了一个 「city」 用于保存用户所在城市: 然后在 「预览与调试」,回复一句:我在深圳,可以看到,变量设置成功: 点击右上角的 「变量」,可以看到保存的数据: 可以在 「人设与回复逻辑」 限定变量的 「具体使用场景」,接着,你还可以在 「工作流」 中添加 「变量节点」 来 「对变量值进行设置或读取」。比如,随手建个工作流试试: 点击 「试运行」,需要选择关联的Bot,在此可以看到其中定义的变量: 点击运行后的输出结果: ?? 试运行这里肯定是拿不到用户id的,直接 「发布工作流」,然后添加到Bot里,编写Prompt来调用: 另外,如果用户更新了数据 (如:在会话中提供了新的城市),Bot会自动修改为最新值。 1.1.3. 记忆-长期记忆模仿人脑形成对用户的个人记忆,基于这些记忆提供个性化回复,「提升用户体验」。主要包含两部分能力: 「自动记录并总结对话信息」;「会在回复用户查询时,对总结的内容进行召回,在此基础上生成最终回复」。?? 感觉这个长期记忆就是 「一种记录多轮对话上下文的管理机制」,用于保持对话的连贯性。点击 「编排页」 的 「记忆-长期记忆」,可以开启或关闭。 开启后,点击右上角 「预览与调试」-「长期记忆」,可以看到总结的对话内容: 「Tips」:每个用户包括Bot开发者,只能看到和使用与Bot对话生成的记忆内容。 1.1.4. 技能-触发器使Bot在 「指定时间」 或 「接收到特定事件」 时自动执行任务,前者无需编写代码,后者会生成 「Webhook URL」,当你的服务端向这个 Webhook URL 发起HTTPS请求,会触发任务执行。 在 「编排页-触发器」,点击+添加触发器,比如添加一个 「定时触发」 的触发器,支持执行三种类型的任务: WebHook类型的事件触发器配置稍微麻烦些,普通用户用不到,感兴趣可以看下官方文档对应介绍~ ?? 触发器只在Bot发布到 「飞书」 时才有效 ?? 每个Bot最多添加10个触发器。 1.1.5. 对话体验-快捷指令在 「对话输入框」 上方配置一些 「按钮」,以供用户 「快速发起预设对话」: 支持两种 「指令行为」,先是 「直接发送-指令内容」: 然后是 「直接使用插件或工作流」,需要进行一些对应的参数配置: 配置完点击按钮会 「弹窗」,用户输入对应的参数,回车触发指令发送 支持 「拖拽按钮」 对指令进行排序,在飞书、微信等渠道支持直接 「输入指令」 (如: /fetch_weather) 唤起快捷指令。 1.1.6. 对话体验-背景图片可以为Bot在 「扣子Bot商店」 添加对话背景图片,点击+上传,能看到横竖屏的效果预览,点击 「确认」 进行设置。 1.1.7. 对话体验-开场白支持Markdown语法在 「编排页」 找到开场白,点击打开Markdown编辑器: 然后可以使用Markdown语法来编写Bot的开场白,右侧可以看到 「效果预览」: 1.1.8. LLM模型更新「云雀」 改名 「豆包」,支持 「moonshot (月之暗面-kimi)」 模型,后面的数字代表模型支持的 「最大上下文长度」,即它们能够处理的文本量,如: 8k 表示模型支持的最长上下文长度为8000 tokens,可以更具自己的实际情况进行选择。 1.1.9. 工作流-消息节点支持在工作流中返回响应内容,可用于解决 「回复消息较多」 或 「工作流较长」 导致用于 「无法及时收到响应」 的问题: 支持两种消息模式: 「非流式输出」:「默认」,接收到全部消息内容再一次性输出。「流式输出」:「打字机效果」,上一个节点一边生成回复内容,一边通过消息节点进行输出,不用等全部内容加载完再返回。消息节点只有在 「大模型节点后」 可才可以开启 「流式响应」,如果配置了多个开启流式返回的消息节点,会按照工作流的执行顺序,先执行的消息节点优先输出消息。1.1.10. 技能-图像流前些天还在 「编排页」 看到过它,昨天再看没了,有消息说会在5.15的发布会后上线,但今天看还是无影??。支持通过 「可视化的方式」,对图像获取、生成、编辑和发布等功能进行组合,实现自定义的图像处理流程,相关功能: ???♂? 现在木得体验,等正式上线了再单独测评下~ 1.2. 灵感乍现的瞬间?? 了解完扣子都更新了啥,接着捋一捋笔者想做一个什么样的Bot,灵感来源??: ?互联网时代,许多人都养成了一种 「囤积癖」,看到好的文章、工具的第一反应就是 「添加收藏/书签」,理由是 「以后有时间再看」,「以后可能会用到」,然后,基本没以后了,绝大概率就是在收藏夹吃灰??。接着,在某天某个情景,突然想起,自己有收藏过相关的链接,但苦寻许久,也可能找不到哪个链接了。?? 2333,这就是没有养成「定时整理」 的习惯... ??? 由此萌生出做一个 「浏览器书签助手」 的Bot,支持:「书签自动分类整理」 + 「AI检索心仪书签」,简易交互流程: ?? 思路有了,接着一步步来实现这个 Bot~ 2. 实践过程2.1. 文件上传?? 有些读者可能还没导出过 「书签文件」,说下方法,依次点击浏览器的 (以笔者的Chrome为例): 「设置 → 书签和清单 → 书签管理器」 → 「右上角整理」 (三个竖排点的图标) → 「导出书签」 然后会生成一个 「bookmarks_年_月_日.html」 的书签文件,如: ?? 先不看文件内容,琢磨下 「文件上传」,除了用户通过Bot上传外,「插件和工作流的代码节点」,都需要用到,就生成的文件,需要传到服务器,生成一个 「访问文件的URL」。扣子是 「支持上传文件」 的,但很可惜,并不支持 「html」 格式 ??,尝试上传html后什么反应都没有: 然后在代码节点,试下写提示词让AI写下代码,看能否套出 「文件上传相关的代码」: AI生成的代码: ?? 打扰了,所以 「文件上传」 这块得自己想办法了,需要一枚 「云存储」,这里直接上 「七牛云」,上传文件的核心代码: fromqiniuimportAuth,put_file,etag access_key='access_key' secret_key='secret_key' bucket_name='仓库名称' q=Auth(access_key,secret_key) base_url="http://xxx.com/{}"#仓库域名 defupload_file(file_path): file_name=file_path.split('/')[-1] token=q.upload_token(bucket_name,file_name) ret,info=put_file(token,file_name,file_path,version='v2') #上传成功滑后返回文件的url ifret['key']==file_pathandret['hash']==etag(file_path): returnbase_url.format(file_name) if__name__=='__main__': print(upload_file("rabbit.ico")) 然后,问题来了:「用户咋上传生成标签文件的url」?总不能让他们每次传文件,都要手动去改 「upload_file()」 传递的文件名吧???随手用 「PySimpleGUI」 库 (「4.60.5」, 5后的版本要付费) 写个有简单页面的 exe: importPySimpleGUIasps_gui #构造GUI页面 defmain_gui(): #判断config.txt文件是否存在 access_key='' secret_key='' bucket_name='' ifos.path.exists("config.txt"): withopen("config.txt","r")asf: lines=f.readlines() access_key=lines[0].strip() secret_key=lines[1].strip() bucket_name=lines[2].strip() layout_control=[ [ #access_key的输入框 ps_gui.Frame(layout=[ [ps_gui.Text("AccessKey"),ps_gui.InputText(key="access_key",default_text=access_key)], [ps_gui.Text("SecretKey"),ps_gui.InputText(key="secret_key",default_text=secret_key)], [ps_gui.Text("BucketName"),ps_gui.InputText(key="bucket_name",default_text=bucket_name)], ],title="请输入七牛云相关参数"), ps_gui.Button("保存",key="save")], [ ps_gui.Frame(layout=[ [ps_gui.FileBrowse("选择文件",key='file_browser',enable_events=True)] ],title="请选中要上传的文件"), ps_gui.Frame(layout=[ [ps_gui.Text("",key="file_path")] ],title="当前选中文件"), ps_gui.Button("上传",key="upload") ], [ ps_gui.Text("文件URL",key="result_label"), ps_gui.Text("",key="result"), ps_gui.Button("复制",key="copy") ], ] window_panel=ps_gui.Window("七牛云文件上传助手",layout_control) whileTrue: event,value=window_panel.read() ifevent==ps_gui.WIN_CLOSED: break ifevent=='save': withopen("config.txt","w")asf: f.write("{}\n{}\n{}\n".format(value['access_key'],value['secret_key'],value['bucket_name'])) ps_gui.popup("保存成功") ifevent=='file_browser': file_path=value[event] window_panel['file_path'].update(file_path) ifevent=="upload": file_path=window_panel['file_path'].get() iffile_path: url=upload_file(file_path,value['access_key'],value['secret_key'],value['bucket_name']) ifurl: window_panel['result_label'].update("上传成功:") window_panel['result'].update(url) else: window_panel['result_label'].update("上传失败:") window_panel['result'].update("") else: ps_gui.popup("请选择文件后上传") ifevent=="copy": url=window_panel['result'].get() ifurl: ps_gui.clipboard_set(url) ps_gui.popup("已复制到剪切板") else: ps_gui.popup("请先上传文件") window_panel.close() if__name__=='__main__': main_gui() 是用 「Pyinstaller」 打包成exe,执行下述命令: pyinstaller--onefile--noconsole--name"七牛云文件上传助手"app.py 双击打包后的exe: ?? OK,文件上传获取远程文件URL的问题 「Fix」?? 2.2. 书签提取?? 接着到 「读取并解析书签文件」,对其中的书签进行提取,打开 「书签html文件」 研究下规律: 「分析结果如下」: 「DL」→ 书签的一个分组/文件夹「DT」 → 书签文件夹标题/具体书签项「ADD_DATE」 → 第一次添加的时间戳「LAST_MODIFIED」 → 上次修改的时间戳「HREF」 → 标签链接「ICON」 → 标签对应网页图标Base64?? 「Coding」 时间到,新建 「插件」,创建方式选 「在 Coze IDE 中创建」,IDE运行时选 「Python3」,点击 「确认:」 创建完会跳页面,点击 「在IDE中创建工具」,写下工具名称和介绍,点确定,然后就可以写代码了??,逻辑: 定义一个BookmarkItem的类存 「书签标题」、「书签链接」、「书签创建时间」、「图标」。请求用户传入的 「书签文件URL」,读取文件内容。编写 「正则表达式」 提取所有书签信息,保存为json文件并上传。输出:文件url、错误信息。添加下 「requests」 和 「qiniu」 依赖库,然后写出具体实现代码: fromruntimeimportArgs fromtypings.extract_bookmark_list.extract_bookmark_listimportInput,Output importre importrequests fromqiniuimportAuth,put_file,etag importtime importos importjson #提取书签的正则 bookmark_item_pattern=re.compile(r'AHREF="(.+?)"ADD_DATE="(\d+)"ICON="(.*?)"(.*?)') #七牛云相关配置 access_key='xxx' secret_key='yyy' bucket_name='zzz' base_url="http://xxx.yyy.zzz/{}" q=Auth(access_key,secret_key) classBookmarkItem: def__init__(self,title,url,add_date,icon): self.title=title self.url=url self.add_date=add_date self.icon=icon def__str__(self): returnstr(self.__dict__) #读取在线文件内容 defread_file_from_url(file_url): response=requests.get(file_url) #避免中文乱码 response.encoding=response.apparent_encoding ifresponse: returnresponse.text else: returnNone #保存文件 defsave_file(uuid,content): file_path="{}{}{}_{}.json".format(os.getcwd(),os.sep,uuid,int(time.time()*1000)) withopen(file_path,'w',encoding='utf-8')asf: f.write(content) returnfile_path #上传文件到七牛云 defupload_file(file_path): q=Auth(access_key,secret_key) file_name=file_path.split('/')[-1] token=q.upload_token(bucket_name,file_name) ret,info=put_file(token,file_name,file_path,version='v2') ifret['key']==file_nameandret['hash']==etag(file_path): returnbase_url.format(file_name) return'' defhandler(args:Args[Input])-Output: #获取用户输入的uuid sys_uuid=args.input.sys_uuid file_url=args.input.input_file_url file_content=read_file_from_url(file_url) iffile_content: match_results=bookmark_item_pattern.findall(file_content) bookmark_item_list=[] ifmatch_resultsandlen(match_results)0: forresultinmatch_results: bookmark_item_list.append(BookmarkItem(result[3],result[0],result[1],result[2])) bookmark_json=json.dumps(bookmark_item_list,default=lambdaobj:obj.__dict__,ensure_ascii=False) output_file_url=upload_file(save_file(sys_uuid,bookmark_json)) return{"output_file_url":output_file_url,"error_msg":""} else: return{"output_file_url":"","error_msg":"未检索到书签项"} 「元数据」 配置下输入输出参数: 接着 「输入」 处点击 「自动生成」,把 「input_file_url」 的值替换为上传的书签文件url,点击 「运行」 试试: 打开输出文件的URL: ?? 书签列表提取成功??~ 2.3. 数据清洗看了下书签列表,发现有些书签是有问题的 → 「没有titile」,而后面的 「分类整理」 和 「AI搜索」 都是很依赖标题的,需要为没有title的书签设置title。?? 在请求站点时,「title字段」 一般都会返回 (即便弄了反爬),毕竟是 「SEO」 (搜索引擎优化) 中一个重要的考虑因素,当然,还有一些其它有用的信息: 然后,「requests」 库的请求是 「同步」 的,发送一个请求,「程序要等服务器响应后才继续执行」,如果没title的书签比较多,耗时会比较久,节点太久没返回输出 ,会直接报错,整个工作流就崩了。 ?? 所以,这里需要尽可能地快完成,直接上 「异步请求+代理」,前者用的 「aiohttp」,后者用的 亮数据,因为时不时有些数据爬取的单子,经常用到代理IP,用得最多的还是它,主要有以下几点: 代理 IP 质量和稳定性比较高,支持获取指定区域的代理IP (国内、国外)。支持API调 「无头浏览器」 爬数据,自动过验证码、重试和指纹管理。提供数据服务,有些都不用自己爬。?? 最重要一点,不用各种恶心的个人/企业认证这里需要 「动态IP」 → 「真人住宅代理」,直接写出爬取代码: importaiohttp importasyncio importre proxy='http://代理地址:端口' title_pattern=re.compile(r'([^]*)/title') asyncdeffetch(bookmark): asyncwithaiohttp.ClientSession()assession: try: asyncwithsession.get(bookmark['url'],proxy=proxy,ssl=False)asresponse: ifresponse: bookmark['title']=title_pattern.search(awaitresponse.text()).group(1) returnbookmark exceptExceptionase: print(str(e)) asyncdeftask(json_content): #构造异步请求任务列表 tasks=[fetch(bookmark)forbookmarkinjson_content] #等待所有任务完成 results=awaitasyncio.gather(*tasks) returnresults defhandler(args:Args[Input])-Output: result=asyncio.get_event_loop().run_until_complete(task(bookmark_json)) return{"result":result} 2.4. 简单分类???♂? 数据清洗完,就该分类了,一个简单粗暴的思路: 遍历提取书签 「域名」 作为Key,值为同一域名下的书签列表,写 「Prompt」 让 「AI模型」 判断它属于什么类型的站点,然后分类。 新建一个 「工作流」: 「开始节点」:传入书签json文件的url「代码节点」:读取文件,遍历书签,组装prompt往后传递「大模型节点」:使用 「moonshot (128k)」 模型来执行前面组装的prompt「代码节点」:提取 「分类标签」,并未每个书签添加上次字段,最后返回更新后的json数据拖拽下节点: 「解析Json拼Prompt节点」 的代码如下: importrequests_async fromurllib.parseimporturlparse asyncdefmain(args:Args)-Output: params=args.params output_prompt=""" 请帮我联网对网站进行分析,不要遗漏任何一个,严格按照这样的json格式返回: ``` [ { "website_url":网站链接, "website_name":网站中文简称, "website_category":网站分类,只显示一个最准确的 } ] ``` 具体网站列表如下: """ website_str="" input_file_url=params["file_url"] response=awaitrequests_async.get(input_file_url) netloc_set=set() ifresponse: response_json=response.json() forbookmark_infoinresponse_json: netloc=urlparse(bookmark_info['url']).netloc netloc_set.add(netloc) fornetlocinnetloc_set: website_str+="-{}\n".format(netloc) ret:Output={ "output_prompt":(output_prompt+website_str).strip(), } returnret 输入前面生成的 「json文件url」 进行测试: 大模型节点直接使用输出结果作为 「提示词」,试运行后的输出结果: ?? 「输出格式」 是没错的,就是数量太少了,才11条,估计是 「提示词太长」 导致的,拆成10个网站列表,然后模型使用 「批处理」,修改下代码节点: 大模型节点也改改: 试运行看看效果: Nice,基本都拿到了,接着就是为每个书签添加对应的分类了, importjson asyncdefmain(args:Args)-Output: params=args.params website_json=json.loads(params['website_json']) new_website_list=[] ret:Output={} #存域名-分类的字典 website_category_dict={} llm_results=params['llm_result'] forllm_resultinllm_results: forwebsiteinjson.loads(str(llm_result['output'])): website_category_dict[website['website_url']]=website['website_category'] forwebsiteinwebsite_json: forwebsite_category_keyinwebsite_category_dict.keys(): ifwebsite_category_keyinwebsite['url']: website['category']=website_category_dict[website_category_key] new_website_list.append(website) ret['output_json']=json.dumps(new_website_list) returnret 最后输出的json字符串: ?? Nice,分类都安排上了~ 2.5. 生成书签文件接着就是写插件,来解析上面的字符串,生成可以导入到浏览器的书签文件。??这一步是真的 「坑」,反复调整生成后的书签文件,然后浏览器导入书签测试。要么就是不显示分组,要么就是不显示书签,折腾了大半天终于成功导入??,给出具体实现代码: fromruntimeimportArgs fromtypings.json_to_bookmark_file.json_to_bookmark_fileimportInput,Output importrequests fromqiniuimportAuth,put_file,etag importtime importos importjson #七牛云相关配置 access_key='xxx' secret_key='yyy' bucket_name='zzz' base_url="http://xxx.yyy.zzz/{}" q=Auth(access_key,secret_key) #读取在线文件内容 defread_file_from_url(file_url): response=requests.get(file_url) #避免中文乱码 response.encoding=response.apparent_encoding ifresponse: returnresponse.text else: returnNone #保存文件 defsave_file(uuid,content): file_path="{}{}{}_{}.html".format(os.getcwd(),os.sep,uuid,int(time.time()*1000)) withopen(file_path,'w',encoding='utf-8')asf: f.write(content) returnfile_path #上传文件到七牛云 defupload_file(file_path): q=Auth(access_key,secret_key) file_name=file_path.split('/')[-1] token=q.upload_token(bucket_name,file_name) ret,info=put_file(token,file_name,file_path,version='v2') ifret['key']==file_nameandret['hash']==etag(file_path): returnbase_url.format(file_name) return'' #生成书签文件内容 defgen_bookmark_file_content(json_content): result_content=""" !DOCTYPENETSCAPE-Bookmark-file-1 !--Thisisanautomaticallygeneratedfile. Itwillbereadandoverwritten. DONOTEDIT!-- METAHTTP-EQUIV="Content-Type"CONTENT="text/html;charset=UTF-8" TITLEBookmarks/TITLE Bookmarks/H1 DTH3ADD_DATE="1678077635"LAST_MODIFIED="1679623908"PERSONAL_TOOLBAR_FOLDER="true"书签栏/H3 """ #生成分类字典 category_dict={} bookmark_list=json.loads(json.loads(json_content)) forbookmarkinbookmark_list: ifcategory_dict.get(bookmark['category'])isNone: category_dict[bookmark['category']]=[] category_dict[bookmark['category']].append(bookmark) #获取当前时间戳 timestamp=str(int(time.time())) forkey,valueincategory_dict.items(): #分组标签 group_str='H3ADD_DATE="{}"LAST_MODIFIED="{}"/H3DLp\n'.format(timestamp,timestamp,key) #书签标签 forbminvalue: bookmark_str='AHREF="{}"ADD_DATE="{}"ICON="{}"/A\n'.format(bm['url'],bm['add_date'], bm['icon'],bm['title']) group_str+=bookmark_str group_str+="/DLp\n" result_content+=group_str result_content+="/DLp/DLp" returnresult_content defhandler(args:Args[Input])-Output: sys_uuid=args.input.sys_uuid bookmark_json_file_url=args.input.bookmark_json_file_url #读取json文件内容 content=read_file_from_url(bookmark_json_file_url) #生成书签html文件后返回url output_file_url=upload_file(save_file(sys_uuid,gen_bookmark_file_content(content))) return{"output_file_url":output_file_url} 测试下代码试试: 把生成后的书签html文件下载到本地,浏览器导入: 2.6. AI书签检索这部分就是把所有标题丢给大模型,让它去找其中可能与用户输入关键字最接近的20个书签,起一个代码节点拼prompt: import requests_async async def main(args: Args) - Output: ret: Output = {} bookmark_dict_list = [] params = args.params input_file_url = params['input_file_url'] keyword = params['keyword'] prompt_str = """ 请从下面的列表中选择与 {} 相关度最高的20个,以字符串数组的形式返回: """.format(keyword) response = await requests_async.get(input_file_url) if response: response_json = response.json() for bookmark_info in response_json: prompt_str += " - {}\n".format(bookmark_info['title']) bookmark_dict_list.append({bookmark_info['title']: bookmark_info['url']}) ret['prompt_str'] = prompt_str ret['bookmark_dict_list'] = bookmark_dict_list return ret 「运行测试下:」 接个大模型节点,执行下生成的prompt,输出结果: ?? 不错,后面再加个代码节点,关联下书签链接,格式化下输出结果: importre asyncdefmain(args:Args)-Output: params=args.params match_str=params['match_str'] bookmark_dict_list=params['bookmark_dict_list'] keyword=params['keyword'] output_str='AI检索到可能与【{}】匹配的书签如下:\n'.format(keyword) fetch_title_pattern=re.compile(r'\d+.?(.*?)\n') title_results=fetch_title_pattern.findall(match_str) forbookmark_dictinbookmark_dict_list: fortitleintitle_results: ifbookmark_dict['title']==title: output_str+="-[{}]({})\n".format(title,bookmark_dict['url']) ret:Output={ "output_str":output_str, } returnret 接着试运行看看 「工作流」 的输出结果: OK,运行无误,接着建个Bot把这些都串起来~ 2.7. 写个Bot串起来?? 随便写点东西: 然后我希望通过 「指令」 的形式来使用Bot: 「/tidy 书签html文件URL」 → 自动整理书签分类「/search」 「书签html文件URL 关键词」 → 书签检索然后要建一个 「自动整理书签分类」 的工作流: 「试运行」 输出结果如下: 配置下 「快捷指令」 调用工作流 「:」 书签检索也配上,点击按钮后,输入对应的参数值: 「回车」 看看输出结果: ??估计是 「收藏夹整理」 的工作流耗时比较久,所以一直报 「运行中止」,但是 「试运行」 又是可以的,后续看下这块怎么优化吧,毕竟大模型批处理10次 ?? 3. 小结?? 还是低估了这个 「Bot的复杂度」 和 「大模型输出结果的不稳定性」,踩坑两天,大部分时间都花在改改上了,勉强实现了一个简单的雏形,虽然可用性 ≈ 0,不过也有收获,比如,使用 「Coze IDE 开发」 两个小技巧??: ① 「插件测试没问题,工作流调用,一跑就报错,而且全是这个错」 ?? 能从这个错误信息里看出为啥报错吗?不能 ??,你只能知道插件挂了,没任何意义... ?? 那就自己 「兜底」,给插件代码包一层 「try-except」,「元字典」 里配一个存错误信息的字段: 然后插件蹦的时候,你就能看到错误了,比如我上面的错误就是在这个目录写文件没权限: ???♂? 而且这样玩还有一个好处 → 「插件测试直接通过」,无脑发布~ ② 「插件开发时能写文件,工作流不行」 ???♀? 我上面的报错就是这个,估摸着是不想让人在服务器上 「乱拉」??(建文件) ,所以工作流的这个路径没给写权限。?? 插件还是只用来处理和返回数据,当然,硬要拉,也是可以的,一种粗暴的方式就是向上和向下递归遍历可以写文件的目录,不过不建议这样玩哈 ??: defhas_write_permission(directory): """检查目录是否有写权限""" returnos.access(directory,os.W_OK) deffind_writeable_directory_upwards(directory): """向上递归查找有写权限的目录""" ifhas_write_permission(directory): returndirectory parent_directory=os.path.dirname(directory) ifparent_directory==directoryorparent_directory=='': #已经到达根目录或无更上一级目录,仍未找到有写权限的目录 returnNone returnfind_writeable_directory_upwards(parent_directory) deffind_writeable_directory_downwards(directory): """向下递归查找有写权限的目录""" forroot,dirs,filesinos.walk(directory): ifhas_write_permission(root): returnroot fornameindirs: dir_path=os.path.join(root,name) ifhas_write_permission(dir_path): returndir_path #如果当前目录及所有子目录都没有写权限 returnNone deffind_writeable_directory(): """找到一个可写入的目录""" current_directory=os.getcwd() ifhas_write_permission(current_directory): returncurrent_directory #向上查找 writeable_directory_upwards=find_writeable_directory_upwards(current_directory) ifwriteable_directory_upwardsisnotNone: returnwriteable_directory_upwards #向上未找到,尝试向下查找 returnfind_writeable_directory_downwards(current_directory) #调用处 writeable_directory=find_writeable_directory() ?? 然后也有一些新的灵感,比如: 使用 「多Agent模式」 来解耦,一个Bot专门处理收藏夹整理,一个Bot专门处理书签搜索。用 「数据库」 来存解析后的书签数据,不用每次都贴一个url。「标签分组规则」 的细化,比如:支持多级目录,基于用户的分组规则对未分组书签进行分组。「书签搜索」:支持搜索词 「精准搜索」 和 「AI模糊搜索」,搜索结果附上精简的总结等等...?? 后续会继续打磨,敬请期待~ bot ID: 7368480012469239845 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2022-08-08_翻拍港剧僵尸片, 泰国广告是真的绝 下一篇:2022-03-29_把轮子装在膝盖上,「机器羊」能走能滑、还载起了人和货

TAG标签:

18
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价