Files
wecom_it_smart_desk/docs/02-技术文档/01-架构设计/复杂场景重构第二阶段-架构设计.md
T
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

32 KiB
Raw Blame History

复杂场景重构第二阶段 — 架构设计

1. 文档信息

属性
文档名称 复杂场景重构第二阶段 — 架构设计文档
版本 v1.0
创建日期 2025-07-11
作者 高见远(架构师)
关联文档 Phase 2 PRD、Phase 1 架构设计

2. 实现方案 + 框架选型

2.1 P2 上下文压缩

2.1.1 Tokenizer 选择

方案 说明 优先级
tiktokencl100k_base OpenAI 官方编码器,与 Dify/GPT 系列模型一致,计数精确 首选
字符估算兜底 tiktoken 不可用时,按 1 token ≈ 1.5 个中文字符估算(误差 ≤10%) 兜底
  • 封装为 TokenCounter 工具类,内部根据 tiktoken 可用性自动热切换
  • 接口统一:count_tokens(text: str) -> int,上层无感知底层差异

2.1.2 压缩引擎设计(ContextCompressor 类)

文件位置backend/app/services/automation/context_compressor.py

核心方法

方法 签名 职责
count_tokens (messages: list) -> int 计算消息列表的 token 总数
should_compress (session_id) -> bool 判断是否需要压缩(token > threshold
compress (session_id, messages, info_items, actions, task_node) -> CompressedContext 执行压缩,返回压缩后上下文
_extract_key_info (info_items, actions, task_node) -> str 提取关键信息(信息项当前值、已执行动作、任务节点)
_summarize_history (messages_to_compress) -> str 调用 LLM 对历史消息生成摘要
_build_compressed_context (key_info, summary, recent_messages) -> str 组装压缩后上下文(结构化 Markdown)
_progressive_compress (context, level=1) -> str 渐进式压缩(最多3级,超出降级截断)

渐进式压缩策略

级别 策略 说明
Level 1 LLM 摘要 + 保留最近4轮对话 标准压缩,token 降低约 60%
Level 2 LLM 摘要 + 保留最近2轮对话 二次压缩,token 降低约 80%
Level 3 纯关键信息 + 保留最近1轮对话 极限压缩,token 降低约 90%
超出 截断最旧消息 降级保护,确保不超限

2.1.3 LLM 调用策略

  • 复用现有 DifyClient,调用 /v1/chat-messages 端点
  • 摘要 prompt 设计要求:将对话历史压缩为结构化摘要,保留关键决策点和信息项
  • 摘要调用超时 30s,失败则降级为截断最旧消息(_progressive_compress 的 Level 3 策略)

2.1.4 压缩触发时机

AutoSessionService._run_background()
  ├── 1. SessionManager.appendMessage(新消息)
  ├── 2. TokenCounter.count_tokens(当前消息列表)
  ├── 3. if tokens > threshold:
  │       ├── ContextCompressor.compress()  ← 压缩
  │       ├── 用压缩后上下文替换原始消息列表
  │       └── 写入 auto_context_compressions 表(日志)
  ├── 4. DifyClient.chat(压缩后/原始消息)  ← 调LLM
  └── 5. 返回响应

2.1.5 压缩后上下文格式

压缩后生成一条结构化 Markdown 系统消息,替换原始历史:

## 会话上下文摘要(系统压缩)
### 已收集信息项
- {name}: {value}v{version}
### 已执行动作
- ✅/⏳ {action_title}{time}
### 当前任务节点
{task_node}
### 历史摘要
{LLM生成的摘要}
### 最近对话
[最近4轮原始对话保留]

2.2 P3 多轮纠错

2.2.1 版本管理

  • 复用 Phase 1 已有的 InformationItem.version + update_history 机制
  • 移除单次更正限制:Phase 1 实际未硬编码限制,需在 IntentRouterSessionManager 中确认无限制逻辑
  • 每次 CORRECT / SUPPLEMENT 意图都正常 version += 1,无次数上限

2.2.2 快照机制(SnapshotService 类)

文件位置backend/app/services/automation/snapshot_service.py

核心方法

方法 签名 职责
create_snapshot (session_id, trigger_item_key, correction_ids) -> InformationSnapshot 在每次更正前创建快照
get_latest_snapshot (session_id) -> Optional[InformationSnapshot] 获取最近未撤销的快照
undo_correction (session_id) -> dict 撤销最近一次更正(限制最近5次)
get_snapshot_history (session_id) -> list 获取快照历史列表
get_version_diff (session_id, v1, v2) -> dict 对比两个版本的差异

撤销逻辑流程

  1. 查询最近一条 is_undone=False 的快照
  2. 将快照中记录的信息项值回滚到快照前的版本
  3. 标记该快照 is_undone=True
  4. 限制:最多撤销最近 5 次(通过计数 is_undone=True 的快照数判断,超过5次拒绝撤销)
  5. 撤销后向 AI 上下文注入"更正撤销"系统消息,让 AI 感知用户撤销了更正

2.2.3 依赖检查(混合方案)

采用模板预定义 + AI 自动标注 + 运行时检查三层混合方案:

层次 机制 说明
模板预定义 ScenarioConfig.actions 中预定义 derived_from 关系 如"设备分配人" derived_from "工号"
AI 自动标注 IntentRouter 识别 CORRECT 意图时,通过 Dify prompt 让 LLM 标注依赖关系 补充模板未覆盖的动态依赖
运行时检查 CorrectionService.check_dependencies(session_id, item_key) -> list 查询所有 derived_from 包含该 item_key 的其他 item,返回联动提示列表

运行时检查逻辑

  • 查询当前 session 中所有 derived_from 数组包含该 item_key 的其他信息项
  • 如果存在依赖项且已有值,生成联动提示(如"修改工号后,设备分配人可能需要同步更新")
  • 返回提示列表,由前端展示给用户/坐席确认

2.2.4 批量更正

  • 方法CorrectionService.batch_correct(session_id, corrections: list) -> dict
  • 原子性:单事务内执行多个更正,任何一项失败则整体回滚(SQLAlchemy transaction rollback
  • 快照策略:只生成一条快照(记录本次批量更正涉及的所有信息项)
  • 结果返回:包含所有已更正项 + 依赖联动警告列表

2.2.5 CorrectionService 类

文件位置backend/app/services/automation/correction_service.py

核心方法

方法 签名 职责
batch_correct (session_id, corrections: list[dict]) -> BatchCorrectResult 批量更正(单事务原子操作)
check_dependencies (session_id, item_key) -> list[dict] 依赖检查,返回联动提示列表
get_correction_history (session_id) -> list 更正历史
get_version_chain (session_id, item_key) -> list 版本链(某信息项的所有版本)
get_version_diff (session_id, v1, v2) -> dict 版本对比
undo_correction (session_id) -> dict 撤销更正(委托 SnapshotService

3. 数据结构和接口

3.1 新增/变更 SQLAlchemy 模型

# ============================================================
# 新增:auto_context_compressions 表(上下文压缩日志)
# ============================================================
class ContextCompression(Base):
    """P2 上下文压缩记录表"""
    __tablename__ = "auto_context_compressions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    session_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
    tokens_before: Mapped[int] = mapped_column(Integer, nullable=False)
    tokens_after: Mapped[int] = mapped_column(Integer, nullable=False)
    compression_ratio: Mapped[float] = mapped_column(Numeric(5, 2), nullable=False)
    task_node: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
    duration_ms: Mapped[int] = mapped_column(Integer, nullable=False)
    compression_level: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=1)
    summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, default=datetime.now
    )


# ============================================================
# 新增:auto_information_snapshots 表(信息项快照)
# ============================================================
class InformationSnapshot(Base):
    """P3 信息项快照表(用于撤销更正)"""
    __tablename__ = "auto_information_snapshots"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    session_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
    trigger_item_key: Mapped[str] = mapped_column(String(64), nullable=False)
    snapshot_data: Mapped[dict] = mapped_column(
        JSON, nullable=False
    )  # {item_key: {value, version}}
    correction_ids: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
    is_undone: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, default=datetime.now
    )


