deerflow-code/offline-backend-20260512/backend/packages/harness/deerflow/agents/ai_writing/state.py
2026-09-07 18:24:55 +08:00

218 lines
9.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import operator
from typing import Annotated, List, Literal, Optional, TypedDict
# ── 素材 ──────────────────────────────────────────────────────────────────────
class Material(TypedDict, total=False):
id: str
title: str
content: str # 截断到 500 字
source: str
published_at: str
relevance_score: float
source_type: str # "internet" | "intranet" | "skill"
url: str
recUuid: str
# skill 类素材附带的元信息(skill_name / container_path / category),其他类型为空
extra_metadata: dict
class SkillStep(TypedDict, total=False):
"""技能检索过程的单步记录,用于前端时间线展示。
type=match_skills:根据用户输入流式分析有没有匹配技能(无 skill_names)
type=list_skills:列出可用技能
type=select_skills:LLM 从中选出的相关技能
type=invoke_skill:实际调用某一个技能检索
type=stop_early:达到素材阈值,提前停止后续技能调用
"""
type: str
message: str
skill_names: List[str]
skill_name: str
matched_count: int
reason: str
class MaterialPackage(TypedDict, total=False):
keywords: List[str]
materials: List[Material]
summary: str
# 是否本次素材来自技能检索(覆盖式替换 web 检索)
skill_search_used: bool
# 技能检索的步骤记录(前端展示"使用了 X 技能"卡片)
skill_search_steps: List[SkillStep]
# 内网知识库接口诊断:端点、每个检索词的命中数/耗时/错误、原始响应片段等。
# 用于排查 "检索很快返回 0 条" 时到底是否真正调用了内网接口。
intranet_debug: dict
# ── 大纲 ──────────────────────────────────────────────────────────────────────
# 三级结构:章节(##) → 小节(###) → 子要点(bullet -)。
# Outline.sections[] = Level 1 章节(渲染为 ## 由系统注入)
# OutlineSection.key_points[] = Level 2 小节(渲染为 ### 标题)
# KeyPoint.sub_points[] = Level 3 子要点(bullet "- ...",非标题)
#
# 历史上 key_points 曾是 List[str](2 级):每个 str 既是小节标题又是渲染单位。
# 升级到 3 级后,key_points 变成 List[KeyPoint],每个 KeyPoint 自带 sub_points
# 列表;旧 session 里的 string 项由 writer_outline._parse_outline 自动 lift 成
# {point_title: <原 string>, sub_points: []},无需迁移数据。
class KeyPoint(TypedDict):
point_title: str # 小节标题(渲染为 ### 二级小标题)
sub_points: List[str] # 子要点(可为空;非空时在小节正文里以 bullet 列表呈现)
class OutlineSection(TypedDict):
section_title: str
key_points: List[KeyPoint] # 注意:旧版是 List[str],解析层做了兼容
material_ids: List[str]
class Outline(TypedDict):
title: str
sections: List[OutlineSection]
# ── 草稿 ──────────────────────────────────────────────────────────────────────
class DraftArticle(TypedDict):
title: str
outline: Outline
full_markdown: str
word_count: int
revision_count: int
# ── 样文仿写 ──────────────────────────────────────────────────────────────────
class SampleStyleProfile(TypedDict, total=False):
summary: str
article_type_guess: str
tone: str
structure: List[str]
language_features: List[str]
sentence_style: str
opening_style: str
ending_style: str
imitation_guidance: List[str]
avoid_copying: List[str]
sample_outline: List[str]
# ── 编辑审核 ──────────────────────────────────────────────────────────────────
class ReviewIssue(TypedDict):
category: str # "fact" | "logic" | "language"
severity: str # "major" | "minor"
location: str
issue: str
suggestion: str
class ReviewResult(TypedDict):
verdict: str # "pass" | "reject"
fact_score: int
logic_score: int
language_score: int
overall_score: int
pass_threshold: int
revision_notes: List[ReviewIssue]
# ── 用户干预 ──────────────────────────────────────────────────────────────────
class UserInterventionPayload(TypedDict, total=False):
action: str
approved_material_ids: List[str]
extra_keywords: List[str]
# PAUSE-1 素材确认 / enable_skill_search 共用:用户在单输入框里填的自然语言
# (可能是要使用的技能名、想检索的来源、或者一段补充检索意图)。
user_query: str
# PAUSE-1 素材确认卡切换的检索方式:general / knowledge_base / notebook。
material_source: str
edited_outline: Optional[Outline]
outline_feedback: str
user_revision_notes: str
override_verdict: bool
# ── 主状态 ────────────────────────────────────────────────────────────────────
WritingStatus = Literal[
"analyzing_sample",
"researching", "awaiting_material_confirm",
"writing_outline", "awaiting_outline_confirm",
"writing_draft", "awaiting_section_help", "awaiting_draft_confirm",
"reviewing", "awaiting_review_confirm",
"revising", "done", "error",
]
class BlockedSection(TypedDict):
"""严格模式下素材不足、无法写作的章节。"""
section_title: str
reason: str
class AIWritingState(TypedDict):
# 写作配置
user_intent: str
# 用户在写作表单粘贴的「已有大纲」原文(可选)。非空时 writer_outline 跳过 LLM 规划,
# 直接解析这份大纲并按用户结构生成,做到「完全按我的大纲写」。留空走正常自动规划。
user_outline_raw: str
# True when setup chat has already produced structured intent fields.
skip_intent_parser: bool
# 写作入口模式:normal=普通写作 / imitate=样文仿写。仿写模式下先分析 sample_text,
# 得到 sample_style_profile,再将其注入大纲与草稿提示词。
writing_mode_type: str
sample_text: str
sample_filename: str
sample_style_profile: Optional[SampleStyleProfile]
imitate_strength: str
preserve_sample_structure: bool
article_type: str
# 当 article_type 是「已有类型库里没有」的新类型时,配置助手识别出的一句话特征说明。
# 仅本次写作临时使用(不入库):在大纲生成时作为 hint 附在类型后,影响全文结构与侧重。
article_type_detail: str
target_audience: str
word_count_target: int
# 素材检索关键词数量(用户指定,1~10,默认 6)
keyword_count: int
# 写作模式:strict=严格依据素材 / loose=可结合通用知识(默认 loose)
writing_mode: str
# 是否在成稿里加参考文献:正文引用素材处加角标 [n],文末按出现顺序生成「参考文献」列表(像论文)。
add_references: bool
# 检索类型:general=通用检索(按素材收集专家 agent 配置的技能编排,默认)/
# knowledge_base=知识库检索(直连 ai_writing.intranet_search_url 内网 ES 接口,
# 不走技能编排——弱模型识别不了技能时用这条)/ notebook=我的空间(仅笔记检索)。
# 旧值 skill 由 researcher 节点按 general 兼容处理。
material_source: str
# 跳过素材收集:用户在初始表单勾选「不收集素材,直接用通用知识写作」时为 True。
# researcher 节点在**首次**进入(无任何 pause 干预)时短路,跳过整个检索流程直接
# 去大纲规划;强制 loose 写作模式(没有素材可严格依据)。
skip_research: bool
# 用户在写作表单选定的主模型。大纲/写作/审核严格使用它(不回退默认)。
# 由 intent_parser 在首次 invocation 从 configurable.model_name 捕获进 state,
# 经 checkpoint 持久化 —— interrupt/resume 是新 invocation、其 config 不带原
# model_name,存进 state 才能让 resume 后才运行的节点拿到用户模型。
model_name: str
# 笔记来源(可选):传入后 researcher 阶段会额外从笔记检索素材
notebook_search_space_id: Optional[int]
notebook_source_ids: Optional[List[int]]
# Agent A 输出
material_package: Optional[MaterialPackage]
# Agent B 输出
current_outline: Optional[Outline]
drafts: Annotated[List[DraftArticle], operator.add]
current_draft: Optional[DraftArticle]
# Agent C 输出
review_result: Optional[ReviewResult]
# 用户干预数据(每个干预点会覆盖)
last_user_intervention: Optional[UserInterventionPayload]
# 严格模式下素材不足、待用户决策的章节(写作节点产出,求助暂停点消费)
blocked_sections: Optional[List[BlockedSection]]
# 流程控制
revision_count: int
max_revisions: int
status: WritingStatus
# SSE 进度推送队列(每个节点追加)
progress_events: Annotated[List[dict], operator.add]