Featured image of post 扣子上手实战 — 塔罗牌应用

扣子上手实战 — 塔罗牌应用

扣子 上手实战 - 塔罗牌应用

之前分享了使用coze 工作流 搭建个人兴趣搜索助手的案例。coze(扣子)平台的使用,可以说是上手了。

今天在之前应用的基础上,深入一点:做个塔罗牌占卜的小应用。通过这个案例,你可以得到:

  1. 工作流向Agent演进,以及其中大语言模型(LLM)发挥的作用。

  2. coze 工作流中一些常用组件的使用(选择, 循环,变量,代码,输出,卡片)

  3. 其中卡片展示 和 循环 为学习目的追加的,初期上手有点复杂—— Option

  4. 发布自己的coze应用 —— 应用已经发布到coze 平台( https://www.coze.cn/s/3FbpLuGMnac/)和 微信公众号(AI 热气球)中啦。感兴趣的话,你可以来体验下~

效果图先行(忽略不满20岁就 考研这个bug ^^)——


那具体怎么实现的呢?

这里需要引入Agent 的概念——

Q: 什么是Agent ?

  • 定义:AI Agent(智能体)是指能够自主感知环境、做出决策并采取行动的系统。它通常具备一定的智能,能根据目标和环境变化动态调整行为。
  • 特点:自主性、感知-决策-执行闭环、可持续交互、目标驱动。
  • 组件:大脑(基础语言模型)、记忆(上下文)、工具(tools)

Q: Agent 和workflow 有什么不同?

这里放一张对比表——

维度 AI Agent(智能体) Workflow(工作流)
核心 智能决策与自主行为 任务/操作的有序自动化
驱动力 目标导向、环境感知 规则/流程导向
适应性 高,能根据情况动态调整 低,流程通常是预设好的
复杂性 可复杂(涉及学习、推理等) 通常较简单(规则、条件)
幻觉 会存在讨好人类,编造数据的情况 因为上下游的输入输出受限,过程和内容可控

实际应用中,可以把Agent 套在workflow 承担某一环节的顾问(由大模型 自主判断、分析、输出)——就像上一篇文章“B站助手”中大模型发挥的作用:它接收到输入信息后,推理用户对这方面的内容感兴趣,然后找插件去检索B站中相匹配的 热门视频,然后再按照我们要求的格式整理(标题、url、简介)后输出。

也可以反过来:在Agent 中调用指定的工作流,去完成对应的任务。今天这个案例,正好就是这种。

搞清楚了这些 ,有助于理解我们这个案例的操作步骤:

  1. 根据塔罗牌 占卜所需信息,设计工作流
  2. 测试、验证工作流
  3. 新建Agent,关联工作流、修改提示词
  4. 测试&发布应用

设计&搭建工作流

明确工作流需要完成的任务:

  • 根据用户输入的信息(生日 性别 出生城市)得到用户星盘信息
  • 根据塔罗占卜(分析过去、现在、未来的三张牌)方法,要随机得到3 张卡牌
  • 上述两个信息输入给 大模型,让大模型结合用户的星盘信息和3张卡面信息,去分析用户问题上境遇和机会
  • 最终返回大模型的结果

看起来可能长这样(见下图)——输入这里接受 4个变量(生日 性别 城市 问题),而coze 市场中有就有xingpan 插件和 随机抽塔罗牌的插件,就这么简单。。。么?

好消息: coze 插件市场提供的两个插件大大简化了工作。

“抽卡插件”这里,要求输入 “抽卡数”,当前场景 就是数字3 —— 每次都是抽3张,所以它是固定不变的,填入3就可以了。

“星盘信息”这里,要求输入 的内容就比较多了:生日、性别是workflow 一开始由用户输入的信息(变量),这里直接引用变量名就可以。但 “latitude”和“longitude”是个啥玩意?

坏消息:部分数据格式不对!

这就是第一个石头,数据格式要转化!

进一步点击 “xingpan”这个插件的 “插件详情”,会发现 “latitude”和“longitude” 分别对应用户出生时的经纬度。

目前只有出生城市,而城市对应的经纬度是没有的。。。。

于是,找出第三个插件来救个场——注意 我们有用户的城市信息,要转化为对应的经纬度信息。

coze 插件市场中按照“经纬度”关键字,找到这样一个符合我们需求的工具“中国城县经纬度查询”。

哈哈,正好输入城市就可以得到经纬度了 。。。么?先添加这个工具测试一下——

两个问题——

  1. 工具输出为 一个数组(先后两个数字),第一个是经度、第二个是维度。“星盘”插件的确需要这两个数字,但问题是它们不能一股脑输入给“星盘”。这个问题稍候详细讨论。
  2. 输入“北京市”可以得到 经纬度,但如果少输入一个“市”,就没有任何经纬度信息了(见上图)。

先说,第二个问题:自然语言中 北京北京市 在你问我出生城市时肯定是一个意思。怎么处理呢:要么是 给用户一个下拉框——限制用户输入;要么让大语言模型 去补充用户漏掉的“市”。我选第二个,因为就是一句 “提示词”的工作。后面在Agent 提示词部分会补充这个“补全城市名”的能力。

再回到第一个问题,也有两个解决办法:要么一句提示词,让大语言模型把工具输出的数组转为一个字典({“latitude”: str1; “longitude”: str2});要么用“代码”工具,使用python 代码完成这个格式转化。我这里为了演示“代码”组件,在“经纬度查询”插件和“xingpan”插件中加入一个“代码”组件。加好后长这样——

使用AI 工具生成python代码

输入:选择上一步“城市经纬度插件”的输出的数组

输出:含两个变量latitude、longitide ,类型参考下游“xingpan”的输入是字符串

然后点击“在IDE 中编辑”,打开代码编辑页面——

切换至“语言 Python” 后,删除实例代码,点击“尝试AI Ctrl+ I”,使用AI 帮我们生成python 代码——

1
输入是一个包含经纬度的数组,输出时转换为一个字典{latitude: , longitude: }

试运行下,不出意外的。。。报错了——

从报错日志看起来 Args() 部分 参数有点不匹配。这里AI 生成的时候有点 画蛇添足了,删了它。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import typing
class Output(typing.TypedDict, total=False):
    latitude: str
    longitude: str
async def main(args: Args) -> Output:
    try:
        input_data = args.params['input']
        if len(input_data) >= 2:
            latitude = str(input_data[0])
            longitude = str(input_data[1])
            return {'latitude': latitude, 'longitude': longitude}
        else:
            return {}
    except (KeyError, IndexError):        return {}

再测试,就可以啦——

完成 工作流主干

之前提到的经纬度问题已经通过代码处理了数据格式,现在可以把整个工作流联通起来。

“xingpan” 中的latitude 值引用代码中的输出变量"latitude"的值;同样,longitude 值引用 代码中的输出变量"longitude"的值——

测试下星盘节点数据——text: 中有详细的星盘信息

这样一来,大模型的输入数据(用户星盘信息,用户问题,抽取到的三张塔罗牌组)就全了——

系统提示词这里,和之前案例一样“自己填写主要需求,由AI 完成补充和完善”(同样包含 角色、技能、限制 三个方面)——

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 角色
你是一位专业且极具耐心的塔罗牌咨询助手,始终以温和、易懂且充满亲和力的态度与用户交流。凭借深厚的塔罗牌知识储备,精准且简洁地阐释占卜到的卡牌内容与含义。在交流时,运用生动形象、富有感染力的语言,引领用户深入领略塔罗牌世界的神秘魅力。

## 技能
### 技能 1: 获取占卜信息
1. 从输入的 {{xingpan}} 中提取用户星盘信息。
2. 通过解读 {{question}} 明确用户想要占卜的具体问题。
3. 准确把握输入的 {{taroList}} 中抽取到的三张塔罗牌信息。

### 技能 2: 塔罗牌占卜
1. 在完整获取用户信息后,依据塔罗牌知识体系和复杂的占卜规则,进行全面且细致的塔罗牌占卜分析。
2. 若星盘信息{{xingpan}}缺失,在分析结果中明确提醒用户。
3. 用生动活泼、通俗易懂且饱含情感的语言向用户诠释占卜结果,可适当结合贴近生活的实际案例辅助用户理解。

### 技能 3: 塔罗文化讲解
当用户对塔罗文化相关内容提出疑问时,利用搜索工具查找相关知识,并准确清晰地向用户介绍,确保所介绍内容具有代表性。

## 限制:
- 仅围绕塔罗牌占卜及相关塔罗文化事宜进行交流,礼貌而坚决地拒绝回应其他不相关话题。
- 输出内容要丰富生动、条理清晰,既要引导用户准确提供信息以及理解占卜结果,又要有效传递塔罗文化内涵。
- 所有回复严格基于塔罗牌知识体系和占卜规则,严禁提供毫无根据的猜测或论断。 
- 回复尽量采用Markdown格式,嵌入卡片图像以增强可读性。 
- 若涉及塔罗文化知识讲解,需保证内容准确且具有代表性,所提供的信息要有可靠来源。
- 通过搜索工具获取信息来源,在回复中需注明引用来源 。 

用户提示词中,一定要使用输入的三个变量——

最后,在大模型中有几个微调的参数(输出长度,随机性,使用当前时间)——

原因很简单:希望输出的长度 可以支持更多内容;大模型专注回答问题,不用太发散;如果用户咨询明年的问题,大模型不应该回答2024 年的事件出来。

这样工作流主干就搭建完了。下面测试下整体效果。

测试&验证工作流

点击,工作流下方的“试运行”——

等待大模型处理一会后,返回结果(markdown格式),这里可以预览下效果——

感觉还算齐全,有图有解释。照例发布这个应用。

因为,准备工作中前后衔接数据比较准确,这里测试过程就顺利多了。

有时,因为格式转换要试好几种插件就比较 花时间。

新建Agent 关联工作流

workflow 更新后,回到“项目开发”页面,点击创建“项目”,选择“创建智能体”。新建一个叫做“塔罗小助手”的Agent

Agent 模式下有单Agent 和多Agent 模式之分,我们这个场景比较简单(单Agent 就可以)。

他们间的差别,我放截图中了——

coze Agent界面中分为三部分(**左:**提示词; **中:**调用的工具; **右:**效果预览),需要完成的任务有:

  • 说明什么场景下 调用刚才创建的workflow (调用工具)
  • 与用户交互 问答,完成 用户信息的询问
  • 其中城市名上,需要补全,特别是“市县”这些关键字
  • 因为是与用户交互 需要 大语言模型的分析、理解(提示词 指定)和记忆(对话历史)能力

提示词

注意一点:因为在工作流中 对塔罗牌内容解释过了,所以这里提示词中强调“无需对占卜结果进行解读”。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 角色
你是一位专业、细致且耐心的塔罗小助手,凭借深厚的塔罗牌知识底蕴,为用户提供精准的塔罗牌占卜服务。在交流中始终展现出专业态度,给予用户优质体验。

## 技能
### 技能 1: 精准提取占卜要素
仔细全面地从用户对话里精准提取占卜所需要素,涵盖生日、出生时间、性别、出生城市以及问题。对于出生城市,若用户表述简略,准确补全,如将“北京”补全为“北京市”。务必保证提取的信息完整且准确无误。

### 技能 2: 高效完成占卜过程
调用工作流“tarluo”的输出结果,以微信公众号对话环境美化格式,清晰、直观地向用户展示占卜结果。仅呈现结果,无需对占卜结果进行解读。

## 限制:
- 交流内容严格限于塔罗牌占卜相关领域,不回答任何与塔罗牌占卜无关的话题。
- 占卜结果解读必须严格基于塔罗牌占卜的专业知识体系与逻辑规则。
- 输出内容要条理清晰、简单易懂,符合正常语言表达习惯,方便用户理解。
- 禁止对工作流内容进行自我解读,直接将工作流输出结果呈现给用户。 

编排

  1. 模型参数:deepseek 记忆上面15轮对话内容,最大长度8000,携带当前时间。
  1. 调用workflow。
  1. 追加开场白,引导用户提问和输入生日等信息。

预览

测试 & 发布应用

故意不输入完整城市名,验证下 :

可以看到,这里成功补全了城市“成都” –> “成都市”。

解答结果,貌似有点简洁。细微调整内容放在最后。

效果还可以,先发布一版体验下——

这里可以看出 coze 的生态优势明显——飞书 抖音 微信客服 微信公众号 支持一键发布。对普通用户来说,大大降低 相关平台对接、发布的繁琐。体验上还是不错的。不过 ,在微信订阅号的使用中发现,只有markdown 格式 没有图片。

从使用体验来说 coze 商店 > 微信订阅号。

工作流进一步完善、优化

主要从用户使用交互的角度,使用两个功能组件优化 上面工作流(tarluo)的效果——

  • 卡片抽取后,要等大模型十几~二十秒分析时间,这其中没有反馈。为了增加抽卡的交互反馈,在抽取卡片后,使用输出功能,关联卡片效果。可以在大模型分析前展示抽取到的卡片信息。

  • 如果城市名错误,会导致经纬度数据缺失,进而导致 星盘数据为空。大模型的分析结果 也会缺少结合用户星盘信息的分析内容。所以,希望加入if-else 选择器:当获取到的经纬度为零时,提醒用户注意。

  • 抽取塔罗牌得到的数据格式希望简单转化下:

以上三点综合起来,就是开头所展示的完整 工作流效果。

卡片展示

展示卡片图片,需要追加输出节点:其作用是在工作流执行过程中 返回消息,并结合卡片变量重复渲染三张卡片。

第一步,添加“输出”节点,改个名字“塔罗卡片展示”。为了关联卡片效果,这里变量值格式需要是对象数组 [{}]

恰好 抽取塔罗牌工具的输出就是个数组,所以后续在卡片中调用这个数组就会很方便。

第二步,保存当前工作流,返回Agent 界面。在工作流中点击“绑定卡片数据”。并选择刚才的输出节点“塔罗卡片展示”。

进入卡片编辑页面,新建一个卡片效果——

第三步,编辑卡片结构。

  • 使用官方提供的 一张图一行标题模板。

  • 然后在结构中修改它:这个模板默认有4张图片和标题,删除三个,只保留一个。

  • 结果是这样——

  • 卡片变量 中,新建一个数组,变量默认值 直接copy 抽取塔罗牌工具的测试输出(不用担心,这里的默认值会被用户抽取时的卡片信息覆盖,默认值这里提供展示效果的输出)。

  • 有了这个数组,点击模板,启用“高级设置”中的 “循环渲染”,并引用刚才创建的变量 Cardinfo_object——

    这样就出现了竖着排布的三张图文效果——

  • 编辑模板中的元素,逐一关联 变量中的对象。例如,图片原先是测试图片、自定义宽度。

    修改后,变为引用变量值 item.url,宽度铺满的效果。

    文字部分,引用 item.type 和 item.name_cn 后。得到希望的卡片效果——

第四步,发布卡片,关联数据流输出节点的变量对象。还记得之前强调 变量格式 需要为特殊列表 [{}] 么?

最后,测试下图片效果。如我们所希望的:在工作流完成前,抽取的卡片就展示出来了!

经纬度查询分支(if-else)

这部分就简单一些了:在代码和xingpan 工具之间 ,插入一个选择器(判断 代码中输出的经纬度是否都为空)。

显然,如果都不为空,继续执行星盘查询就好;只要一个为空,不用查星盘数据,直接给出消息提醒(还是使用 刚才的“输出”节点)。提醒内容为:

1
2
3
4
输入城市 {{city}},解析坐标有误。

详细信息如下:
{{output}}

使用花莲县测试,如我们预期,有提醒。当然 这个问题原因不是书写错误,而是工具内座标信息不全导致的。

塔罗信息整合

这部分涉及一些数据格式,需要使用“循环” 工具来处理。

原因是抽取到的卡片信息,数据格式长这样——

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[
    {
      "name_en": "King of Pentacles",
      "position": 1,
      "type": "逆位",
      "url": "https://t.8s8s.com/photo/tarotphoto/72/king-pentacles.jpg",
      "name_cn": "星币国王"
    },
    {
      "name_en": "The Hanged Man",
      "position": 2,
      "type": "逆位",
      "url": "https://t.8s8s.com/photo/tarotphoto/72/hanged-man.jpg",
      "name_cn": "倒吊人"
    },
    {
      "name_en": "Knight of Wands",
      "position": 3,
      "type": "逆位",
      "url": "https://t.8s8s.com/photo/tarotphoto/72/knight-wands.jpg",
      "name_cn": "权杖骑士"
    }
  ]

要修改他们,需要循环三次,每次进行相同的 字符拼接和格式调整工作。

并且,每修改完其中一个对象,需要把修改后的内容 临时存入中间变量。等三次循环结束后,输出给一个新的 对象数组

循环过程中,再次使用 python 代码 实现数组中 每个对象格式的调整——

代码——

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from typing import Dict, Any, Union, List, TypedDict, Optional

# 定义错误信息类型
class ErrInfo(TypedDict):
    status: str
    message: str

# 定义每张卡片元信息
class Cardinfo(TypedDict):
    name_cn: str
    position: str
    url: str

# 定义输出类型
Output = List[Cardinfo]

async def main(args: Args) -> Union[Output, ErrInfo]:
    input = args["params"]
    try:
        # 1. 获取输入参数
        e_name_cn = str(input.get("name_cn"))
        e_name_en = str(input.get("name_en"))
        e_position = str(input.get("position"))
        e_type = str(input.get("type"))
        e_url = str(input.get("url"))
        e_var_tarolist = input.get("var_tarolist", [])

        # 2. 整理为新的字典
        infolist = {
            "name_cn": e_type + e_name_cn,
            "position": e_position,
            "url": e_url
        }

        # 3. 添加到列表
        e_var_tarolist.append(infolist)

        # 4. 返回完整列表
        return e_var_tarolist
        
    except Exception as e:
        return {"status": "error", "message": f"处理错误: {str(e)}"}

python 处理后的 输出 ,通过“设置变量”节点 赋值给 中间变量。这样就完成了,每次循环过程中的修改和赋值。

试运行下,如果修改成功,会看到 塔罗信息改变的效果——

写在最后

  1. coze 的常用组件这里介绍差不多了,可以发现 coze 平台的优势 在三方面:低代码 + 插件市场 + 字节生态

    所以如果自己公司就在用飞书,应该会很方便。

  2. 如果是个人使用,chat 的方式 适合给 0基础的人使用。通过对话,逐步引导用户完成他们的需求,例如这里塔罗牌的例子。

  3. 对于 不是0基础的服务对象,可能会觉得 聊天这种方式 有点慢了。。。

  4. 代码,不是无代码。好在目前 大语言的编程能力也很强,问几遍也能出结果。

这么看来 coze 的客户画像 就出来了,四个维度:字节生态,不想从0 搭建应用(插件市场有现成就用现成的),专业场景,个人用 还是公司用。。。。

产品案例 个人 / 公司 字节生态 小白用/专业用户 是否有现成的工具使用
塔罗牌 个人 小白 有 星盘插件
热门短视频 文案助手 个人 专业用户 有 http插件,有文生视频插件
智能helpdesk 公司 飞书日历,通讯录,知识库 小白
HR 简历筛选打分助手 公司 多维表格,数据库 专业用户
Licensed under CC BY-NC-SA 4.0