Skip to content

Commit ac91446

Browse files
committed
[docs] Add docs of conversation plugin
1 parent 97da012 commit ac91446

5 files changed

Lines changed: 111 additions & 2 deletions

File tree

docs/content/docs/development/meta.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"protocol",
77
"plugin",
88
"logging",
9-
"context"
9+
"context",
10+
"plugins"
1011
]
1112
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
---
2+
title: '@fraqjs/plugin-conversation'
3+
---
4+
5+
<NpmBadge packageName="@fraqjs/plugin-conversation" />
6+
7+
`@fraqjs/plugin-conversation` 提供了对多轮对话的支持,帮助开发者编写交互式机器人功能。
8+
9+
## 安装与配置
10+
11+
将插件添加至 `dependencies`,然后在创建 `Context` 时引入并配置插件:
12+
13+
```typescript
14+
import { ConversationPlugin } from '@fraqjs/plugin-conversation';
15+
16+
ctx.install(ConversationPlugin, {
17+
// 在这里传入 ConversationService 的配置选项
18+
});
19+
```
20+
21+
`ConversationPlugin` 有如下配置项:
22+
23+
- `defaultTimeout`:默认的对话超时时间,单位是毫秒。默认值为 30000(30 秒)。如果用户在这个时间内没有回复,当前对话会自动结束。
24+
- `onCollision`:当用户触发一个新的会话时,如何处理旧的会话。可选值有:
25+
- `reject-incoming`:默认值,拒绝建立新的会话,抛出一个 `ConversationRejectionError`
26+
- `abort-existing`:结束旧的会话,建立新的会话,并且在旧的会话处抛出一个 `ConversationAbortionError`
27+
28+
如果你是插件开发者,请将本插件添加到项目的 `peerDependencies` 中,并在自己的插件中声明依赖:
29+
30+
```typescript
31+
import { ConversationService } from '@fraqjs/plugin-conversation';
32+
33+
definePlugin({
34+
name: 'my-plugin',
35+
inject: {
36+
conversation: ConversationService,
37+
},
38+
apply(ctx) {
39+
// 使用 ctx.conversation 来访问 ConversationService
40+
},
41+
});
42+
```
43+
44+
## 开发交互功能
45+
46+
考虑下面的对话场景:
47+
48+
```
49+
用户: 天气
50+
机器人: 你想查询哪个城市的天气呢?
51+
用户: 北京
52+
机器人: 北京今天晴,最高气温...
53+
```
54+
55+
传统的 `ctx.router.command` 遵循单纯的命令-响应模式,不适合处理多轮对话。但使用 `ConversationService`,你可以轻松实现这个场景:
56+
57+
```typescript
58+
ctx.router.command('天气', {}, async (session) => {
59+
await session.reply(msg`你想查询哪个城市的天气呢?`);
60+
const city = await ctx.conversation.open<string>(
61+
session,
62+
({ router, done }) => {
63+
router.rawPattern({ city: param.str() }, async (_, { city }) => {
64+
done(city);
65+
});
66+
},
67+
); // city: string | null
68+
if (city === null) {
69+
return;
70+
}
71+
const weatherInfo = await getWeatherForCity(city);
72+
await session.reply(msg`${weatherInfo}`);
73+
});
74+
```
75+
76+
在这个例子中,插件接收到 “天气” 这个输入时,会先回复一个问题,然后通过 `ctx.conversation.open` 启动一个新的会话。`open<R>` 函数传入的回调函数接受一个 `ConversationContext<R>` 对象,包含以下属性:
77+
78+
- `session`:当前会话的 `Session` 对象,与第一个参数传入的 `session` 是同一个实例。
79+
- `router`:一个新的 `Router` 实例,用于在会话中定义输入模式和对应的处理函数。在这个 `Router` 中定义的路由只会在当前会话中生效,不会影响全局的 `ctx.router`。此外,传入这个 `router` 的回调函数的 `session` 中,`raw` 字段会被替换为触发当前路由的原始输入内容,其余字段与你传入 `open``session` 相同。
80+
- `done(result: R)`:一个函数,用于结束会话并返回结果。当你调用 `done` 时,`ctx.conversation.open` 返回的 Promise 会被 resolved,值就是你传入 `done` 的参数。
81+
- `abort(reason?: string)`:一个函数,用于主动终止会话,并且可以传入一个字符串作为终止原因。当你调用 `abort` 时,`ctx.conversation.open` 返回的 Promise 会被 rejected,错误类型是 `ConversationAbortionError`,错误消息包含你传入的终止原因。
82+
83+
我们在代码中使用了其中的 `router` 来定义了一个 `rawPattern` 路由,这个路由会匹配用户的任意输入,并且将输入内容作为字符串参数 `city` 传入回调函数。当用户输入符合这个模式时,我们调用 `done(city)` 来结束会话,并将用户输入的城市名称作为结果返回。
84+
85+
## 处理特殊情况
86+
87+
从代码中可以看出,`open` 方法有可能返回 `null`,这表示会话超出了配置的等待时间限制,你需要显式地检查这种情况来处理超时。代码中的处理是直接 `return`,即不再执行后续逻辑;但你也可以发送一个提示消息,或使用 `while` 循环来重新发起会话等。
88+
89+
`open` 方法还可能抛出两种错误:
90+
91+
- `ConversationRejectionError`:当发生会话冲突且 `onCollision` 配置为 `reject-incoming` 时抛出,表示新的会话请求被拒绝。
92+
- `ConversationAbortionError`
93+
- 当发生会话冲突且 `onCollision` 配置为 `abort-existing` 时,旧的会话会被结束,并在旧的会话处抛出这个错误。
94+
- 当调用 `abort` 方法时抛出,表示当前会话被主动终止,并且包含终止原因。
95+
96+
你可以通过 `try...catch` 来捕获这些错误,并进行相应的处理。
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
title: 官方插件
3+
---
4+
5+
所有由 Fraq 官方维护的插件都以 `@fraqjs/plugin-` 开头命名,并且在 npm 上公开发布。目前的官方插件包括:
6+
7+
- [`@fraqjs/plugin-conversation`](./conversation.mdx):提供了对多轮对话的支持,帮助开发者编写交互式机器人功能。
8+
9+
点击链接以查看每个插件的详细文档。
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"defaultOpen": false
3+
}

plugins/conversation/src/service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class ConversationService {
8080
if (this.onCollision === 'reject-incoming') {
8181
return Promise.reject(new ConversationRejectionError('conversation already active'));
8282
}
83-
this.abort(existing);
83+
this.abort(existing, 'aborted due to new conversation');
8484
}
8585

8686
return new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)