# ============================================================
# 变更:InformationItem 新增2列
# ============================================================
class InformationItem(Base):
    """信息项模型(Phase 2 变更)"""
    # ... Phase 1 已有字段省略 ...

    # Phase 2 新增字段
    derived_from: Mapped[Optional[list]] = mapped_column(
        JSON, nullable=True
    )  # 推导来源 item_key 数组,如 ["工号"]
    correction_reason: Mapped[Optional[str]] = mapped_column(
        String(200), nullable=True
    )  # 更正备注

3.2 Mermaid 类图

classDiagram
    class AutoSession {
        +str id
        +str scenario_id
        +str status
        +str current_task_node
        +datetime created_at
        +datetime updated_at
        +appendMessage(message)
        +getContext()
        +updateTaskNode(node)
    }

    class InformationItem {
        +str id
        +str session_id
        +str item_key
        +str item_name
        +str value
        +int version
        +dict update_history
        +Optional~list~ derived_from
        +Optional~str~ correction_reason
        +datetime created_at
        +updateValue(new_value, reason)
        +getHistory()
        +checkDependency()
    }

    class ContextCompression {
        +int id
        +str session_id
        +int tokens_before
        +int tokens_after
        +float compression_ratio
        +Optional~str~ task_node
        +int duration_ms
        +int compression_level
        +Optional~str~ summary
        +datetime created_at
    }

    class InformationSnapshot {
        +int id
        +str session_id
        +str trigger_item_key
        +dict snapshot_data
        +list correction_ids
        +bool is_undone
        +datetime created_at
    }

    class ContextCompressor {
        +TokenCounter token_counter
        +DifyClient dify_client
        +count_tokens(messages) int
        +should_compress(session_id) bool
        +compress(session_id, messages, info_items, actions, task_node) CompressedContext
        -_extract_key_info(info_items, actions, task_node) str
        -_summarize_history(messages) str
        -_build_compressed_context(key_info, summary, recent) str
        -_progressive_compress(context, level) str
    }

    class SnapshotService {
        +create_snapshot(session_id, trigger_item_key, correction_ids) InformationSnapshot
        +get_latest_snapshot(session_id) Optional~InformationSnapshot~
        +undo_correction(session_id) dict
        +get_snapshot_history(session_id) list
        +get_version_diff(session_id, v1, v2) dict
    }

    class CorrectionService {
        +batch_correct(session_id, corrections) BatchCorrectResult
        +check_dependencies(session_id, item_key) list
        +get_correction_history(session_id) list
        +get_version_chain(session_id, item_key) list
        +get_version_diff(session_id, v1, v2) dict
        +undo_correction(session_id) dict
    }

    AutoSession "1" --> "*" InformationItem : session_id 关联
    AutoSession "1" --> "*" ContextCompression : session_id 关联
    AutoSession "1" --> "*" InformationSnapshot : session_id 关联
    ContextCompressor --> AutoSession : 压缩会话上下文
    ContextCompressor --> ContextCompression : 写入压缩日志
    SnapshotService --> InformationSnapshot : 管理快照
    SnapshotService --> InformationItem : 回滚信息项值
    CorrectionService --> SnapshotService : 委托撤销
    CorrectionService --> InformationItem : 批量更正
    CorrectionService --> InformationSnapshot : 创建快照

