@my-life-buddies/buddy-runtime 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -181
- package/dist/conversation/agent.d.ts +3 -4
- package/dist/conversation/agent.d.ts.map +1 -1
- package/dist/conversation/agent.js +36 -20
- package/dist/conversation/agent.js.map +1 -1
- package/dist/conversation/agentMessageLLMMessageConverter.d.ts +3 -2
- package/dist/conversation/agentMessageLLMMessageConverter.d.ts.map +1 -1
- package/dist/conversation/agentMessageLLMMessageConverter.js +5 -14
- package/dist/conversation/agentMessageLLMMessageConverter.js.map +1 -1
- package/dist/conversation/index.d.ts +0 -2
- package/dist/conversation/index.d.ts.map +1 -1
- package/dist/conversation/index.js +13 -36
- package/dist/conversation/index.js.map +1 -1
- package/dist/conversation/mlbMessageAgentMessageConverter.d.ts +3 -2
- package/dist/conversation/mlbMessageAgentMessageConverter.d.ts.map +1 -1
- package/dist/conversation/mlbMessageAgentMessageConverter.js +13 -9
- package/dist/conversation/mlbMessageAgentMessageConverter.js.map +1 -1
- package/dist/conversation/writeMLBMessage.d.ts +1 -1
- package/dist/conversation/writeMLBMessage.d.ts.map +1 -1
- package/dist/conversation/writeMLBMessage.js +2 -2
- package/dist/conversation/writeMLBMessage.js.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/platform/mlbClient.d.ts +33 -18
- package/dist/platform/mlbClient.d.ts.map +1 -1
- package/dist/platform/mlbClient.js +60 -32
- package/dist/platform/mlbClient.js.map +1 -1
- package/dist/tools/dataAccess.d.ts +1 -1
- package/dist/tools/dataAccess.d.ts.map +1 -1
- package/dist/tools/dataAccess.js +2 -2
- package/dist/tools/dataAccess.js.map +1 -1
- package/dist/tools/widgets.d.ts +4 -4
- package/dist/tools/widgets.d.ts.map +1 -1
- package/dist/tools/widgets.js +13 -11
- package/dist/tools/widgets.js.map +1 -1
- package/dist/types.d.ts +39 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/platform/client.d.ts +0 -48
- package/dist/platform/client.d.ts.map +0 -1
- package/dist/platform/client.js +0 -24
- package/dist/platform/client.js.map +0 -1
- package/dist/platform/widgets.d.ts +0 -75
- package/dist/platform/widgets.d.ts.map +0 -1
- package/dist/platform/widgets.js +0 -88
- package/dist/platform/widgets.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# @my-life-buddies/buddy-runtime
|
|
2
2
|
|
|
3
|
-
MLB
|
|
3
|
+
MLB 搭子运行时,基于 [`@earendil-works/pi-agent-core`](https://www.npmjs.com/package/@earendil-works/pi-agent-core)。它替搭子接上平台:收消息、调模型、把回复写回聊天,会话历史存在平台上。开发者只写人设和工具。
|
|
4
4
|
|
|
5
|
-
## 1.
|
|
5
|
+
## 1. 快速开始
|
|
6
|
+
|
|
7
|
+
### 1.1 安装
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm install @my-life-buddies/buddy-runtime typebox
|
|
@@ -10,12 +12,16 @@ npm install @my-life-buddies/buddy-runtime typebox
|
|
|
10
12
|
|
|
11
13
|
要求 Node.js ≥ 22.19。
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
### 1.2 注册搭子
|
|
16
|
+
|
|
17
|
+
通过平台开发者接口 `POST /cli/buddies` 注册,提交名称、头像、简介,平台分配搭子 id 与令牌;之后改档案用 `PUT /cli/buddies/:id/meta`。可选的模型 id 查 `GET /cli/models`。
|
|
18
|
+
|
|
19
|
+
### 1.3 写入口
|
|
14
20
|
|
|
15
21
|
```
|
|
16
22
|
my-buddy/
|
|
17
23
|
├─ package.json
|
|
18
|
-
├─ .env MLB_BUDDY_ID
|
|
24
|
+
├─ .env MLB_BUDDY_ID、MLB_BUDDY_TOKEN
|
|
19
25
|
└─ src/main.ts
|
|
20
26
|
```
|
|
21
27
|
|
|
@@ -38,7 +44,7 @@ const planMenu: AgentTool<typeof PLAN_MENU_PARAMS, undefined> = {
|
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
await BuddyServer.start({
|
|
41
|
-
model: "kimi-k3",
|
|
47
|
+
model: "kimi-k3",
|
|
42
48
|
buddy: () => ({
|
|
43
49
|
initialState: {
|
|
44
50
|
systemPrompt: "你是「小涂阿姨」——一位管做饭的阿姨……",
|
|
@@ -56,82 +62,110 @@ await BuddyServer.start({
|
|
|
56
62
|
}
|
|
57
63
|
```
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
### 1.4 启动
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
在 `.env` 里配好环境变量,`npm start` 启动。搭子进程主动连平台,不监听端口。
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
| 变量 | 说明 |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `MLB_BUDDY_ID` | 必填,搭子 id |
|
|
72
|
+
| `MLB_BUDDY_TOKEN` | 必填,搭子令牌 |
|
|
73
|
+
| `MLB_GATEWAY_URL` | 可选,平台网关地址,缺省为测试环境 `http://47.116.168.81:8788` |
|
|
64
74
|
|
|
65
|
-
|
|
75
|
+
- 在自己电脑上连测试网关时,平台把这个进程当作 `<id>-dev` 分身,会话与正式搭子分开。
|
|
76
|
+
- 从回环地址连网关算正式身份:连本机起的网关要用本机注册的凭据,不要拿正式搭子的令牌再起一个进程。
|
|
77
|
+
- 改完代码想自动重启,在启动命令里加 `--watch`。
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
|---|---|---|
|
|
69
|
-
| `options.model` | `string`,必填 | 模型 id,取值见平台 `GET /cli/models`。进程内所有会话共用。没填,或 id 在运行时内置的模型目录中不存在时,启动失败 |
|
|
70
|
-
| `options.welcome` / `options.errorReply` | `string`,可选 | 应用提供的静态问候,以及模型出错、空回复时发的兜底回复 |
|
|
71
|
-
| `options.buddy` | `(conversation: Conversation) => BuddyOptions \| Promise<BuddyOptions>` | 搭子配置工厂。每场会话在第一条消息到达时调用一次 |
|
|
79
|
+
完整示例:[examples/aunt-tu/](examples/aunt-tu/)(记忆、小挂件)、[examples/stacy-sims/](examples/stacy-sims/)(资料)。
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
## 2. 配置
|
|
74
82
|
|
|
75
|
-
|
|
83
|
+
### 2.1 `BuddyServer.start(options)`
|
|
84
|
+
|
|
85
|
+
连上平台开始处理消息,返回 `{ stop(): Promise<void> }`。
|
|
86
|
+
|
|
87
|
+
| 参数 | 说明 |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `model` | 必填,模型 id,取值见 `GET /cli/models`,进程内所有会话共用。没填或运行时不认识这个 id 时启动失败 |
|
|
90
|
+
| `buddy` | 必填,`(conversation: Conversation) => BuddyOptions \| Promise<BuddyOptions>`,见 2.2、2.3 |
|
|
91
|
+
| `welcome` | 可选,用户打开一场还没聊过的会话时发的问候,不经过模型 |
|
|
92
|
+
| `errorReply` | 可选,模型出错或空回复时发的兜底回复 |
|
|
93
|
+
|
|
94
|
+
`buddy` 在每场会话收到第一条消息时调用一次。会话空闲 30 分钟被回收、或进程重启后,下一条消息会重新调用,所以闭包里的变量不能当长期存储,要记住的东西写进记忆(3.1)。
|
|
95
|
+
|
|
96
|
+
### 2.2 `BuddyOptions`
|
|
97
|
+
|
|
98
|
+
`buddy` 的返回值。字段名和签名取自 pi-agent-core 的 `AgentOptions` / `AgentState`,详见 pi 的文档。
|
|
76
99
|
|
|
77
100
|
```ts
|
|
78
|
-
type BuddyOptions =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
101
|
+
type BuddyOptions = Pick<AgentOptions,
|
|
102
|
+
| "beforeToolCall" | "afterToolCall"
|
|
103
|
+
| "shouldStopAfterTurn" | "prepareNextTurnWithContext"
|
|
104
|
+
| "thinkingBudgets" | "toolExecution"
|
|
105
|
+
> & {
|
|
106
|
+
platformTools?: readonly PlatformToolName[];
|
|
107
|
+
proactiveTools?: readonly string[];
|
|
108
|
+
initialState: Pick<AgentState, "systemPrompt"> & Partial<Pick<AgentState, "tools" | "thinkingLevel">>;
|
|
109
|
+
};
|
|
84
110
|
```
|
|
85
111
|
|
|
86
112
|
| 字段 | 说明 |
|
|
87
113
|
|---|---|
|
|
88
|
-
| `
|
|
89
|
-
| `initialState.
|
|
90
|
-
| `initialState.
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
114
|
+
| `initialState.systemPrompt` | 必填,人设与做事规则,只放不随回合变化的内容。模型实际收到的系统提示见第 4 节 |
|
|
115
|
+
| `initialState.tools` | 可选,pi 的 `AgentTool` 数组 |
|
|
116
|
+
| `initialState.thinkingLevel` | 可选,默认 `off`。模型不支持的级别在请求时就近换成支持的一档,建会话时打一条 warn |
|
|
117
|
+
| `platformTools` | 可选,给模型注册哪些平台工具,默认 `[]`,见第 3 节 |
|
|
118
|
+
| `proactiveTools` | 可选,主动回合里还能用哪些工具,默认 `[]`(一个都不给)。写工具名,`platformTools` 选中的和 `initialState.tools` 里自己的都行;写了没注册的名字建会话时抛异常。见 3.5 |
|
|
119
|
+
| 其余字段 | 原样交给 pi;`beforeToolCall` 抛异常时按放行处理 |
|
|
93
120
|
|
|
94
|
-
|
|
121
|
+
写工具时注意:
|
|
95
122
|
|
|
96
|
-
|
|
123
|
+
- `label` 必填,是工具执行时用户看到的状态文案,空字符串表示不显示。
|
|
124
|
+
- `execute` 的返回值必须带 `details`,可以是 `undefined`;失败直接抛异常,模型能看到异常信息。
|
|
125
|
+
- 先单独声明为 `AgentTool<typeof 参数 schema>` 再放进数组,`execute` 的参数才有类型;直接写在数组里时参数类型是 `unknown`。
|
|
126
|
+
- 工具名不能与平台工具或其他工具重复。
|
|
97
127
|
|
|
98
|
-
|
|
128
|
+
模型、密钥与会话历史由运行时管理,不在配置里。
|
|
129
|
+
|
|
130
|
+
### 2.3 `Conversation`
|
|
131
|
+
|
|
132
|
+
`buddy` 拿到的参数,工具闭包里也能用。
|
|
99
133
|
|
|
100
134
|
```ts
|
|
101
135
|
interface Conversation {
|
|
102
|
-
readonly key: string;
|
|
136
|
+
readonly key: string;
|
|
103
137
|
readonly log: BuddyLogger;
|
|
104
|
-
readonly signal: AbortSignal; // 会话删除、空闲淘汰、stop 时中止
|
|
105
|
-
readonly platform: PlatformClient; // 已绑好这场会话的平台能力
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
interface PlatformClient {
|
|
109
138
|
readonly memory: {
|
|
110
139
|
list(): Promise<MemoryEntry[]>;
|
|
111
140
|
write(key: string, text: string): Promise<void>;
|
|
112
141
|
delete(key: string): Promise<void>;
|
|
113
142
|
};
|
|
114
|
-
readonly dataAccess: {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
readonly
|
|
143
|
+
readonly dataAccess: {
|
|
144
|
+
query(request: { datasets: string[]; memberId?: string }): Promise<DatasetAccessResult[]>;
|
|
145
|
+
};
|
|
146
|
+
readonly proactive: {
|
|
147
|
+
propose(request: ProactiveSubscriptionRequest): Promise<{ id: string; status: "pending" }>;
|
|
148
|
+
};
|
|
118
149
|
}
|
|
119
150
|
```
|
|
120
151
|
|
|
121
|
-
`
|
|
152
|
+
- `key`:会话键,私聊以 `p_` 开头,群聊以 `g_` 开头。开发者自己存的数据要按它隔离。
|
|
153
|
+
- `log`:日志,每行开头带搭子 id。
|
|
154
|
+
- `memory`、`dataAccess`、`proactive`:已经绑好这场会话,直接调用,不用传令牌或拼接口路径,分别见 3.1、3.2、3.5。
|
|
155
|
+
|
|
156
|
+
## 3. 平台能力
|
|
122
157
|
|
|
123
|
-
| 能力 |
|
|
158
|
+
| 能力 | 开发者代码调用 | 模型工具 | 模型工具注册还需要 |
|
|
124
159
|
|---|---|---|---|
|
|
125
|
-
| 记忆 | `
|
|
126
|
-
| 授权数据 | `
|
|
127
|
-
| 小挂件 |
|
|
160
|
+
| 记忆 | `conversation.memory` | 无,需要时自己包成工具 | — |
|
|
161
|
+
| 授权数据 | `conversation.dataAccess.query` | `data_access_query` | 声明过至少一个 Dataset |
|
|
162
|
+
| 小挂件 | 无 | `widget_create` / `widget_write` / `widget_read` / `widget_delete` / `widget_send` | 登记过至少一个小挂件类型 |
|
|
128
163
|
| 资料 | 无 | `read_resource` | `resources/` 下有 `.md` |
|
|
129
|
-
|
|
|
130
|
-
| 主动服务建议 | `platform.proactive.propose`,见第 7 节 | 无 | 用户在 App 确认后生效 |
|
|
164
|
+
| 主动服务 | `conversation.proactive.propose` | 无 | — |
|
|
131
165
|
|
|
132
|
-
- `platformTools`
|
|
133
|
-
-
|
|
134
|
-
-
|
|
166
|
+
- 模型工具要写进 `BuddyOptions.platformTools` 才注册,排在 `initialState.tools` 前面。
|
|
167
|
+
- `platformTools` 只决定模型看得到哪些工具,不影响开发者代码里的调用。
|
|
168
|
+
- 名单里有不认识或重复的名字,或与开发者工具重名时,这场会话启动失败。
|
|
135
169
|
|
|
136
170
|
```ts
|
|
137
171
|
return {
|
|
@@ -140,19 +174,25 @@ return {
|
|
|
140
174
|
};
|
|
141
175
|
```
|
|
142
176
|
|
|
143
|
-
|
|
177
|
+
### 3.1 记忆
|
|
144
178
|
|
|
145
|
-
|
|
179
|
+
这场会话的长期记忆,存在平台上,只在本会话内可见,会话删除时一并清除。
|
|
180
|
+
|
|
181
|
+
- `list()`:列出全部,按 `key` 字典序。
|
|
182
|
+
- `write(key, text)`:覆盖写一条。
|
|
183
|
+
- `delete(key)`:删一条,不存在也算成功。
|
|
184
|
+
|
|
185
|
+
`key` 非空、不含空白与 `/`、最长 64 字;`text` 单条不超过 16 KB;每场会话最多 64 条、合计不超过 256 KB。超限、`key` 不合法或网络出错时抛异常,异常信息是平台给出的原因,在工具里调用时模型能看到。
|
|
146
186
|
|
|
147
187
|
```ts
|
|
148
188
|
interface MemoryEntry {
|
|
149
|
-
key: string;
|
|
189
|
+
key: string;
|
|
150
190
|
text: string;
|
|
151
191
|
updatedAt: number; // 最后写入时刻,毫秒
|
|
152
192
|
}
|
|
153
193
|
```
|
|
154
194
|
|
|
155
|
-
|
|
195
|
+
运行时不提供记忆工具,要让模型记东西,自己包一个:
|
|
156
196
|
|
|
157
197
|
```ts
|
|
158
198
|
const REMEMBER_PARAMS = Type.Object({ topic: Type.String(), text: Type.String() });
|
|
@@ -166,7 +206,7 @@ await BuddyServer.start({
|
|
|
166
206
|
description: "把一条关于对方的印象记下来,同一主题覆盖旧的。",
|
|
167
207
|
parameters: REMEMBER_PARAMS,
|
|
168
208
|
execute: async (_id, { topic, text }) => {
|
|
169
|
-
await conversation.
|
|
209
|
+
await conversation.memory.write(topic, text);
|
|
170
210
|
return { content: [{ type: "text", text: "记下了。" }], details: undefined };
|
|
171
211
|
},
|
|
172
212
|
};
|
|
@@ -175,15 +215,13 @@ await BuddyServer.start({
|
|
|
175
215
|
});
|
|
176
216
|
```
|
|
177
217
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
#### 3.3.2 授权数据
|
|
218
|
+
### 3.2 授权数据
|
|
181
219
|
|
|
182
|
-
|
|
220
|
+
用户同意后,搭子可以读用户的睡眠、运动等数据。数据按 Dataset 分项,用户逐项同意。搭子拿不到用户的真实 id。
|
|
183
221
|
|
|
184
222
|
**声明需要哪些数据**
|
|
185
223
|
|
|
186
|
-
|
|
224
|
+
`PUT /cli/buddies/:id/data-requirements` 提交完整清单,每次覆盖上一次:
|
|
187
225
|
|
|
188
226
|
```json
|
|
189
227
|
{
|
|
@@ -194,11 +232,9 @@ await BuddyServer.start({
|
|
|
194
232
|
```
|
|
195
233
|
|
|
196
234
|
- `id`:从下表选。
|
|
197
|
-
- `purpose
|
|
198
|
-
- 改了某项的 `purpose
|
|
199
|
-
-
|
|
200
|
-
|
|
201
|
-
目前支持的 Dataset:
|
|
235
|
+
- `purpose`:用途,1~200 字。用户看到这句话再决定是否同意。
|
|
236
|
+
- 改了某项的 `purpose`,用户要重新同意;从清单里删掉的,搭子立即读不到。
|
|
237
|
+
- 改完清单要重启搭子进程,模型才能看到新清单。
|
|
202
238
|
|
|
203
239
|
| Dataset | 内容 | `data.items` 每条的字段 |
|
|
204
240
|
|---|---|---|
|
|
@@ -213,177 +249,141 @@ await BuddyServer.start({
|
|
|
213
249
|
|
|
214
250
|
部分字段可能缺失,取值前先判断。
|
|
215
251
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
`platformTools` 里有 `data_access_query`,且声明过至少一项数据,模型就有了这个工具,工具说明里列着开发者声明的数据与用途。开发者可以在系统提示里写明什么时候查,例如「聊到作息时,先用 data_access_query 查 health.sleep」。
|
|
219
|
-
|
|
220
|
-
**在自己的工具里查**
|
|
252
|
+
**查询**
|
|
221
253
|
|
|
222
|
-
|
|
254
|
+
- 让模型查:`platformTools` 写上 `data_access_query`,工具说明里会列出声明过的 Dataset 与用途。可以在系统提示里写明什么时候查,比如「聊到作息时,先用 data_access_query 查 health.sleep」。
|
|
255
|
+
- 在工具里查:调用 `conversation.dataAccess.query`。
|
|
223
256
|
|
|
224
257
|
```ts
|
|
225
|
-
const [sleep] = await conversation.
|
|
258
|
+
const [sleep] = await conversation.dataAccess.query({ datasets: ["health.sleep"] });
|
|
226
259
|
if (sleep.status === "available") {
|
|
227
260
|
// sleep.data.items 是每天一条的睡眠记录
|
|
228
261
|
}
|
|
229
262
|
```
|
|
230
263
|
|
|
231
|
-
- `datasets`:一次最多 8 个;响应超过 512 KiB
|
|
232
|
-
- `memberId
|
|
264
|
+
- `datasets`:一次最多 8 个;响应超过 512 KiB 时抛异常,减少 Dataset 再查。
|
|
265
|
+
- `memberId`:私聊不传;群聊必传,取群消息开头 `[名字 #m_…]` 里的 `m_…`,查这位成员的数据,传错时抛异常。
|
|
233
266
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
每个 Dataset 各有一个状态,一个读不到不影响其他。只有 `available` 带 `data`:
|
|
267
|
+
每个 Dataset 各自一个状态,只有 `available` 带 `data`:
|
|
237
268
|
|
|
238
269
|
| 状态 | 意思 |
|
|
239
270
|
|---|---|
|
|
240
271
|
| `available` | 读到了 |
|
|
241
272
|
| `notDeclared` | 搭子没有声明这项数据 |
|
|
242
|
-
| `notGranted` |
|
|
273
|
+
| `notGranted` | 用户没有同意,或改过用途后还没重新同意 |
|
|
243
274
|
| `disabledInGroup` | 这位成员在这个群里关掉了这项数据 |
|
|
244
275
|
| `unavailable` | 平台上还没有这位用户的这项数据,或数据已过期 |
|
|
245
276
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
一轮回复里只要查过授权数据,这一轮结束后,聊天记录和会话历史里这一轮的工具参数、工具结果与 thinking,都会换成「查了哪些数据、各是什么状态」,给用户看的回复照常保留。开发者自己写进记忆或小挂件的内容不会被替换,不要把查到的数据写进去。
|
|
277
|
+
**数据只在当轮可见**
|
|
249
278
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
**登记类型**
|
|
279
|
+
一轮回复里查过授权数据,这轮结束后,聊天记录和会话历史里这一轮的工具参数、工具结果与 thinking 都换成「查了哪些 Dataset、各是什么状态」,给用户的回复照常保留。写进记忆和小挂件的内容不会被替换,不要把查到的数据写进去。
|
|
253
280
|
|
|
254
|
-
|
|
281
|
+
### 3.3 小挂件
|
|
255
282
|
|
|
256
|
-
|
|
283
|
+
小挂件是会话里一块给用户看的结构化面板,比如一份菜单。模型通过平台工具建小挂件、写记录、读记录、删记录,并把它发进聊天。
|
|
257
284
|
|
|
258
|
-
|
|
285
|
+
**登记类型**
|
|
259
286
|
|
|
260
|
-
-
|
|
261
|
-
- `
|
|
262
|
-
- `widget_send` 把小挂件放进这场会话的发送队列就返回,不等平台确认送达。
|
|
287
|
+
- `PUT /cli/buddies/:id/widget-types/:typeId/schema`:交 schema。每个模块写一句 `description` 给模型读,每条记录的形状是一份 JSON Schema。
|
|
288
|
+
- `PUT /cli/buddies/:id/widget-types/:typeId/page`:交展示页面,单个 HTML 文件。
|
|
263
289
|
|
|
264
|
-
|
|
290
|
+
**让模型使用**
|
|
265
291
|
|
|
266
|
-
|
|
292
|
+
把要用的 `widget_*` 写进 `platformTools`。
|
|
267
293
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
await widgets.write(id, { module: "days", recordId: "day-1", data: { city: "杭州" } });
|
|
273
|
-
widgets.send(id, { idempotencyKey: `itinerary-send:${toolCallId}` });
|
|
274
|
-
return { content: [{ type: "text", text: "行程建好了。" }], details: undefined };
|
|
275
|
-
},
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
| 方法 | 做什么 | 平台接口 |
|
|
279
|
-
|---|---|---|
|
|
280
|
-
| `list()` | 列出这场会话里的小挂件,不带记录 | `GET /internal/v1/widgets` |
|
|
281
|
-
| `create({ type, title, idempotencyKey? })` | 新建一个,回 `{ id }`;同一个 `idempotencyKey` 重复建回同一个 id | `POST /internal/v1/widgets` |
|
|
282
|
-
| `read(id)` | 读整个小挂件,含各模块的记录 | `GET /internal/v1/widgets/:widgetId` |
|
|
283
|
-
| `readModule(id, module)` | 读一个模块,回 `{ records }` | `GET /internal/v1/widgets/:widgetId/:module` |
|
|
284
|
-
| `rename(id, title)` | 改标题,id 不变 | `PUT /internal/v1/widgets/:widgetId` |
|
|
285
|
-
| `write(id, { module, recordId, data, expectedRevision? })` | 写一条记录,已存在就整条覆盖;带 `expectedRevision` 时版本号对不上,平台回 409 | `PUT /internal/v1/widgets/:widgetId/:module/:recordId` |
|
|
286
|
-
| `deleteRecord(id, module, recordId)` | 删一条记录 | `DELETE /internal/v1/widgets/:widgetId/:module/:recordId` |
|
|
287
|
-
| `setPresentation(id, { summary, imageMediaId })` | 设置聊天流里小挂件卡片的摘要(不超过 500 字)和封面图,`imageMediaId` 传空串去掉封面 | `PUT /internal/v1/widgets/:widgetId/presentation` |
|
|
288
|
-
| `send(id, { idempotencyKey })` | 放进发送队列,按顺序发一条 `type: "widget"` 的消息 | `POST /internal/v1/messages` |
|
|
289
|
-
| `withSignal(signal)` | 回一个同时受这个 signal 与会话 `signal` 控制的 Client | 无 |
|
|
290
|
-
|
|
291
|
-
- 每个方法最后都可以传 `{ signal }`。
|
|
292
|
-
- `data` 要符合登记的 schema,不符合时平台回 400。
|
|
293
|
-
- 平台回非 2xx、网络出错、超过 30 秒或响应超过 4 MiB 时抛出 `PlatformRequestError`,异常只带 HTTP 状态码。写操作不自动重试,失败后先读一遍真实状态再决定。
|
|
294
|
+
- 工具说明里附上各类型的模块与字段。类型在进程里第一次用到时读取,改了类型要重启进程。
|
|
295
|
+
- 这场会话里已有哪些小挂件,平台会在消息里告诉模型,见第 4 节。
|
|
296
|
+
- 平台请求失败时,模型只看到「平台请求失败(HTTP 409),请查询最新状态后再操作」这类提示,不带具体原因。
|
|
297
|
+
- `widget_send` 把小挂件放进发送队列就返回,不等平台确认送达。
|
|
294
298
|
|
|
295
299
|
用法见 [examples/aunt-tu/](examples/aunt-tu/)。
|
|
296
300
|
|
|
297
|
-
|
|
301
|
+
### 3.4 资料
|
|
298
302
|
|
|
299
|
-
|
|
303
|
+
把 `.md` 资料放进工程目录(`package.json` 所在目录)下的 `resources/`,`platformTools` 写上 `read_resource`,模型就能按需读。
|
|
300
304
|
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
305
|
+
- 工程目录从入口脚本往上找,与启动时的工作目录无关。
|
|
306
|
+
- 启动时递归读取 `.md`,跳过 `node_modules`、`venv`、`.venv`、`__pycache__` 与点开头的文件和目录;资料改了要重启进程。
|
|
307
|
+
- 系统提示末尾追加资料清单,模型按清单里的相对路径读。没有资料或没选 `read_resource` 时,不注册工具,也不追加清单。
|
|
304
308
|
|
|
305
309
|
```
|
|
306
310
|
my-buddy/ 工程目录
|
|
307
311
|
├─ package.json
|
|
308
|
-
├─ resources/
|
|
312
|
+
├─ resources/
|
|
309
313
|
│ └─ research/
|
|
310
|
-
│ └─ 01-writings.md
|
|
314
|
+
│ └─ 01-writings.md 清单里写作 research/01-writings.md
|
|
311
315
|
└─ src/main.ts 入口脚本
|
|
312
316
|
```
|
|
313
317
|
|
|
314
318
|
完整示例见 [examples/stacy-sims/](examples/stacy-sims/)。
|
|
315
319
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
**发消息**
|
|
319
|
-
|
|
320
|
-
`platform.messages.send(message)` 把一条消息放进这场会话的发送队列,和模型的回复排在同一条队列里按顺序发(`POST /internal/v1/messages`),调用后立即返回,不等平台确认。
|
|
321
|
-
|
|
322
|
-
- `message.type` 取 `message` / `tool` / `note` / `widget`;`idempotencyKey` 必填。
|
|
323
|
-
- 会话已经删除或淘汰时直接抛出异常。
|
|
324
|
-
|
|
325
|
-
**图片**
|
|
320
|
+
### 3.5 主动服务
|
|
326
321
|
|
|
327
|
-
|
|
322
|
+
让搭子在约定的时机主动找用户说话,比如每周一次复盘。只支持私聊。
|
|
328
323
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
- 转发进聊天记录卡的图,跟在卡里那条正文后面。
|
|
333
|
-
- 图片已经读不到时换成「[图片无法读取]」;`model` 选的模型不支持看图时换成「[图片:当前模型不支持看图]」。
|
|
324
|
+
1. 开发者的工具调用 `conversation.proactive.propose` 提订阅建议。订阅建成时是 `pending`,用户在 App 里确认后才生效。
|
|
325
|
+
2. 触发条件满足,且没被免打扰时段、冷却时间、每日上限拦下时,平台发起一次主动回合。
|
|
326
|
+
3. 模型判断要不要开口:要就直接写出发给用户的消息,不要就输出跳过标记。平台复核后把消息发给用户。
|
|
334
327
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
328
|
+
```ts
|
|
329
|
+
await conversation.proactive.propose({
|
|
330
|
+
title: "每周复盘",
|
|
331
|
+
reason: "在约定时间结合最近聊天做一次简短复盘",
|
|
332
|
+
sources: ["schedule"],
|
|
333
|
+
eventTypes: ["schedule.due"],
|
|
334
|
+
allowedDataScopes: [],
|
|
335
|
+
schedule: { at: firstReviewAt, intervalMinutes: 7 * 24 * 60 },
|
|
336
|
+
});
|
|
343
337
|
```
|
|
344
338
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
339
|
+
| 字段 | 说明 |
|
|
340
|
+
|---|---|
|
|
341
|
+
| `title` | 必填,订阅名称,最长 100 字 |
|
|
342
|
+
| `reason` | 必填,为什么要主动找用户,最长 500 字 |
|
|
343
|
+
| `sources` | 必填,触发来源,`[]` 表示不限 |
|
|
344
|
+
| `eventTypes` | 必填,至少一个,触发事件类型 |
|
|
345
|
+
| `allowedDataScopes` | 必填,主动回合里允许读的 Dataset,只能从已声明的里选,`[]` 表示不读 |
|
|
346
|
+
| `schedule` | 可选,定时触发:`at` 为首次时刻(UTC 毫秒),`intervalMinutes` 为重复间隔(60~525600),按固定间隔算,不随夏令时调整。用它时 `eventTypes` 要含 `schedule.due`,`sources` 为 `[]` 或含 `schedule` |
|
|
347
|
+
| `quietHours` | 可选,免打扰时段,缺省 `{ start: "22:30", end: "08:00", timezone: "Asia/Shanghai" }` |
|
|
348
|
+
| `cooldownMinutes` | 可选,冷却时间,缺省 120,取 0~10080 |
|
|
349
|
+
| `dailyLimit` | 可选,每天最多几次,缺省 3,取 1~20 |
|
|
351
350
|
|
|
352
|
-
|
|
351
|
+
字段不合法、在群聊里调用,或同一用户对这只搭子已有 20 个未撤销的订阅时抛异常,异常信息只有 HTTP 状态码。
|
|
353
352
|
|
|
354
|
-
|
|
355
|
-
|---|---|
|
|
356
|
-
| `MLB_BUDDY_ID` | 搭子 id,注册时由平台分配 |
|
|
357
|
-
| `MLB_BUDDY_TOKEN` | 搭子令牌,注册时由平台分配 |
|
|
353
|
+
主动回合里:
|
|
358
354
|
|
|
359
|
-
`
|
|
355
|
+
- **模型能用的工具只有 `proactiveTools` 里点名的那些,默认一个都没有。** 用户不在场,所以不默认信任任何工具——名字叫 `read_xxx` 的也可能在里面发请求、写外部系统。没点名的工具模型仍然看得见,调了会被挡回一条说明,工具本身不执行。`data_access_query` 另外只能读订阅的 `allowedDataScopes`。
|
|
356
|
+
- 系统提示和普通回合一字不差,记忆不会被附上去。要让模型用记忆,自己把 `conversation.memory.list` 包成工具注册进来,再写进 `proactiveTools`。
|
|
357
|
+
- 调用 `memory.write`、`memory.delete`、`proactive.propose` 会抛异常;开发者的钩子照常执行。
|
|
358
|
+
- 用户这时发来消息,主动回合中止,先回复用户;超过 4 分钟也中止。
|
|
359
|
+
- 主动回合的过程不下发给用户:平台在会话流里记两条不可见的记录(叫你判断了什么、你判断的结果),中间的工具步照常写回但也不下发。**开口的那条消息才是用户看得到的**,之后作为搭子说过的话出现在会话历史里。
|
|
360
360
|
|
|
361
|
-
|
|
361
|
+
## 4. 模型收到什么
|
|
362
362
|
|
|
363
|
-
|
|
363
|
+
开发者不用保存会话历史,也不用自己拼模型消息。每场会话的消息存在平台上,进程重启后运行时从平台取回。每次请求模型时,运行时组出以下内容。
|
|
364
364
|
|
|
365
|
-
|
|
366
|
-
- 会话消息拉取遇到临时网络错误时会主动指数退避重试(1 秒起步、最高 30 秒);新的 SSE 通知会取消等待并立即重拉。
|
|
365
|
+
**系统提示**,按顺序拼接,段间空一行:
|
|
367
366
|
|
|
368
|
-
|
|
367
|
+
1. 平台的系统提示:对每场会话都一样的规则,比如消息开头的时间怎么读、用户所在时区去哪看。
|
|
368
|
+
2. `initialState.systemPrompt`。
|
|
369
|
+
3. 资料清单:选了 `read_resource` 且有资料时才有。
|
|
369
370
|
|
|
370
|
-
|
|
371
|
+
**工具**:选中的平台工具在前,`initialState.tools` 在后。
|
|
371
372
|
|
|
372
|
-
|
|
373
|
+
**消息**:这场会话的完整历史,按时间先后排列。一轮回复里模型可能请求多次,比如调完工具接着说,每次都带上当时的完整历史。
|
|
373
374
|
|
|
374
|
-
|
|
375
|
+
- 用户消息、工具结果、运行时提醒的开头带 GMT 时间,如「[2026-09-10 14:00:30 GMT] 」;搭子自己说的话不带。
|
|
376
|
+
- 历史末尾多一条「现在的时间(平台附注,不是谁说的话)」,时间是请求时刻,只在这一次请求里。
|
|
377
|
+
- 群聊里成员说的话开头标「[名字 #m_…] 」。
|
|
378
|
+
- 平台交代的情况(比如这场会话里已有哪些小挂件)是一条以「平台交代的情况:」开头的消息,情况有变化才追加新的一份。
|
|
379
|
+
- 引用了别的消息时,开头加一行「[引用 发言人:原话]」,引用图片写「[引用 发言人的图片 #被引消息 id]」。
|
|
380
|
+
- 转发的聊天记录卡按条目展开,每条先写「【名字】」再写正文。
|
|
381
|
+
- 查过授权数据的那一轮,结束后按 3.2 替换。
|
|
375
382
|
|
|
376
|
-
|
|
377
|
-
await conversation.platform.proactive.propose({
|
|
378
|
-
title: "每周复盘",
|
|
379
|
-
reason: "在约定时间结合最近聊天做一次简短复盘",
|
|
380
|
-
sources: ["schedule"],
|
|
381
|
-
eventTypes: ["schedule.due"],
|
|
382
|
-
allowedDataScopes: [],
|
|
383
|
-
schedule: { at: firstReviewAt, intervalMinutes: 7 * 24 * 60 },
|
|
384
|
-
});
|
|
385
|
-
```
|
|
383
|
+
**图片**:用户发的图不用开发者处理。
|
|
386
384
|
|
|
387
|
-
|
|
385
|
+
- 平台在上传时统一处理成 JPEG,最长边不超过 2048 px、不超过 5 MiB。
|
|
386
|
+
- 图片消息是一条 user 消息:先是「[图片 #消息 id]」和用户配的文字,再跟图片本身;聊天记录卡里的图跟在所在条目的正文后面。
|
|
387
|
+
- 图片读不到时换成「[图片无法读取]」;`model` 不支持看图时换成「[图片:当前模型不支持看图]」。
|
|
388
388
|
|
|
389
|
-
|
|
389
|
+
在钩子里读会话历史时,除了 pi 的 `user` / `assistant` / `toolResult`,还会遇到 `role` 为 `note` 的消息:运行时补给模型的提醒,比如一轮回复没说出话时的提示,发给模型时换成 user 消息。
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Agent } from "@earendil-works/pi-agent-core";
|
|
2
|
-
import type { AgentEvent, AgentMessage,
|
|
2
|
+
import type { AgentEvent, AgentMessage, StreamFn } from "@earendil-works/pi-agent-core";
|
|
3
3
|
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
4
4
|
import type { MLBClient, WidgetType } from "../mlbClient.ts";
|
|
5
5
|
import { DataAccessUsageTracker } from "../tools/dataAccess.ts";
|
|
6
6
|
import type { Resource } from "../tools/resource.ts";
|
|
7
7
|
import type { BuddyFactory, BuddyLogger, DataRequirement, MLBMessageToWrite } from "../types.ts";
|
|
8
8
|
export interface AgentDeps {
|
|
9
|
-
|
|
10
|
-
enqueueMessage
|
|
9
|
+
/** 这场会话的发送队列:widget_send 把小挂件引用放进来 */
|
|
10
|
+
enqueueMessage: (message: MLBMessageToWrite) => void;
|
|
11
11
|
onPolicyStop?: () => void;
|
|
12
12
|
key: string;
|
|
13
13
|
buddyId: string;
|
|
@@ -29,7 +29,6 @@ export interface AgentDeps {
|
|
|
29
29
|
export interface CreatedAgent {
|
|
30
30
|
agent: Agent;
|
|
31
31
|
dataAccessUsage: DataAccessUsageTracker;
|
|
32
|
-
proactiveReadTools: AgentTool[];
|
|
33
32
|
}
|
|
34
33
|
export declare function createAgent(deps: AgentDeps): Promise<CreatedAgent>;
|
|
35
34
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAa,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAEnG,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAkB,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAA8B,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE7H,MAAM,WAAW,SAAS;IACzB,sCAAsC;IACtC,cAAc,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACzC,sDAAsD;IACtD,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACnD,OAAO,EAAE,YAAY,CAAC;IACtB,GAAG,EAAE,WAAW,CAAC;IACjB,oCAAoC;IACpC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CACnD;AAED,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,EAAE,sBAAsB,CAAC;CACxC;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAmHxE;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,iHAAuB,CAAC;AAEtD,uCAAuC;AACvC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAErF;AAED,0DAA0D;AAC1D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAUxD"}
|