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( '汽车环车视频旧伤检测', [], { 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('不要用它直接分析视频文件'); }); });