Implement Experience V1 reflect, retrieval boost, and product metrics.

Add rule-based reflect() to aggregate task outcomes, environment-aware search scoring, experience_saved/recalled events, and structured injection blocks per the V1 schema.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-02 14:30:59 +08:00
parent 6d48480b1c
commit 2ec6fdbe11
11 changed files with 675 additions and 58 deletions
+31 -7
View File
@@ -137,6 +137,24 @@ export function experienceSearchHaystack(row) {
.toLowerCase();
}
export function formatExperienceMonth(timestampMs) {
const ts = Number(timestampMs);
if (!Number.isFinite(ts) || ts <= 0) return 'unknown';
const date = new Date(ts);
const year = date.getUTCFullYear();
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
return `${year}-${month}`;
}
export function formatExperienceActionSummary(action) {
if (!action || typeof action !== 'object') return '';
const steps = Array.isArray(action.steps) ? action.steps.filter(Boolean) : [];
if (steps.length > 0) return steps.slice(0, 3).join(' → ');
const artifacts = Array.isArray(action.artifacts) ? action.artifacts.filter(Boolean) : [];
if (artifacts.length > 0) return `artifacts: ${artifacts.slice(0, 3).join(', ')}`;
return action.executor ? String(action.executor) : '';
}
export function formatExperienceInjectionBlock(hits = []) {
if (!Array.isArray(hits) || hits.length === 0) return '';
const lines = hits.map((hit) => formatExperienceInjectionLine(hit));
@@ -144,19 +162,25 @@ export function formatExperienceInjectionBlock(hits = []) {
}
export function formatExperienceInjectionLine(hit) {
const parts = [`- ${hit.title}: ${hit.body}`];
if (hit.problem) parts.push(` 问题: ${hit.problem}`);
if (hit.result) {
const conf = hit.confidence == null ? '' : `, conf=${hit.confidence}`;
parts.push(` 结果: ${hit.result}${conf}`);
}
const month = formatExperienceMonth(hit.createdAt);
const headline = hit.problem || hit.title;
const resultPart = hit.result
? ` (${hit.result}${hit.confidence == null ? '' : `, conf=${hit.confidence}`})`
: '';
const parts = [`- [${month}] ${headline}${resultPart}`];
const runtime = hit.environment?.runtime;
const components = Array.isArray(hit.environment?.components)
? hit.environment.components.filter(Boolean).join(' + ')
: '';
if (runtime || components) {
parts.push(` 环境: ${[runtime, components].filter(Boolean).join(' · ')}`);
parts.push(` 环境: ${[runtime, components].filter(Boolean).join(' + ')}`);
}
const actionSummary = formatExperienceActionSummary(hit.action);
if (actionSummary) parts.push(` 处理: ${actionSummary}`);
parts.push(` 摘要: ${hit.body}`);
return parts.join('\n');
}