chaimi-keep-mcp 3.1.33-beta.6 → 3.1.34
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 +6 -0
- package/SKILL.md +52 -217
- package/package.json +1 -1
- package/server.js +41 -18
package/README.md
CHANGED
|
@@ -141,6 +141,12 @@ AI 会自动调用 `save_income` 工具记录收入。
|
|
|
141
141
|
|
|
142
142
|
## 更新日志
|
|
143
143
|
|
|
144
|
+
### v3.1.34 (2026-04-14)
|
|
145
|
+
- **修复** 日期/时区处理:纯日期格式默认使用12:00,不再使用当前服务器时间
|
|
146
|
+
- **优化** SKILL.md 精简:从385行精简到176行,聚焦Agent必须执行的行为规范
|
|
147
|
+
- **新增** 时间语义解析规则:明确"早餐"→08:00、"午餐"→12:00等映射关系
|
|
148
|
+
- **优化** 云函数日期存储:统一使用ISO 8601字符串格式,避免时区转换问题
|
|
149
|
+
|
|
144
150
|
### v3.1.23 (2026-04-13)
|
|
145
151
|
- **安全** 新增请求签名验证,防止 Agent 直接 curl 访问云函数
|
|
146
152
|
- **安全** Token 加密存储(AES-256-CBC),绑定机器标识
|
package/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: chaimi-
|
|
2
|
+
name: chaimi-keep-mcp
|
|
3
3
|
description: >-
|
|
4
4
|
柴米AI记账 — 微信小程序记账工具,支持 AI Agent 直接记账。
|
|
5
5
|
TRIGGER: 用户提及"记账/记录/保存"并涉及金额或商品信息。
|
|
@@ -25,7 +25,6 @@ argument-hint: "[记账内容]"
|
|
|
25
25
|
**如果不执行此步骤:**
|
|
26
26
|
- ❌ 直接调用 `save_expense` / `save_receipt` / `save_income` 会被拒绝
|
|
27
27
|
- ❌ 返回错误:`SKILL_NOT_READ`
|
|
28
|
-
- ❌ 错误提示:"记账失败:未读取最新 Skill 定义"
|
|
29
28
|
|
|
30
29
|
**正确流程:**
|
|
31
30
|
```
|
|
@@ -33,14 +32,8 @@ argument-hint: "[记账内容]"
|
|
|
33
32
|
2. 【必须】调用 get_skill 工具
|
|
34
33
|
3. 【必须】仔细阅读返回的 Skill 文档
|
|
35
34
|
4. 【必须】按照 Skill 文档执行记账
|
|
36
|
-
5. 调用 save_expense 等工具
|
|
37
35
|
```
|
|
38
36
|
|
|
39
|
-
**为什么强制要求?**
|
|
40
|
-
- Skill 文档会频繁更新
|
|
41
|
-
- Agent 的"记忆"不可靠,会过时
|
|
42
|
-
- 必须每次都读取最新规范,避免凭记忆操作导致错误
|
|
43
|
-
|
|
44
37
|
---
|
|
45
38
|
|
|
46
39
|
## 触发规则
|
|
@@ -49,54 +42,24 @@ argument-hint: "[记账内容]"
|
|
|
49
42
|
|
|
50
43
|
1. 用户明确提及以下关键词之一:
|
|
51
44
|
- "记账"、"记一笔"、"记录"
|
|
52
|
-
- "保存"(结合上下文判断为记账场景)
|
|
53
45
|
|
|
54
46
|
2. 包含以下信息之一:
|
|
55
47
|
- 金额数字(如:35、100.5)
|
|
56
|
-
-
|
|
48
|
+
- 商品/服务名称(如:午餐、咖啡)
|
|
57
49
|
- 小票/发票信息
|
|
58
50
|
|
|
59
51
|
### 禁止触发
|
|
60
52
|
|
|
61
53
|
- 明确使用其他记账工具时(如:"用支付宝记账")
|
|
62
|
-
- 金额信息不明确且无法推断(如:"记点东西")
|
|
63
|
-
|
|
64
|
-
### 上下文延续
|
|
65
|
-
|
|
66
|
-
当前对话已在进行记账操作时,后续消息无需再次提及"记账"即可触发。
|
|
67
54
|
|
|
68
55
|
---
|
|
69
56
|
|
|
70
57
|
## 安全约束(最高优先级)
|
|
71
58
|
|
|
72
|
-
### 1. 禁止直接访问
|
|
73
|
-
|
|
74
59
|
- **禁止**直接使用 curl/http 访问云函数端点
|
|
75
|
-
- **禁止**暴露 `MCP_HUB_URL`、`MCP_OAUTH_URL`、`MCP_PROMPT_URL` 给 Agent
|
|
76
|
-
- 所有请求**必须**通过 MCP Server 中转
|
|
77
|
-
|
|
78
|
-
### 2. Token 保护
|
|
79
|
-
|
|
80
60
|
- **禁止**读取或输出 `~/.mcporter/oauth-token.json` 内容
|
|
81
|
-
- Token 仅用于内部 HTTP 请求,不向用户展示
|
|
82
|
-
- **禁止**将 Token 作为参数传递给其他命令
|
|
83
|
-
|
|
84
|
-
### 3. 配置保护
|
|
85
|
-
|
|
86
61
|
- **禁止**主动设置 `MCP_OAUTH_URL`、`MCP_HUB_URL`、`MCP_PROMPT_URL` 环境变量
|
|
87
|
-
- URL 由 MCP Server 内部管理,对用户不可见
|
|
88
|
-
|
|
89
|
-
### 4. 金额确认
|
|
90
|
-
|
|
91
62
|
- 单笔金额 **> 1000 元** 时必须用户确认
|
|
92
|
-
- 批量记账(**> 10 笔**)时必须用户确认
|
|
93
|
-
- 涉及退款、负数金额时必须用户确认
|
|
94
|
-
|
|
95
|
-
### 5. 认证流程
|
|
96
|
-
|
|
97
|
-
- **必须**使用内置授权流程完成 Device Flow 授权
|
|
98
|
-
- **禁止**直接调用 `chaimi-keep-mcp` 的授权子命令
|
|
99
|
-
- **禁止**绕过授权流程直接操作
|
|
100
63
|
|
|
101
64
|
---
|
|
102
65
|
|
|
@@ -104,99 +67,56 @@ argument-hint: "[记账内容]"
|
|
|
104
67
|
|
|
105
68
|
每次触发时按顺序执行:
|
|
106
69
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
command -v chaimi-keep-mcp
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**未安装**:引导用户执行安装
|
|
115
|
-
```bash
|
|
116
|
-
npm install -g chaimi-keep-mcp
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### 2. 授权检查
|
|
120
|
-
|
|
121
|
-
检查是否已完成授权:
|
|
122
|
-
```bash
|
|
123
|
-
test -f ~/.mcporter/oauth-token.json
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**未授权**:引导完成 Device Flow 授权流程
|
|
127
|
-
|
|
128
|
-
### 3. Token 有效性检查
|
|
129
|
-
|
|
130
|
-
检查 Token 文件是否存在且未过期。
|
|
131
|
-
|
|
132
|
-
**Token 无效特征**:
|
|
133
|
-
- 文件不存在
|
|
134
|
-
- 已过期(超过30天)
|
|
135
|
-
- 调用云函数返回 401/403 错误
|
|
136
|
-
|
|
137
|
-
**无效**:引导重新授权
|
|
70
|
+
1. **安装检查**:`command -v chaimi-keep-mcp`
|
|
71
|
+
2. **授权检查**:`test -f ~/.mcporter/oauth-token.json`
|
|
72
|
+
3. **Token 有效性检查**:调用云函数验证
|
|
138
73
|
|
|
139
74
|
---
|
|
140
75
|
|
|
141
76
|
## 工具调用
|
|
142
77
|
|
|
143
|
-
###
|
|
78
|
+
### 文字记账(save_expense)
|
|
144
79
|
|
|
145
|
-
|
|
80
|
+
**流程:**
|
|
81
|
+
1. 调用 `get_text_parse_prompt()` 获取解析 Prompt
|
|
82
|
+
2. 使用 Prompt 解析用户输入,获取 JSON 结果
|
|
83
|
+
3. 调用 `save_expense` 保存(传入所有解析结果字段)
|
|
146
84
|
|
|
147
|
-
|
|
85
|
+
**Agent 必须执行的时间语义解析:**
|
|
86
|
+
| 用户说法 | 解析为 |
|
|
87
|
+
|---------|--------|
|
|
88
|
+
| "早餐"、"早饭" | 08:00 |
|
|
89
|
+
| "午餐"、"午饭" | 12:00 |
|
|
90
|
+
| "晚餐"、"晚饭" | 18:00 或 19:00 |
|
|
91
|
+
| "夜宵" | 23:00 |
|
|
92
|
+
| "早上" | 09:00 |
|
|
93
|
+
| "下午" | 15:00 |
|
|
94
|
+
| "晚上" | 20:00 |
|
|
95
|
+
| 具体时间(如"11点半") | 使用具体时间 |
|
|
96
|
+
| 无时间信息 | 使用当前时间 |
|
|
148
97
|
|
|
149
|
-
|
|
150
|
-
1. 调用 `get_parse_prompt` 获取 prompt 模板
|
|
151
|
-
2. 使用 prompt 模板调用大模型解析小票图片
|
|
152
|
-
3. **调用 `save_receipt` 前,必须完成字段核对清单(见下文)**
|
|
153
|
-
4. 调用 `save_receipt` 保存解析结果
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
### 📝 简单记账字段核对清单(save_expense)
|
|
98
|
+
**重要:** 最终传入的 `date` 参数必须包含时间部分,格式:`2026-04-14T12:00:00+08:00`
|
|
158
99
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
| 字段 | 是否必须 | 说明 |
|
|
162
|
-
|------|----------|------|
|
|
163
|
-
| `name` | ✅ | 商品/服务名称 |
|
|
164
|
-
| `amount` | ✅ | 金额(正数)|
|
|
165
|
-
| `category` | ✅ **新增** | 分类(如:餐饮、交通)|
|
|
166
|
-
| `date` | ✅ | 日期(ISO 8601 格式)|
|
|
167
|
-
| `store` | 可选 | 商家名称 |
|
|
168
|
-
| `note` | 可选 | 备注 |
|
|
169
|
-
|
|
170
|
-
**⚠️ 注意**:category 是记账的基本信息,必须提供!
|
|
100
|
+
**必填字段:** `name`、`amount`、`category`、`date`
|
|
171
101
|
|
|
172
102
|
---
|
|
173
103
|
|
|
174
|
-
###
|
|
175
|
-
|
|
176
|
-
**从 prompt 解析结果中提取的所有字段,必须全部传递给 `save_receipt`**:
|
|
104
|
+
### 小票记账(save_receipt)
|
|
177
105
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|------|----------|------|
|
|
193
|
-
| `name` | ✅ | 商品名称 |
|
|
194
|
-
| `amount` | ✅ | 金额(= price × quantity)|
|
|
195
|
-
| `price` | ✅ | 单价 |
|
|
196
|
-
| `quantity` | ✅ | 数量 |
|
|
197
|
-
| `category` | ✅ **新增** | 分类(如:食品、日用品)|
|
|
198
|
-
|
|
199
|
-
**⚠️ 重要**:缺少必填字段会导致保存失败。服务器会自动校验数据完整性和准确性。
|
|
106
|
+
**流程:**
|
|
107
|
+
1. 调用 `get_parse_prompt` 获取 prompt 模板
|
|
108
|
+
2. 使用 prompt 调用大模型解析小票图片
|
|
109
|
+
3. **字段核对**:确保所有解析结果字段都传给 `save_receipt`
|
|
110
|
+
4. 调用 `save_receipt` 保存
|
|
111
|
+
|
|
112
|
+
**必填字段:**
|
|
113
|
+
- `store`:商家名称
|
|
114
|
+
- `date`:ISO 8601 格式,必须带时区
|
|
115
|
+
- `totalAmount`:商品原价总和
|
|
116
|
+
- `actualAmount`:实付金额(优惠后)
|
|
117
|
+
- `originalAmount`:原价(优惠前)
|
|
118
|
+
- `discountAmount`:优惠金额
|
|
119
|
+
- `items`:商品数组,每个商品必须有 `name`、`amount`、`price`、`quantity`、`category`
|
|
200
120
|
|
|
201
121
|
---
|
|
202
122
|
|
|
@@ -204,58 +124,24 @@ Agent 通过 MCP 协议自动获取工具列表,无需手动查询。
|
|
|
204
124
|
|
|
205
125
|
| 风险等级 | 场景 | 策略 |
|
|
206
126
|
|----------|------|------|
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
127
|
+
| **高** | 金额 > 1000元、批量 > 10笔、退款/负数 | 必须确认 |
|
|
128
|
+
| **中** | 分类不明确、商家名称模糊 | 确认后执行 |
|
|
129
|
+
| **低** | 信息完整明确 | 直接执行 |
|
|
210
130
|
|
|
211
131
|
---
|
|
212
132
|
|
|
213
133
|
## 防重复录入(Agent 本地实现)
|
|
214
134
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
### 实现方式
|
|
218
|
-
|
|
219
|
-
Agent 维护本地记账记录日志(内存或本地文件):
|
|
220
|
-
|
|
221
|
-
```javascript
|
|
222
|
-
// 记录格式
|
|
223
|
-
{
|
|
224
|
-
store: "商家名称",
|
|
225
|
-
date: "2026-01-08", // 只记录到日期
|
|
226
|
-
actualAmount: 25.88,
|
|
227
|
-
timestamp: 1704694947000 // 记账时间戳
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### 重复检测规则
|
|
232
|
-
|
|
135
|
+
**规则:**
|
|
233
136
|
- **时间窗口**:24 小时内
|
|
234
137
|
- **匹配条件**:`store` + `date(到天)` + `actualAmount` 相同
|
|
235
|
-
-
|
|
236
|
-
1. 发现重复时,提醒用户:"⚠️ 24 小时内已有相似记录(华润万家春风店 25.88元),确认要重复录入吗?"
|
|
237
|
-
2. 用户确认后,继续调用 `save_receipt`
|
|
238
|
-
3. 用户取消,停止操作
|
|
239
|
-
|
|
240
|
-
### 代码示例
|
|
241
|
-
|
|
242
|
-
```javascript
|
|
243
|
-
// 检查是否重复
|
|
244
|
-
function isDuplicate(store, date, actualAmount) {
|
|
245
|
-
const fingerprint = `${store}_${date.substring(0,10)}_${actualAmount}`;
|
|
246
|
-
const recentRecord = recentLogs.find(log => {
|
|
247
|
-
const timeDiff = Date.now() - log.timestamp;
|
|
248
|
-
return log.fingerprint === fingerprint && timeDiff < 24 * 60 * 60 * 1000;
|
|
249
|
-
});
|
|
250
|
-
return recentRecord;
|
|
251
|
-
}
|
|
252
|
-
```
|
|
138
|
+
- **处理方式**:提醒用户,用户确认后继续
|
|
253
139
|
|
|
254
140
|
---
|
|
255
141
|
|
|
256
142
|
## 回复规范(必须严格遵守)
|
|
257
143
|
|
|
258
|
-
|
|
144
|
+
记账成功后,回复格式:
|
|
259
145
|
|
|
260
146
|
```
|
|
261
147
|
✅ 【商品名/店名】¥【金额】 已录入柴米AI记账。
|
|
@@ -263,65 +149,15 @@ function isDuplicate(store, date, actualAmount) {
|
|
|
263
149
|
[自定义其他内容]
|
|
264
150
|
|
|
265
151
|
MCP Server: v【版本号】
|
|
266
|
-
|
|
152
|
+
【友好结束语】
|
|
267
153
|
```
|
|
268
154
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
| 购物类 | 买得开心!🛍️ |
|
|
276
|
-
| 收入类 | 入账顺利!💰 |
|
|
277
|
-
| 通用 | 记账完成!继续保持~ / 理财小能手!✨ |
|
|
278
|
-
|
|
279
|
-
### 示例
|
|
280
|
-
|
|
281
|
-
**用户:** 记一笔午餐 35元
|
|
282
|
-
|
|
283
|
-
**Agent 回复:**
|
|
284
|
-
```
|
|
285
|
-
✅ 午餐¥35 已录入柴米AI记账。
|
|
286
|
-
|
|
287
|
-
今天也要好好吃饭哦~
|
|
288
|
-
|
|
289
|
-
MCP Server: v3.1.23
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
---
|
|
293
|
-
|
|
294
|
-
## 使用示例
|
|
295
|
-
|
|
296
|
-
### 示例1:简单记账
|
|
297
|
-
|
|
298
|
-
**用户:** 记一笔午餐 35元
|
|
299
|
-
|
|
300
|
-
**Agent:**
|
|
301
|
-
1. 触发 Skill
|
|
302
|
-
2. 前置检查通过
|
|
303
|
-
3. 调用 `save_expense(name="午餐", amount=35, category="餐饮")`
|
|
304
|
-
4. 返回结果:✅ 已记录:午餐 35元
|
|
305
|
-
|
|
306
|
-
### 示例2:小票记账
|
|
307
|
-
|
|
308
|
-
**用户:** 保存小票,沃尔玛,买了苹果5元、香蕉3元
|
|
309
|
-
|
|
310
|
-
**Agent:**
|
|
311
|
-
1. 触发 Skill
|
|
312
|
-
2. 前置检查通过
|
|
313
|
-
3. 调用 `save_receipt(store="沃尔玛", items=[...], totalAmount=8)`
|
|
314
|
-
4. 返回结果:✅ 已保存小票:沃尔玛,共8元
|
|
315
|
-
|
|
316
|
-
### 示例3:首次使用(未授权)
|
|
317
|
-
|
|
318
|
-
**用户:** 记一笔咖啡 25元
|
|
319
|
-
|
|
320
|
-
**Agent:**
|
|
321
|
-
1. 触发 Skill
|
|
322
|
-
2. 前置检查发现未授权
|
|
323
|
-
3. 引导用户完成 Device Flow 授权
|
|
324
|
-
4. 授权完成后执行记账
|
|
155
|
+
**友好结束语参考:**
|
|
156
|
+
- 餐饮类:用餐愉快!🍚 / 好好吃饭哦~
|
|
157
|
+
- 交通类:出行顺利!🚗
|
|
158
|
+
- 购物类:买得开心!🛍️
|
|
159
|
+
- 收入类:入账顺利!💰
|
|
160
|
+
- 通用:记账完成!继续保持~ ✨
|
|
325
161
|
|
|
326
162
|
---
|
|
327
163
|
|
|
@@ -332,7 +168,6 @@ MCP Server: v3.1.23
|
|
|
332
168
|
| 未安装 | 引导安装:`npm install -g chaimi-keep-mcp` |
|
|
333
169
|
| 未授权 | 引导完成 Device Flow 授权 |
|
|
334
170
|
| Token 过期 | 引导重新授权 |
|
|
335
|
-
| 网络错误 | 提示用户检查网络,稍后重试 |
|
|
336
171
|
| 参数错误 | 提示用户补充必要信息 |
|
|
337
172
|
|
|
338
173
|
---
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -139,7 +139,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
139
139
|
properties: {
|
|
140
140
|
name: { type: 'string', description: '商品名称(必填)' },
|
|
141
141
|
amount: { type: 'number', description: '金额(必填)' },
|
|
142
|
-
category: { type: 'string', description: '
|
|
142
|
+
category: { type: 'string', description: '分类(必填,如:餐饮、食品、交通)' },
|
|
143
143
|
store: { type: 'string', description: '商家名称(可选)' },
|
|
144
144
|
date: { type: 'string', description: '消费时间(ISO格式)(可选)' },
|
|
145
145
|
note: { type: 'string', description: '备注(可选)' },
|
|
@@ -158,11 +158,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
158
158
|
type: 'object',
|
|
159
159
|
properties: {
|
|
160
160
|
store: { type: 'string', description: '商家名称(必填)' },
|
|
161
|
-
date: { type: 'string', description: '
|
|
162
|
-
totalAmount: { type: 'number', description: '
|
|
163
|
-
originalAmount: { type: 'number', description: '
|
|
164
|
-
discountAmount: { type: 'number', description: '
|
|
165
|
-
actualAmount: { type: 'number', description: '
|
|
161
|
+
date: { type: 'string', description: '(必填)消费时间(ISO 8601 格式,必须包含日期和时间,如:2026-04-10T13:06:21)' },
|
|
162
|
+
totalAmount: { type: 'number', description: '(必填)总金额(所有商品amount之和)' },
|
|
163
|
+
originalAmount: { type: 'number', description: '(必填)应付金额(优惠前)' },
|
|
164
|
+
discountAmount: { type: 'number', description: '(必填)优惠金额' },
|
|
165
|
+
actualAmount: { type: 'number', description: '(必填)实付金额(优惠后)' },
|
|
166
166
|
paymentMethod: { type: 'string', description: '支付方式,如:微信支付、支付宝' },
|
|
167
167
|
receiptNo: { type: 'string', description: '小票编号' },
|
|
168
168
|
items: {
|
|
@@ -179,7 +179,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
179
179
|
amount: { type: 'number', description: '【必填】金额,必须等于 price × quantity' },
|
|
180
180
|
weight: { type: 'string', description: '重量' },
|
|
181
181
|
marketPrice: { type: 'string', description: '市场单价(元/500g)' },
|
|
182
|
-
category: { type: 'string', description: '
|
|
182
|
+
category: { type: 'string', description: '(必填)分类,如:餐饮、食品、购物' },
|
|
183
183
|
},
|
|
184
184
|
required: ['name', 'amount', 'price', 'quantity'],
|
|
185
185
|
},
|
|
@@ -233,7 +233,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
233
233
|
properties: {
|
|
234
234
|
name: { type: 'string', description: '收入来源(如:工资、奖金、红包)' },
|
|
235
235
|
amount: { type: 'number', description: '收入金额' },
|
|
236
|
-
category: { type: 'string', description: '
|
|
236
|
+
category: { type: 'string', description: '(必填)收入分类(如:工资、奖金、投资)' },
|
|
237
237
|
store: { type: 'string', description: '付款方(如:公司名称)' },
|
|
238
238
|
date: { type: 'string', description: '收入时间(ISO格式)(可选)' },
|
|
239
239
|
note: { type: 'string', description: '备注' },
|
|
@@ -924,6 +924,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
924
924
|
break;
|
|
925
925
|
}
|
|
926
926
|
|
|
927
|
+
case 'get_text_parse_prompt': {
|
|
928
|
+
// 获取文字记账解析 Prompt
|
|
929
|
+
const textParseResult = await callMcpHub('getTextParsePrompt', {}, token);
|
|
930
|
+
|
|
931
|
+
if (textParseResult.success) {
|
|
932
|
+
result = {
|
|
933
|
+
success: true,
|
|
934
|
+
data: {
|
|
935
|
+
prompt: textParseResult.data.prompt,
|
|
936
|
+
currentDate: textParseResult.data.currentDate,
|
|
937
|
+
currentTime: textParseResult.data.currentTime,
|
|
938
|
+
instructions: '请将返回的 prompt 作为 system message,把用户输入的记账文字作为 user message,调用大模型解析。解析完成后,调用 save_expense 或 save_income 工具保存结果。'
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
userMessage = `✅ 获取文字记账解析 Prompt 成功\n\n当前日期: ${textParseResult.data.currentDate}\n当前时间: ${textParseResult.data.currentTime}\n\n## Prompt\n\n请将以下内容作为 system message 发送给大模型:\n\n\`\`\`\n${textParseResult.data.prompt}\n\`\`\`\n\n## 使用说明\n\n1. 将上面的 Prompt 作为 system message\n2. 将用户输入的记账文字(如"午餐 24块")作为 user message\n3. 调用大模型解析,获取 JSON 结果\n4. 解析完成后,调用 save_expense 或 save_income 工具保存结果\n\n## 解析结果示例\n\n\`\`\`json\n{\n "name": "午餐",\n "amount": 24,\n "category": "餐饮",\n "datetime": "2026-04-14T12:30:00+08:00",\n "transactionType": "expense"\n}\n\`\`\``;
|
|
942
|
+
} else {
|
|
943
|
+
result = { success: false, error: textParseResult.error };
|
|
944
|
+
userMessage = `❌ 获取文字记账解析 Prompt 失败:${textParseResult.error}`;
|
|
945
|
+
}
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
|
|
927
949
|
case 'submit_feedback': {
|
|
928
950
|
const feedbackData = {
|
|
929
951
|
content: processedArgs.content,
|
|
@@ -1068,19 +1090,13 @@ function validateQuantity(quantity) {
|
|
|
1068
1090
|
|
|
1069
1091
|
/**
|
|
1070
1092
|
* 格式化日期为 ISO 8601 格式(带时区)
|
|
1071
|
-
*
|
|
1093
|
+
* 注意:此函数只负责格式标准化,不负责语义解析
|
|
1094
|
+
* 语义解析(如"午餐"→12:00)应由 Agent 完成
|
|
1072
1095
|
*/
|
|
1073
1096
|
function formatDateWithTimezone(dateStr) {
|
|
1074
|
-
//
|
|
1097
|
+
// 如果未提供日期,返回空字符串(让云函数使用服务器时间)
|
|
1075
1098
|
if (!dateStr) {
|
|
1076
|
-
|
|
1077
|
-
const year = now.getFullYear();
|
|
1078
|
-
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
1079
|
-
const day = String(now.getDate()).padStart(2, '0');
|
|
1080
|
-
const hours = String(now.getHours()).padStart(2, '0');
|
|
1081
|
-
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
1082
|
-
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
1083
|
-
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}+08:00`;
|
|
1099
|
+
return '';
|
|
1084
1100
|
}
|
|
1085
1101
|
|
|
1086
1102
|
try {
|
|
@@ -1095,6 +1111,13 @@ function formatDateWithTimezone(dateStr) {
|
|
|
1095
1111
|
return dateStr + '+08:00';
|
|
1096
1112
|
}
|
|
1097
1113
|
|
|
1114
|
+
// 如果是纯日期格式(如 2026-04-14),默认使用中午12:00
|
|
1115
|
+
// 注意:这是合理的默认值,因为纯日期通常表示"当天的消费"
|
|
1116
|
+
// 如果 Agent 需要更精确的时间(如晚餐18:00),应该在传入时就包含时间部分
|
|
1117
|
+
if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
1118
|
+
return `${dateStr}T12:00:00+08:00`;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1098
1121
|
// 其他情况,尝试解析并格式化为带时区的格式
|
|
1099
1122
|
const date = new Date(dateStr);
|
|
1100
1123
|
if (isNaN(date.getTime())) {
|