Files
wecom_it_smart_desk/docs/02-技术文档/技术架构/技术方案-REQ-AI-004-AI回复来源标识-v1.0.md
T
Simon 44e77dcb0e chore(docs): docs/ 目录全面重新编号 + 重组
**重构前**(旧编号 02-11):
- docs/02-产品需求/      → 00 产品规划/PRD
- docs/03-技术架构/      → 01-05 子目录散落
- docs/04-原型设计/      → 01-02 产品设计(HTML 原型)
- docs/05-原型设计/      → screens/
- docs/06-测试素材/      → 02-E2E / 03-功能 / 04-版本测试
- docs/07-项目管理/      → 任务说明书/日报/计划
- docs/08-安全审计/      → 审计报告
- docs/09-堡垒运维/      → toolbox / deploy
- docs/10-项目管理/      → 任务说明书(重复)
- docs/11-历史归档/      → deploy-nas-archived

**重构后**(新编号 00-07,语义化):
- docs/00-产品开发流程与文档管理规范.md
- docs/00-版本迭代总览.md
- docs/01-产品文档/      (PRD/原型/认证/会话/AI 服务/坐席/集成)
- docs/02-技术文档/      (技术方案/架构图/重构记录/前端改造/实现配置)
- docs/03-测试文档/      (E2E/功能用例/版本报告/缺陷单)
- docs/04-运维文档/      (部署运维/运维指南)
- docs/05-运营文档/      (品牌推广/用户手册)
- docs/06-安全审计/      (审计报告)
- docs/07-项目管理/      (任务说明书/日报/计划/看板)

**净收益**:
- 目录编号与产品文档管理规范对齐(按文档阶段 01-07 编号)
- 消除 02-产品需求 与 10-项目管理 的编号重叠
- 子目录按文档类型分组(如 01-产品文档/00-产品规划、01-产品文档/01-认证与登录)
- 把运维/安全/项目管理从 0X 散落改为 04/06/07

合计 494 文件 + 78495 行 / - 14076 行
2026-08-03 18:46:55 +08:00

7.5 KiB
Raw Blame History

技术方案 - AI回复来源标识

版本: v1.0 日期: 2026-07-20 状态: [待评审] 作者: 许清楚 REQ编号: REQ-AI-004 关联PRD: 01-产品文档/03-AI服务/PRD-REQ-AI-004-AI回复来源标识-v1.0.md


一、方案概述

本技术方案实现 AI 回复来源标识功能,在 AI 回复消息上显示来源图标(图谱/Dify/本地路由等),提升来源透明度和可追溯性。


二、数据库设计

2.1 消息表新增字段

-- 消息表新增字段
ALTER TABLE messages ADD COLUMN reply_source VARCHAR(50) DEFAULT NULL;
-- 存储格式:JSON数组,如 '["graph"]' 或 '["routing","assets"]'

2.2 字段说明

字段 类型 说明
reply_source VARCHAR(50) 来源标识,JSON数组格式

三、后端实现

3.1 来源标识配置

# 来源标识配置
REPLY_SOURCE_MARKERS = {
    "byod": "🔒",       # BYOD拦截
    "rule": "⚡️",      # 本地快判(打招呼/呼叫人工)
    "vision": "🖼️",     # 图片增强
    "graph": "🕸️",      # Neo4j图谱
    "dify": "🤖",       # Dify主推理
    "routing": "📋",     # 业务路由
    "assets": "💻",      # 资产推荐
    "fallback": "⚠️",    # 降级回复
}

# 来源优先级
REPLY_SOURCE_PRIORITY = [
    "rule", "byod", "vision", "graph", "dify", "routing", "assets", "fallback"
]

3.2 来源标识追加函数

from collections import OrderedDict
from typing import List

def append_source_marker(content: str, sources: List[str]) -> str:
    """在回复内容末尾追加来源标识"""
    # 获取对应图标
    markers = [REPLY_SOURCE_MARKERS.get(s, "") for s in sources if s in REPLY_SOURCE_MARKERS]

    # 按优先级排序并去重
    seen = set()
    unique_markers = []
    for marker in markers:
        if marker and marker not in seen:
            seen.add(marker)
            unique_markers.append(marker)

    if unique_markers:
        return content + " " + "".join(unique_markers)
    return content

3.3 各路由返回格式

# 各路由返回时携带来源标识
def handle_local_rule(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["rule"]  # 来源列表
    }

def handle_byod(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["byod"]
    }

def handle_vision(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["vision"]
    }

def handle_graph(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["graph"]
    }

def handle_dify(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["dify"]
    }

def handle_routing(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["routing"]
    }

def handle_assets(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["assets"]
    }

