首页 > 其他分享 >rasa 实现简易的多轮对话

rasa 实现简易的多轮对话

时间:2024-12-18 17:12:00浏览次数:3  
标签:多轮 const name rasa error 简易 intent date action

1.3 实现简易的多轮对话

1.3.1 场景描述

用户希望查询特定日期的天气信息。对话过程如下:

  1. 用户:你好
  2. 助手:你好!我可以帮你查询天气信息。你想查询哪一天的天气?
  3. 用户:今天天气怎么样
  4. 助手:今天是晴天,温度25°C。
  5. 用户:明天的天气如何
  6. 助手:你想查询明天的天气。那天是多云,温度22°C。

1.3.2 需求分析

对话系统能够处理用户询问天气信息的多轮对话

既然要尝试多轮对话就会涉及 data\nlu.yml、data\rules.yml、data\stories.yml、domain.yml、config.yml,还需要配置 endpoints.yml 实现对外部服务的调用(本文中用的是 node)

1.3.3 stories 设计

因为 rasa 是通过学习故事的方式来学习对话管理知识

本文进行了简单的设计

version: "3.1"

stories:
  - story: 用户询问天气,但未提供非今天日期
    steps:
      - intent: ask_weather
      - action: action_fetch_weather

  - story: 用户询问天气后提供非今天日期
    steps:
      - intent: ask_weather
      - action: action_fetch_weather
      - intent: inform
      - action: action_handle_inform

  - story: 完整对话
    steps:
      - intent: greet
      - action: utter_greet
      - intent: ask_weather
      - action: action_fetch_weather
      - intent: inform
      - action: action_handle_inform
      - intent: goodbye
      - action: utter_goodbye

本文中我去掉了rules,只是用 stories,规则的优先级是高于故事的,没用规则的时候 rasa 就会依赖 stories 执行了

1.3.4 自然语言处理(nlu)设计

examples 即为输入,intent 为处理完的意图

version: "3.1"

nlu:
- intent: greet
  examples: |
    - 你好
    - 嗨
    - 早上好
    - 晚上好

- intent: goodbye
  examples: |
    - 再见
    - 拜拜
    - 下次见

- intent: ask_weather
  examples: |
    - 今天天气怎么样
    - 你能告诉我天气吗
    - 今天会下雨吗

- intent: inform
  examples: |
    - [明天](date)的天气怎么样?
    - [后天](date)的温度是多少?
    - 那[下周](date)的天气如何?
    - [明天](date)会刮风吗?
    - 我想知道[下个月](date)的天气
    - 请告诉我[下周五](date)的气温
    - [昨天](date)的天气如何?    

1.3.5 domain 设计

domain(领域)定义了对话机器人需要知道的所有信息,包括意图(intent)、实体(entity)、词槽(slot)、动作(action)、表单(from)和回复(response)

当前设计涉及 intents、entities、slots、responses、actions

version: "3.1"

intents:
  - greet
  - goodbye
  - ask_weather
  - inform

entities:
  - date

slots:
  requested_date:
    type: text
    influence_conversation: false
    mappings:
      - type: from_entity
        entity: date

responses:
  utter_greet:
    - text: "你好!我可以帮你查询天气信息。你想查询哪一天的天气?"

  utter_goodbye:
    - text: "再见!祝你有美好的一天。"

  utter_ask_date:
    - text: "你想查询哪一天的天气?"


actions:
  - action_fetch_weather
  - action_handle_inform

session_config:
  session_expiration_time: 60
  carry_over_slots_to_new_session: true

1.3.6 action 设计

action 通过外部动作服务器实现,笔者这里用的是 node

需要在 endpoints.yml 配置

# 启动自定义输入通道
input_channel:
    custom_input_channel.CustomInputChannel
# 外部动作服务器配置
action_endpoint:
  url: "http://localhost:6001/webhook"

action_service.js 代码如下:

const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');  // 引入 fs 模块

const app = express();
const port = 6001;

// 中间件
app.use(bodyParser.json());
// 日志文件路径
const logFilePath = 'action_logs.txt';

