aps-agent/scripts/context-builder.test.mjs

66 lines
3.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** 上下文构建用例(任务 A3.8):预算、折叠、追溯、钉住引用。 */
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { buildContext } from '@aps/server/app/context-builder';
const message = (id, role, text) => ({ messageId: id, role, ts: 1, text });
test('历史很短时全部保留,不产生摘要', () => {
const messages = [message('m1', 'user', '你好'), message('m2', 'assistant', '你好,有什么可以帮你')];
const result = buildContext({ messages });
assert.ok(result.prefix.includes('【最近的对话】'));
assert.deepEqual(result.includedMessageIds, ['m1', 'm2']);
assert.deepEqual(result.droppedMessageIds, []);
assert.equal(result.summary, '');
});
test('空历史不注入任何前缀', () => {
assert.equal(buildContext({ messages: [] }).prefix, '');
});
test('超预算时老消息折叠进摘要,且能追到原始消息 id', () => {
const messages = [];
for (let index = 0; index < 40; index += 1) {
messages.push(message(`m${index}`, index % 2 === 0 ? 'user' : 'assistant', `第 ${index} 条消息,`.repeat(20)));
}
const result = buildContext({ messages, budget: { maxChars: 1200, recentMessages: 4, summaryMaxChars: 800 } });
assert.ok(result.droppedMessageIds.length > 0, '应有消息被折叠');
assert.equal(result.summarySourceIds.length, result.droppedMessageIds.length, '摘要来源应与折叠集合一致');
assert.ok(result.summary.length > 0);
// 摘要里逐条带来源 id,任何结论都能追回原文
for (const id of result.summarySourceIds.slice(0, 3)) {
assert.ok(result.summary.includes(id), `摘要应包含来源 ${id}`);
}
// 最近若干轮必须原样保留
const lastIds = messages.slice(-4).map((item) => item.messageId);
for (const id of lastIds) assert.ok(result.includedMessageIds.includes(id), `${id} 应保留`);
});
test('工具执行只以引用进摘要,不进历史原文', () => {
const messages = Array.from({ length: 30 }, (_, index) =>
message(`m${index}`, index % 2 === 0 ? 'user' : 'assistant', `内容 ${index} `.repeat(40)),
);
const result = buildContext({
messages,
executions: [
{ capabilityId: 'fake.echo', status: 'succeeded', executionId: 'exec-123', paramsDigest: 'sha256:abc', createdAt: 1 },
],
budget: { maxChars: 900, recentMessages: 2, summaryMaxChars: 600 },
});
assert.ok(result.prefix.includes('【相关工具执行(只给引用,需要细节请调对应能力)】'), result.prefix.slice(0, 200));
assert.ok(result.prefix.includes('exec-123'), '应给出执行号引用');
assert.ok(result.prefix.includes('sha256:abc'), '应给出参数摘要');
assert.ok(!result.prefix.includes('"echo"'), '不应把工具结果原文塞进上下文');
});
test('钉住的消息即使很老也保留', () => {
const messages = Array.from({ length: 30 }, (_, index) =>
message(`m${index}`, index % 2 === 0 ? 'user' : 'assistant', `内容 ${index} `.repeat(40)),
);
const result = buildContext({ messages, pinnedMessageIds: ['m0'], budget: { maxChars: 800, recentMessages: 2, summaryMaxChars: 400 } });
assert.ok(result.includedMessageIds.includes('m0'), '钉住的消息必须保留');
assert.ok(!result.droppedMessageIds.includes('m0'));
});