文档编辑AI对话代码结构评审.md 12 KB

文档编辑 AI 对话代码结构评审

评审日期:2026-05-27
评审范围:views/document_chatcore/document_chat、相关 config 配置文件
评审重点:AI 对话功能的代码结构层级、文件组织、职责边界与后续可维护性

1. 总体结论

当前 AI 对话功能的整体分层方向是合理的,主链路也比较清楚:

HTTP 接口层 views/document_chat
  -> 工作流编排层 core/document_chat/workflows
    -> 业务组件层 core/document_chat/component
      -> 技能层 core/document_chat/skills
        -> 基础设施层 foundation / Milvus / LLM

从现在只有 document-answerdocument-modify 两个技能的规模来看,当前结构可以支撑功能运行和短期维护。

但如果后续继续扩展更多技能、检索策略、SSE 事件或复杂对话能力,现有结构会逐渐变重。主要风险集中在两个文件:

  • core/document_chat/component/retrieval_service.py
  • core/document_chat/workflows/document_chat_workflow.py

这两个文件已经承担了较多职责,是后续重构的优先位置。

综合评价:7/10

2. 当前模块结构

2.1 对外入口

文件:views/document_chat/views.py

主要职责:

  • 注册 /sgbx/document_chat 接口。
  • 支持 JSON 同步响应和 SSE 流式响应。
  • 生成 callback_task_id
  • 记录请求和响应日志。
  • 调用 document_chat_workflow
  • 将 LangGraph 的节点更新转换为前端 SSE 事件。

调用入口:

POST /sgbx/document_chat
GET  /sgbx/document_chat/health

2.2 核心工作流

文件:core/document_chat/workflows/document_chat_workflow.py

主要职责:

  • 构建 LangGraph 状态图。
  • 定义对话流程节点。
  • 路由意图识别结果。
  • 串联 RAG 链路。
  • 调用具体技能。
  • 组装最终响应数据。

核心流程:

validate_input
  -> 无选中章节: general_answer -> complete
  -> 有选中章节: load_context
    -> load_skill_registry
    -> recognize_intent
    -> route_intent
      -> clarify -> complete
      -> unsupported -> complete
      -> answer/modify
        -> build_retrieval_query
        -> vector_recall
        -> rerank_context
        -> quality_gate
        -> run_answer_skill / run_modify_skill
        -> complete

2.3 数据模型

文件:

  • core/document_chat/schemas.py
  • core/document_chat/component/state_models.py

schemas.py 定义 API 请求、响应、技能输入输出等结构:

  • DocumentChatRequest
  • IntentResult
  • DocumentChatSkillInput
  • DocumentChatSkillOutput
  • DocumentChatData
  • DocumentChatResponse

state_models.py 定义 LangGraph 内部状态:

  • DocumentChatState

整体上,API schema 和 workflow state 分开是合理的。

2.4 业务组件

目录:core/document_chat/component

主要文件:

文件 当前职责
intent_recognizer.py LLM 意图识别和启发式兜底
skill_dispatcher.py 加载技能 YAML,分发到技能 handler
retrieval_service.py Query 构建、多路召回、RRF 融合、候选清洗、scope 过滤
rerank_service.py 对召回结果做重排
retrieval_quality_gate.py 过滤低质量参考资料
conversation_context.py 对上下文做轻量整理
prompt_loader.py 加载 prompt 配置
llm_utils.py JSON 提取、模型输出解析等工具
document_chat_logger.py 文档对话结构化日志

2.5 技能层

目录:core/document_chat/skills

当前技能:

  • document-answer
  • document-modify

实现文件:

  • document_answer.py
  • document_modify.py
  • base.py

配置文件:

  • skills/document-answer/skill.yaml
  • skills/document-modify/skill.yaml

当前做法是:YAML 描述技能元信息,Python 文件实现具体能力,SkillDispatcher 负责加载和分发。

2.6 配置层