// 接收 Rasa 的自定义动作请求
app.post('/webhook', (req, res) => {
    console.log('Received request:', req.body);
    const actionName = req.body.next_action;
    const tracker = req.body.tracker; // 包含槽位、会话上下文等
    console.log('Tracker Slots:', tracker.slots);  // 输出所有插槽值
    const requestedDate = tracker.slots.requested_date;
    console.log('Requested Date:', requestedDate);  // 输出 requested_date 的值
    const senderId = tracker.sender_id;

    // 日志记录格式
    const logMessage = `SenderId: ${senderId}, Action: ${actionName}, Time: ${new Date().toISOString()}\n`;

    // 将日志写入 .txt 文件
    fs.appendFileSync(logFilePath, logMessage, 'utf8');

    // 处理不同的自定义动作
    if (actionName === 'action_fetch_weather') {
        // 模拟返回天气信息
        const weatherInfo = "今天是晴天,温度25°C。";
        res.json({
            events: [], // 可以定义槽位更新事件
            responses: [{ text: weatherInfo }]
        });
    } else if (actionName === 'action_handle_inform') {
        const requestedDate = tracker.slots.requested_date || "未知日期";
        const followUpInfo = `你想查询${requestedDate}的天气。那天是多云,温度22°C。`;
        res.json({
            events: [],
            responses: [{ text: followUpInfo }]
        });
    } else {
        res.status(404).json({ error: "Action not found" });
    }
});

// 启动服务器
app.listen(port, () => {
    console.log(`Action service is running on http://localhost:${port}`);
});

然后需要启动 node 服务器

1.3.7 初次尝试

这里笔者配置的对话管理为

# 配置对话管理
policies:
  - name: "MemoizationPolicy"
    max_history: 5
  - name: RulePolicy
    core_fallback_action_name: "action_default_fallback"
    core_fallback_threshold: 0.3
    enable_fallback_prediction: True

其中 config.yml 整体配置为

recipe: default.v1
assistant_id: 20241120-170106-khaki-margarine
language: zh # 改为中文
pipeline: 
  - name: JiebaTokenizer  # 使用支持中文的 Tokenizer
  - name: CountVectorsFeaturizer
    analyzer: "word"  # 基于分词结果生成特征
    min_ngram: 1
    max_ngram: 2
  - name: DIETClassifier
    epochs: 100
  - name: EntitySynonymMapper
policies:
  - name: "MemoizationPolicy"
    max_history: 5
  - name: RulePolicy
    core_fallback_action_name: "action_default_fallback"
    core_fallback_threshold: 0.3
    enable_fallback_prediction: True

在 rules.yml 中简单配置了一个规则

version: "3.1"

rules:
- rule: 问候规则
  steps:
  - intent: greet
  - action: utter_greet

测试前别忘了先训练模型

rasa train

先使用

rasa shell

测试

image-20241218110810659

显然这结果不是我们想得到的,明天天气并没有回答直接退出了

下面命令是验证模型是否正确提取了实体

rasa shell nlu

可以进行实体解读

NLU model loaded. Type a message and press enter to parse it.
Next message:
明天天气怎么样
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\rmc\AppData\Local\Temp\jieba.cache
Loading model cost 0.378 seconds.
Prefix dict has been built successfully.
{
  "text": "明天天气怎么样",
  "intent": {
    "name": "inform",
    "confidence": 0.9999532699584961
  },
  "entities": [
    {
      "entity": "date",
      "start": 0,
      "end": 2,
      "confidence_entity": 0.998909592628479,
      "value": "明天",
      "extractor": "DIETClassifier",
      "processors": [
        "EntitySynonymMapper"
      ]
    }
  ],
  "text_tokens": [
    [
      0,
      2
    ],
    [
      2,
      4
    ],
    [
      4,
      7
    ]
  ],
  "intent_ranking": [
    {
      "name": "inform",
      "confidence": 0.9999532699584961
    },
    {
      "name": "goodbye",
      "confidence": 3.9685361116426066e-05
    },
    {
      "name": "greet",
      "confidence": 5.52419078303501e-06
    },
    {
      "name": "ask_weather",
      "confidence": 1.507416641288728e-06
    }
  ]
}
Next message:

