- vstore 独立安装包(8094):解析/嵌入/加密 vault/faiss 检索,存储路径支持本地 file 与 http(s):// URL,启动握手校验,集群化预留(embed 独立模块) - BFF docgen 路由:上传入库/generate/编辑/导出/下载/主库管理,意图护栏(与前端同词典),vstore 失败兜底,quota 507 与 TTL 惰性清理 - 前端:自然触发意图路由、拖拽上传 chips、能力徽标、右侧文档 TAB(WYSIWYG 编辑/prompt 修改/PDF+DOCX 导出下载)、右栏拖宽+手柄仿左栏 - 批量导入工具 docgen_import.py(并发 8、导入即加密+删源明文) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
3.7 KiB
Python
95 lines
3.7 KiB
Python
"""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 = ["写", "撰写", "拟", "起草", "编制", "生成", "编写", "拟定", "草拟", "制定"]
|
||
|
||
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:写类动词 + 文档类型词
|
||
if _contains_any(text, WRITE_VERB) and _has_doc_type(text):
|
||
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
|