44e77dcb0e
**重构前**(旧编号 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 行
7.5 KiB
7.5 KiB
技术方案 - 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 |
交互原型 |