Projection:按预算投影内容
先缩短文本,再减少条目;源数据、投影结果和预算由 App 显式区分。
value 应是可序列化的 JSON 数据。defineContentProjection 的 counts 与 lengths 是 App 指定的尝试顺序;project({value,maxChars}) 返回 {text,truncated}。已在预算内时直接返回 JSON.stringify 的结果,超限后依次尝试 render 与 fallback。fallback 的结果由 App 负责,不能把它当成 Core 的最终输入预算保证。
projectContinuousExcerpt 对可连续截取的正文做二分前缀选择;render(0) 已超限时返回 undefined,保留已有代理对。defineTreeProjection 同步按属性/数组顺序遍历,超过 maxDepth 或 omit 返回 true 时省略节点;只有 undefined 表示省略,null、false、0 均保留。
由简到繁的同业务示例
为同一份查询结果准备紧凑 JSON
入门 · Node.js
ts
import { Projection } from '@jangts/agent-core/kits/projection';
const projection = Projection.defineContentProjection({
counts: [10, 3, 1], lengths: [200, 80, 20],
render: value => JSON.stringify(value), fallback: () => '{}',
});
const result = projection.project({ value: { items: [{ title: '配置说明', body: '正文'.repeat(100) }] }, maxChars: 120 });
console.log(result.text, result.truncated);js
import { Projection } from '@jangts/agent-core/kits/projection';
const projection = Projection.defineContentProjection({
counts: [10, 3, 1], lengths: [200, 80, 20],
render: value => JSON.stringify(value), fallback: () => '{}',
});
const result = projection.project({ value: { items: [{ title: '配置说明', body: '正文'.repeat(100) }] }, maxChars: 120 });
console.log(result.text, result.truncated);相同数据先移除私有字段,再按预算投影
配置 · Node.js
ts
import { Projection } from '@jangts/agent-core/kits/projection';
const tree = Projection.defineTreeProjection({
maxDepth: 4,
visitRecord(record) { delete record.privateNote; },
});
const source = { items: [{ title: '配置说明', body: '正文'.repeat(100), privateNote: '不向模型提供' }] };
// 调用者明确选择副本所有权;visitor 不会碰原记录。
const publicData = tree.visit(structuredClone(source), undefined);
const compact = Projection.defineContentProjection({ counts: [10, 3, 1], lengths: [200, 80, 20], render: JSON.stringify, fallback: () => '{}' });
console.log(compact.project({ value: publicData, maxChars: 120 }));js
import { Projection } from '@jangts/agent-core/kits/projection';
const tree = Projection.defineTreeProjection({
maxDepth: 4,
visitRecord(record) { delete record.privateNote; },
});
const source = { items: [{ title: '配置说明', body: '正文'.repeat(100), privateNote: '不向模型提供' }] };
// 调用者明确选择副本所有权;visitor 不会碰原记录。
const publicData = tree.visit(structuredClone(source), undefined);
const compact = Projection.defineContentProjection({ counts: [10, 3, 1], lengths: [200, 80, 20], render: JSON.stringify, fallback: () => '{}' });
console.log(compact.project({ value: publicData, maxChars: 120 }));保留连续正文而非拆散段落
扩展 · Node.js
render 的输出长度须随前缀长度单调增长,二分搜索才有意义。
ts
import { Projection } from '@jangts/agent-core/kits/projection';
const body = '一段连续的资料正文。'.repeat(100);
const excerpt = Projection.projectContinuousExcerpt(body, 120, length => JSON.stringify({ excerpt: body.slice(0, length) }));
if (excerpt) console.log(JSON.parse(excerpt.text));js
import { Projection } from '@jangts/agent-core/kits/projection';
const body = '一段连续的资料正文。'.repeat(100);
const excerpt = Projection.projectContinuousExcerpt(body, 120, length => JSON.stringify({ excerpt: body.slice(0, length) }));
if (excerpt)
console.log(JSON.parse(excerpt.text));