def handle_fallback(...) -> dict:
    return {
        "content": "回复内容",
        "reply_source": ["fallback"]
    }

3.4 Persist层统一处理

def persist_message(conversation_id: str, content: str, reply_source: List[str], ...):
    """持久化消息,统一处理来源标识"""

    # 追加来源标识到显示内容
    display_content = append_source_marker(content, reply_source)

    # 保存到数据库(原始content + reply_source字段)
    message = Message(
        conversation_id=conversation_id,
        content=content,  # 原始内容
        reply_source=json.dumps(reply_source),  # 来源列表
        created_at=datetime.now()
    )
    db.save(message)

    # WebSocket推送(带标识的内容)
    ws_push(conversation_id, {
        "type": "ai_reply",
        "content": display_content,  # 带标识
        "reply_source": reply_source
    })

四、配置文件

# config/ai_reply.yaml
reply_source:
  markers:
    byod: "🔒"
    rule: "⚡️"
    vision: "🖼️"
    graph: "🕸️"
    dify: "🤖"
    routing: "📋"
    assets: "💻"
    fallback: "⚠️"
  priority:
    - rule
    - byod
    - vision
    - graph
    - dify
    - routing
    - assets
    - fallback
  max_display: 3  # 最多显示3个标识

五、前端实现

5.1 常量定义

// 来源标识映射
const SOURCE_MARKERS = {
  byod: "🔒",
  rule: "⚡️",
  vision: "🖼️",
  graph: "🕸️",
  dify: "🤖",
  routing: "📋",
  assets: "💻",
  fallback: "⚠️"
};

// 来源标签(坐席端详细展示)
const SOURCE_LABELS = {
  byod: "BYOD拦截",
  rule: "本地快判",
  vision: "图片增强",
  graph: "Neo4j图谱",
  dify: "Dify推理",
  routing: "业务路由",
  assets: "资产推荐",
  fallback: "降级回复"
};

5.2 H5员工端 - 消息气泡组件

// src/components/MessageBubble.vue
<template>
  <div class="ai-bubble">
    <div class="content">{{ content }}</div>
    <div v-if="markers" class="markers">{{ markers }}</div>
  </div>
</template>

<script>
export default {
  props: {
    content: String,
    replySource: Array
  },
  computed: {
    markers() {
      if (!this.replySource || !this.replySource.length) return '';
      return this.replySource
        .map(s => SOURCE_MARKERS[s])
        .join('');
    }
  }
}
</script>

<style scoped>
.markers {
  margin-top: 4px;
  font-size: 14px;
}
</style>

5.3 坐席工作台 - 来源详情展示

// src/components/SourceDetail.vue
<template>
  <div class="source-detail">
    <span class="label">来源</span>
    <span class="value">{{ sourceLabels }}</span>
  </div>
</template>

<script>
export default {
  props: {
    sources: Array
  },
  computed: {
    sourceLabels() {
      if (!this.sources || !this.sources.length) return '';
      return this.sources
        .map(s => SOURCE_LABELS[s])
        .join(' + ');
    }
  }
}
</script>

六、接口设计

6.1 消息查询接口(扩展)

// GET /api/messages/{conversation_id}
{
  "messages": [
    {
      "id": "msg_001",
      "content": "重置密码有以下方式...",
      "reply_source": ["graph"],
      "sender_type": "ai",
      "created_at": "2026-07-20T10:00:00Z"
    }
  ]
}

6.2 WebSocket推送(扩展)

{
  "type": "ai_reply",
  "content": "重置密码有以下方式... 🕸️",
  "reply_source": ["graph"],
  "message_id": "msg_001"
}

七、测试用例

用例 输入 预期输出
TC1 reply_source=["graph"] 显示 "🕸️"
TC2 reply_source=["routing","assets"] 显示 "📋💻"
TC3 reply_source=[] 不显示标识
TC4 reply_source长度>3 按优先级取前3个

八、部署与配置

8.1 配置热更新

标识配置支持热更新,无需重启服务:

# 配置热加载
def reload_config():
    global REPLY_SOURCE_MARKERS, REPLY_SOURCE_PRIORITY
    config = load_yaml("config/ai_reply.yaml")
    REPLY_SOURCE_MARKERS = config['reply_source']['markers']
    REPLY_SOURCE_PRIORITY = config['reply_source']['priority']

8.2 数据库迁移

# 执行迁移
alembic upgrade head

# 或手动执行
ALTER TABLE messages ADD COLUMN reply_source VARCHAR(50) DEFAULT NULL;

九、关联文档

文档 说明
PRD-REQ-AI-004-AI回复来源标识-v1.0.md 产品需求文档
原型-REQ-AI-004-AI回复来源标识-v1.0.html 交互原型