From d81f03d3185cde28e037120f6dc8d45d2383af8a Mon Sep 17 00:00:00 2001 From: hailin Date: Fri, 6 Feb 2026 21:28:56 -0800 Subject: [PATCH] feat(agents): persist evaluation gate failures as system experiences for cross-conversation learning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将评估门控的失败教训自动沉淀为全局系统经验,实现跨对话学习闭环: 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 --- .../coordinator/coordinator-agent.service.ts | 59 ++++++++++++++++++- .../knowledge/knowledge-client.service.ts | 32 ++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/packages/services/conversation-service/src/infrastructure/agents/coordinator/coordinator-agent.service.ts b/packages/services/conversation-service/src/infrastructure/agents/coordinator/coordinator-agent.service.ts index 8aa291d..a824e01 100644 --- a/packages/services/conversation-service/src/infrastructure/agents/coordinator/coordinator-agent.service.ts +++ b/packages/services/conversation-service/src/infrastructure/agents/coordinator/coordinator-agent.service.ts @@ -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 { + 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 // ============================================================ diff --git a/packages/services/conversation-service/src/infrastructure/knowledge/knowledge-client.service.ts b/packages/services/conversation-service/src/infrastructure/knowledge/knowledge-client.service.ts index 65c942a..91f73cc 100644 --- a/packages/services/conversation-service/src/infrastructure/knowledge/knowledge-client.service.ts +++ b/packages/services/conversation-service/src/infrastructure/knowledge/knowledge-client.service.ts @@ -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 { + 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; + return data.success ? data.data : null; + } catch (error) { + console.error('[KnowledgeClient] saveExperience error:', error); + return null; + } + } + // ============================================================ // Convenience Methods (for Specialist Agents) // ============================================================