相关文件:

  • config/document_chat_retrieval.yaml
  • config/prompt/document_chat_intent.yaml
  • config/prompt/document_answer_prompt.yaml
  • config/prompt/document_modify_prompt.yaml

检索参数和 prompt 外置是合理的,方便调参和迭代提示词。

3. 结构合理的地方

3.1 主链路清晰

从接口到工作流,再到组件和技能,方向比较明确。阅读 document_chat_workflow.py 可以快速理解 AI 对话功能的完整执行路径。

3.2 工作流和技能有基本解耦

工作流并不直接写死问答和修改的全部实现,而是通过 SkillDispatcher 调用具体技能。后续新增技能时,理论上可以沿用 YAML + handler 的方式扩展。

3.3 RAG 链路被拆成多个阶段

当前 RAG 链路包括:

  • 检索 query 构建
  • 多路召回
  • rerank
  • quality gate
  • approved references 注入技能 prompt

这个流程划分是清楚的,比直接把向量库结果全部塞进 LLM 更稳。

3.4 配置外置

检索阈值、召回数量、prompt 都放在配置文件中,便于调参,不需要频繁改业务代码。

3.5 技能注册有白名单

SkillDispatcher 中的 _HANDLER_CLASSES 是显式白名单,比从 YAML 中任意动态 import 更安全,也更容易追踪依赖。

4. 主要结构问题

4.1 component 层职责偏杂

component 当前包含了服务类、工具函数、状态模型、日志工具、prompt loader、检索核心算法等多类内容。

短期可以接受,但长期来看,component 会越来越像杂物层。建议逐步拆成更明确的子包:

core/document_chat/
  application/
  workflows/
  retrieval/
  skills/
  events/
  schemas.py

4.2 retrieval_service.py 过重

文件:core/document_chat/component/retrieval_service.py

该文件当前承担了较多职责:

  • 检索配置加载。
  • 检索 query 构建。
  • 关键词提取。
  • scope 提取。
  • Milvus filter 表达式构建。
  • parent vector 召回。
  • child locator 召回。
  • tag 召回。
  • chapter similarity 召回。
  • RRF 融合。
  • 候选对象构建。
  • metadata 标准化。
  • 候选清洗和去重。
  • 日志打包。

这已经不是单一 service,而是完整的 retrieval 子系统。后续如果要调某一路召回,很容易影响整个文件。

建议拆分为:

core/document_chat/retrieval/
  config.py
  query_builder.py
  service.py
  fusion.py
  scope.py
  candidate.py
  keyword_extractor.py
  retrievers/
    parent_vector.py
    child_locator.py
    tag.py
    chapter_similarity.py

4.3 document_chat_workflow.py 偏重

文件:core/document_chat/workflows/document_chat_workflow.py

该文件当前同时承担:

  • 图结构定义。
  • 所有节点实现。
  • 路由函数。
  • 通用回答 LLM 调用。
  • SSE stream writer 捕获。
  • 技能输入构造。
  • 最终响应组装。
  • 错误状态构造。

建议把图定义和节点实现拆开:

core/document_chat/workflows/
  graph.py
  nodes.py
  response_mapper.py

其中:

  • graph.py 只负责 LangGraph 节点和边。
  • nodes.py 负责节点逻辑。
  • response_mapper.py 负责 state -> response data。

4.4 general_answer 没有进入技能体系

当前:

  • document-answer 是技能。
  • document-modify 是技能。
  • general_answer 直接写在 workflow 中。

这会导致能力扩展模式不一致。

建议把通用回答也做成技能:

core/document_chat/skills/general-answer/
  skill.yaml

core/document_chat/skills/general_answer.py

这样工作流只负责路由,不直接内联 LLM 调用。

4.5 SSE 层依赖内部节点名

文件:views/document_chat/views.py

当前 SSE 事件构建依赖这些节点名:

  • recognize_intent
  • rerank_context
  • quality_gate
  • run_answer_skill
  • run_modify_skill
  • general_answer

如果 workflow 节点名调整,前端事件逻辑也要跟着改。

