feat: 添加风险规则及 agent assets 功能增强
This commit is contained in:
@@ -177,16 +177,16 @@ SLOT_LABELS = {
|
||||
}
|
||||
|
||||
DATE_TEXT_PATTERN = re.compile(r"(\d{4}[年/-]\d{1,2}[月/-]\d{1,2}日?)")
|
||||
AMOUNT_TEXT_PATTERN = re.compile(
|
||||
r"(\d+(?:\.\d+)?)\s*(?:万元|万员|万圆|万园|万块|万元整|元整|块钱|块|元|员|圆|园|万)"
|
||||
)
|
||||
AMOUNT_TEXT_PATTERN = re.compile(
|
||||
r"(\d+(?:\.\d+)?)\s*(?:万元|万员|万圆|万园|万块|万元整|元整|块钱|块|元|员|圆|园|万)"
|
||||
)
|
||||
DOCUMENT_AMOUNT_PATTERN = re.compile(
|
||||
r"(?:价税合计|合计金额|费用合计|订单(?:总)?金额|支付(?:金额)?|实付(?:金额)?|实收(?:金额)?|总(?:额|计|价)|票价|金额|车费|消费金额)"
|
||||
r"[::\s¥¥人民币]*([0-9]+(?:[.,][0-9]{1,2})?)"
|
||||
)
|
||||
DOCUMENT_CURRENCY_AMOUNT_PATTERN = re.compile(r"[¥¥]\s*([0-9]+(?:[.,][0-9]{1,2})?)")
|
||||
|
||||
SOURCE_LABELS = {
|
||||
SOURCE_LABELS = {
|
||||
"user_text": "用户描述",
|
||||
"user_form": "用户修改",
|
||||
"ocr": "票据识别",
|
||||
@@ -215,7 +215,7 @@ INFERRED_REASON_LABELS = {
|
||||
"welfare": "员工福利",
|
||||
"other": "其他费用",
|
||||
}
|
||||
SYSTEM_GENERATED_REASON_PREFIXES = (
|
||||
SYSTEM_GENERATED_REASON_PREFIXES = (
|
||||
"我上传了",
|
||||
"请按当前已识别信息",
|
||||
"请把当前上传的票据",
|
||||
@@ -225,20 +225,20 @@ SYSTEM_GENERATED_REASON_PREFIXES = (
|
||||
"我已修改识别信息",
|
||||
"查看报销草稿",
|
||||
"请解释一下当前这笔报销的合规风险和待补充项",
|
||||
)
|
||||
AMOUNT_UNIT_ALIASES = {
|
||||
"员": "元",
|
||||
"圆": "元",
|
||||
"园": "元",
|
||||
"块": "元",
|
||||
"块钱": "元",
|
||||
"元整": "元",
|
||||
"万员": "万元",
|
||||
"万圆": "万元",
|
||||
"万园": "万元",
|
||||
"万块": "万元",
|
||||
"万元整": "万元",
|
||||
}
|
||||
)
|
||||
AMOUNT_UNIT_ALIASES = {
|
||||
"员": "元",
|
||||
"圆": "元",
|
||||
"园": "元",
|
||||
"块": "元",
|
||||
"块钱": "元",
|
||||
"元整": "元",
|
||||
"万员": "万元",
|
||||
"万圆": "万元",
|
||||
"万园": "万元",
|
||||
"万块": "万元",
|
||||
"万元整": "万元",
|
||||
}
|
||||
|
||||
|
||||
class UserAgentService:
|
||||
@@ -1742,7 +1742,7 @@ class UserAgentService:
|
||||
if is_submitted:
|
||||
body = (
|
||||
f"主题:{subject}\n"
|
||||
f"结论:报销单已提交,当前节点为 {approval_stage or '审批中'}。\n"
|
||||
f"结论:报销单已提交,当前节点为 {approval_stage or '审批中'}。\n"
|
||||
"建议:后续可在个人报销列表中跟踪审批进度,必要时再补充说明或附件。\n"
|
||||
f"原始问题:{payload.message}"
|
||||
)
|
||||
@@ -2381,7 +2381,7 @@ class UserAgentService:
|
||||
if review_action == "next_step":
|
||||
if draft_payload is not None and draft_payload.status == "submitted":
|
||||
stage_text = draft_payload.approval_stage or "审批中"
|
||||
return f"报销单 {draft_payload.claim_no or ''} 已提交,当前节点为 {stage_text}。".strip()
|
||||
return f"报销单 {draft_payload.claim_no or ''} 已提交,当前节点为 {stage_text}。".strip()
|
||||
if payload.tool_payload.get("submission_blocked"):
|
||||
return str(payload.tool_payload.get("message") or "").strip() or "当前报销单暂时还不能提交审批。"
|
||||
return (
|
||||
@@ -2947,19 +2947,19 @@ class UserAgentService:
|
||||
"expense_type_code": "",
|
||||
}
|
||||
participants: list[str] = []
|
||||
for item in payload.ontology.entities:
|
||||
if item.type == "employee" and not values["employee_name"]:
|
||||
values["employee_name"] = item.value
|
||||
elif item.type == "customer" and not values["customer"]:
|
||||
values["customer"] = item.value
|
||||
elif item.type == "amount" and item.role != "threshold" and not values["amount"]:
|
||||
normalized_amount = str(item.normalized_value or "").strip()
|
||||
values["amount"] = f"{normalized_amount}元" if normalized_amount else item.value
|
||||
elif item.type == "expense_type" and not values["expense_type_code"]:
|
||||
values["expense_type_code"] = item.normalized_value
|
||||
values["expense_type"] = EXPENSE_TYPE_LABELS.get(
|
||||
item.normalized_value,
|
||||
item.value,
|
||||
for item in payload.ontology.entities:
|
||||
if item.type == "employee" and not values["employee_name"]:
|
||||
values["employee_name"] = item.value
|
||||
elif item.type == "customer" and not values["customer"]:
|
||||
values["customer"] = item.value
|
||||
elif item.type == "amount" and item.role != "threshold" and not values["amount"]:
|
||||
normalized_amount = str(item.normalized_value or "").strip()
|
||||
values["amount"] = f"{normalized_amount}元" if normalized_amount else item.value
|
||||
elif item.type == "expense_type" and not values["expense_type_code"]:
|
||||
values["expense_type_code"] = item.normalized_value
|
||||
values["expense_type"] = EXPENSE_TYPE_LABELS.get(
|
||||
item.normalized_value,
|
||||
item.value,
|
||||
)
|
||||
elif item.type in {"participant", "person"} and item.value.strip():
|
||||
participants.append(item.value.strip())
|
||||
@@ -3189,7 +3189,24 @@ class UserAgentService:
|
||||
evidence="来源于用户修改后的结构化表单。",
|
||||
)
|
||||
|
||||
inferred_reason = self._infer_reason_from_claim_groups(
|
||||
claim_groups=claim_groups,
|
||||
)
|
||||
reason_value = self._resolve_reason_text(self._resolve_reason_source_text(payload))
|
||||
if inferred_reason:
|
||||
return self._build_slot_value(
|
||||
value=inferred_reason,
|
||||
raw_value=reason_value or inferred_reason,
|
||||
normalized_value=inferred_reason,
|
||||
source="ocr",
|
||||
confidence=0.82,
|
||||
evidence=(
|
||||
"系统已根据票据识别结果预置场景类型;原始描述仍保留为补充说明。"
|
||||
if reason_value
|
||||
else "系统已根据票据识别场景补全通用事由,若需更具体说明可继续修改。"
|
||||
),
|
||||
)
|
||||
|
||||
if reason_value:
|
||||
return self._build_slot_value(
|
||||
value=reason_value,
|
||||
@@ -3199,19 +3216,6 @@ class UserAgentService:
|
||||
confidence=0.76,
|
||||
evidence="系统从用户原始描述中提取了本次费用事由,建议继续核对。",
|
||||
)
|
||||
|
||||
inferred_reason = self._infer_reason_from_claim_groups(
|
||||
claim_groups=claim_groups,
|
||||
)
|
||||
if inferred_reason:
|
||||
return self._build_slot_value(
|
||||
value=inferred_reason,
|
||||
raw_value=inferred_reason,
|
||||
normalized_value=inferred_reason,
|
||||
source="ocr",
|
||||
confidence=0.68,
|
||||
evidence="系统已根据票据识别场景补全通用事由,若需更具体说明可继续修改。",
|
||||
)
|
||||
return self._build_slot_value()
|
||||
|
||||
def _build_amount_slot(
|
||||
@@ -3358,17 +3362,17 @@ class UserAgentService:
|
||||
return self._build_slot_value()
|
||||
|
||||
@staticmethod
|
||||
def _normalize_amount_text(value: str) -> str:
|
||||
cleaned = str(value or "").strip()
|
||||
if not cleaned:
|
||||
return ""
|
||||
for alias, canonical in sorted(AMOUNT_UNIT_ALIASES.items(), key=lambda item: len(item[0]), reverse=True):
|
||||
cleaned = cleaned.replace(alias, canonical)
|
||||
match = AMOUNT_TEXT_PATTERN.search(cleaned)
|
||||
if not match:
|
||||
return cleaned
|
||||
number = float(match.group(1))
|
||||
return f"{number:.2f}元"
|
||||
def _normalize_amount_text(value: str) -> str:
|
||||
cleaned = str(value or "").strip()
|
||||
if not cleaned:
|
||||
return ""
|
||||
for alias, canonical in sorted(AMOUNT_UNIT_ALIASES.items(), key=lambda item: len(item[0]), reverse=True):
|
||||
cleaned = cleaned.replace(alias, canonical)
|
||||
match = AMOUNT_TEXT_PATTERN.search(cleaned)
|
||||
if not match:
|
||||
return cleaned
|
||||
number = float(match.group(1))
|
||||
return f"{number:.2f}元"
|
||||
|
||||
@staticmethod
|
||||
def _normalize_expense_type_input(value: str) -> tuple[str, str]:
|
||||
|
||||
Reference in New Issue
Block a user