3.3 新增 Pydantic Schema

# ============================================================
# 请求模型
# ============================================================

class BatchCorrectRequest(BaseModel):
    """批量更正请求"""
    corrections: List[CorrectRequest]  # 批量更正数组
    reason: Optional[str] = None  # 更正备注


class UndoCorrectRequest(BaseModel):
    """撤销更正请求"""
    pass  # 无参数,撤销最近一次


class VersionDiffRequest(BaseModel):
    """版本对比请求"""
    v1: int
    v2: int
    item_key: Optional[str] = None  # 指定信息项,None则对比全局快照


# ============================================================
# 响应模型
# ============================================================

class ContextCompressionResponse(BaseModel):
    """上下文压缩响应"""
    id: int
    session_id: str
    tokens_before: int
    tokens_after: int
    compression_ratio: float
    task_node: Optional[str]
    duration_ms: int
    compression_level: int
    summary: Optional[str]
    created_at: Optional[str]


class SnapshotResponse(BaseModel):
    """快照响应"""
    id: int
    session_id: str
    trigger_item_key: str
    snapshot_data: dict
    correction_ids: list
    is_undone: bool
    created_at: Optional[str]


class VersionDiffResponse(BaseModel):
    """版本差异响应"""
    item_key: str
    v1: int
    v1_value: str
    v2: int
    v2_value: str
    changed: bool


class BatchCorrectResponse(BaseModel):
    """批量更正响应"""
    corrected_items: List[InformationItemResponse]
    snapshot_id: int
    dependency_warnings: List[dict] = Field(default_factory=list)

