feat(agents): persist evaluation gate failures as system experiences for cross-conversation learning

将评估门控的失败教训自动沉淀为全局系统经验,实现跨对话学习闭环:

1. KnowledgeClientService 新增 saveExperience() 方法
   - 封装 knowledge-service 的 POST /memory/experience API
   - 支持保存 experienceType、scenario、content、confidence 等字段
   - 错误处理:失败时返回 null,不影响主流程

2. CoordinatorAgentService 评估门控回调扩展
   - Gate 失败(RETRY/SUPPLEMENT)时,异步将失败教训写入 knowledge-service
   - fire-and-forget 模式:不阻塞重试流程,失败仅打印警告
   - 经验格式:scenario = "阶段 + 规则名",content = "具体要求描述"
   - experienceType = KNOWLEDGE_GAP,confidence = 45(低于默认50,需积累)

3. 学习闭环机制
   - 经验写入后状态为 PENDING,需 admin 审核激活(isActive=true)
   - 激活后 Context Injector 的 searchExperiences() 语义匹配检索
   - 未来对话中注入 prompt → AI 第一次就遵循规则,避免重复犯错
   - knowledge-service 自动去重:0.9 相似度阈值合并,confidence +5/次

4. 零侵入保证
   - 无评估规则时 Gate 直接 PASS → 不保存任何经验 → 原有流程零影响
   - agent-loop.ts 未修改,所有逻辑在 coordinator 回调闭包内完成

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-06 21:28:56 -08:00
parent 8d8df53e56
commit d81f03d318
2 changed files with 89 additions and 2 deletions

View File

@ -219,8 +219,9 @@ export class CoordinatorAgentService implements OnModuleInit {
turnCount: number,
agentsUsedInLoop: string[],
) => {
return this.evaluationGate.evaluate(null, {
stage: context.consultingState?.currentStageId || null,
const stage = context.consultingState?.currentStageId || null;
const gateResult = await this.evaluationGate.evaluate(null, {
stage,
collectedInfo: context.consultingState?.collectedInfo || null,
assessmentResult: context.consultingState?.assessmentResult || null,
responseText,
@ -229,6 +230,22 @@ export class CoordinatorAgentService implements OnModuleInit {
hasConverted: false,
agentsUsed: agentsUsedInLoop,
});
// Gate 失败时将失败教训异步保存为系统经验fire-and-forget
if (
!gateResult.passed &&
(gateResult.action === 'RETRY' || gateResult.action === 'SUPPLEMENT')
) {
this.persistGateFailureAsExperience(
gateResult,
stage,
context.conversationId,
).catch(err =>
this.logger.warn(`Failed to persist gate failure experience: ${err}`),
);
}
return gateResult;
};
// 6. Build agent loop params
@ -501,6 +518,44 @@ export class CoordinatorAgentService implements OnModuleInit {
}
}
// ============================================================
// Gate Failure → System Experience
// ============================================================
/**
*
* knowledge-service PENDING admin
* Context Injector searchExperiences() prompt
* AI
*/
private async persistGateFailureAsExperience(
gateResult: import('./evaluation-gate.service').GateResult,
stage: string | null,
conversationId: string,
): Promise<void> {
const failedRules = gateResult.results.filter(r => !r.passed);
if (failedRules.length === 0) return;
const stageLabel = stage || '通用';
for (const rule of failedRules) {
const scenario = `${stageLabel}阶段 — ${rule.ruleName}`;
const content = `${stageLabel}阶段,${rule.message || rule.ruleName + '检查未通过'}。AI 回复前应确保满足此要求,避免生成不合格内容后重试。`;
await this.knowledgeClient.saveExperience({
experienceType: 'KNOWLEDGE_GAP',
scenario,
content,
sourceConversationId: conversationId,
confidence: 45,
});
this.logger.debug(
`Persisted gate failure as experience: [${rule.ruleType}] ${scenario}`,
);
}
}
// ============================================================
// Event Mapping
// ============================================================

View File

@ -287,6 +287,38 @@ export class KnowledgeClientService implements OnModuleInit {
}
}
/**
*
*
*/
async saveExperience(params: {
experienceType: string;
content: string;
scenario: string;
sourceConversationId: string;
confidence?: number;
relatedCategory?: string;
}): Promise<SystemExperience | null> {
try {
const response = await fetch(`${this.baseUrl}/api/v1/memory/experience`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
});
if (!response.ok) {
console.error(`[KnowledgeClient] saveExperience failed: ${response.status}`);
return null;
}
const data = (await response.json()) as ApiResponse<SystemExperience>;
return data.success ? data.data : null;
} catch (error) {
console.error('[KnowledgeClient] saveExperience error:', error);
return null;
}
}
// ============================================================
// Convenience Methods (for Specialist Agents)
// ============================================================