“/”应用程序中的服务器错误。
无法为具有固定名称“MySql.Data.MySqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.10.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d”。请确保使用限定程序集的名称且该程序集对运行的应用程序可用。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=260882。
打开 nuget,卸载并重新安装 MySql.Data 和 MySql.Data.Entity
如果没有效果,打开 web.config,检查是否多次定义了 MySql.Data.MySqlClient

“/”应用程序中的服务器错误。
未能加载文件或程序集“MySql.Data.Entity.EF6, Version=6.10.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)
6.10.7 版本的 MySql.Data.Entity.EF6.dll 文件发布后会自动变成 6.10.6,文件大小一样,只要用 FTP 上传覆盖一下即可。

“/”应用程序中的服务器错误。
The host xxx does not support SSL connections.
说明: 执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。
异常详细信息: MySql.Data.MySqlClient.MySqlException: The host xxx does not support SSL connections.
在 Web.config 的 connectionStrings 节点找到相关数据库连接,在 connectionString 中增加属性值:SslMode=none

如果 .NET Core 项目发布到服务器上报以下错误:
HTTP Error 502.5 - Process Failure
Common causes of this issue:
The application process failed to start
The application process started but then stopped
The application process started but failed to listen on the configured port
Troubleshooting steps:
Check the system event log for error messages
Enable logging the application process' stdout messages
Attach a debugger to the application process and inspect
For more information visit: https://go.microsoft.com/fwlink/?LinkID=808681
在服务器上安装最新的 .NET Core Runtime 即可(Windows Server 选择 Hosting Bundle Installer)。

