Files
adminandClaude Fable 5 f18286f022 fix(r4-47a/52): DOCX 红头标题样式同源解析 + 意图词典补总结/概括/归纳
#47a: 红头/标题字号颜色从 render_html 产物解析(新增 docgen/html_css.py:
内嵌 style 块 + 调用方传入的模板 css,组合选择器展开、px->pt),解析失败
回退历史常量(FF0000/22pt、h1/h2/h3 22/16/14pt,行为不变)。converter 与
导出路由把 css 传入 DOCX 转换,与 PDF/预览同源;非红头模板(如 report 灰
#333)不误渲染红头。r3 的 doc-body 包裹/表格/图片结构不回归。
#52: intent.py WRITE_VERB 补总结/概括/归纳,新增动词自指保护(唯一命中的
DOC_TYPE 等于命中动词本身时不算写文档意图,如「总结一下植物光合作用的过程」
仍 qa);cases_intent.md 同步规则与用例 31-34(共 34 条)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 14:19:04 +08:00

104 lines
4.4 KiB
Python
Raw Permalink 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.
"""services/docgen/intent.py —— 服务端意图路由(规则词典法)。
唯一事实源:docs/team/cases_intent.md(30 条用例表,前后端同表测试)。
漏判安全原则:不命中一律 qa。
判定顺序(按优先级,命中即返回):
1. export:has_active_artifact 且 EXPORT_WORD + FORMAT_WORD
2. edit:has_active_artifact 且 EDIT_WORD
3. docgen:WRITE_VERB + DOC_TYPE
4. docgen:has_attachments + WRITE_VERB
5. 其余 qa
"""
from __future__ import annotations
# ---- 词典(与 cases_intent.md 逐条一致,不得自造)----
EXPORT_WORD = ["导出", "转成", "转为", "转换成", "下载成", "输出为", "存为", "转pdf", "转word"]
FORMAT_WORD = ["pdf", "docx", "doc", "word"] # 英文不区分大小写(contains 判定)
EDIT_WORD = ["改", "修改", "调整", "删", "删除", "加一段", "补充", "润色",
"缩写", "扩写", "重写", "改写", "优化", "加个", "增加一段"]
WRITE_VERB = ["写", "撰写", "拟", "起草", "编制", "生成", "编写", "拟定", "草拟", "制定",
"总结", "概括", "归纳"] # r4 #52(2026-09-11)
DOC_TYPE = [
"论文", "报告", "通知", "通告", "公告", "请示", "批复", "函", "申请书",
"申请", "申报书", "标书", "合同", "协议", "方案", "计划", "总结", "汇报",
"讲话稿", "致辞", "证明", "纪要", "简报", "材料", "说明书", "指南", "文档",
"申报材料", "工作总结", "调研报告", "可行性报告", "项目书", "策划书",
"演讲稿", "新闻稿", "文案", "简历", "制度", "规定", "办法", "细则", "章程",
"声明", "邀请函", "感谢信", "慰问信", "介绍信", "推荐信", "倡议书",
"述职报告", "开题报告", "结题报告", "实验报告", "分析报告", "会议纪要",
]
# 明确排除(防误判,不属于 DOC_TYPE)
DOC_TYPE_EXCLUDE = ["介绍", "内容", "代码", "诗", "作业", "问题", "建议", "想法"]
def _contains_any(text: str, words: list[str]) -> bool:
"""text 含 words 任一子串。"""
return any(w in text for w in words)
def _contains_any_ci(text: str, words: list[str]) -> bool:
"""不区分大小写匹配(英文词典用)。"""
lower = text.lower()
return any(w.lower() in lower for w in words)
def _has_doc_type(text: str) -> bool:
"""含 DOC_TYPE 任一,且不含 DOC_TYPE_EXCLUDE 任一(排除优先)。"""
if _contains_any(text, DOC_TYPE_EXCLUDE):
return False
return _contains_any(text, DOC_TYPE)
def route(text: str, has_attachments: bool, has_active_artifact: bool) -> str:
"""意图路由:qa | docgen | edit | export。
判定顺序按 cases_intent.md 冻结表,漏判安全(不命中一律 qa)。
"""
if not text or not text.strip():
return "qa"
# 1. export(需有活动文档)
if has_active_artifact:
if _contains_any(text, EXPORT_WORD) and _contains_any_ci(text, FORMAT_WORD):
return "export"
# 2. edit(需有活动文档)
if _contains_any(text, EDIT_WORD):
return "edit"
# 3. docgen:写类动词 + 文档类型词
# r4 #52(2026-09-11)动词自指保护:当唯一命中的 DOC_TYPE 恰好等于
# 命中的动词本身(如「总结一下植物光合作用的过程」——「总结」既是动词
# 又是文档类型词),不视为写文档意图(继续向下判定)。「写一篇总结」
# (动词=写,类型=总结)与「总结这篇论文」(动词=总结,类型=论文)
# 不受影响。规则详见 docs/team/cases_intent.md 用例 31-34。
_matched_verbs = [w for w in WRITE_VERB if w in text]
_matched_types = [w for w in DOC_TYPE if w in text]
if _matched_verbs and _matched_types and _has_doc_type(text):
if set(_matched_types) != set(_matched_verbs):
return "docgen"
# 4. docgen:附件信号(拖入文件 + 写类动词)
if has_attachments and _contains_any(text, WRITE_VERB):
return "docgen"
# 5. 漏判安全:其余一律 qa
return "qa"
async def is_docgen_intent(text: str) -> bool:
"""dev2 护栏布尔接口(async 契约):route(...) == "docgen"。
dev2 的 generate/edit/export 均 await 此函数;路由异常时护栏
放行(与 dev2 桩行为一致,护栏不阻断)。
"""
try:
return route(text, has_attachments=False, has_active_artifact=False) == "docgen"
except Exception:
return True