显然 entities 解读出来了

接下来使用调试模式

rasa shell --debug

tips:使用这个命令之后继续对话,state 还会继续记录,即使使用 /exit 停止之后,继续对话,对话 state 还是会保留,最好直接退出 Rasa Shell,也就是使用 Ctrl+c;或者 /stop 也可以

根据调试记录

2024-12-18 15:52:43 DEBUG    rasa.core.policies.memoization  - [debug    ] memoization.predict.actions    tracker_states=[{'user': {'intent': 'greet'}, 'prev_action': {'action_name': 'action_listen'}}, {'user': {'intent': 'greet'}, 'prev_action': {'action_name': 'utter_greet'}}, {'user': {'intent': 'ask_weather'}, 'prev_action': {'action_name': 'action_listen'}}, {'user': {'intent': 'ask_weather'}, 'prev_action': {'action_name': 'action_fetch_weather'}}, {'user': {'intent': 'inform', 'entities': ('date',)}, 'prev_action': {'action_name': 'action_listen'}}]
2024-12-18 15:52:43 DEBUG    rasa.core.policies.memoization  - There is no memorised next action

经过测试 Memoization Policy 无法完成后面的记忆,实力有点弱,按理说我的 stories 都给他设计好了,不应该无法获取下一个 action

1.3.8 启用 TEDPolicy

TED(Transformer Embedding Dialogue) Policy, Transformer 嵌入对话策略是一种用于下一步动作预测和实体识别的多任务架构

该架构由两个任务共享的多个 Transformer 编码器组成

在输入词条序列的对应用户序列 Transformer 编码输出上通过一个条件随机场(CRF)预测实体标签序列

对于下一个动作预测,对话 Transformer 编码器输出和系统动作标签被嵌入到同一个语义向量空间中

我们使用点积损失来最大化与目标标签的相似性,同时最小化与负样本的相似性

官网的介绍就比较专业

总之这个策略比较牛,主要用于下一步动作预测实体识别,核心用的是 transformer,还用了多个 transformer 编码器

# 配置对话管理
policies:
  - name: "MemoizationPolicy"
    max_history: 5
  - name: TEDPolicy
    max_history: 5
    epochs: 100

修改一下对话策略

image-20241218163800498

加上 TEDpolicy 之后直接秒了

1.3.9 Apipost交互

input_service.js 的代码

const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');

const app = express();
const port = 6000;

// 中间件
app.use(bodyParser.json());

const sessions = {}; // 用于存储 sender_id 和上下文

// 定义与 Rasa 交互的端点
app.post('/input', async (req, res) => {
    const userMessage = req.body.message;
    const userId = req.body.sender || 'default';

    if (!userMessage) {
        return res.status(400).json({ error: "Message is required" });
    }

    try {
        // 获取用户上下文
        if (!sessions[userId]) {
            sessions[userId] = { id: userId, context: {} };
        }
        // 发送用户消息到 Rasa
        const response = await axios.post('http://127.0.0.1:5005/webhooks/rest/webhook', {
            sender: sessions[userId].id,
            message: userMessage
        });

        // 收集 Rasa 的响应
        const rasaResponses = response.data.map(r => r.text).filter(text => text);

        // 返回响应到客户端
        res.json({ replies: rasaResponses });
    } catch (error) {
        console.error('Error communicating with Rasa:', error.message);

        // 检查 Axios 错误并处理
        if (error.response) {
            // Rasa 服务返回了非 2xx 状态
            res.status(error.response.status).json({ error: error.response.data });
        } else if (error.request) {
            // 没有收到 Rasa 的响应
            res.status(500).json({ error: 'No response from Rasa server' });
        } else {
            // 请求配置错误
            res.status(500).json({ error: 'Request error: ' + error.message });
        }
    }
});

// 启动服务器
app.listen(port, () => {
    console.log(`Node.js server is running on http://localhost:${port}`);
});

我们还可以在 package.json 中配置

  "scripts": {
    "start:input": "node input_service.js",
    "start:action": "node action_service.js",
    "start": "concurrently \"npm run start:input\" \"npm run start:action\""
  },

