GPU_GUARD_MONOREPO/packages/backend/test/prompt_builder.test.ts

196 lines
6.5 KiB
TypeScript
Raw Permalink Normal View History

2026-05-20 21:39:12 +08:00
import {
getModelGuidance,
getToolBehaviorGuidance,
} from '../src/modules/netaclaw/runtime/prompt_guidance.js';
import {
collectAvailableToolNames,
buildSystemPrompt,
BuildSystemPromptParams,
buildLLMMessages,
} from '../src/modules/netaclaw/runtime/prompt_builder.js';
// ─── Mock Helper ───
function createMockSkillLoader(returnValue: string) {
return { buildSkillsPrompt: jest.fn().mockReturnValue(returnValue) } as any;
}
// ─── collectAvailableToolNames ───
describe('collectAvailableToolNames', () => {
it('基础工具始终包含', () => {
const names = collectAvailableToolNames({});
expect(names).toEqual(expect.arrayContaining(['bash', 'read_file', 'write_file', 'list_dir', 'todo']));
});
it('memoryEnabled 追加记忆工具', () => {
const names = collectAvailableToolNames({ memoryEnabled: true });
expect(names).toContain('memory_save');
expect(names).toContain('memory_recall');
});
it('hasSkills 追加技能工具', () => {
const names = collectAvailableToolNames({ hasSkills: true });
expect(names).toContain('read_skill');
expect(names).toContain('read_skill_file');
expect(names).toContain('skill_manage');
});
it('crewRole=master 追加委派工具', () => {
const names = collectAvailableToolNames({ crewRole: 'master' });
expect(names).toContain('delegate_task');
expect(names).toContain('delegate_parallel');
expect(names).toContain('escalate');
});
it('crewRole=sub 不追加委派工具', () => {
const names = collectAvailableToolNames({ crewRole: 'sub' });
expect(names).not.toContain('delegate_task');
expect(names).not.toContain('delegate_parallel');
expect(names).not.toContain('escalate');
});
});
// ─── getModelGuidance ───
describe('getModelGuidance', () => {
it('Claude 返回空字符串', () => {
expect(getModelGuidance('claude-3-opus')).toBe('');
expect(getModelGuidance('Claude-Sonnet')).toBe('');
});
it('MiniMax 匹配 minimax 规则', () => {
const g = getModelGuidance('minimax-abab6');
expect(g).toContain('一个工具');
});
it('GPT 匹配 gpt 规则', () => {
const g = getModelGuidance('gpt-4o');
expect(g).toContain('tool_persistence');
});
it('DeepSeek 匹配 deepseek 规则', () => {
const g = getModelGuidance('deepseek-chat');
expect(g).toContain('并行');
});
it('doubao 匹配 doubao 规则', () => {
const g = getModelGuidance('doubao-pro-32k');
expect(g).toContain('non_interactive');
});
it('未知模型返回默认指导', () => {
const g = getModelGuidance('some-unknown-model-xyz');
expect(g).toContain('通用操作规范');
});
});
// ─── getToolBehaviorGuidance ───
describe('getToolBehaviorGuidance', () => {
it('包含 todo 时注入任务规划策略', () => {
const g = getToolBehaviorGuidance(['todo', 'bash']);
expect(g).toContain('任务规划策略');
});
it('包含 memory_save 时注入记忆策略', () => {
const g = getToolBehaviorGuidance(['memory_save']);
expect(g).toContain('记忆使用策略');
});
it('无匹配工具时返回空字符串', () => {
const g = getToolBehaviorGuidance(['bash', 'read_file']);
expect(g).toContain('命令执行策略');
expect(g).toContain('文件读取策略');
});
});
// ─── buildSystemPrompt ───
describe('buildSystemPrompt', () => {
const baseParams: BuildSystemPromptParams = {
agentSystemPrompt: '你是一个测试助手',
modelId: 'gpt-4o',
availableToolNames: ['bash', 'todo'],
skills: [],
skillLoader: createMockSkillLoader(''),
memoryEnabled: false,
};
it('基础场景:包含 Agent 身份、工具纪律、模型指导、工具行为策略、元信息', () => {
const { layers } = buildSystemPrompt(baseParams);
const names = layers.map(l => l.name);
expect(names).toContain('Agent 身份');
expect(names).toContain('工具使用纪律');
expect(names).toContain('模型特定指导');
expect(names).toContain('工具行为策略');
expect(names).toContain('元信息');
expect(names).not.toContain('Skill 索引');
expect(names).not.toContain('记忆系统');
expect(names).not.toContain('Crew 编排上下文');
});
it('完整场景skill + 记忆 + memoryContext → 包含所有层', () => {
const loader = createMockSkillLoader('技能索引内容');
const { layers } = buildSystemPrompt({
...baseParams,
skills: ['skill_a'],
skillLoader: loader,
memoryEnabled: true,
memoryContext: '用户偏好数据',
});
const names = layers.map(l => l.name);
expect(names).toContain('Skill 索引');
expect(names).toContain('记忆行为');
expect(loader.buildSkillsPrompt).toHaveBeenCalled();
});
it('Crew 场景:注入 crewContext → 包含 Crew 编排上下文层', () => {
const { layers } = buildSystemPrompt({
...baseParams,
crewContext: '你是主Agent负责分配任务',
});
const names = layers.map(l => l.name);
expect(names).toContain('Crew 编排上下文');
const crewLayer = layers.find(l => l.name === 'Crew 编排上下文');
expect(crewLayer!.content).toContain('你是主Agent');
});
it('空 agentSystemPrompt 不生成 Agent 身份层', () => {
const { layers } = buildSystemPrompt({ ...baseParams, agentSystemPrompt: '' });
const names = layers.map(l => l.name);
expect(names).not.toContain('Agent 身份');
});
it('Claude 模型不生成模型特定指导层', () => {
const { layers } = buildSystemPrompt({ ...baseParams, modelId: 'claude-3-opus' });
const names = layers.map(l => l.name);
expect(names).not.toContain('模型特定指导');
});
});
describe('buildLLMMessages', () => {
it('视频附件有旧伤检测 skill 时提示使用 execute_skill 而不是 image_recognize', () => {
const messages = buildLLMMessages(
'<skill name="vehicle-damage-inspection" type="compute-entry">汽车环车视频旧伤检测</skill>',
[],
{
content: '请分析',
metadata: {
attachments: [{
type: 'video',
name: 'car.mp4',
url: '/upload/20260507/car.mp4',
}],
},
},
['execute_skill', 'image_recognize'],
);
const hint = messages[messages.length - 1].content;
expect(hint).toContain('execute_skill');
expect(hint).toContain('vehicle-damage-inspection');
expect(hint).toContain('/upload/20260507/car.mp4');
expect(hint).toContain('不要用它直接分析视频文件');
});
});