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:
parent
8d8df53e56
commit
d81f03d318
|
|
@ -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
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
// ============================================================
|
||||
|
|
|
|||
Loading…
Reference in New Issue