Compare commits

...

2 Commits

Author SHA1 Message Date
dzq 5b40a79d6d feat(cabinet-tool): 优化返回数据并新增统计信息
- 返回数据简化: cells 列表仅保留第一条,减少 LLM 数据量
- 新增 stats 统计字段: 包含总数/空闲/占用数及各类型数量
- 完整数据存入缓存: 通过 cacheId 供代码执行工具后续提取
- 更新 Agent 提示词: 说明数据变化及缓存使用方式
- 添加优化方案文档

BREAKING CHANGE: cabinetTool 返回数据结构变更,新增 stats 字段
2026-01-10 17:56:14 +08:00
dzq 3654ad60f7 ♻️ refactor(agent): 移除模式列表查询功能并更新数据结构
- 移除 shopTool 的 modeList 查询类型及相关 API 调用
- 移除 goodsTool 中的 categoryId 查询参数
- 更新 DTO 接口定义,调整字段命名规范
- 新增 ProductInfoDTO、CellInfoDTO、CabinetInfoDTO 接口

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-10 09:36:02 +08:00
6 changed files with 345 additions and 59 deletions

View File

@ -0,0 +1,223 @@
# cabinetTool 返回数据优化方案
## 背景
`cabinetTool` 查询智能柜详情时,返回的 `cells` 数组可能包含大量格口信息,导致返回给 LLM 的数据量过大,影响响应效率。
## 修改目标
1. **减少返回数据量**列表数据cells只返回第一条
2. **增加统计信息**:返回中新增完整的统计信息
3. **保留完整数据**:完整数据保留在缓存中,供后续使用
4. **引导使用代码执行**:通过 prompt 说明,引导 Agent 使用 code-executor-tool 提取完整数据
## 修改内容
### 1. cabinet-tool.ts
#### outputSchema 变更
新增 `stats` 统计字段:
```typescript
outputSchema: z.object({
success: z.boolean(),
message: z.string(),
data: z.any().optional().describe("柜机信息cells列表仅返回第一条完整数据在缓存中"),
stats: z.object({
cellsTotal: z.number().describe("格口总数"),
cellsFree: z.number().describe("空闲格口数"),
cellsOccupied: z.number().describe("占用格口数"),
cellsSmall: z.number().describe("小格数量"),
cellsMedium: z.number().describe("中格数量"),
cellsLarge: z.number().describe("大格数量"),
cellsExtraLarge: z.number().describe("超大格数量"),
}).optional().describe("统计信息"),
cacheId: z.string().optional().describe("缓存ID用于后续引用该查询结果"),
expiresAt: z.number().optional().describe("缓存过期时间戳"),
}),
```
#### execute 逻辑变更
1. 计算完整统计信息
2. 将简化数据(仅包含第一条 cell返回给 LLM
3. 完整数据(含 stats存入缓存
```typescript
const cells = response.data?.cells || [];
// 计算统计信息
const stats = {
cellsTotal: cells.length,
cellsFree: cells.filter(c => c.usageStatus === 1).length,
cellsOccupied: cells.filter(c => c.usageStatus === 2).length,
cellsSmall: cells.filter(c => c.cellType === 1).length,
cellsMedium: cells.filter(c => c.cellType === 2).length,
cellsLarge: cells.filter(c => c.cellType === 3).length,
cellsExtraLarge: cells.filter(c => c.cellType === 4).length,
};
// 简化数据仅保留第一条cell
const firstCell = cells[0] || null;
const simplifiedData = {
...response.data,
cells: firstCell ? [firstCell] : [],
};
// 返回结果
const result = {
success: response.code === 0,
message: response.msg || "查询成功",
data: simplifiedData,
stats,
};
```
### 2. multi-function-agent.ts
#### 柜机查询说明更新
```
2. **柜机查询**使用柜机工具根据柜机ID查询智能柜详细信息。返回数据中格口cells列表仅包含第一条完整数据保留在缓存中。返回结果包含 stats 统计信息(总数、空闲/占用数、各类型数量)。如需获取完整格口列表,请使用代码执行工具通过 cacheId 提取。
```
#### 代码执行示例更新
新增 cabinetTool 的缓存使用示例:
```markdown
- 缓存-柜机code: "return cacheCabinet_0?.data?.cells?.filter(c => c.usageStatus === 1)", cacheIds: ["cacheCabinet_0"] -> 获取所有空闲格口
- 缓存-柜机code: "return cacheCabinet_0?.data?.cells?.filter(c => c.cellType === 2)", cacheIds: ["cacheCabinet_0"] -> 获取所有中格
```
## 返回数据结构对比
### 修改前
```json
{
"success": true,
"message": "查询成功",
"data": {
"cabinetId": 1,
"cabinetName": "柜机A",
"cells": [
{ "cellId": 1, "cellNo": 1, ... },
{ "cellId": 2, "cellNo": 2, ... },
{ "cellId": 3, "cellNo": 3, ... }
// ... 更多格口
]
},
"cacheId": "cacheCabinet_1",
"expiresAt": 1700000000000
}
```
### 修改后
```json
{
"success": true,
"message": "查询成功",
"data": {
"cabinetId": 1,
"cabinetName": "柜机A",
"cells": [
{ "cellId": 1, "cellNo": 1, ... }
]
},
"stats": {
"cellsTotal": 50,
"cellsFree": 30,
"cellsOccupied": 20,
"cellsSmall": 10,
"cellsMedium": 20,
"cellsLarge": 15,
"cellsExtraLarge": 5
},
"cacheId": "cacheCabinet_1",
"expiresAt": 1700000000000
}
```
### 缓存数据(完整)
```json
{
"success": true,
"message": "查询成功",
"data": {
"cabinetId": 1,
"cabinetName": "柜机A",
"cells": [
{ "cellId": 1, "cellNo": 1, ... },
{ "cellId": 2, "cellNo": 2, ... },
{ "cellId": 3, "cellNo": 3, ... }
// ... 所有格口
]
},
"stats": {
"cellsTotal": 50,
"cellsFree": 30,
"cellsOccupied": 20,
"cellsSmall": 10,
"cellsMedium": 20,
"cellsLarge": 15,
"cellsExtraLarge": 5
}
}
```
## 使用示例
### 场景1查询柜机概况
直接使用 cabinetTool获取统计信息
```
用户查询柜机1的概况
Agent -> cabinetTool(cabinetId: 1) -> 返回 stats 信息
```
### 场景2获取所有空闲格口
使用代码执行工具通过 cacheId 提取:
```
用户柜机1有哪些空闲格口
Agent -> cabinetTool(cabinetId: 1) -> codeExecutorTool(
code: "return cacheCabinet_0?.data?.cells?.filter(c => c.usageStatus === 1)",
cacheIds: ["cacheCabinet_0"]
) -> 返回空闲格口列表
```
### 场景3筛选特定类型格口
结合 context 参数筛选:
```
用户柜机1有哪些大格可以存放商品
Agent -> cabinetTool(cabinetId: 1) -> codeExecutorTool(
code: "return cacheCabinet_0?.data?.cells?.filter(c => c.cellType === 3 && c.usageStatus === 1)",
cacheIds: ["cacheCabinet_0"]
) -> 返回空闲大格列表
```
## 修改文件清单
| 文件 | 修改类型 | 说明 |
|------|---------|------|
| `src/mastra/tools/cabinet-tool.ts` | 修改 | outputSchema 新增 stats 字段execute 逻辑简化返回数据 |
| `src/mastra/agents/multi-function-agent.ts` | 修改 | 更新 instructions说明返回数据和缓存使用方式 |
| `doc/cabinet-tool-optimization.md` | 新建 | 文档总结修改方案 |
## 验证步骤
1. **构建项目**:运行 `pnpm build` 确保无编译错误
2. **启动服务**:运行 `pnpm dev` 启动开发服务器
3. **测试 API**:调用 cabinetTool检查返回数据是否符合预期
4. **测试场景**
- 查询单个柜机,检查返回数据是否只包含第一条 cell
- 使用 code-executor-tool 提取所有空闲格口
- 使用 code-executor-tool 统计格口类型分布

View File

@ -47,8 +47,8 @@ export const multiFunctionAgent = new Agent({
content: `你是一个具有内存功能的多功能助手,具备以下能力:
1. ****使ID获取单个商品详情
2. ****使ID查询智能柜详细信息
3. ****使ID获取门店详细信息
2. ****使ID查询智能柜详细信息cells stats /使 cacheId
3. ****使ID获取门店详细信息
4. ****使IDID
5. ****使
-
@ -63,13 +63,16 @@ export const multiFunctionAgent = new Agent({
- cacheId ->
- cacheGoods_0 -> cacheGoods_0
- cacheGoods_1 -> cacheGoods_1
- cacheCabinet_0 -> cacheCabinet_0
- cacheCabinet_1 -> cacheCabinet_1
- cacheShop_0 -> cacheShop_0
- cacheDynamicInfo_0 -> cacheDynamicInfo_0
- cacheSearchEntity_0 -> cacheSearchEntity_0
-
- code: "return a + b", context: {a: 10, b: 20} -> 30
- code: "return cacheGoods_1.filter(g => g.price > 50)", cacheIds: ["cacheGoods_1"] -> 使
- -code: "return cacheGoods_1.filter(g => g.price > 50)", cacheIds: ["cacheGoods_1"] -> 使
- -code: "return cacheCabinet_0?.data?.cells?.filter(c => c.usageStatus === 1)", cacheIds: ["cacheCabinet_0"] ->
- -code: "return cacheCabinet_0?.data?.cells?.filter(c => c.cellType === 2)", cacheIds: ["cacheCabinet_0"] ->
- code: "return cacheGoods_1.filter(g => g.price > threshold)", context: {threshold: 100}, cacheIds: ["cacheGoods_1"] -> +
7. ****
- cacheId
@ -86,6 +89,7 @@ export const multiFunctionAgent = new Agent({
- cellusageStatus1- 2-
- 1 2 3 4

View File

@ -8,36 +8,53 @@ export interface IdNameDTO {
name: string
}
export interface CellDTO {
/** 单元格ID */
id: number
/** 单元格编号 */
cellNo: string
/** 单元格名称 */
cellName: string
/** 单元格状态 */
status: number
export interface ProductInfoDTO {
/** 商品ID */
goodsId: number
/** 商品名称 */
goodsName: string
/** 商品价格 */
price: number
/** 封面图URL */
coverImg: string
}
export interface CellInfoDTO {
/** 格子ID */
cellId: number
/** 格子编号 */
cellNo: number
/** 引脚编号 */
pinNo: number
/** 库存数量 */
stock: number
/** 格子价格 */
cellPrice: number
/** 订单ID */
orderId: number
/** 订单商品ID */
orderGoodsId: number
/** 商品信息 */
product: ProductInfoDTO
/** 密码 */
password: string
/** 使用状态1空闲 2已占用 */
usageStatus: number
/** 格口类型1小格 2中格 3大格 4超大格 */
cellType: number
/** 备注 */
remark: string
}
export interface CabinetDetailDTO {
/** 柜机ID */
id: number
/** 柜机编号 */
cabinetNo: string
cabinetId: number
/** 柜机名称 */
cabinetName: string
/** 所属店铺ID */
shopId: number
/** 所属店铺名称 */
shopName: string
/** 柜机状态 */
status: number
/** 单元格列表 */
cells: CellDTO[]
/** 锁控编号 */
lockControlNo: number
/** 格子列表 */
cells: CellInfoDTO[]
}
export interface CabinetSimpleDTO {
@ -53,21 +70,36 @@ export interface CabinetSimpleDTO {
export interface ShopDetailDTO {
/** 店铺ID */
id: number
shopId: number
/** 店铺名称 */
shopName: string
/** 店铺编码 */
shopCode: string
/** 联系人姓名 */
contactName: string
/** 联系电话 */
contactPhone: string
/** 店铺地址 */
address: string
/** 店铺状态 */
status: number
/** 企业微信id */
corpid: string
/** 归属类型0-借还柜 1-固资通) */
belongType: number
/** 运行模式0-支付模式 1-审批模式 2-借还模式 3-会员模式 4-耗材模式 5-暂存模式) */
mode: number
/** 借呗支付1-正常使用 0-禁止使用) */
balanceEnable: number
/** 柜机列表 */
cabinets: CabinetSimpleDTO[]
cabinets: CabinetInfoDTO[]
}
export interface CabinetInfoDTO {
/** 柜机唯一ID */
cabinetId: number
/** 柜机名称 */
cabinetName: string
/** 归属主柜ID */
mainCabinet: number
/** MQTT服务ID */
mqttServerId: number
/** 柜机模版编号 */
templateNo: string
/** 锁控板序号 */
lockControlNo: number
/** 归还期限0表示不限制 */
returnDeadline: number
}
export type EntityType = "shop" | "cabinet" | "goods"

View File

@ -14,7 +14,16 @@ export const cabinetTool = createTool({
outputSchema: z.object({
success: z.boolean(),
message: z.string(),
data: z.any().optional(),
data: z.any().optional().describe("柜机信息cells列表仅返回第一条完整数据在缓存中"),
stats: z.object({
cellsTotal: z.number().describe("格口总数"),
cellsFree: z.number().describe("空闲格口数"),
cellsOccupied: z.number().describe("占用格口数"),
cellsSmall: z.number().describe("小格数量"),
cellsMedium: z.number().describe("中格数量"),
cellsLarge: z.number().describe("大格数量"),
cellsExtraLarge: z.number().describe("超大格数量"),
}).optional().describe("统计信息"),
cacheId: z.string().optional().describe("缓存ID用于后续引用该查询结果"),
expiresAt: z.number().optional().describe("缓存过期时间戳"),
}),
@ -23,25 +32,52 @@ export const cabinetTool = createTool({
try {
const { cabinetId } = context;
let result: any;
if (!cabinetId) {
result = {
return {
success: false,
message: "cabinetId参数为必填",
};
} else {
const response = await cabinetDetail(cabinetId);
result = {
success: response.code === 0,
message: response.msg || "查询成功",
data: response.data,
};
}
const response = await cabinetDetail(cabinetId);
const cells = response.data?.cells || [];
// 计算统计信息
const stats = {
cellsTotal: cells.length,
cellsFree: cells.filter((c: any) => c.usageStatus === 1).length,
cellsOccupied: cells.filter((c: any) => c.usageStatus === 2).length,
cellsSmall: cells.filter((c: any) => c.cellType === 1).length,
cellsMedium: cells.filter((c: any) => c.cellType === 2).length,
cellsLarge: cells.filter((c: any) => c.cellType === 3).length,
cellsExtraLarge: cells.filter((c: any) => c.cellType === 4).length,
};
// 完整结果(用于缓存)
const fullResult = {
success: response.code === 0,
message: response.msg || "查询成功",
data: response.data,
stats,
};
// 简化数据用于返回仅保留第一条cell
const firstCell = cells[0] || null;
const simplifiedData = {
...response.data,
cells: firstCell ? [firstCell] : [],
};
const result = {
success: response.code === 0,
message: response.msg || "查询成功",
data: simplifiedData,
stats,
};
// 添加缓存
const cacheId = toolCacheService.generateCacheId('cabinet', cabinetId);
const cached = toolCacheService.set(cacheId, result);
const cached = toolCacheService.set(cacheId, fullResult);
return {
...result,

View File

@ -76,7 +76,6 @@ export const goodsTool = createTool({
// 列表查询
const query: SearchShopGoodsQuery = {
goodsName,
categoryId,
status,
autoApproval,
minPrice,

View File

@ -2,7 +2,6 @@ import { createTool } from "@mastra/core/tools";
import { z } from "zod";
import {
getShopListApi,
getModeListApi,
GetShopListParams
} from "../api/shop";
import { shopDetail } from "../api/agent/agent";
@ -10,11 +9,11 @@ import { toolCacheService } from "../services";
export const shopTool = createTool({
id: "shop",
description: "查询门店相关信息,支持查询门店列表、模式列表、门店详情。查询结果会被缓存cacheId 可用于 code-executor-tool 进行数据处理。",
description: "查询门店相关信息,支持查询门店列表、门店详情。查询结果会被缓存cacheId 可用于 code-executor-tool 进行数据处理。",
inputSchema: z.object({
queryType: z.enum(["shopList", "modeList", "shopDetail"]).describe(
"查询类型:'shopList'表示获取门店列表,'modeList'表示获取模式列表,'shopDetail'表示获取门店详情"
queryType: z.enum(["shopList", "shopDetail"]).describe(
"查询类型:'shopList'表示获取门店列表,'shopDetail'表示获取门店详情"
),
corpid: z.string().optional().describe("企业微信ID查询门店列表时必填"),
mode: z.number().optional().describe("需要排除的运行模式(查询门店列表时可选,该参数表示不查询该运行模式的门店,默认值为-1"),
@ -57,13 +56,6 @@ export const shopTool = createTool({
data: response.data,
};
}
} else if (queryType === "modeList") {
const response = await getModeListApi();
result = {
success: response.code === 0,
message: response.msg || "查询成功",
data: response.data,
};
} else if (queryType === "shopDetail") {
if (!shopId) {
result = {