feat: 增强规则资产管理与审计页面运行时调试

后端新增规则资产版本管理和规则文件 CRUD 接口,优化风险
规则生成模板执行和员工数据模型字段,知识库 RAG 增强本
地回退和文档提取能力,清理旧风险规则文件统一由生成引擎
管理,前端审计页面增加运行时调试面板和规则资产编辑交互,
补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-24 21:44:17 +08:00
parent 575f093c74
commit 50b1c3f9a9
113 changed files with 13896 additions and 5044 deletions

View File

@@ -131,6 +131,8 @@ def test_user_agent_knowledge_prompt_enforces_knowledge_boundary() -> None:
assert "不能用常识、外部知识或主观推断补齐缺失条件" in messages[0]["content"]
assert "不能只依赖排在最前面的片段" in messages[0]["content"]
assert "不能把第一列的数值直接套给后面的列名" in messages[0]["content"]
assert "最终答复必须像助手在认真回答问题" in messages[0]["content"]
assert "禁止使用“已命中”“答案整理阶段”“稍后重试”" in messages[0]["content"]
assert "knowledge_evidence_blocks" in messages[0]["content"]
assert '"knowledge_answer_evidence": []' in messages[1]["content"]
@@ -162,8 +164,9 @@ def test_user_agent_knowledge_fallback_is_honest_and_personalized() -> None:
)
assert answer.startswith("张三,您好。")
assert "答案整理阶段本轮没有及时返回" in answer
assert "先给你当前最直接的依据" in answer
assert "我先根据当前制度依据给出可以确认的部分" in answer
assert "已命中" not in answer
assert "答案整理阶段本轮没有及时返回" not in answer
assert "《差旅费制度》" in answer
@@ -241,6 +244,40 @@ def test_user_agent_prefers_relevant_raw_hit_over_generic_appendix() -> None:
assert "组织人事部" in selected[0]["content"]
def test_user_agent_model_hit_selection_keeps_later_relevant_hits() -> None:
selected = UserAgentService._select_knowledge_model_hits(
{
"hits": [
{"content": "一般说明一"},
{"content": "一般说明二"},
{"content": "一般说明三"},
{"content": "一般说明四"},
{"content": "一般说明五"},
{"content": "一般说明六"},
{"content": "一般说明七"},
{
"content": (
"# 问答线索补充\n\n"
"- 第二章 报销时限:差旅费应在行程结束三个月内提交;逾期不予报销出差补贴。"
)
},
]
},
question="差旅费报销时限是多少?",
)
assert "三个月内提交" in selected[0]["content"]
def test_user_agent_knowledge_terms_keep_accounting_subject_in_long_query() -> None:
terms = UserAgentService._extract_knowledge_query_terms(
"远光软件财务基础知识手册里的常用会计科目是什么?"
)
assert "常用会计科目" in terms
assert "会计科目" in terms
def test_user_agent_uses_fast_knowledge_answer_without_model(monkeypatch) -> None:
session_factory = build_session_factory()
with session_factory() as db:
@@ -286,12 +323,170 @@ def test_user_agent_uses_fast_knowledge_answer_without_model(monkeypatch) -> Non
)
assert response.answer.startswith("张三,您好。")
assert "当前能直接确认的是" in response.answer
assert "**结论**" in response.answer
assert "30 日内提交报销申请" in response.answer
assert "## 依据" not in response.answer
assert "答案整理阶段本轮没有及时返回" not in response.answer
def test_user_agent_fast_knowledge_answer_focuses_inline_section_items() -> None:
session_factory = build_session_factory()
with session_factory() as db:
ontology = SemanticOntologyService(db).parse(
OntologyParseRequest(
query="主要税种介绍",
user_id="pytest",
context_json={"session_type": "knowledge"},
)
)
service = UserAgentService(db)
answer = service._build_fast_knowledge_answer(
UserAgentRequest(
run_id=ontology.run_id,
user_id="pytest",
message="主要税种介绍",
ontology=ontology,
context_json={
"session_type": "knowledge",
"user_input_text": "主要税种介绍",
},
tool_payload={
"result_type": "knowledge_search",
"hits": [
{
"title": "财务基础知识",
"content": (
"资产类 银行存款 企业存放在银行的款项 负债类 应付账款 "
"因购买商品或接受劳务应付的款项 负债类 应交税费 应缴纳的各种税费 "
"第二部分 税务基础知识 三、主要税种介绍 "
"增值税公司为一般纳税人软件服务适用6%税率软件产品销售适用13%税率。 "
"企业所得税税率为25%高新技术企业享受15%优惠税率。 "
"(三)个人所得税:员工工资薪金由公司代扣代缴。 "
"(四)印花税:购销合同、账簿等按规定缴纳。"
),
}
],
},
),
citations=[],
)
assert answer is not None
assert "主要税种介绍包括:增值税、企业所得税、个人所得税、印花税" in answer
assert "软件服务适用6%税率" in answer
assert "软件产品销售适用13%税率" in answer
assert "高新技术企业享受15%优惠税率" in answer
assert "员工工资薪金由公司代扣代缴" in answer
assert "购销合同、账簿等按规定缴纳" in answer
assert "应付账款" not in answer
assert "银行存款" not in answer
def test_user_agent_fast_knowledge_answer_summarizes_financial_statements() -> None:
session_factory = build_session_factory()
with session_factory() as db:
ontology = SemanticOntologyService(db).parse(
OntologyParseRequest(
query="三大财务报表 是什么?",
user_id="pytest",
context_json={"session_type": "knowledge"},
)
)
service = UserAgentService(db)
answer = service._build_fast_knowledge_answer(
UserAgentRequest(
run_id=ontology.run_id,
user_id="pytest",
message="三大财务报表 是什么?",
ontology=ontology,
context_json={
"session_type": "knowledge",
"user_input_text": "三大财务报表 是什么?",
},
tool_payload={
"result_type": "knowledge_search",
"hits": [
{
"title": "财务基础知识",
"content": (
"第三部分 财务报表解读 四、三大财务报表 "
"(一)资产负债表:反映企业在某一特定日期的财务状况。 "
"(二)利润表:反映企业在一定期间的经营成果。 "
"(三)现金流量表:反映企业在一定期间现金和现金等价物的流入和流出。"
),
}
],
},
),
citations=[],
)
assert answer is not None
assert "三大财务报表包括:资产负债表、利润表、现金流量表" in answer
assert "资产负债表:反映企业在某一特定日期的财务状况" in answer
assert "利润表:反映企业在一定期间的经营成果" in answer
assert "现金流量表:反映企业在一定期间现金和现金等价物的流入和流出" in answer
assert "第三部分 财务报表解读" not in answer
def test_user_agent_fast_knowledge_answer_expands_broad_accounting_table() -> None:
session_factory = build_session_factory()
with session_factory() as db:
ontology = SemanticOntologyService(db).parse(
OntologyParseRequest(
query="常用会计科目是什么?",
user_id="pytest",
context_json={"session_type": "knowledge"},
)
)
service = UserAgentService(db)
answer = service._build_fast_knowledge_answer(
UserAgentRequest(
run_id=ontology.run_id,
user_id="pytest",
message="常用会计科目是什么?",
ontology=ontology,
context_json={
"session_type": "knowledge",
"user_input_text": "常用会计科目是什么?",
},
tool_payload={
"result_type": "knowledge_search",
"hits": [
{
"title": "财务基础知识",
"content": (
"二、常用会计科目\n\n"
"| 科目类别 | 科目名称 | 说明 |\n"
"| --- | --- | --- |\n"
"| 资产类 | 库存现金 | 公司持有的现金 |\n"
"| 资产类 | 银行存款 | 存放在银行的资金 |\n"
"| 资产类 | 应收账款 | 因销售商品或提供劳务应收的款项 |\n"
"| 资产类 | 固定资产 | 使用年限超过一年的有形资产 |\n"
"| 负债类 | 应付账款 | 因购买商品或接受劳务应付的款项 |\n"
"| 负债类 | 应交税费 | 应缴纳的各种税费 |\n"
"| 负债类 | 应付职工薪酬 | 应付给职工的工资、福利等 |\n"
"| 损益类 | 主营业务收入 | 主要经营业务产生的收入 |\n"
"| 损益类 | 管理费用 | 为管理生产经营发生的费用 |\n"
"| 损益类 | 销售费用 | 为销售产品发生的费用 |\n"
),
}
],
},
),
citations=[],
)
assert answer is not None
assert "| 科目类别 | 科目名称 | 说明 |" in answer
assert "| 资产类 | 库存现金 | 公司持有的现金 |" in answer
assert "| 负债类 | 应付职工薪酬 | 应付给职工的工资、福利等 |" in answer
assert "| 损益类 | 销售费用 | 为销售产品发生的费用 |" in answer
def test_user_agent_fast_knowledge_answer_renders_relevant_table_preview() -> None:
session_factory = build_session_factory()
with session_factory() as db:
@@ -337,9 +532,65 @@ def test_user_agent_fast_knowledge_answer_renders_relevant_table_preview() -> No
assert answer is not None
assert "| 项目 | 港澳台 | 其他地区 | 国外 |" in answer
assert "| 餐补 | 75 | 55 | 140 |" in answer
assert "餐补的标准为" in answer
assert "## 依据" not in answer
def test_user_agent_fast_knowledge_answer_uses_user_grade_for_table_row() -> None:
session_factory = build_session_factory()
with session_factory() as db:
ontology = SemanticOntologyService(db).parse(
OntologyParseRequest(
query="我的住宿费标准是多少?",
user_id="pytest",
context_json={"session_type": "knowledge"},
)
)
service = UserAgentService(db)
answer = service._build_fast_knowledge_answer(
UserAgentRequest(
run_id=ontology.run_id,
user_id="pytest",
message="我的住宿费标准是多少?",
ontology=ontology,
context_json={
"name": "张三",
"grade": "P5",
"position": "实施经理",
"session_type": "knowledge",
"user_input_text": "我的住宿费标准是多少?",
},
tool_payload={
"result_type": "knowledge_search",
"hits": [
{
"title": "费用报销制度",
"content": (
"# 结构化表格补充\n\n"
"## 国内住宿限额标准\n\n"
"| 职级 | 直辖市/特区/港澳台 | 省会城市 | 其他地区 |\n"
"| --- | --- | --- | --- |\n"
"| 公司领导P8及以上 | 800 | 500 | 400 |\n"
"| 高层经理P7 | 700 | 450 | 400 |\n"
"| 中层经理、基层经理P4P6、外聘专家 | 600 | 400 | 350 |\n"
"| 其他员工 | 500 | 350 | 300 |\n"
),
}
],
},
),
citations=[],
)
assert answer is not None
assert answer.startswith("张三,您好。")
assert "中层经理、基层经理P4P6、外聘专家的标准为" in answer
assert "| 中层经理、基层经理P4P6、外聘专家 | 600 | 400 | 350 |" in answer
assert "| 公司领导P8及以上 | 800 | 500 | 400 |" not in answer
assert "| 高层经理P7 | 700 | 450 | 400 |" not in answer
def test_user_agent_fast_knowledge_answer_notes_missing_location_grounding() -> None:
session_factory = build_session_factory()
with session_factory() as db:
@@ -384,6 +635,7 @@ def test_user_agent_fast_knowledge_answer_notes_missing_location_grounding() ->
assert answer is not None
assert "没有直接写出“北京”对应的地区档位或映射关系" in answer
assert "**说明**" in answer
assert "## 依据" not in answer
@@ -429,7 +681,7 @@ def test_user_agent_fast_knowledge_answer_expands_lead_in_list_items() -> None:
)
assert answer is not None
assert "当前能直接确认的是" in answer
assert "**结论**" in answer
assert "登机牌、高速道路通行记录" in answer
assert "支付记录" in answer
assert "出差审批邮件、短信、微信等" in answer