148 lines
7.6 KiB
Python
148 lines
7.6 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import json
|
|||
|
|
from typing import Any
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_risk_rule_compiler_messages(
|
|||
|
|
*,
|
|||
|
|
domain: str,
|
|||
|
|
domain_label: str,
|
|||
|
|
expense_category: str | None,
|
|||
|
|
expense_category_label: str,
|
|||
|
|
natural_language: str,
|
|||
|
|
available_fields: list[dict[str, Any]],
|
|||
|
|
) -> list[dict[str, str]]:
|
|||
|
|
"""构造自然语言规则编译提示词。
|
|||
|
|
|
|||
|
|
大模型只负责把业务语言拆成“语义计划”,后端会校验字段、操作符和模板。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
schema = {
|
|||
|
|
"name": "规则名称,短句",
|
|||
|
|
"description": "面向业务和审核人员的说明,不要写实现细节",
|
|||
|
|
"template_key": "field_required_v1 | field_compare_v1 | keyword_match_v1 | composite_rule_v1",
|
|||
|
|
"semantic_type": (
|
|||
|
|
"可选。可用稳定英文短语描述语义类型;"
|
|||
|
|
"已知差旅票据城市/路线一致性可使用 travel_route_city_consistency,其他规则按业务含义命名"
|
|||
|
|
),
|
|||
|
|
"field_keys": ["只能选择 available_fields.key"],
|
|||
|
|
"condition_summary": "用公式化语言描述判断依据,不要写'是否出现风险关键词'",
|
|||
|
|
"rule_ir": {
|
|||
|
|
"facts": "事实变量数组,例如 A=票据事实、B=业务申报事实、E=例外说明",
|
|||
|
|
"conditions": "条件数组,必须能被人解释",
|
|||
|
|
"hit_logic": "命中逻辑,例如 D AND ((A NOT_IN B) OR DATE_OUTSIDE(T,R)) AND NOT EXCEPTION(E)",
|
|||
|
|
},
|
|||
|
|
"conditions": [
|
|||
|
|
{
|
|||
|
|
"id": "稳定英文标识",
|
|||
|
|
"operator": (
|
|||
|
|
"exists_any | exists_all | in_scope | not_in_scope | overlap | "
|
|||
|
|
"not_overlap | date_outside_range | contains_any | not_contains_any"
|
|||
|
|
),
|
|||
|
|
"fields": ["exists/contains 类操作使用"],
|
|||
|
|
"left_fields": ["集合比较左侧字段"],
|
|||
|
|
"right_fields": ["集合比较右侧字段"],
|
|||
|
|
"date_fields": ["日期字段"],
|
|||
|
|
"range_start_fields": ["日期范围开始字段"],
|
|||
|
|
"range_end_fields": ["日期范围结束字段"],
|
|||
|
|
"keywords": ["例外或风险词"],
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"hit_logic": {"all": ["condition_id", {"any": ["condition_id"]}]},
|
|||
|
|
"formula": "可执行逻辑公式,字段使用事实变量表达",
|
|||
|
|
"message_template": "命中后的业务提示",
|
|||
|
|
"unsupported_fields": ["用户规则提到但 available_fields 中暂时没有的字段"],
|
|||
|
|
"keywords": "仅 keyword_match_v1 使用,且必须是真正风险词,不得把例外说明词当风险词",
|
|||
|
|
"exception_keywords": "例外说明词,例如绕行、跨城办事、临时改签",
|
|||
|
|
"risk_scoring_evidence": {
|
|||
|
|
"impact_level": "low | medium | high | critical",
|
|||
|
|
"violation_certainty": "low | medium | high | critical",
|
|||
|
|
"evidence_strength": "low | medium | high | critical",
|
|||
|
|
"exception_dependence": "low | medium | high | critical",
|
|||
|
|
"control_action": "remind | supplement | manual_review | return | block",
|
|||
|
|
"business_sensitivity": "low | medium | high | critical",
|
|||
|
|
"reason": "用一句话说明这些评分证据来自哪些业务语义",
|
|||
|
|
},
|
|||
|
|
"flow": {
|
|||
|
|
"start": "流程起点",
|
|||
|
|
"evidence": "读取哪些事实",
|
|||
|
|
"decision": "判断公式或分支条件",
|
|||
|
|
"pass": "未命中时说明",
|
|||
|
|
"fail": "命中时说明",
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
guardrails = [
|
|||
|
|
"只能输出 JSON 对象,不能输出 Markdown 或解释。",
|
|||
|
|
"字段必须来自 available_fields,不能编造字段。",
|
|||
|
|
"多步骤规则要使用 composite_rule_v1:先抽取事实变量,再写 conditions 和 hit_logic,不要压扁成单个关键词判断。",
|
|||
|
|
"城市/地点/路线一致性必须用 field_compare_v1 或 semantic_type=travel_route_city_consistency。",
|
|||
|
|
"涉及多个字段、日期范围、金额范围、集合关系、例外说明的规则必须使用 composite_rule_v1。",
|
|||
|
|
"日期字段必须区分事实日期、票据日期和业务期间;如果只能拿到替代字段,要在 rule_ir 中说明这是 fallback evidence。",
|
|||
|
|
"composite_rule_v1 只能使用受控 operator:exists_any、exists_all、in_scope、not_in_scope、overlap、not_overlap、date_outside_range、contains_any、not_contains_any。",
|
|||
|
|
"差旅路线规则中,交通票行程城市和住宿发票城市属于附件城市集合。",
|
|||
|
|
"申报目的地和明细发生地点属于申报行程城市集合。",
|
|||
|
|
"员工常驻地/出发地如可用,属于合理起终点集合,不等同于申报目的地。",
|
|||
|
|
"绕行、跨城办事、临时改签是例外说明证据,不是风险命中关键词。",
|
|||
|
|
"如果票据路线出现申报目的地和常驻地之外的额外城市,应描述为中途周转/绕行异常。",
|
|||
|
|
"keyword_match_v1 只用于品名、摘要、票据全文中出现明确风险词的规则。",
|
|||
|
|
"不要直接指定 risk_level 或 risk_score;只输出 risk_scoring_evidence,后端会按固定评分模型计算 0-100 分和风险等级。",
|
|||
|
|
"评分证据必须围绕六个指标:业务影响、违规确定性、证据强度、例外/规避空间、处置强度、场景敏感度。",
|
|||
|
|
]
|
|||
|
|
examples = [
|
|||
|
|
{
|
|||
|
|
"user_rule": (
|
|||
|
|
"差旅报销时,交通票或住宿票据中的城市均无法与申报目的地、"
|
|||
|
|
"明细地点形成一致关系,且事由未说明绕行或改签原因,则高风险。"
|
|||
|
|
),
|
|||
|
|
"expected": {
|
|||
|
|
"template_key": "field_compare_v1",
|
|||
|
|
"semantic_type": "travel_route_city_consistency",
|
|||
|
|
"field_keys": [
|
|||
|
|
"attachment.route_cities",
|
|||
|
|
"attachment.hotel_city",
|
|||
|
|
"claim.location",
|
|||
|
|
"item.item_location",
|
|||
|
|
"employee.location",
|
|||
|
|
"claim.reason",
|
|||
|
|
"item.item_reason",
|
|||
|
|
],
|
|||
|
|
"condition_summary": (
|
|||
|
|
"A=交通票行程城市∪住宿发票城市,B=申报目的地∪明细发生地点,"
|
|||
|
|
"C=员工常驻地/合理起终点;A与B无交集且无合理说明,或A中出现B∪C之外城市时命中。"
|
|||
|
|
),
|
|||
|
|
"keywords": [],
|
|||
|
|
"exception_keywords": ["绕行", "跨城办事", "临时改签"],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
return [
|
|||
|
|
{
|
|||
|
|
"role": "system",
|
|||
|
|
"content": "\n".join(
|
|||
|
|
[
|
|||
|
|
"你是 X-Financial 风险规则语义编译器。",
|
|||
|
|
"你的任务是把自然语言规则转换成可校验 JSON 语义计划。",
|
|||
|
|
"后端执行器只接受受控模板和受控字段,所以你必须严格遵守以下约束:",
|
|||
|
|
*[f"- {item}" for item in guardrails],
|
|||
|
|
]
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"role": "user",
|
|||
|
|
"content": json.dumps(
|
|||
|
|
{
|
|||
|
|
"business_domain": domain,
|
|||
|
|
"business_domain_label": domain_label,
|
|||
|
|
"expense_category": expense_category,
|
|||
|
|
"expense_category_label": expense_category_label,
|
|||
|
|
"natural_language": natural_language,
|
|||
|
|
"available_fields": available_fields,
|
|||
|
|
"required_json_shape": schema,
|
|||
|
|
"examples": examples,
|
|||
|
|
},
|
|||
|
|
ensure_ascii=False,
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
]
|