方便一下子打开两个服务

rasa 启动!!

rasa run -m models --enable-api --cors "*" --debug
image-20241218164821045 image-20241218164927016

不得不说 TEDPolicy 的实力强大

END

本文主要制作了简易的多轮对话,包括场景描述,需求分析以及实现的设计,会涉及 nlu、rules、stories、domain、config、endpoints,其中 rules 最后没用,主要用到 menmories Policy 和 TED Policy,menmories Policy在使用过程中遇到了问题,笔者多次尝试未果,动作服务器和交互都使用的 Node.js 实现,最终实现简单的多轮对话

标签:多轮,const,name,rasa,error,简易,intent,date,action
From: https://www.cnblogs.com/goicandoit/p/18615391

相关文章

  • AlertDialog的简易使用
    警告弹窗(AlertDialog)使用文档一、概述警告弹窗(AlertDialog)用于向用户显示重要信息或获取用户的确认反馈。它从APIVersion7开始支持,在元服务中从APIversion11开始支持,其功能依赖UI的执行上下文,不可在UI上下文不明确的地方使用(从APIversion10开始,可通过UIContext......
  • Flask入门:打造简易投票系统
    目录准备工作创建项目结构编写HTML模板编写Flask应用代码解读进一步优化结语Flask,这个轻量级的PythonWeb框架,因其简洁和易用性,成为很多开发者入门Web开发的首选。今天,我们就用Flask来做一个简单的投票系统,让你快速上手Web开发,同时理解Flask的核心概念。准备工作......
  • C#实现一个HttpClient集成通义千问-多轮对话功能实现
    多轮对话功能实现视频教程实现原理消息的类型功能开发消息类修改请求体修改发送请求函数修改用户消息输入多轮对话的token消息完整文档消息类型视频教程.Net+AI开发入门HttpClient实现通义千问集成-多轮对话功能实现实现原理一直保留更新messages现在设......
  • Java代码执行流程(简易易懂版)上部
    很多同学刚开始学java时看懂了怎么用,却不知道他内存怎么运行的过程,所以会感觉很迷茫,感觉白学了,我也和大家一样,这里我用了三天的时间给大家整理了代码执行时的过程,并且注意的一些事项,如果有不对的地方请大家指出,我在改正我们先定义一个A类在main函数创建A类的对象实例我们来......
  • 简易记事本项目—基于SSM+Vue前后端分离
    ......
  • Python爬取数据插入mysql(简易记录)
    importmysql.connectorimportrequestsfromlxmlimporthtml#连接MySQL数据库db=mysql.connector.connect(host="?",user="?",password="?",database="?")cursor=db.cursor()company_url=......
  • Java-25 深入浅出 Spring - 实现简易Ioc-01 Servlet介绍 基本代码编写
    点一下关注吧!!!非常感谢!!持续更新!!!大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html案例思路参考来源来自网络视频,这里的案例是转账的案例。这里我们直接使用接口的方式,就不实现具体的页面了,我们直接通过接口调用的方式来模拟这一块。最终将实......
  • node和rasa交互之可通信
    1.2通过node和rasa交互1.2.1配置更改config.yml#Theconfigrecipe.#https://rasa.com/docs/rasa/model-configuration/recipe:default.v1#Theassistantprojectuniqueidentifier#Thisdefaultvaluemustbereplacedwithauniqueassistantnamewithinyo......
  • rasa 最简单对话实现
    1.1简单实现1.1.1定义意图和实体最简单的就是data/nlu.yml中编辑version:"3.1"nlu:-intent:greetexamples:|-你好-嗨-早上好-晚上好-intent:goodbyeexamples:|-再见-拜拜-下次见-intent:ask_weatherex......
  • JQuery 实现简易记事簿
    这里运用到的技术:1、localStorage保存数据到浏览器,和从浏览器中localStorage取到数据2、JSON.stringify()方法和JSON.parse()方法的运用3、setSelectionRange()方法4、数组的prop添加内容,splice(i,1)删除和替换splice(i,0,'aa')5、attr()和prop()6、on绑定事件html:<ht......