本文未完成,部分测试方法、条件或结果可能有误,请谨慎参考! :)
本文基于 MySQL 的 InnoDB BTREE 方法的索引进行测试。
以一张包含 2000 万条记录的表做实验:
CREATE TABLE `dt_read` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime(0) NOT NULL,
`a_id` int(11) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
);
这张表是用于记录文章点击量的,
`id` 为主键,int(11) 自增;
`time` 为非空 datetime,表示文章打开时间,测试数据是从 2017-03-11 至 2018-04-28;
`a_id` 为非空 int(11),表示文章 ID,在此表中不唯一,测试数据是从 1 至 260218。
体验“全表扫描”
首先来体验一下什么是全表扫描,执行下面语句:
SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' LIMIT 10
> 时间: 0.012s
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' LIMIT 10
> 时间: 7.317s
表中数据是按主键从小到大排列的,当查询条件为 `time` < '2020-1-1' 时,能很快地从表的前端找到 10 条满足条件的数据,所以不再继续判断后面的记录,立刻返回结果,耗时 0.012 秒;但当条件改为 `time` < '2000-1-1' 时,同样逐条判断,直到最后一条也没有找到,这种情况就是所谓的“全表扫描”,耗时 7 秒。
索引对 ORDER BY 的 ASC 和 DESC 的影响
我们给 `time` 建一个索引,同样执行刚才需要全表扫描的语句:
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' LIMIT 10
> 时间: 0.012s
创建 `time` 的索引后,相当于生成了一张按 `time` 字段排列的新表,这时 MySQL 就能够很快地定位并找到符合条件的记录,避免了全表扫描。
试试按 `time` 倒序排:
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `time` DESC LIMIT 10
> 时间: 0.013s
结论:索引对 ORDER BY 的顺序(ASC)和倒序(DESC)都是有效的。
索引字段的次序对 WHERE 和 ORDER BY 的影响
删除所有索引,创建一个新的索引,字段依次为 `time`, `a_id`。
分别执行以下查询:
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' AND `a_id` < 0 LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` WHERE `a_id` < 0 AND `time` < '2000-1-1' LIMIT 10
> 时间: 0.013s
结论:MySQL 会自动优化 WHERE 条件的次序来匹配最合适的索引。
但在 ORDER BY 中却不是这么回事了:
SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` ORDER BY `a_id`, `time` LIMIT 10
> 时间: 14.066s
原因也很好理解,对两个字段进行排序,先后次序肯定会影响结果集,因此只能以 SQL 语句指定的字段次序来 ORDER BY,这样,按索引的字段次序进行 ORDER BY 查询无疑是更快的。
索引中的字段必须依次使用
保持上例创建的索引不变,即 `time`, `a_id`。
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' AND `a_id` < 0 LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` WHERE `a_id` < 0 LIMIT 10
> 时间: 6.438s
上句合理利用了索引的字段,而下句跳过了 `time`,直接 WHERE 了 `a_id`,这是不受该索引支持的。
我们可以想象一下这张由索引生成的虚拟表,其实就是一张普通的平面二维表格,按索引指定的字段次序进行了排序,所以全表中仅仅是索引指定的第一个字段是按大小排列的,第二个字段是在第一个字段值相同的区域内按大小排列,后同。所以,跳过索引指定的第一个字段直接对第二个字段进行检索,是无法应用该索引的。这个结论也同样也体现在 ORDER BY 语句中:
SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` ORDER BY `a_id` LIMIT 10
> 时间: 29.566s
WHERE 和 ORDER BY 混合
保持上例创建的索引不变,即 `time`, `a_id`。
先来执行这两句:
SELECT * FROM `dt_read` ORDER BY `a_id` LIMIT 10
> 时间: 12.29s
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 0.013s
仅仅 WHERE 了一个 `time`,对 ORDER BY `a_id` 的效率却有质的提升,是因为 WHERE 中的 `time` 和 ORDER BY 中的 `a_id` 一起找到了索引吗?答案是否定的。
我们把时间改大,让它能马上找到符合条件的数据:
SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 22.34s
为什么这个语句就不走索引了呢?
其实,一个简单的 SELECT 查询语句,首先执行 WHERE,然后 ORDER BY,最后是 LIMIT。每一步都独自去找了索引,而非 WHERE 和 ORDER BY 混在一起去找索引。必须保证每一步是快的,最终才是快的。
当 `time` < '2000-1-1' 时,WHERE 用到了索引,所以很快,ORDER BY 却没有用到索引,但为什么也很快呢?因为 WHERE 的结果集非常小(示例中为 0 条)。
当 `time` < '2020-1-1' 时,WHERE 也用到了索引,但其结果集非常大(示例中为所有记录),再 ORDER BY `a_id` 就非常慢了,因为我们没有创建以 `a_id` 开头的索引。
现在把索引改成只有 `time` 一个字段。
SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 6.033s
因为索引里有 `
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` WHERE `a_id` < 0 ORDER BY `time` LIMIT 10
> 时间: 6.033s
第二句先 WHERE `a_id`,后 ORDER BY `time` 是不能匹配所建的索引的。
索引中的字段越多越好
分别在创建索引(`time`)和索引(`time`, `a_id`)的情况下执行下面语句:
本例使用 ORDER BY 而不是 WHERE 来测试是因为,在 WHERE 的多个条件下,如果符合前一条件的筛选结果集过小会导致判断第二条件时数据量不足,无法判断索引是否起作用。
SELECT * FROM `dt_read` ORDER BY `time` LIMIT 10
仅创建索引(`time`)的情况下:
> 时间: 0.013s
仅创建索引(`time`, `a_id`)的情况下:
> 时间: 0.013s
SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10
仅创建索引(`time`)的情况下:
> 时间: 15.015s
仅创建索引(`time`, `a_id`)的情况下:
> 时间: 0.014s
可以看到,在索引字段依次使用的前提下,索引字段数不少于查询字段数才能避免全表扫描。
虽然索引中的字段越多越好,但必须依次使用,否则也是无效索引。
索引对 INSERT / UPDATE / DELETE 的效率影响
分别在创建索引(`time`)和索引(`time`, `a_id`)的情况下执行下面语句:
INSERT INTO `dt_read` (`time`, `a_id`) VALUES ('2018-4-28', 260218)
不建索引的情况下:
> 时间: 0.01s
仅创建索引(`time`)的情况下:
> 时间: 0.01s
同时创建索引(`time`)和索引(`time`, `a_id`)的情况下:
> 时间: 0.01s
UPDATE `dt_read` SET `time` = '2018-4-28' WHERE `id` = 20000000(注:存在该 id 值的记录)
不建索引的情况下:
> 时间: 0.01s
仅创建索引(`time`)的情况下:
> 时间: 0.01s
同时创建索引(`time`)和索引(`time`, `a_id`)的情况下:
> 时间: 0.01s
虽然在 INSERT / UPDATE / DELETE 时数据库会更新索引,但从实测数据来看,索引对其效率的影响可忽略不计。
一些误区
“in 语法效率很低”?
in 语法也是应用索引的,网传 in 会比一个一个 WHERE OR 要慢得多的说法是不靠谱的。in 主键和 in 索引同理。
另外:
对于字符串类型,LIKE '%abc%' 是不能应用索引的,但 LIKE 'abc%' 可以。更多关于字符串类型的索引,请查阅全文索引(FULLTEXT)。
索引的字段是可以指定长度的,类似字符串索引指定前面若干唯一字符就可以优化效率。
本文系个人实践总结,欢迎批评指正!

删除主题时,会变更主题表 pre_forum_thread:
displayorder = -1
moderated = 1
帖子表(或分表) pre_forum_post* 该主题的所有帖子的
invisible = -1
同时 pre_forum_threadmod 会新增一条 DEL 记录,用于显示到 Discuz! 控制面板 - 内容 - 主题回收站。
删除回帖时,会变更回帖表 pre_forum_post(或分表):
invisible = -5
屏蔽回复时,会变更回复表 pre_forum_post(或分表):
status = 1

短信通判断了 30 天内提交错误验证码超过 5 次会有该提示,代码:
可按实际情况进行修改,涉及文件:
/source/plugin/smstong/smstong.class.php
/source/class/class_member.php

Code 128 条形码可以表示全部 128 个 ASCII 码字符(数字、大小写字母、符号和控制符)。
Code 128 有三个子集:Code 128A、Code 128B 和 Code 128C。
Code 128A 用来编码数字、大写字母、控制符、部分符号;
Code 128B 用来编码数字、大写字母、小写字母、符号;
Code 128C 用来编码 2 位数字 [00,99]。
码条大小有 4 种,按照宽度,我们用 1、2、3、4 来表示。
一个完整的 Code 128 条码是由:起始符、字符串、检验位、终止符组成的。
每个字符由 3 个条、3 个空、11 个单元构成,字符串可变长。
黑色线条用 B(Bar)表示,白色空位用 S(Space)表示,那么一个字符是由 BSBSBS 组成的。
起始符有三种,“CODE-A”、“CODE-B”和“CODE-C”。起始符的类型决定了后面字符的构成。
当采用码来设置字符时(CODE-A、CODE-B 和 CODE-C),起始符为 CODE-A 的条码在条码的处理中可以变为采用 CODE-B 或 CODE-C 栏的字符。
当采用“SHIFT”时,只有紧靠 SHIFT 的一个字符可以在下一栏被变更(A 到 B,B 到 C,C 到 A)。(和电脑键盘上的 SHIFT 键操作类似)
校验字符通过 MOD103 算法,下面举例说明:
例 1:xoyozo.net
信息:StartB x o y o z o . n e t
值:104 88 79 89 79 90 79 14 78 69 84
位置:- 1 2 3 4 5 6 7 8 9 10
计算:104 + 88 * 1+ 79 * 2 + 89 * 3 + 79 * 4 +90 * 5 + 79 * 6 + 14 * 7 + 78 * 8 + 69 * 9 + 84 * 10 = 4040
取模:4040 % 103 = 23
完整的条形码信息: (Start B)xoyozo.net(7)(STOP)
例 2:C08244
信息:StartB C 0 CodeC 82 44
值:104 35 16 99 82 44
位置:- 1 2 3 4 5 6
计算:104 + 35 * 1+ 16 * 2 + 99 * 3 + 82 * 4 +44 * 5 = 1016
取模:1016 % 103 = 89
完整的条形码信息: (Start B)C0(CodeC)8244(y)(STOP)
在线示例:https://xoyozo.net/Tools/Code128
附 Code 128 编码表:
Value | Code A | Code B | Code C | Pattern | ASCII Code |
BSBSBS | |||||
0 | SP | SP | 00 | 212222 | SP (ASCII 32) |
1 | ! | ! | 01 | 222122 | ! (ASCII 33) |
2 | " | " | 02 | 222221 | " (ASCII 34) |
3 | # | # | 03 | 121223 | # (ASCII 35) |
4 | $ | $ | 04 | 121322 | $ (ASCII 36) |
5 | % | % | 05 | 131222 | % (ASCII 37) |
6 | & | & | 06 | 122213 | & (ASCII 38) |
7 | ' | ' | 07 | 122312 | ' (ASCII 39) |
8 | ( | ( | 08 | 132212 | ( (ASCII 40) |
9 | ) | ) | 09 | 221213 | ) (ASCII 41) |
10 | * | * | 10 | 221312 | * (ASCII 42) |
11 | + | + | 11 | 231212 | + (ASCII 43) |
12 | , | , | 12 | 112232 | , (ASCII 44) |
13 | - | - | 13 | 122132 | - (ASCII 45) |
14 | . | . | 14 | 122231 | . (ASCII 46) |
15 | / | / | 15 | 113222 | / (ASCII 47) |
16 | 0 | 0 | 16 | 123122 | 0 (ASCII 48) |
17 | 1 | 1 | 17 | 123221 | 1(ASCII 49) |
18 | 2 | 2 | 18 | 223211 | 2 (ASCII 50) |
19 | 3 | 3 | 19 | 221132 | 3 (ASCII 51) |
20 | 4 | 4 | 20 | 221231 | 4 (ASCII 52) |
21 | 5 | 5 | 21 | 213212 | 5 (ASCII 53) |
22 | 6 | 6 | 22 | 223112 | 6 (ASCII 54) |
23 | 7 | 7 | 23 | 312131 | 7 (ASCII 55) |
24 | 8 | 8 | 24 | 311222 | 8 (ASCII 56) |
25 | 9 | 9 | 25 | 321122 | 9 (ASCII 57) |
26 | : | : | 26 | 321221 | : (ASCII 58) |
27 | ; | ; | 27 | 312212 | ; (ASCII 59) |
28 | < | < | 28 | 322112 | < (ASCII 60) |
29 | = | = | 29 | 322211 | = (ASCII 61) |
30 | > | > | 30 | 212123 | > (ASCII 62) |
31 | ? | ? | 31 | 212321 | ? (ASCII 63) |
32 | @ | @ | 32 | 232121 | @ (ASCII 64) |
33 | A | A | 33 | 111323 | A (ASCII 65) |
34 | B | B | 34 | 131123 | B (ASCII 66) |
35 | C | C | 35 | 131321 | C (ASCII 67) |
36 | D | D | 36 | 112313 | D (ASCII 68) |
37 | E | E | 37 | 132113 | E (ASCII 69) |
38 | F | F | 38 | 132311 | F (ASCII 70) |
39 | G | G | 39 | 211313 | G (ASCII 71) |
40 | H | H | 40 | 231113 | H (ASCII 72) |
41 | I | I | 41 | 231311 | I (ASCII 73) |
42 | J | J | 42 | 112133 | J (ASCII 74) |
43 | K | K | 43 | 112331 | K (ASCII 75) |
44 | L | L | 44 | 132131 | L (ASCII 76) |
45 | M | M | 45 | 113123 | M (ASCII 77) |
46 | N | N | 46 | 113321 | N (ASCII 78) |
47 | O | O | 47 | 133121 | O (ASCII 79) |
48 | P | P | 48 | 313121 | P (ASCII 80) |
49 | Q | Q | 49 | 211331 | Q (ASCII 81) |
50 | R | R | 50 | 231131 | R (ASCII 82) |
51 | S | S | 51 | 213113 | S (ASCII 83) |
52 | T | T | 52 | 213311 | T (ASCII 84) |
53 | U | U | 53 | 213131 | U (ASCII 85) |
54 | V | V | 54 | 311123 | V (ASCII 86) |
55 | W | W | 55 | 311321 | W (ASCII 87) |
56 | X | X | 56 | 331121 | X (ASCII 88) |
57 | Y | Y | 57 | 312113 | Y (ASCII 89) |
58 | Z | Z | 58 | 312311 | Z (ASCII 90) |
59 | [ | [ | 59 | 332111 | [ (ASCII 91) |
60 | \ | \ | 60 | 314111 | \ (ASCII 92) |
61 | ] | ] | 61 | 221411 | ] (ASCII 93) |
62 | ^ | ^ | 62 | 431111 | ^ (ASCII 94) |
63 | _ | _ | 63 | 111224 | _ (ASCII 95) |
64 | NUL | ` | 64 | 111422 | ` (ASCII 96) |
65 | SOH | a | 65 | 121124 | a (ASCII 97) |
66 | STX | b | 66 | 121421 | b (ASCII 98) |
67 | ETX | c | 67 | 141122 | c (ASCII 99) |
68 | EOT | d | 68 | 141221 | d (ASCII 100) |
69 | ENQ | e | 69 | 112214 | e (ASCII 101) |
70 | ACK | f | 70 | 112412 | f (ASCII 102) |
71 | BEL | g | 71 | 122114 | g (ASCII 103) |
72 | BS | h | 72 | 122411 | h (ASCII 104) |
73 | HT | i | 73 | 142112 | i (ASCII 105) |
74 | LF | j | 74 | 142211 | j (ASCII 106) |
75 | VT | k | 75 | 241211 | k (ASCII 107) |
76 | FF | l | 76 | 221114 | l (ASCII 108) |
77 | CR | m | 77 | 413111 | m (ASCII 109) |
78 | SO | n | 78 | 241112 | n (ASCII 110) |
79 | SI | o | 79 | 134111 | o (ASCII 111) |
80 | DLE | p | 80 | 111242 | p (ASCII 112) |
81 | DC1 | q | 81 | 121142 | q (ASCII 113) |
82 | DC2 | r | 82 | 121241 | r (ASCII 114) |
83 | DC3 | s | 83 | 114212 | s (ASCII 115) |
84 | DC4 | t | 84 | 124112 | t (ASCII 116) |
85 | NAK | u | 85 | 124211 | u (ASCII 117) |
86 | SYN | v | 86 | 411212 | v (ASCII 118) |
87 | ETB | w | 87 | 421112 | w (ASCII 119) |
88 | CAN | x | 88 | 421211 | x (ASCII 120) |
89 | EM | y | 89 | 212141 | y (ASCII 121) |
90 | SUB | z | 90 | 214121 | z (ASCII 122) |
91 | ESC | { | 91 | 412121 | { (ASCII 123) |
92 | FS | | | 92 | 111143 | | (ASCII 124) |
93 | GS | } | 93 | 111341 | } (ASCII 125) |
94 | RS | ~ | 94 | 131141 | ~ (ASCII 126) |
95 (Hex 7F) | US | DEL | 95 | 114113 | DEL (ASCII 127) |
96 (Hex 80) | FNC 3 | FNC 3 | 96 | 114311 | Ç (ASCII 128) |
97 (Hex 81) | FNC 2 | FNC 2 | 97 | 411113 | ü (ASCII 129) |
98 (Hex 82) | SHIFT | SHIFT | 98 | 411311 | é (ASCII 130) |
99 (Hex 83) | CODE C | CODE C | 99 | 113141 | â (ASCII 131) |
100 (Hex 84) | CODE B | FNC 4 | CODE B | 114131 | ä (ASCII 132) |
101 (Hex 85) | FNC 4 | CODE A | CODE A | 311141 | à (ASCII 133) |
102 (Hex 86) | FNC 1 | FNC 1 | FNC 1 | 411131 | å (ASCII 134) |
103 (Hex 87) | START (Code A) | 211412 | ‡ (ASCII 135) | ||
104 (Hex 88) | START (Code B) | 211214 | ˆ (ASCII 136) | ||
105 (Hex 89) | START (Code C) | 211232 | ‰ (ASCII 137) | ||
106 (Hex 6A) | STOP (All Codes) | 2331112 | Š (ASCII 138) |

* 仅列出部分数据表及字段
mag_ads 广告
名 | 类型 | 注释 |
id | int | |
title | varchar | 标题 |
pic | varchar | 图片 |
begin_time | int | 开始时间 |
end_time | int | 结束时间 |
link | varchar | 链接 |
…… |
mag_circle 圈子(相当于 PC 版的版块)
名 | 类型 | 注释 |
id | int | |
name | varchar | 名称 |
…… |
mag_common_applaud_(n) 点赞详情表(分表)n 与 content_id 末位一致
名 | 类型 | 注释 |
id | int | |
type | varchar | 类型(主题/回复/打卡等) |
content_id | int | 内容 id,末位与表名后缀一致 |
user_id | int | 用户 id |
status | int | 1:点赞;0:取消点赞 |
mag_common_attachment_(n) 附件详情表(分表)n 与 aid 末位一致
名 | 类型 | 注释 |
aid | int | 附件ID(末位与表名后缀一致) |
…… |
mag_common_attachment_index 附件索引表
名 | 类型 | 注释 |
id | int | 附件ID |
user_id | int | 用户ID |
table_id | int | 分表标志 |
…… |
mag_common_comment_index 评论索引表(相当于 PC 版的回复表)
名 | 类型 | 注释 |
id | int | 附件ID |
content_id | int | 文章ID(mag_show_content) |
user_id | int | 用户ID |
content | varchar | 评论内容 |
…… |
mag_common_content_log 对圈子内容的操作日志(点赞、评论等)
名 | 类型 | 注释 |
id | int | |
type | varchar | 用户ID |
type_value | int | 内容ID |
user_id | int | 用户ID |
create_time | int | 操作时间 |
…… |
mag_show_content 圈子内容(相当于 PC 版的主题表)
名 | 类型 | 注释 |
id | int | |
user_id | int | 用户ID |
content | varchar | 内容 |
pics | varchar | 以半角逗号分隔的图片(附件)id |
…… |
mag_user 用户表
名 | 类型 | 注释 |
user_id | int | 用户ID |
name | varchar | 用户名/昵称 |
head | varchar | 头像 |
…… |

阅读官方文档了解基本概念和逻辑:http://doc-bot.tmall.com/docs/doc.htm?treeId=393&articleId=106714&docType=1
文档中的示例是以自己创建的实体来示范的,如果我们需要创建一个“找工作”的意图,那么不可能把所有的工种一一添加到实体中,必须在语料中使用 sys:any 这个系统实体来匹配工种。例如:
添加语料:我想找会计的工作。把会计标注为 sys:any,参数名称可以取为 job,必选,追问语句:请说出您要找的工作类型。这样,Web Hook 就能接收到参数 job 了,而且值可以为用户说的任意工种。
同样的,我们可以再添加一个“租房子”的意图。
有了两个或多个意图后,必须选择其中一个为默认意图。如果我们的意图功能有同等的重要性,那么可以再添加一个入口意图,引导用户自己选择需要使用的意图,实现类似 10086 的层级选项。
添加一个“想干嘛”的意图并设为默认,不管是否添加语料或参数,意图完成时最终还是会调用 Web Hook,那么我们就在 Web Hook 中返回引导语句,如:您要找工作还是租房子呢?
然后在“找工作”意图中添加语料:我要找工作。在“租房子”意图中添加语料:我要租房子。顺利引导用户进入相应意图。
为了避免对话中断,需要在“想干嘛”意图的 Web Hook 中将 resultType 设为 ASK_INF。
AliGenie 的语料匹配过程并非是:
先匹配语料填充参数,再通过追问语句引导匹配其它语料填充剩余必选参数,最后调用 Web Hook。
我觉得更应该是:
先匹配语料填充参数,再通过追问语句引导单个填充剩余必选参数,最后调用 Web Hook。
也就是说追问语句应尽量引导用户回答单个实体值(如在查天气示例中:“明天”、“北京”,加一些助词之类的也是没问题的,毕竟是人工智能嘛),而不是引导用户去匹配其它语料。
当我们成功完成一次找工作的检索后,如果用户想继续询问其它岗位,需要再重新匹配一次语料。添加连续对话语料可以实现更简洁的对话场景:
连续对话语料:厨师呢
其中厨师标注为 job
可以实现如下对话:
我要找工作
请说您要找的工作类型
驾驶员
没有找到关于 驾驶员 的招聘信息。
司机呢
为您找到 司机 的招聘信息。 xxxxxxxx
如果没有这个连续对话语料,那么最后一句的回答是:对不起,我不明白你的意思
为了提高用户体验,当成功返回用户所要找的工作信息时追问“重听还是下一条?”,这时我在连续对话语料或普通语料中添加“重听”,会丢失 job 参数,直接返回了 job 参数的追问语句,无法调用 Web Hook,期待大家指点迷津。
更多需要注意的地方:
修改配置(意图、实体等信息)后,如果在线测试未即时生效,可能有缓存或 bug,第二天再继续测试,当然也不排除配置有误,应仔细排查。
文档中心底部可以直接向提交问题,官方会给予回复(不保证),及时性令人捉急。
用户的语句是按:连续对话语料 -> 普通语料 -> 意图 -> 技能 -> 平台 的顺序来匹配的。也就是说,如果上一步是在处理意图 A 的逻辑,那么下一步会优先匹配意图 A 的连续对话语料和普通语料,如果未匹配,但匹配了意图 B 的语料,那么照样会执行意图 B 的逻辑。如果是真机测试,那么甚至是跳出技能来匹配官方技能(如唱一首歌)。
AliGenie 会在匹配语料之前对用户的话进行建模,如果添加了语料“我要找工作”,实际测试中,有时候能匹配语句“我想找工作”,有时候却不能,有时候甚至连“我要找工作”都不能匹配,具体的匹配逻辑令人无法捉摸,可能这样更像是“人工智能”吧。
真机测试时最好在手机上开启“天猫精灵”App,时刻观察天猫精灵识别到的内容,以减少我们对 AliGenie 处理逻辑的误判。
sys:time 可以匹配:今天、明天、后天、大后天等,但不能匹配“昨天”,神马情况?
AliGenie 目前还不完善,特别是添加修改语料时经常出错莫名错误和乱码,期待阿里能尽快完善,能让更多的开发者参与到其中来,共同推动 AI 发展。
