From b55cd4bc1e6fd33da4755ea7eea079967716b366 Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 7 Feb 2026 10:25:59 -0800 Subject: [PATCH] fix(agents): widen answer length limits and preserve followUp continuations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit INTENT_MAX_ANSWER_LENGTH was too tight (objection_expression 200 chars truncated good responses). Bumped all limits ~25-50%. Also fixed followUp filter that silently dropped content when model split answer across answer+followUp fields — now appends followUp as continuation when answer ends mid-sentence. Co-Authored-By: Claude Opus 4.6 --- .../agents/coordinator/agent-loop.ts | 20 +++++++++++++++---- .../schemas/coordinator-response.schema.ts | 14 ++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/services/conversation-service/src/infrastructure/agents/coordinator/agent-loop.ts b/packages/services/conversation-service/src/infrastructure/agents/coordinator/agent-loop.ts index e91091d..2772b46 100644 --- a/packages/services/conversation-service/src/infrastructure/agents/coordinator/agent-loop.ts +++ b/packages/services/conversation-service/src/infrastructure/agents/coordinator/agent-loop.ts @@ -369,10 +369,22 @@ export async function* agentLoop( yield { type: 'text', content: answer, timestamp: Date.now() }; - // followUp 必须是面向用户的问题(含?),过滤掉内部策略备注 - if (parsed.followUp && /?|\?/.test(parsed.followUp)) { - const followUp = smartTruncate(parsed.followUp, MAX_FOLLOWUP_LENGTH); - yield { type: 'text', content: '\n\n' + followUp, timestamp: Date.now() }; + // followUp 处理: + // 1. 含 ?/? → 作为跟进问题输出 + // 2. answer 未以句末标点结束 → followUp 可能是 answer 的延续部分,追加输出 + // 3. 其余情况 → 过滤掉(内部策略备注等) + if (parsed.followUp) { + const isQuestion = /?|\?/.test(parsed.followUp); + const answerEndsClean = /[。!?;!?]$/.test(answer.replace(/\.{3}$/, '')); + + if (isQuestion) { + const followUp = smartTruncate(parsed.followUp, MAX_FOLLOWUP_LENGTH); + yield { type: 'text', content: '\n\n' + followUp, timestamp: Date.now() }; + } else if (!answerEndsClean) { + // answer 被截断或模型将内容分到 followUp → 追加(不换行) + logger.debug(`[Turn ${currentTurn + 1}] followUp appended as answer continuation`); + yield { type: 'text', content: parsed.followUp, timestamp: Date.now() }; + } } } else { // JSON 合法但缺少 answer 字段 → yield 原始文本 diff --git a/packages/services/conversation-service/src/infrastructure/agents/schemas/coordinator-response.schema.ts b/packages/services/conversation-service/src/infrastructure/agents/schemas/coordinator-response.schema.ts index 8b06d5b..067b052 100644 --- a/packages/services/conversation-service/src/infrastructure/agents/schemas/coordinator-response.schema.ts +++ b/packages/services/conversation-service/src/infrastructure/agents/schemas/coordinator-response.schema.ts @@ -29,13 +29,13 @@ export type CoordinatorResponse = z.infer; /** 每种意图对应的最大 answer 长度(字符数)— 程序级硬约束 */ export const INTENT_MAX_ANSWER_LENGTH: Record = { - factual_question: 200, // 1-3句,直接给答案 - yes_no_question: 120, // 1-2句,结论+理由 - comparison_question: 250, // 2-3句,推荐+理由 - assessment_request: 400, // 按需但有上限 - objection_expression: 200, // 共情+事实+引导 - detailed_consultation: 500, // 复杂咨询允许较长 - casual_chat: 80, // 1句 + factual_question: 250, // 1-3句,直接给答案 + yes_no_question: 150, // 1-2句,结论+理由 + comparison_question: 300, // 2-3句,推荐+理由 + assessment_request: 500, // 按需但有上限 + objection_expression: 350, // 共情+事实+引导(3个组件各需空间) + detailed_consultation: 600, // 复杂咨询允许较长 + casual_chat: 100, // 1句 }; /** followUp 问题最大长度 */