4. 程序调用流程

4.1 P2 上下文压缩时序图

sequenceDiagram
    participant Employee as 员工
    participant SM as SessionManager
    participant TC as TokenCounter
    participant CC as ContextCompressor
    participant DB as Database
    participant LLM as DifyClient / LLM

    Employee->>SM: 发送消息
    SM->>SM: appendMessage(新消息)

    SM->>TC: count_tokens(当前消息列表)
    TC-->>SM: 返回 token 总数

    alt token > 压缩阈值
        SM->>CC: should_compress(session_id)
        CC-->>SM: true

        SM->>CC: compress(session_id, messages, info_items, actions, task_node)

        CC->>CC: _extract_key_info(info_items, actions, task_node)
        Note over CC: 提取已收集信息项当前值<br/>已执行动作、当前任务节点

        CC->>LLM: _summarize_history(messages_to_compress)
        Note over LLM: 摘要 prompt<br/>将历史压缩为结构化摘要<br/>保留关键决策点和信息项
        LLM-->>CC: 返回历史摘要文本

        alt LLM 摘要超时/失败(30s)
            CC->>CC: 降级 _progressive_compress(level=3)
            Note over CC: 截断最旧消息,保留关键信息
        end

        CC->>CC: _build_compressed_context(key_info, summary, recent_messages)
        Note over CC: 组装结构化 Markdown 上下文

        CC->>DB: 写入 auto_context_compressions 表
        Note over DB: 记录 tokens_before/after<br/>compression_ratio, duration_ms

        CC-->>SM: 返回 CompressedContext
        SM->>SM: 用压缩后上下文替换原始消息列表
    end

    SM->>LLM: chat(压缩后/原始消息)
    LLM-->>SM: 返回 AI 响应
    SM-->>Employee: 返回响应

4.2 P3 多轮纠错时序图