建议后续让 workflow 或事件转换层输出标准事件,例如:

DocumentChatEvent(
  type="retrieval_result",
  payload={...}
)

views 层只负责把事件格式化为 SSE。

4.6 数据契约有少量漂移

schemas.py 中定义了:

  • DiffItem
  • DiffResult
  • DocumentChatData.old_content_hash
  • DocumentChatData.new_content_hash
  • DocumentChatData.diff
  • DocumentChatData.diff_granularity

state_models.py 中也有:

  • diff_result

但当前 workflow 没有实际生成和回填 diff 结果。

这说明有部分字段可能是预留或遗留设计。建议确认前端是否仍需要这些字段:

  • 如果需要,应补齐 diff 生成流程。
  • 如果不需要,应减少响应契约中的无效字段。

5. 推荐目标结构

一个更适合后续扩展的结构可以是:

core/document_chat/
  __init__.py
  schemas.py

  workflows/
    graph.py
    nodes.py
    response_mapper.py

  retrieval/
    config.py
    query_builder.py
    service.py
    fusion.py
    scope.py
    candidate.py
    keyword_extractor.py
    quality_gate.py
    rerank_service.py
    retrievers/
      base.py
      parent_vector.py
      child_locator.py
      tag.py
      chapter_similarity.py

  intent/
    recognizer.py

  skills/
    base.py
    dispatcher.py
    document_answer.py
    document_modify.py
    general_answer.py
    document-answer/
      skill.yaml
    document-modify/
      skill.yaml
    general-answer/
      skill.yaml

  events/
    sse_mapper.py

  observability/
    logger.py

  utils/
    llm_output.py
    prompt_loader.py

这不是必须一次性完成的重构目标,可以按风险和收益逐步迁移。

6. 建议重构优先级

P1:优先拆检索层

收益最大,风险可控。

建议顺序:

  1. 提取 RetrievalConfigload_retrieval_configretrieval/config.py
  2. 提取 query 和关键词逻辑到 retrieval/query_builder.py
  3. 提取 scope 逻辑到 retrieval/scope.py
  4. 提取 RRF 融合到 retrieval/fusion.py
  5. 四路召回分别拆到 retrieval/retrievers/
  6. 保留一个薄的 DocumentChatRetrievalService 作为编排入口。

P2:拆 workflow

建议顺序:

  1. 提取 to_response_dataresponse_mapper.py
  2. 提取 _build_skill_input 到单独 mapper 或 factory。
  3. 把节点实现移到 nodes.py
  4. graph.py 只负责定义节点和边。

P3:统一技能体系

建议把 general_answer 也做成技能,避免 workflow 内联 LLM 调用。

P4:清理 schema 和 state

确认 diff 字段是否仍属于对话功能的稳定契约:

  • 如果需要,补上 diff 生成。
  • 如果不需要,标记废弃或移除。

P5:抽象 SSE 事件

将前端事件构建从 views.py 中移出,避免接口层直接感知 workflow 内部节点名。

7. 保持现状时的注意事项

如果短期不做结构重构,至少建议做到:

  • 新增检索策略时,不继续往 retrieval_service.py 中堆大段逻辑。
  • 新增对话能力时,优先做成 skill,不直接写进 workflow。
  • 修改 workflow 节点名时,同步检查 views/document_chat/views.py 的 SSE 事件映射。
  • 调整 DocumentChatData 字段时,同步确认前端消费逻辑。
  • intent_recognizerretrieval_quality_gatererank_serviceskill_dispatcher 增加单元测试。

8. 最终判断

当前代码结构不是乱的,主干清楚、功能边界已有雏形,适合继续迭代。

真正需要警惕的是:retrieval_service.pydocument_chat_workflow.py 已经开始承担子系统级职责。继续扩展时,如果不拆分,这两个文件会成为后续维护、测试和排查问题的主要成本点。

建议后续重构从检索层开始,因为它体量最大、职责最多,而且拆分后对工作流和 API 的外部行为影响最小。