docs: test reports + knowledge iteration design + PRDs

提交 OTP/RBAC/Tier0/Tier1/P0+P2 测试报告、方案A E2E 验证、知识库迭代设计(PRD/mermaid/html 原型)、项目状态看板更新; 根配置 docker-compose.yml/mkdocs.yml。
This commit is contained in:
Simon
2026-07-09 11:50:19 +08:00
parent 584c975e7f
commit e4e2de47bb
21 changed files with 3311 additions and 214 deletions
+321
View File
@@ -0,0 +1,321 @@
classDiagram
direction TB
%% ── 枚举 ──────────────────────────────────────────
class AudienceEnum {
<<enumeration>>
employee_quick_reply
engineer_workguide
}
class SuggestionStatusEnum {
<<enumeration>>
pending
queued
approved
rejected
applied
graph_synced
expired
}
class GraphSyncStatusEnum {
<<enumeration>>
pending
synced
failed
}
class SourceTypeEnum {
<<enumeration>>
annotation
conversation
ai_uncertain
manual
document_ragflow
}
class RelationTypeEnum {
<<enumeration>>
LEADS_TO
RELATES_TO
CAN_JUMP_TO
}
%% ── PostgreSQL 模型 ───────────────────────────────
class KnowledgeSuggestion {
+str id
+str suggestion_type
+SuggestionStatusEnum status
+str title
+str content
+str category
+List~str~ tags
+SourceTypeEnum source_type
+List~str~ source_data
+str reason
+str reject_reason
+str reviewer_id
+datetime reviewed_at
+datetime created_at
+datetime updated_at
+float confidence
+AudienceEnum audience
+str issue
+str action
+RelationTypeEnum relation_type
+str parent_issue
+dict graph_meta
+GraphSyncStatusEnum graph_sync_status
+bool source_failed
+datetime queued_at
+datetime applied_at
}
class KnowledgeBase {
+str id
+str category
+str title
+str content
+List~str~ tags
+int view_count
+int use_count
+GraphSyncStatusEnum graph_sync_status
+str graph_node_uuid
+datetime created_at
+datetime updated_at
}
class Conversation {
+str id
+str employee_id
+str status
+str session_type
+datetime created_at
}
%% ── Neo4j 图节点模型 ──────────────────────────────
class IssueNode {
+str uuid
+str name
+str category
+datetime created_at
+datetime updated_at
+str source_suggestion_id
}
class ActionNode {
+str uuid
+str name
+str description
+datetime created_at
+str source_suggestion_id
}
class InfoNode {
+str uuid
+str name
+str value
+List~str~ modifiers
+datetime created_at
}
class RelationEdge {
+str from_uuid
+str to_uuid
+RelationTypeEnum type
+int order
+float weight
}
%% ── Pydantic Schema ───────────────────────────────
class KnowledgeSuggestionCreate {
+str suggestion_type
+str title
+str content
+str category
+List~str~ tags
+SourceTypeEnum source_type
+List~str~ source_data
+str reason
+float confidence
+AudienceEnum audience
+str issue
+str action
+RelationTypeEnum relation_type
+str parent_issue
+dict graph_meta
}
class KnowledgeSuggestionResponse {
+str id
+str suggestion_type
+SuggestionStatusEnum status
+str title
+str content
+str category
+List~str~ tags
+SourceTypeEnum source_type
+float confidence
+AudienceEnum audience
+str issue
+str action
+RelationTypeEnum relation_type
+str parent_issue
+dict graph_meta
+GraphSyncStatusEnum graph_sync_status
+bool source_failed
+datetime queued_at
+datetime applied_at
+datetime created_at
}
class KnowledgeSuggestionApprove {
<<request>>
}
class KnowledgeSuggestionReject {
<<request>>
+str reject_reason
}
class KnowledgeSuggestionRewrite {
<<request>>
+str title
+str content
+str category
+List~str~ tags
+float confidence
+AudienceEnum audience
+str issue
+str action
+RelationTypeEnum relation_type
+str parent_issue
}
class VisionRequest {
<<request>>
+str conversation_id
+bytes image_file
}
class VisionResponse {
+str description
+float confidence
+dict metadata
}
class RagflowIngestionRequest {
<<request>>
+str file_name
+bytes file_data
+str category_hint
}
class RagflowIngestionResponse {
+str task_id
+str status
+List~KnowledgeSuggestionResponse~ suggestions
}
%% ── 服务类 ────────────────────────────────────────
class KnowledgeIterationService {
-str ai_api_url
-str ai_api_key
+analyze_and_generate_suggestions(db, days) dict
-_analyze_annotation_data(db, days) dict
-_analyze_conversation_data(db, days) dict
-_generate_update_suggestion(db, source_type, source_data, reason) KnowledgeSuggestion
-_generate_new_faq_suggestion(db, source_type, source_data, reason) KnowledgeSuggestion
-_check_existing_suggestion(db, source_id) bool
-_auto_tag_audience(db, source_type, source_data) AudienceEnum
+approve_suggestion(db, suggestion_id, reviewer_id) KnowledgeSuggestion
+reject_suggestion(db, suggestion_id, reviewer_id, reason) KnowledgeSuggestion
+rewrite_suggestion(db, suggestion_id, reviewer_id, data) KnowledgeSuggestion
+queue_suggestion(db, suggestion_id) KnowledgeSuggestion
+dequeue_approve(db, suggestion_id, reviewer_id) KnowledgeSuggestion
+sync_to_neo4j(neo4j_client, suggestion) bool
+get_suggestion_stats(db) dict
+get_queue_stats(db) dict
}
class WingmanService {
-str api_url
-str api_key
-int timeout
-httpx.AsyncClient _client
+generate_draft(conversation_id, messages, db) dict
+generate_summary(conversation_id, messages) dict
+suggest_tags(conversation_id, messages, existing_tags) dict
+generate_knowledge_suggestion(context_messages) dict
-_build_context_messages(messages, system_prompt) list
-_call_wingman_api(context_messages) str
-_parse_json_response(content, default) dict
-_estimate_confidence(content) float
+close()
}
class Neo4jClient {
-str uri
-str user
-str password
-str database
-AsyncDriver _driver
+initialize()
+close()
+create_issue_node(issue) IssueNode
+create_action_node(action) ActionNode
+create_relation(from_uuid, to_uuid, rel) RelationEdge
+merge_issue(name, category, props) IssueNode
+merge_action(name, props) ActionNode
+find_issue_by_name(name) IssueNode
+find_related_issues(uuid, rel_type) List~IssueNode~
+execute_write_query(cypher, params) result
+execute_read_query(cypher, params) result
+health_check() bool
}
class VisionService {
-str dify_vision_api_url
-str dify_vision_api_key
-str local_vision_model
+analyze_screenshot(image_bytes, conversation_id) VisionResponse
-_preprocess_image(image_bytes) bytes
-_call_vision_workflow(processed_image) dict
+inject_to_conversation_context(description, conversation_id)
}
class RagflowIngestionService {
-RagflowClient client
+upload_and_process(file_data, file_name, category_hint) RagflowIngestionResponse
+poll_processing_status(task_id) str
+create_suggestions_from_result(result) List~KnowledgeSuggestion~
}
%% ── 关系 ──────────────────────────────────────────
KnowledgeSuggestion ..> AudienceEnum : uses
KnowledgeSuggestion ..> SuggestionStatusEnum : uses
KnowledgeSuggestion ..> SourceTypeEnum : uses
KnowledgeSuggestion ..> RelationTypeEnum : uses
KnowledgeSuggestion ..> GraphSyncStatusEnum : uses
KnowledgeBase ..> GraphSyncStatusEnum : uses
KnowledgeIterationService --> WingmanService : 调用 AI 生成
KnowledgeIterationService --> Neo4jClient : 写图同步
KnowledgeIterationService --> KnowledgeSuggestion : 管理
KnowledgeIterationService --> KnowledgeBase : 落库
KnowledgeSuggestionCreate --> KnowledgeSuggestion : 创建
KnowledgeSuggestionResponse --> KnowledgeSuggestion : 返回
KnowledgeSuggestionApprove --> KnowledgeSuggestion : 状态变更
KnowledgeSuggestionReject --> KnowledgeSuggestion : 状态变更
KnowledgeSuggestionRewrite --> KnowledgeSuggestion : 内容更新
IssueNode <--> RelationEdge : 关联
ActionNode <--> RelationEdge : 关联
Neo4jClient --> IssueNode : CRUD
Neo4jClient --> ActionNode : CRUD
Neo4jClient --> RelationEdge : 管理
VisionService --> WingmanService : 复用 _call_wingman_api 范式
RagflowIngestionService --> KnowledgeSuggestion : 产出
@@ -0,0 +1,106 @@
sequenceDiagram
actor 员工 as 👤 员工
actor 坐席 as 👤 坐席
actor 训练师 as 👤 AI训练师
participant H5 as H5前端
participant AgentFE as 坐席控制台
participant API as FastAPI
participant KIS as KnowledgeIterationService
participant WS as WingmanService
participant Dify as Dify AI Platform
participant DB as PostgreSQL
participant NEO as Neo4jClient
participant N4J as Neo4j 图数据库
Note over 员工,N4J: === 通道 A: 会话→Dify→建议 ===
员工->>H5: 发送 IT 问题(如"VPN 连不上"
H5->>API: POST /api/h5/conversations/current/messages
API->>Dify: 调用 Dify Agent(员工端 AI
Dify-->>API: AI 回复 + confidence: 0.62
API-->>H5: 返回消息(含 confidence
alt confidence < 0.7(门控触发)
H5->>H5: 渲染「转人工」卡片 + 已收集上下文
员工->>H5: 点击「转人工」
H5->>API: POST 转人工请求(附上下文快照)
end
Note over API,N4J: === 会话结束后触发分析 ===
API->>KIS: analyze_and_generate_suggestions(db, days=7)
KIS->>DB: 查询过去N天转人工/标注为无用 的会话
DB-->>KIS: 返回候选数据
loop 每个候选会话
KIS->>WS: generate_knowledge_suggestion(context_messages)
WS->>WS: _build_context_messages(messages, KN_SUGGEST_PROMPT)
WS->>Dify: _call_wingman_api(context_messages)
Dify-->>WS: 结构化 JSONtitle/content/category/tags/confidence/issue/action/relation
WS->>WS: _parse_json_response(content, default)
WS-->>KIS: dict {title, content, category, confidence, issue, action, ...}
KIS->>KIS: _auto_tag_audience(source_type, source_data)
Note over KIS: source_session_type=="employee" → employee_quick_reply<br/>source_session_type=="engineer" → engineer_workguide
KIS->>DB: INSERT KnowledgeSuggestionstatus=pending, 含图字段)
end
KIS->>API: 返回分析结果统计
Note over 坐席,N4J: === D7 内联审批 ===
坐席->>AgentFE: 浏览会话中的提案卡片
AgentFE->>API: GET /api/admin/knowledge-iteration/suggestions?status=pending
API-->>AgentFE: 提案列表(含拓扑预览、confidence、audience
alt 坐席选择「内联审批」
坐席->>AgentFE: 在会话内联卡片点击「采纳」
AgentFE->>API: POST /api/admin/knowledge-iteration/suggestions/{id}/approve
Note over API: 鉴权: require_admin / require_trainer
API->>KIS: approve_suggestion(db, suggestion_id, reviewer_id)
KIS->>DB: UPDATE status=approved, reviewed_at=now()
KIS->>DB: INSERT KnowledgeBase (含 graph_sync_status=pending)
KIS->>DB: UPDATE suggestion status=applied
Note over KIS,N4J: D1 解读2·直接写图
KIS->>NEO: merge_issue(issue_name, category, props)
NEO->>N4J: MERGE (i:Issue {name: $name}) ON CREATE SET i+=$props
N4J-->>NEO: IssueNode(uuid=...)
KIS->>NEO: merge_action(action_name, props)
NEO->>N4J: MERGE (a:Action {name: $name}) ON CREATE SET a+=$props
N4J-->>NEO: ActionNode(uuid=...)
KIS->>NEO: create_relation(issue_uuid, action_uuid, rel)
NEO->>N4J: MATCH (i),(a) WHERE i.uuid=$i AND a.uuid=$a CREATE (i)-[:LEADS_TO {order:$o,weight:$w}]->(a)
KIS->>DB: UPDATE suggestion graph_sync_status=synced
KIS->>DB: UPDATE KnowledgeBase graph_sync_status=synced, graph_node_uuid=...
API-->>AgentFE: 200 OK(含更新后提案)
else 坐席选择「驳回」
AgentFE->>API: POST /api/admin/knowledge-iteration/suggestions/{id}/reject {reject_reason}
API->>KIS: reject_suggestion(db, id, reviewer_id, reason)
KIS->>DB: UPDATE status=rejected
API-->>AgentFE: 200 OK
else 坐席选择「改写」
AgentFE->>API: POST /api/admin/knowledge-iteration/suggestions/{id}/rewrite {title, content, ...}
API->>KIS: rewrite_suggestion(db, id, reviewer_id, data)
KIS->>DB: UPDATE title/content/category/tags/confidence...
KIS->>DB: UPDATE status=pending(重新审批)
API-->>AgentFE: 200 OK
end
Note over 训练师,N4J: === D7 独立队列(未处理提案) ===
训练师->>AgentFE: 打开独立队列页
AgentFE->>API: GET /api/admin/approval-queue/queued?status=pending
Note over API: 未处理的=SESSION_CLOSED 后仍 pending 的提案<br/>或手动标记 queued
API-->>AgentFE: 队列列表
训练师->>AgentFE: 审核队列中的提案
AgentFE->>API: POST /api/admin/approval-queue/{id}/dequeue-approve
Note over API,N4J: 同 approve_suggestion 流程→写图
File diff suppressed because it is too large Load Diff