sequenceDiagram
    participant Employee as 员工
    participant IR as IntentRouter
    participant CS as CorrectionService
    participant SS as SnapshotService
    participant DB as Database
    participant AI as AI 上下文

    Employee->>IR: 发送更正请求(如"工号改成12345"

    IR->>IR: 识别 CORRECT 意图
    Note over IR: AI 自动标注 derived_from 依赖关系<br/>(通过 Dify prompt

    IR->>CS: batch_correct(session_id, corrections)

    CS->>SS: create_snapshot(session_id, trigger_item_key, correction_ids)
    Note over SS: 在更正前创建快照<br/>记录所有涉及信息项的当前值和版本

    SS->>DB: INSERT auto_information_snapshots
    SS-->>CS: 返回 snapshot_id

    CS->>DB: BEGIN TRANSACTION
    Note over CS: 单事务内批量更新信息项

    loop 遍历 corrections 列表
        CS->>DB: UPDATE InformationItem<br/>SET value=new_value, version=version+1
        alt 某项更新失败
            CS->>DB: ROLLBACK
            CS-->>IR: 抛出异常,整体回滚
        end
    end

    CS->>DB: COMMIT

    CS->>CS: check_dependencies(session_id, item_key)
    Note over CS: 查询所有 derived_from 包含该 item_key 的信息项<br/>生成联动提示列表

    CS-->>IR: 返回 BatchCorrectResult<br/>{corrected_items, snapshot_id, dependency_warnings}

    IR->>AI: 注入更正结果系统消息
    Note over AI: AI 感知信息项已更正<br/>后续对话基于新值继续

    IR-->>Employee: 返回更正结果 + 联动提示

4.3 P3 更正撤销时序图

sequenceDiagram
    participant User as 用户/坐席
    participant API as CorrectionAPI
    participant SS as SnapshotService
    participant DB as Database
    participant AI as AI 上下文

    User->>API: POST /undo-correction (session_id)

    API->>SS: undo_correction(session_id)

    SS->>DB: 查询最近一条 is_undone=False 的快照
    DB-->>SS: 返回 latest_snapshot

    alt 无可撤销快照
        SS-->>API: 返回错误"无可撤销的更正"
        API-->>User: 400 无可撤销
    else 已撤销次数 >= 5
        SS-->>API: 返回错误"撤销次数超限"
        API-->>User: 400 撤销次数超限
    else 正常撤销
        SS->>DB: 读取 snapshot_data<br/>{item_key: {value, version}}
        Note over SS: 快照记录了更正前的信息项值和版本

        loop 遍历 snapshot_data 中每个信息项
            SS->>DB: UPDATE InformationItem<br/>SET value=快照前值, version=快照前版本
            Note over DB: 回滚信息项到更正前状态
        end

        SS->>DB: UPDATE InformationSnapshot<br/>SET is_undone=True
        Note over DB: 标记该快照为已撤销

        SS->>AI: 注入"更正撤销"系统消息
        Note over AI: 系统消息内容:<br/>"用户撤销了最近一次更正,<br/>信息项 {item_key} 已回滚到 v{version}"<br/>AI 后续对话基于回滚后的值

        SS-->>API: 返回撤销结果<br/>{undone_items, restored_values}
        API-->>User: 200 撤销成功
    end

5. 文件列表及相对路径

5.1 后端新增文件

文件路径 职责
backend/app/services/automation/context_compressor.py P2 上下文压缩引擎(ContextCompressor 类),包含 token 计数、压缩判断、LLM 摘要、渐进式压缩、压缩日志记录
backend/app/services/automation/snapshot_service.py P3 快照管理服务(SnapshotService 类),包含创建快照、撤销更正、快照历史查询、版本对比
backend/app/services/automation/correction_service.py P3 纠错服务(CorrectionService 类),包含批量更正、依赖检查、更正历史、版本链、撤销委托
backend/app/utils/token_counter.py Token 计数工具类(TokenCounter),封装 tiktoken + 字符估算兜底,支持热切换
backend/app/schemas/automation_p2.py P2/P3 Pydantic Schema 定义(BatchCorrectRequest/Response、UndoCorrectRequest、VersionDiffRequest/Response、ContextCompressionResponse、SnapshotResponse 等)
backend/app/models/automation_p2.py P2/P3 SQLAlchemy 模型定义(ContextCompression、InformationSnapshot
backend/migrations/versions/049_add_p2_p3_tables.py 数据库迁移脚本:新增 auto_context_compressionsauto_information_snapshots 表;information_items 表新增 derived_fromcorrection_reason

5.2 后端修改文件

文件路径 修改内容
backend/app/services/automation/auto_session_service.py _run_background() 方法中增加压缩触发逻辑:每次调用 LLM 前检查 token 数,超阈值执行压缩;注入压缩后上下文
backend/app/services/automation/session_manager.py 确认无单次更正限制逻辑;增加更正结果和撤销结果的系统消息注入方法
backend/app/services/automation/intent_router.py CORRECT 意图识别后调用 CorrectionService.batch_correct;增加 AI 自动标注 derived_from 依赖关系的 prompt
backend/app/models/automation.py InformationItem 模型新增 derived_fromJSON)和 correction_reasonString)两个映射字段
backend/app/api/v1/automation.py 新增 API 端点:POST /batch-correctPOST /undo-correctionGET /correction-historyGET /version-diffGET /compression-logs
backend/app/config.py 新增 P2/P3 配置项:CONTEXT_COMPRESS_THRESHOLD(压缩阈值)、CONTEXT_COMPRESS_TIMEOUT(摘要超时)、MAX_UNDO_COUNT(最大撤销次数)等
backend/app/scenarios/it_helpdesk.py 场景配置中预定义 derived_from 依赖关系(如设备分配人 derived_from 工号)

5.3 前端新增/修改文件

H5 端(员工侧)

文件路径 职责
frontend-h5/src/components/automation/CorrectionNotice.vue 更正结果通知组件:展示更正成功提示 + 依赖联动警告
frontend-h5/src/components/automation/UndoButton.vue 撤销更正按钮组件:展示可撤销状态 + 撤销次数剩余提示

坐席端(Agent 侧)

文件路径 职责
frontend-agent/src/views/automation/CorrectionHistory.vue 更正历史页面:展示更正时间线、版本链、快照列表
frontend-agent/src/views/automation/VersionDiff.vue 版本对比页面:选择两个版本对比信息项值差异,高亮变更项
frontend-agent/src/views/automation/CompressionLogs.vue 上下文压缩日志页面:展示压缩记录、压缩率、耗时、摘要内容
frontend-agent/src/components/automation/DependencyWarning.vue 依赖联动提示组件:更正某项时展示受影响的关联信息项列表
frontend-agent/src/api/automation_p2.ts P2/P3 API 请求封装:batchCorrect、undoCorrection、getCorrectionHistory、getVersionDiff、getCompressionLogs

6. 任务列表

6.1 后端任务(按实现顺序)

任务 ID 描述 依赖 涉及文件 复杂度
T01 数据库迁移 + 模型层:新增 ContextCompression、InformationSnapshot 模型;InformationItem 新增 derived_from、correction_reason 列;编写迁移脚本 049 models/automation_p2.pymodels/automation.pymigrations/versions/049_*.py
T02 Token 计数工具类 + 上下文压缩引擎:实现 TokenCountertiktoken + 兜底)、ContextCompressor(压缩判断、关键信息提取、LLM 摘要、渐进式压缩、日志记录) T01 utils/token_counter.pyservices/automation/context_compressor.py
T03 快照服务 + 纠错服务:实现 SnapshotService(创建快照、撤销更正、历史查询、版本对比)、CorrectionService(批量更正、依赖检查、版本链、撤销委托) T01 services/automation/snapshot_service.pyservices/automation/correction_service.py
T04 API 层 + Schema + 集成:定义 Pydantic Schema;新增 API 端点;修改 AutoSessionService 注入压缩逻辑;修改 IntentRouter 接入纠错服务;修改 SessionManager 注入系统消息;场景配置预定义依赖关系 T02, T03 schemas/automation_p2.pyapi/v1/automation.pyservices/automation/auto_session_service.pyservices/automation/intent_router.pyservices/automation/session_manager.pyscenarios/it_helpdesk.pyconfig.py

