文件
OmniCard/backend/test/modules/ai/sandbox.test.ts
wikiboss eec259a3a7 feat: 完成全系统30个功能点开发与E2E全链路验收 (8轮调度32个子代理闭环)
- 模块一: 出海案例作品集、博客动态发布、人名发音语音条、RTL阿语多语言切换
- 模块二: AI顾问人设配置、行业术语库润色、RAG切片沙盒调试、流式会话与意向留资卡片
- 模块三: 全球访客雷达大屏、多维加权意向算分、外部CRM对接推送、团队公海池流转
- 模块四: 移动端NFC安全挂失、NFC动态路由规则配置、动态防伪验签、展会制卡Zip批量导出
- 模块五: 组织架构与名片分配、移动端个人中心设置、离职交接与历史线索迁移、安全审计日志
- 模块六: Stripe国际信用卡订阅Webhook、独立二级域名与SSL检测、坐席配额预警、多币种发票
- 模块七: 纸质名片OCR解析入库、国际商务日程RFC 5545 ICS日历、微官网离线SWR预加载、展会实时排行榜大屏
- 模块八: 全球出海SEO OpenGraph元标签注入与社交爬虫探测、30点端到端全链路业务流集成测试套件
2026-09-20 23:47:47 +08:00

204 行
7.7 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

