# 意图驱动话术流程 - 设计文档 ## 1. 架构概览 ### 1.1 系统架构图 ``` ┌─────────────────────────────────────────────────────────────┐ │ 前端配置界面 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 模式选择器 │ │ 意图配置表单 │ │ 约束管理器 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ HTTP API ▼ ┌─────────────────────────────────────────────────────────────┐ │ 后端 API 层 │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ ScriptFlowService (CRUD) │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ FlowEngine (执行引擎) │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ start() / advance() │ │ │ │ ↓ │ │ │ │ _generate_step_content() ← 核心扩展点 │ │ │ │ ├─ fixed: 返回 content │ │ │ │ ├─ flexible: 调用 ScriptGenerator │ │ │ │ └─ template: 调用 TemplateEngine │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ScriptGenerator│ │TemplateEngine│ │ Orchestrator │ │ (新增) │ │ (新增) │ │ (LLM调用) │ └──────────────┘ └──────────────┘ └──────────────┘ ``` ### 1.2 数据流图 ``` 用户配置流程 │ ├─ 选择 script_mode │ ├─ fixed: 配置 content │ ├─ flexible: 配置 intent + constraints │ └─ template: 配置 content (模板) │ ▼ 保存到数据库 (ScriptFlow.steps JSON) │ ▼ 执行时加载流程 │ ├─ FlowEngine.start() / advance() │ │ │ ├─ 获取当前步骤配置 │ │ │ ├─ 调用 _generate_step_content() │ │ │ │ │ ├─ fixed: 直接返回 content │ │ │ │ │ ├─ flexible: │ │ │ ├─ 构建 Prompt (intent + constraints + history) │ │ │ ├─ 调用 LLM 生成话术 │ │ │ └─ 失败时返回 fallback (content) │ │ │ │ │ └─ template: │ │ ├─ 解析模板变量 │ │ ├─ 调用 LLM 生成变量值 │ │ └─ 替换模板占位符 │ │ │ └─ 返回生成的话术 │ ▼ 返回给用户 ``` --- ## 2. 核心模块设计 ### 2.1 后端:话术生成引擎 #### 2.1.1 FlowEngine 扩展 **文件位置**: `ai-service/app/services/flow/engine.py` **新增方法**: ```python async def _generate_step_content( self, step: dict, context: dict, history: list[dict] ) -> str: """ [AC-IDS-03] 根据步骤配置生成话术内容 Args: step: 步骤配置 (包含 script_mode, intent, constraints 等) context: 会话上下文 (从 FlowInstance.context 获取) history: 对话历史 (最近 N 轮) Returns: 生成的话术文本 """ script_mode = step.get("script_mode", "fixed") if script_mode == "fixed": return step.get("content", "") elif script_mode == "flexible": return await self._generate_flexible_script(step, context, history) elif script_mode == "template": return await self._generate_template_script(step, context, history) else: logger.warning(f"Unknown script_mode: {script_mode}, fallback to fixed") return step.get("content", "") ``` **修改方法**: ```python async def start( self, tenant_id: str, session_id: str, flow_id: uuid.UUID, ) -> tuple[FlowInstance | None, str | None]: """ [AC-IDS-05] 修改:启动流程时生成首步话术 """ # ... 现有逻辑 ... first_step = flow.steps[0] # 修改:调用话术生成引擎 history = await self._get_conversation_history(tenant_id, session_id, limit=3) first_content = await self._generate_step_content( first_step, instance.context, history ) return instance, first_content ``` #### 2.1.2 ScriptGenerator (新增模块) **文件位置**: `ai-service/app/services/flow/script_generator.py` **职责**: 灵活模式的话术生成逻辑 ```python class ScriptGenerator: """ [AC-IDS-04] 灵活模式话术生成器 """ def __init__(self, orchestrator): self._orchestrator = orchestrator async def generate( self, intent: str, intent_description: str | None, constraints: list[str], context: dict, history: list[dict], fallback: str ) -> str: """ 生成灵活话术 Args: intent: 步骤意图 intent_description: 意图详细说明 constraints: 话术约束条件 context: 会话上下文 history: 对话历史 fallback: 失败时的 fallback 话术 Returns: 生成的话术文本 """ try: prompt = self._build_prompt( intent, intent_description, constraints, context, history ) # 调用 LLM,设置 2 秒超时 response = await asyncio.wait_for( self._orchestrator.generate(prompt), timeout=2.0 ) return response.strip() except asyncio.TimeoutError: logger.warning(f"[AC-IDS-05] Script generation timeout, use fallback") return fallback except Exception as e: logger.error(f"[AC-IDS-05] Script generation failed: {e}, use fallback") return fallback def _build_prompt( self, intent: str, intent_description: str | None, constraints: list[str], context: dict, history: list[dict] ) -> str: """ [AC-IDS-04] 构建 LLM Prompt """ prompt_parts = [ "你是一个客服对话系统,当前需要执行以下步骤:", "", f"【步骤目标】{intent}" ] if intent_description: prompt_parts.append(f"【详细说明】{intent_description}") if constraints: prompt_parts.append("【约束条件】") for c in constraints: prompt_parts.append(f"- {c}") if history: prompt_parts.append("") prompt_parts.append("【对话历史】") for msg in history[-3:]: # 最近 3 轮 role = "用户" if msg["role"] == "user" else "客服" prompt_parts.append(f"{role}: {msg['content']}") if context.get("inputs"): prompt_parts.append("") prompt_parts.append("【已收集信息】") for inp in context["inputs"]: prompt_parts.append(f"- {inp}") prompt_parts.extend([ "", "请生成一句符合目标和约束的话术(不超过50字)。", "只返回话术内容,不要解释。" ]) return "\n".join(prompt_parts) ``` #### 2.1.3 TemplateEngine (新增模块) **文件位置**: `ai-service/app/services/flow/template_engine.py` **职责**: 模板模式的变量填充逻辑 ```python import re class TemplateEngine: """ [AC-IDS-06] 模板话术引擎 """ VARIABLE_PATTERN = re.compile(r'\{(\w+)\}') def __init__(self, orchestrator): self._orchestrator = orchestrator async def fill_template( self, template: str, context: dict, history: list[dict] ) -> str: """ 填充模板变量 Args: template: 话术模板(包含 {变量名} 占位符) context: 会话上下文 history: 对话历史 Returns: 填充后的话术 """ # 提取模板中的变量 variables = self.VARIABLE_PATTERN.findall(template) if not variables: return template # 为每个变量生成值 variable_values = {} for var in variables: value = await self._generate_variable_value(var, context, history) variable_values[var] = value # 替换模板中的占位符 result = template for var, value in variable_values.items(): result = result.replace(f"{{{var}}}", value) return result async def _generate_variable_value( self, variable_name: str, context: dict, history: list[dict] ) -> str: """ 为单个变量生成值 """ # 先尝试从上下文中获取 if variable_name in context: return str(context[variable_name]) # 否则调用 LLM 生成 prompt = f""" 根据对话历史,为变量 "{variable_name}" 生成合适的值。 对话历史: {self._format_history(history[-3:])} 只返回变量值,不要解释。 """ try: response = await asyncio.wait_for( self._orchestrator.generate(prompt), timeout=1.0 ) return response.strip() except: return f"[{variable_name}]" # fallback ``` --- ### 2.2 前端:配置界面设计 #### 2.2.1 类型定义扩展 **文件位置**: `ai-service-admin/src/types/script-flow.ts` ```typescript export type ScriptMode = 'fixed' | 'flexible' | 'template' export interface FlowStep { step_id: string step_no: number // 原有字段 content: string wait_input: boolean timeout_seconds?: number timeout_action?: 'repeat' | 'skip' | 'transfer' next_conditions?: NextCondition[] // 新增字段 script_mode?: ScriptMode intent?: string intent_description?: string script_constraints?: string[] expected_variables?: string[] } export const SCRIPT_MODE_OPTIONS = [ { value: 'fixed', label: '固定话术', description: '话术内容固定不变' }, { value: 'flexible', label: '灵活话术', description: 'AI根据意图和上下文生成' }, { value: 'template', label: '模板话术', description: 'AI填充模板中的变量' } ] ``` #### 2.2.2 配置表单组件 **文件位置**: `ai-service-admin/src/views/admin/script-flow/index.vue` **UI 结构**: ```vue ``` #### 2.2.3 约束管理组件 **文件位置**: `ai-service-admin/src/views/admin/script-flow/components/ConstraintManager.vue` ```vue ``` --- ## 3. 数据模型设计 ### 3.1 数据库 Schema **无需修改表结构**,因为 `script_flows.steps` 已经是 JSON 类型。 **现有结构**: ```sql CREATE TABLE script_flows ( id UUID PRIMARY KEY, tenant_id VARCHAR NOT NULL, name VARCHAR NOT NULL, description TEXT, steps JSONB NOT NULL, -- 直接扩展此字段 is_enabled BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ``` **扩展后的 steps JSON 示例**: ```json [ { "step_no": 1, "script_mode": "flexible", "intent": "获取用户姓名", "intent_description": "礼貌询问用户姓名", "script_constraints": ["必须礼貌", "语气自然"], "content": "请问怎么称呼您?", "wait_input": true, "timeout_seconds": 60 } ] ``` ### 3.2 向后兼容策略 **读取时**: ```python def _normalize_step(step: dict) -> dict: """确保步骤配置包含所有必需字段""" return { "script_mode": step.get("script_mode", "fixed"), "intent": step.get("intent"), "intent_description": step.get("intent_description"), "script_constraints": step.get("script_constraints", []), "expected_variables": step.get("expected_variables", []), **step # 保留其他字段 } ``` **写入时**: - 前端默认 `script_mode = 'fixed'` - 后端不做强制校验,允许字段缺失 --- ## 4. 技术决策 ### 4.1 为什么选择 JSON 扩展而不是新表? **决策**: 在现有的 `steps` JSON 字段中扩展,而不是创建新表 **理由**: 1. **简化数据模型**: 步骤配置是流程的一部分,不需要独立管理 2. **避免数据迁移**: 无需修改表结构,现有数据自动兼容 3. **灵活性**: JSON 字段易于扩展,未来可以继续添加新字段 4. **性能**: 步骤数量通常不多(<20),JSON 查询性能足够 **权衡**: 无法对意图字段建立索引,但实际场景中不需要按意图查询流程 ### 4.2 为什么设置 2 秒超时? **决策**: LLM 调用超时设置为 2 秒 **理由**: 1. **用户体验**: 对话系统需要快速响应,2 秒是可接受的上限 2. **Fallback 保障**: 超时后立即返回 fallback 话术,不影响流程执行 3. **成本控制**: 避免长时间等待消耗资源 **权衡**: 可能导致部分复杂话术生成失败,但有 fallback 保障 ### 4.3 为什么对话历史只取最近 3 轮? **决策**: 传递给 LLM 的对话历史限制为最近 3 轮 **理由**: 1. **Token 成本**: 减少 Prompt 长度,降低成本 2. **相关性**: 最近 3 轮对话最相关,更早的对话影响较小 3. **性能**: 减少数据库查询和网络传输 **权衡**: 可能丢失更早的上下文信息,但实际影响有限 ### 4.4 为什么不缓存生成的话术? **决策**: 不对生成的话术进行缓存 **理由**: 1. **灵活性优先**: 每次生成都考虑最新的上下文,更符合"灵活话术"的定位 2. **缓存复杂度**: 需要考虑缓存失效策略(上下文变化、配置变化) 3. **实际收益有限**: 同一步骤在同一会话中通常只执行一次 **未来优化**: 如果性能成为瓶颈,可以考虑基于上下文哈希的缓存 --- ## 5. 错误处理与降级策略 ### 5.1 话术生成失败 **场景**: LLM 调用超时或返回错误 **处理**: 1. 记录错误日志(包含 tenant_id, session_id, flow_id, step_no) 2. 返回 `step.content` 作为 fallback 3. 在 ChatMessage 中标记 `is_error=False`(因为有 fallback,不算错误) ### 5.2 配置错误 **场景**: flexible 模式但 intent 为空 **处理**: 1. 前端校验:提交时检查必填字段 2. 后端容错:如果 intent 为空,降级为 fixed 模式 ### 5.3 模板解析错误 **场景**: 模板语法错误(如 `{unclosed`) **处理**: 1. 捕获正则匹配异常 2. 返回原始模板(不做替换) 3. 记录警告日志 --- ## 6. 性能考虑 ### 6.1 预期性能指标 | 指标 | 目标值 | 说明 | |------|--------|------| | 话术生成延迟 (P95) | < 2s | LLM 调用时间 | | API 响应时间增加 | < 10% | 相比固定模式 | | 数据库查询增加 | +1 次 | 获取对话历史 | ### 6.2 优化策略 1. **并行查询**: 获取对话历史和流程配置可以并行 2. **限制历史长度**: 只查询最近 3 轮对话 3. **超时控制**: 严格的 2 秒超时,避免长时间等待 --- ## 7. 测试策略 ### 7.1 单元测试 **测试文件**: `ai-service/tests/services/flow/test_script_generator.py` **测试用例**: - 固定模式:直接返回 content - 灵活模式:正常生成、超时 fallback、异常 fallback - 模板模式:变量替换、变量缺失、模板语法错误 ### 7.2 集成测试 **测试文件**: `ai-service/tests/api/test_script_flow_intent_driven.py` **测试场景**: 1. 创建灵活模式流程 2. 启动流程,验证首步话术生成 3. 推进流程,验证后续步骤话术生成 4. 验证对话历史正确传递 ### 7.3 端到端测试 **测试场景**: 1. 前端配置灵活模式流程 2. 保存并启用流程 3. 通过 Provider API 触发流程 4. 验证生成的话术符合意图和约束 --- ## 8. 部署与发布 ### 8.1 发布顺序 1. **Phase 1**: 后端数据模型和 API 扩展 - 部署后端代码 - 验证 API 向后兼容性 2. **Phase 2**: 后端话术生成引擎 - 部署话术生成逻辑 - 验证 fallback 机制 3. **Phase 3**: 前端配置界面 - 部署前端代码 - 验证配置保存和加载 4. **Phase 4**: 灰度发布 - 选择部分租户启用灵活模式 - 监控性能和错误率 - 全量发布 ### 8.2 回滚策略 **如果出现问题**: 1. 前端回滚:恢复旧版本,用户无法配置灵活模式 2. 后端回滚:恢复旧版本,灵活模式降级为固定模式 3. 数据无需回滚:JSON 字段扩展,旧版本可以忽略新字段 --- ## 9. 监控与告警 ### 9.1 关键指标 | 指标 | 说明 | 告警阈值 | |------|------|---------| | script_generation_latency | 话术生成延迟 | P95 > 2.5s | | script_generation_timeout_rate | 超时率 | > 5% | | script_generation_error_rate | 错误率 | > 1% | | fallback_usage_rate | Fallback 使用率 | > 10% | ### 9.2 日志记录 **关键日志**: ```python logger.info( f"[AC-IDS-03] Generated script: tenant={tenant_id}, " f"session={session_id}, flow={flow_id}, step={step_no}, " f"mode={script_mode}, latency={latency_ms}ms" ) logger.warning( f"[AC-IDS-05] Script generation timeout, use fallback: " f"tenant={tenant_id}, session={session_id}, step={step_no}" ) ``` --- ## 10. 未来扩展 ### 10.1 短期优化(v1.1) - 话术生成缓存(基于上下文哈希) - 更丰富的约束条件预设 - 话术效果评估(用户满意度) ### 10.2 长期规划(v2.0) - 多轮对话规划(提前生成后续步骤话术) - 话术 A/B 测试 - 基于历史数据的话术优化建议