6.2 前端任务

任务 ID 描述 依赖 涉及文件 复杂度
T05 H5 端组件 + 坐席端页面 + API 封装:实现更正通知、撤销按钮(H5 端);更正历史、版本对比、压缩日志、依赖提示页面(坐席端);API 请求封装 T04 frontend-h5/src/components/automation/*.vuefrontend-agent/src/views/automation/*.vuefrontend-agent/src/components/automation/DependencyWarning.vuefrontend-agent/src/api/automation_p2.ts

任务依赖图

graph LR
    T01[T01: 数据库迁移 + 模型层]
    T02[T02: Token计数 + 压缩引擎]
    T03[T03: 快照服务 + 纠错服务]
    T04[T04: API层 + Schema + 集成]
    T05[T05: 前端组件 + 页面 + API封装]

    T01 --> T02
    T01 --> T03
    T02 --> T04
    T03 --> T04
    T04 --> T05

7. 依赖包列表

包名 版本要求 用途
tiktoken >= 0.5.0 Token 计数(OpenAI cl100k_base 编码,与 Dify/GPT 模型一致)

说明:除 tiktoken 外,P2/P3 其余功能均复用 Phase 1 已有技术栈(SQLAlchemy、FastAPI、Pydantic、DifyClient 等),无新增依赖。


8. 共享知识

8.1 命名约定

约定 规则 示例
压缩日志表 auto_context_compressions
快照表 auto_information_snapshots
服务类命名 XxxService / XxxCompressor CorrectionServiceContextCompressorSnapshotService
Schema 命名 XxxRequest / XxxResponse BatchCorrectRequestSnapshotResponse
API 路径前缀 /api/v1/automation/ /api/v1/automation/batch-correct
系统消息注入 [SYSTEM] 前缀标识 [SYSTEM] 更正撤销:信息项 工号 已回滚到 v2

8.2 错误码扩展

错误码 含义 HTTP 状态码 说明
4013 上下文压缩失败 500 LLM 摘要调用超时或异常,已降级为截断处理
4014 撤销次数超限 400 已撤销次数达到上限(5次),无法继续撤销
4015 批量更正部分失败 500 事务回滚,所有更正均未生效

错误响应格式(与 Phase 1 统一):

{
  "code": 4014,
  "data": null,
  "message": "撤销次数超限,最多可撤销最近5次更正"
}

8.3 配置项

配置项 默认值 说明
CONTEXT_COMPRESS_THRESHOLD 6000 上下文压缩触发阈值(token 数),超过则执行压缩
CONTEXT_COMPRESS_TIMEOUT 30 LLM 摘要调用超时时间(秒),超时降级截断
CONTEXT_MAX_COMPRESS_LEVEL 3 渐进式压缩最大级别,超出则降级截断
CONTEXT_KEEP_RECENT_TURNS 4 压缩后保留的最近对话轮数(Level 1)
MAX_UNDO_COUNT 5 最大撤销更正次数
CONTEXT_COMPRESS_RATIO_WARN 0.3 压缩率警告阈值(压缩后/压缩前 < 0.3 时告警)

9. 数据库迁移方案

迁移脚本:049_add_p2_p3_tables.py

"""Add P2 context compression and P3 snapshot tables

Revision ID: 049
Revises: 048
Create Date: 2025-07-11
"""

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

revision = "049"
down_revision = "048"


def upgrade():
    # 1. 新增 auto_context_compressions 表
    op.create_table(
        "auto_context_compressions",
        sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
        sa.Column("session_id", sa.String(36), nullable=False),
        sa.Column("tokens_before", sa.Integer(), nullable=False),
        sa.Column("tokens_after", sa.Integer(), nullable=False),
        sa.Column("compression_ratio", sa.Numeric(5, 2), nullable=False),
        sa.Column("task_node", sa.String(128), nullable=True),
        sa.Column("duration_ms", sa.Integer(), nullable=False),
        sa.Column("compression_level", sa.SmallInteger(), nullable=False, server_default="1"),
        sa.Column("summary", sa.Text(), nullable=True),
        sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("ix_auto_context_compressions_session_id", "auto_context_compressions", ["session_id"])

    # 2. 新增 auto_information_snapshots 表
    op.create_table(
        "auto_information_snapshots",
        sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
        sa.Column("session_id", sa.String(36), nullable=False),
        sa.Column("trigger_item_key", sa.String(64), nullable=False),
        sa.Column("snapshot_data", postgresql.JSON(), nullable=False),
        sa.Column("correction_ids", postgresql.JSON(), nullable=False, server_default="[]"),
        sa.Column("is_undone", sa.Boolean(), nullable=False, server_default="false"),
        sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("ix_auto_information_snapshots_session_id", "auto_information_snapshots", ["session_id"])

    # 3. information_items 表新增列
    op.add_column("information_items", sa.Column("derived_from", postgresql.JSON(), nullable=True))
    op.add_column("information_items", sa.Column("correction_reason", sa.String(200), nullable=True))


def downgrade():
    op.drop_column("information_items", "correction_reason")
    op.drop_column("information_items", "derived_from")
    op.drop_index("ix_auto_information_snapshots_session_id", table_name="auto_information_snapshots")
    op.drop_table("auto_information_snapshots")
    op.drop_index("ix_auto_context_compressions_session_id", table_name="auto_context_compressions")
    op.drop_table("auto_context_compressions")

迁移影响评估

影响项 说明
新增表 2 张(auto_context_compressionsauto_information_snapshots),均为追加表,不影响现有数据
变更表 1 张(information_items 新增 2 列),均为 nullable 列,向后兼容
数据迁移 无需迁移历史数据,新列默认 NULL
回滚风险 低,downgrade() 完整可逆

10. 待明确事项

序号 待明确事项 当前假设 影响范围
1 压缩阈值的精确值需根据实际模型上下文窗口确定 暂设 6000 tokensGPT-3.5 4K 上下文留 2K 余量) ContextCompressor 配置项
2 LLM 摘要使用的 Dify 应用是否独立配置专用摘要 prompt 假设复用现有 Dify 应用,通过 system message 注入摘要 prompt ContextCompressor._summarize_history
3 derived_from 依赖关系的场景模板具体内容需与业务方确认 已在 it_helpdesk.py 中预定义常见依赖(如设备分配人 derived_from 工号),待业务方补充 scenarios/it_helpdesk.py
4 撤销操作是否需要二次确认(防误操作) 假设前端弹出确认框,后端直接执行 前端 UndoButton.vue
5 压缩摘要是否需要人工审核后才生效 假设自动生效,无需人工审核 ContextCompressor.compress
6 tiktoken 在企业内网环境(无外网)是否可正常安装和运行 假设可通过内网 PyPI 镜像安装;运行时编码数据文件(~/.cache/tiktoken)需预置 utils/token_counter.py
7 批量更正的最大数量限制 假设无硬限制,由前端 UI 交互控制(单次通常 ≤10 项) CorrectionService.batch_correct