import * as fs from 'fs';
import * as path from 'path';
import { CONTROLLER_KEY, getClassMetadata } from '@midwayjs/core';
import { BaseController, CurdOption } from '@cool-midway/core';
import { AiKnowledgeService } from '../../../src/modules/ai/service/knowledge';
import {
AdminAiKnowledgeController,
adminAiKnowledgeCurdOption,
} from '../../../src/modules/ai/controller/admin/knowledge';
import { KnowledgeChunk } from '../../../src/comm/types/omni';
interface CoolControllerMeta {
prefix?: string;
curdOption?: CurdOption;
}
async function waitCoolMeta(
target: new (...args: never[]) => unknown
): Promise<CoolControllerMeta> {
const started = Date.now();
while (Date.now() - started < 2000) {
const meta = getClassMetadata(CONTROLLER_KEY, target) as
| CoolControllerMeta
| undefined;
if (meta?.curdOption) {
return meta;
}
await new Promise<void>(resolve => setImmediate(resolve));
}
throw new Error(`${target.name} 的 CoolController 元数据未就绪`);
}
function serviceApiMethods(apis: CurdOption['serviceApis'] = []): string[] {
return apis.map(api => (typeof api === 'string' ? api : api.method));
}
describe('Point 07: AI 知识库分块切片与召回沙盒 (modules/ai knowledge sandbox)', () => {
let service: AiKnowledgeService;
beforeEach(() => {
service = new AiKnowledgeService();
});
describe('1. splitTextToChunks 切片算法边界', () => {
it('空文本或空白文本应返回空数组', () => {
expect(service.splitTextToChunks('')).toEqual([]);
expect(service.splitTextToChunks(' \n\t ')).toEqual([]);
});
it('小文本边界:文本长度 <= chunkSize 应返回单一切片', () => {
const text = 'OmniCard 全球数字名片';
const chunks = service.splitTextToChunks(text, 50, 10);
expect(chunks).toHaveLength(1);
expect(chunks[0].content).toBe(text);
expect(chunks[0].index).toBe(0);
expect(chunks[0].length).toBe(text.length);
});
it('长文本滑动切片:验证切片数、重叠字符与末尾覆盖', () => {
// 构造长度为 45 的文本
const text = '1234567890abcdefghij1234567890abcdefghij12345';
const chunkSize = 20;
const overlap = 5;
const chunks = service.splitTextToChunks(text, chunkSize, overlap);
// step = 20 - 5 = 15:
// chunk 0: [0, 20]
// chunk 1: [15, 35]
// chunk 2: [30, 45] (末尾到达 45)
expect(chunks).toHaveLength(3);
expect(chunks[0].content).toBe(text.slice(0, 20));
expect(chunks[1].content).toBe(text.slice(15, 35));
expect(chunks[2].content).toBe(text.slice(30, 45));
// 验证重叠:chunk 0 的末尾 5 字符应等于 chunk 1 的开头 5 字符
expect(chunks[0].content.slice(-5)).toBe(chunks[1].content.slice(0, 5));
// 验证末尾完整覆盖
expect(chunks[chunks.length - 1].content.endsWith('12345')).toBe(true);
});
it('重叠截断边界:overlap >= chunkSize 时应自动纠正步长,防止死循环', () => {
const text = '这是一段用来测试重叠过大保护机制的测试长文本';
// overlap 设为 30,大于 chunkSize 20
const chunks = service.splitTextToChunks(text, 20, 30);
expect(chunks.length).toBeGreaterThan(1);
expect(chunks[0].length).toBeLessThanOrEqual(20);
});
it('未指定 chunkSize 与 overlap 时应具有合理默认值', () => {
const longText = 'A'.repeat(500);
const chunks = service.splitTextToChunks(longText);
expect(chunks.length).toBeGreaterThan(1);
expect(chunks[0].content.length).toBe(200);
});
});
describe('2. simulateRetrieval 召回算法与排序计算', () => {
const mockChunks: KnowledgeChunk[] = [
{
index: 0,
content: 'OmniCard 提供 NFC 智能实体卡,碰一碰即可传输联系方式与微官网。',
length: 37,
},
{
index: 1,
content: '雷达系统支持出海 CRM 访客轨迹追踪、停留时长与意向度自动评分。',
length: 33,
},
{
index: 2,
content: 'AI 知识库提供文档分块切片预览与 RAG 检索增强沙盒调试能力。',
length: 34,
},
{
index: 3,
content: 'Bento Grid 便当盒美学布局,响应式适配全球多语言及 RTL 界面。',
length: 34,
},
];
it('空 query 或空 chunks 应返回空数组', () => {
expect(service.simulateRetrieval('', mockChunks)).toEqual([]);
expect(service.simulateRetrieval('NFC', [])).toEqual([]);
});
it('应按匹配置信度降序排序,完全匹配/高频命中排第一', () => {
const query = 'NFC 实体卡 碰一碰';
const results = service.simulateRetrieval(query, mockChunks, 3);
expect(results.length).toBeGreaterThan(0);
// 第一条应该是 index 为 0 的 NFC 相关切片
expect(results[0].index).toBe(0);
expect(results[0].score).toBeGreaterThan(0.5);
expect(results[0].score).toBeLessThanOrEqual(1.0);
});
it('应支持 topK 截断返回数量', () => {
const query = '系统 智能 适配';
const results = service.simulateRetrieval(query, mockChunks, 2);
expect(results.length).toBeLessThanOrEqual(2);
});
it('应支持 threshold 阈值过滤低相关度切片', () => {
const query = 'RAG 沙盒调试';
// 只有切片 2 高度匹配,设置高阈值过滤其他低分或无关切片
const results = service.simulateRetrieval(query, mockChunks, 4, 0.4);
expect(results).toHaveLength(1);
expect(results[0].index).toBe(2);
expect(results[0].content).toContain('RAG 检索增强沙盒调试');
});
it('应兼容接收纯字符串数组 chunks', () => {
const stringChunks = [
'出海多语言支持英文、中文、阿拉伯语',
'国内网络环境配置说明',
];
const results = service.simulateRetrieval('阿拉伯语 出海', stringChunks, 2);
expect(results.length).toBe(1);
expect(results[0].content).toContain('阿拉伯语');
expect(results[0].score).toBeGreaterThan(0);
});
});
describe('3. AdminAiKnowledgeController 与 /admin/ai/knowledge/sandbox-test 接口', () => {
const controllerFile = path.join(
__dirname,
'../../../src/modules/ai/controller/admin/knowledge.ts'
);
let meta: CoolControllerMeta;
let option: CurdOption;
beforeAll(async () => {
meta = await waitCoolMeta(AdminAiKnowledgeController);
option = meta.curdOption as CurdOption;
});
it('控制器文件与路由前缀符合规范', () => {
expect(fs.existsSync(controllerFile)).toBe(true);
expect(meta.prefix).toBe('/admin/ai/knowledge');
expect(AdminAiKnowledgeController.prototype).toBeInstanceOf(BaseController);
});
it('serviceApis 应注册 sandboxTest,且控制器包含 sandboxTest 方法', () => {
const methods = serviceApiMethods(option.serviceApis);
expect(methods).toContain('sandboxTest');
expect(typeof (AdminAiKnowledgeController.prototype as unknown as Record<string, unknown>).sandboxTest).toBe('function');
});
it('AiKnowledgeService.sandboxTest 应能执行切片并召回', async () => {
const result = await service.sandboxTest({
query: 'NFC 实体卡',
content: 'OmniCard NFC 智能实体名片碰一碰极速交换。另有 AI 智能数字人与访客雷达。',
chunkSize: 30,
overlap: 5,
topK: 2,
});
expect(result.totalChunks).toBeGreaterThan(0);
expect(result.chunks.length).toBe(result.totalChunks);
expect(result.recallList.length).toBeGreaterThan(0);
expect(result.recallList[0].score).toBeGreaterThan(0);
});
});
});