feat: 增强 agent_assets 功能,支持更多资产操作
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
from __future__ import annotations
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from datetime import UTC, date, datetime
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
import json
|
||||
from datetime import UTC, date, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import inspect, select, text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.agent_enums import (
|
||||
@@ -26,16 +28,23 @@ from app.db.session import get_session_factory
|
||||
from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetVersion
|
||||
from app.models.agent_run import AgentRun, AgentToolCall, SemanticParseLog
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.models.financial_record import (
|
||||
AccountsPayableRecord,
|
||||
AccountsReceivableRecord,
|
||||
ExpenseClaim,
|
||||
ExpenseClaimItem,
|
||||
)
|
||||
from app.services.expense_rule_runtime import (
|
||||
build_scene_submission_standard_markdown,
|
||||
build_travel_risk_control_standard_markdown,
|
||||
)
|
||||
from app.models.financial_record import (
|
||||
AccountsPayableRecord,
|
||||
AccountsReceivableRecord,
|
||||
ExpenseClaim,
|
||||
ExpenseClaimItem,
|
||||
)
|
||||
from app.services.agent_asset_spreadsheet import (
|
||||
AgentAssetSpreadsheetManager,
|
||||
COMPANY_TRAVEL_EXPENSE_RULE_CODE,
|
||||
COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,
|
||||
FINANCE_RULES_LIBRARY,
|
||||
RuleSpreadsheetMeta,
|
||||
)
|
||||
from app.services.expense_rule_runtime import (
|
||||
build_scene_submission_standard_markdown,
|
||||
build_travel_risk_control_standard_markdown,
|
||||
)
|
||||
|
||||
logger = get_logger("app.services.agent_foundation")
|
||||
|
||||
@@ -77,7 +86,8 @@ LEGACY_RULE_CODES = (
|
||||
"rule.ap.payment_dual_review",
|
||||
)
|
||||
|
||||
ATTACHMENT_RULE_ASSET_CODE = "rule.expense.attachment_submission_requirements"
|
||||
ATTACHMENT_RULE_ASSET_CODE = "rule.expense.attachment_submission_requirements"
|
||||
COMPANY_TRAVEL_RULE_VERSION = "v1.0.0"
|
||||
|
||||
ATTACHMENT_RULE_RUNTIME_CONFIG = {
|
||||
"kind": "policy_rule_draft",
|
||||
@@ -156,10 +166,11 @@ class AgentFoundationService:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
|
||||
def ensure_foundation_ready(self) -> None:
|
||||
try:
|
||||
Base.metadata.create_all(bind=self.db.get_bind())
|
||||
self._seed_agent_assets()
|
||||
def ensure_foundation_ready(self) -> None:
|
||||
try:
|
||||
Base.metadata.create_all(bind=self.db.get_bind())
|
||||
self._ensure_agent_asset_schema()
|
||||
self._seed_agent_assets()
|
||||
self._sync_demo_financial_records()
|
||||
self._seed_runs_and_logs()
|
||||
self.db.commit()
|
||||
@@ -174,7 +185,7 @@ class AgentFoundationService:
|
||||
return
|
||||
self._purge_demo_financial_records()
|
||||
|
||||
def _seed_agent_assets(self) -> None:
|
||||
def _seed_agent_assets(self) -> None:
|
||||
existing_codes = set(self.db.scalars(select(AgentAsset.code)).all())
|
||||
if existing_codes:
|
||||
self._top_up_agent_assets(existing_codes)
|
||||
@@ -189,8 +200,10 @@ class AgentFoundationService:
|
||||
scenario_json=["expense", "risk_check", "attachment_policy", "invoice_anomaly"],
|
||||
owner="财务制度管理组",
|
||||
reviewer="高嘉禾",
|
||||
status=AgentAssetStatus.REVIEW.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.REVIEW.value,
|
||||
current_version="v1.0.0",
|
||||
published_version=None,
|
||||
working_version="v1.0.0",
|
||||
config_json={
|
||||
"severity": "high",
|
||||
"enabled": False,
|
||||
@@ -209,8 +222,10 @@ class AgentFoundationService:
|
||||
scenario_json=["expense", "risk_check", "scene_policy", "attachment_policy"],
|
||||
owner="费用运营组",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={
|
||||
"severity": "high",
|
||||
"enabled": True,
|
||||
@@ -218,17 +233,19 @@ class AgentFoundationService:
|
||||
"rule_template_label": "系统内置场景矩阵规则",
|
||||
},
|
||||
)
|
||||
travel_policy_rule = AgentAsset(
|
||||
asset_type=AgentAssetType.RULE.value,
|
||||
code="rule.expense.travel_risk_control_standard",
|
||||
name="差旅报销风险管控制度",
|
||||
travel_policy_rule = AgentAsset(
|
||||
asset_type=AgentAssetType.RULE.value,
|
||||
code="rule.expense.travel_risk_control_standard",
|
||||
name="差旅报销风险管控制度",
|
||||
description="统一定义差旅报销的行程闭环、酒店地点一致性、职级差标和风险处置口径。",
|
||||
domain=AgentAssetDomain.EXPENSE.value,
|
||||
scenario_json=["expense", "risk_check", "travel_policy", "travel_standard"],
|
||||
owner="风控与审计部",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.1.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.1.0",
|
||||
published_version="v1.1.0",
|
||||
working_version="v1.1.0",
|
||||
config_json={
|
||||
"severity": "high",
|
||||
"enabled": True,
|
||||
@@ -236,21 +253,45 @@ class AgentFoundationService:
|
||||
"warning_on_medium_risk": True,
|
||||
"source_doc": "document/development/risks/travel-risk-control-standard.md",
|
||||
"runtime_kind": "travel_policy",
|
||||
"rule_template_key": "travel_standard_v1",
|
||||
"rule_template_label": "差旅标准模板",
|
||||
},
|
||||
)
|
||||
skill_expense_asset = AgentAsset(
|
||||
asset_type=AgentAssetType.SKILL.value,
|
||||
code="skill.expense.summary_lookup",
|
||||
"rule_template_key": "travel_standard_v1",
|
||||
"rule_template_label": "差旅标准模板",
|
||||
},
|
||||
)
|
||||
company_travel_rule = AgentAsset(
|
||||
asset_type=AgentAssetType.RULE.value,
|
||||
code=COMPANY_TRAVEL_EXPENSE_RULE_CODE,
|
||||
name="公司差旅费报销规则",
|
||||
description="通过 Excel 明细表维护差旅费报销标准、票据要求和审批口径。",
|
||||
domain=AgentAssetDomain.EXPENSE.value,
|
||||
scenario_json=["expense", "travel_policy", "travel_standard"],
|
||||
owner="财务制度管理组",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
published_version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
working_version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
config_json={
|
||||
"severity": "medium",
|
||||
"enabled": True,
|
||||
"tag": "财务规则",
|
||||
"detail_mode": "spreadsheet",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_template_label": "差旅报销 Excel 模板",
|
||||
},
|
||||
)
|
||||
skill_expense_asset = AgentAsset(
|
||||
asset_type=AgentAssetType.SKILL.value,
|
||||
code="skill.expense.summary_lookup",
|
||||
name="报销汇总查询技能",
|
||||
description="根据时间、员工和部门汇总报销金额与单据数量。",
|
||||
domain=AgentAssetDomain.EXPENSE.value,
|
||||
scenario_json=["expense", "query", "summary"],
|
||||
owner="平台研发组",
|
||||
reviewer="陈硕",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"input_schema": ["time_range", "employee", "department"]},
|
||||
)
|
||||
skill_ar_asset = AgentAsset(
|
||||
@@ -262,8 +303,10 @@ class AgentFoundationService:
|
||||
scenario_json=["accounts_receivable", "query", "aging_summary"],
|
||||
owner="平台研发组",
|
||||
reviewer="陈硕",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"input_schema": ["customer", "aging_bucket", "status"]},
|
||||
)
|
||||
invoice_mcp_asset = AgentAsset(
|
||||
@@ -275,8 +318,10 @@ class AgentFoundationService:
|
||||
scenario_json=["expense", "invoice_validation"],
|
||||
owner="平台研发组",
|
||||
reviewer="周悦宁",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"endpoint": "mock://invoice/verify", "timeout_ms": 1200},
|
||||
)
|
||||
ledger_mcp_asset = AgentAsset(
|
||||
@@ -288,8 +333,10 @@ class AgentFoundationService:
|
||||
scenario_json=["expense", "accounts_receivable", "accounts_payable"],
|
||||
owner="平台研发组",
|
||||
reviewer="周悦宁",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"endpoint": "mock://ledger/snapshot", "timeout_ms": 1500},
|
||||
)
|
||||
task_asset = AgentAsset(
|
||||
@@ -301,8 +348,10 @@ class AgentFoundationService:
|
||||
scenario_json=["schedule", "risk_check"],
|
||||
owner="风控与审计部",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"cron": "0 9 * * *", "agent": AgentName.HERMES.value},
|
||||
)
|
||||
ar_summary_task = AgentAsset(
|
||||
@@ -314,8 +363,10 @@ class AgentFoundationService:
|
||||
scenario_json=["schedule", "accounts_receivable", "summary"],
|
||||
owner="风控与审计部",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"cron": "0 10 * * 1", "agent": AgentName.HERMES.value},
|
||||
)
|
||||
rule_digest_task = AgentAsset(
|
||||
@@ -327,8 +378,10 @@ class AgentFoundationService:
|
||||
scenario_json=["schedule", "rule_center", "review_digest"],
|
||||
owner="风控与审计部",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"cron": "0 18 * * *", "agent": AgentName.HERMES.value},
|
||||
)
|
||||
knowledge_index_task = AgentAsset(
|
||||
@@ -340,30 +393,39 @@ class AgentFoundationService:
|
||||
scenario_json=["schedule", "knowledge", "rule_center"],
|
||||
owner="财务制度管理组",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version="v1.0.0",
|
||||
published_version="v1.0.0",
|
||||
working_version="v1.0.0",
|
||||
config_json={"cron": "0 0 * * *", "agent": AgentName.HERMES.value},
|
||||
)
|
||||
|
||||
self.db.add_all(
|
||||
[
|
||||
attachment_rule,
|
||||
scene_submission_rule,
|
||||
travel_policy_rule,
|
||||
skill_expense_asset,
|
||||
skill_ar_asset,
|
||||
invoice_mcp_asset,
|
||||
attachment_rule,
|
||||
scene_submission_rule,
|
||||
travel_policy_rule,
|
||||
company_travel_rule,
|
||||
skill_expense_asset,
|
||||
skill_ar_asset,
|
||||
invoice_mcp_asset,
|
||||
ledger_mcp_asset,
|
||||
task_asset,
|
||||
ar_summary_task,
|
||||
rule_digest_task,
|
||||
knowledge_index_task,
|
||||
]
|
||||
)
|
||||
self.db.flush()
|
||||
|
||||
self.db.add_all(
|
||||
[
|
||||
)
|
||||
self.db.flush()
|
||||
|
||||
company_travel_rule_meta = self._ensure_company_travel_rule_spreadsheet_seed(
|
||||
company_travel_rule,
|
||||
version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
actor_name="系统初始化",
|
||||
)
|
||||
|
||||
self.db.add_all(
|
||||
[
|
||||
AgentAssetVersion(
|
||||
asset=attachment_rule,
|
||||
version="v0.9.0",
|
||||
@@ -402,17 +464,29 @@ class AgentFoundationService:
|
||||
change_note="首版差旅制度执行规则,覆盖行程闭环与基础差标校验。",
|
||||
created_by="系统初始化",
|
||||
),
|
||||
AgentAssetVersion(
|
||||
asset=travel_policy_rule,
|
||||
version="v1.1.0",
|
||||
content=self._travel_risk_control_standard_markdown(version="v1.1.0"),
|
||||
content_type=AgentAssetContentType.MARKDOWN.value,
|
||||
change_note="补充可执行规则块,供审核引擎直接消费差旅制度标准。",
|
||||
created_by="系统初始化",
|
||||
),
|
||||
AgentAssetVersion(
|
||||
asset=skill_expense_asset,
|
||||
version="v1.0.0",
|
||||
AgentAssetVersion(
|
||||
asset=travel_policy_rule,
|
||||
version="v1.1.0",
|
||||
content=self._travel_risk_control_standard_markdown(version="v1.1.0"),
|
||||
content_type=AgentAssetContentType.MARKDOWN.value,
|
||||
change_note="补充可执行规则块,供审核引擎直接消费差旅制度标准。",
|
||||
created_by="系统初始化",
|
||||
),
|
||||
AgentAssetVersion(
|
||||
asset=company_travel_rule,
|
||||
version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
content=AgentAssetSpreadsheetManager.build_version_markdown(
|
||||
rule_name=company_travel_rule.name,
|
||||
version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
metadata=company_travel_rule_meta,
|
||||
),
|
||||
content_type=AgentAssetContentType.MARKDOWN.value,
|
||||
change_note="初始化差旅费报销 Excel 规则表。",
|
||||
created_by="系统初始化",
|
||||
),
|
||||
AgentAssetVersion(
|
||||
asset=skill_expense_asset,
|
||||
version="v1.0.0",
|
||||
content=self._json_content(
|
||||
{
|
||||
"inputs": ["time_range", "employee", "department"],
|
||||
@@ -545,16 +619,24 @@ class AgentFoundationService:
|
||||
review_note="可作为报销场景统一审核标准正式执行。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
),
|
||||
AgentAssetReview(
|
||||
asset=travel_policy_rule,
|
||||
version="v1.1.0",
|
||||
reviewer="顾承宇",
|
||||
review_status=AgentReviewStatus.APPROVED.value,
|
||||
review_note="制度口径已确认,并已补充可执行配置供审核引擎读取。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
),
|
||||
]
|
||||
)
|
||||
AgentAssetReview(
|
||||
asset=travel_policy_rule,
|
||||
version="v1.1.0",
|
||||
reviewer="顾承宇",
|
||||
review_status=AgentReviewStatus.APPROVED.value,
|
||||
review_note="制度口径已确认,并已补充可执行配置供审核引擎读取。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
),
|
||||
AgentAssetReview(
|
||||
asset=company_travel_rule,
|
||||
version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
reviewer="顾承宇",
|
||||
review_status=AgentReviewStatus.APPROVED.value,
|
||||
review_note="首版 Excel 规则表已确认,可作为财务规则使用。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def _seed_financial_records(self) -> None:
|
||||
if self.db.scalar(select(ExpenseClaim.id).limit(1)) is not None:
|
||||
@@ -913,9 +995,12 @@ class AgentFoundationService:
|
||||
scene_submission_rule = self.db.scalar(
|
||||
select(AgentAsset).where(AgentAsset.code == "rule.expense.scene_submission_standard")
|
||||
)
|
||||
travel_policy_rule = self.db.scalar(
|
||||
select(AgentAsset).where(AgentAsset.code == "rule.expense.travel_risk_control_standard")
|
||||
)
|
||||
travel_policy_rule = self.db.scalar(
|
||||
select(AgentAsset).where(AgentAsset.code == "rule.expense.travel_risk_control_standard")
|
||||
)
|
||||
company_travel_rule = self.db.scalar(
|
||||
select(AgentAsset).where(AgentAsset.code == COMPANY_TRAVEL_EXPENSE_RULE_CODE)
|
||||
)
|
||||
|
||||
if ATTACHMENT_RULE_ASSET_CODE not in existing_codes:
|
||||
attachment_rule = self._create_seed_asset(
|
||||
@@ -939,9 +1024,12 @@ class AgentFoundationService:
|
||||
},
|
||||
)
|
||||
|
||||
if attachment_rule is not None:
|
||||
attachment_rule.current_version = "v1.0.0"
|
||||
attachment_rule.status = AgentAssetStatus.REVIEW.value
|
||||
if attachment_rule is not None:
|
||||
if not str(attachment_rule.current_version or "").strip():
|
||||
attachment_rule.current_version = "v1.0.0"
|
||||
if not str(attachment_rule.working_version or "").strip():
|
||||
attachment_rule.working_version = attachment_rule.current_version
|
||||
attachment_rule.status = attachment_rule.status or AgentAssetStatus.REVIEW.value
|
||||
attachment_rule.description = "统一定义报销提交时的附件数量、票据类型和补件处理口径,作为上线前待审核规则。"
|
||||
attachment_rule.config_json = {
|
||||
"severity": "high",
|
||||
@@ -1002,9 +1090,14 @@ class AgentFoundationService:
|
||||
},
|
||||
)
|
||||
|
||||
if scene_submission_rule is not None:
|
||||
scene_submission_rule.current_version = "v1.0.0"
|
||||
scene_submission_rule.status = AgentAssetStatus.ACTIVE.value
|
||||
if scene_submission_rule is not None:
|
||||
if not str(scene_submission_rule.current_version or "").strip():
|
||||
scene_submission_rule.current_version = "v1.0.0"
|
||||
if not str(scene_submission_rule.working_version or "").strip():
|
||||
scene_submission_rule.working_version = scene_submission_rule.current_version
|
||||
if not str(scene_submission_rule.published_version or "").strip():
|
||||
scene_submission_rule.published_version = scene_submission_rule.current_version
|
||||
scene_submission_rule.status = scene_submission_rule.status or AgentAssetStatus.ACTIVE.value
|
||||
scene_submission_rule.description = "统一定义各报销场景的必填字段、附件类型要求和金额阈值。"
|
||||
scene_submission_rule.config_json = {
|
||||
"severity": "high",
|
||||
@@ -1053,9 +1146,14 @@ class AgentFoundationService:
|
||||
},
|
||||
)
|
||||
|
||||
if travel_policy_rule is not None:
|
||||
travel_policy_rule.current_version = "v1.1.0"
|
||||
travel_policy_rule.status = AgentAssetStatus.ACTIVE.value
|
||||
if travel_policy_rule is not None:
|
||||
if not str(travel_policy_rule.current_version or "").strip():
|
||||
travel_policy_rule.current_version = "v1.1.0"
|
||||
if not str(travel_policy_rule.working_version or "").strip():
|
||||
travel_policy_rule.working_version = travel_policy_rule.current_version
|
||||
if not str(travel_policy_rule.published_version or "").strip():
|
||||
travel_policy_rule.published_version = travel_policy_rule.current_version
|
||||
travel_policy_rule.status = travel_policy_rule.status or AgentAssetStatus.ACTIVE.value
|
||||
travel_policy_rule.config_json = {
|
||||
"severity": "high",
|
||||
"enabled": True,
|
||||
@@ -1087,12 +1185,79 @@ class AgentFoundationService:
|
||||
version="v1.1.0",
|
||||
reviewer="顾承宇",
|
||||
review_status=AgentReviewStatus.APPROVED.value,
|
||||
review_note="制度口径已确认,并已补充可执行配置供审核引擎读取。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
)
|
||||
|
||||
if "skill.ar.aging_summary" not in existing_codes:
|
||||
asset = self._create_seed_asset(
|
||||
review_note="制度口径已确认,并已补充可执行配置供审核引擎读取。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
)
|
||||
|
||||
if COMPANY_TRAVEL_EXPENSE_RULE_CODE not in existing_codes:
|
||||
company_travel_rule = self._create_seed_asset(
|
||||
asset_type=AgentAssetType.RULE.value,
|
||||
code=COMPANY_TRAVEL_EXPENSE_RULE_CODE,
|
||||
name="公司差旅费报销规则",
|
||||
description="通过 Excel 明细表维护差旅费报销标准、票据要求和审批口径。",
|
||||
domain=AgentAssetDomain.EXPENSE.value,
|
||||
scenario_json=["expense", "travel_policy", "travel_standard"],
|
||||
owner="财务制度管理组",
|
||||
reviewer="顾承宇",
|
||||
status=AgentAssetStatus.ACTIVE.value,
|
||||
current_version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
config_json={
|
||||
"severity": "medium",
|
||||
"enabled": True,
|
||||
"tag": "财务规则",
|
||||
"detail_mode": "spreadsheet",
|
||||
"rule_template_label": "差旅报销 Excel 模板",
|
||||
},
|
||||
)
|
||||
|
||||
if company_travel_rule is not None:
|
||||
if not str(company_travel_rule.current_version or "").strip():
|
||||
company_travel_rule.current_version = COMPANY_TRAVEL_RULE_VERSION
|
||||
if not str(company_travel_rule.working_version or "").strip():
|
||||
company_travel_rule.working_version = company_travel_rule.current_version
|
||||
if not str(company_travel_rule.published_version or "").strip():
|
||||
company_travel_rule.published_version = company_travel_rule.current_version
|
||||
if not str(company_travel_rule.status or "").strip():
|
||||
company_travel_rule.status = AgentAssetStatus.ACTIVE.value
|
||||
company_travel_rule.description = "通过 Excel 明细表维护差旅费报销标准、票据要求和审批口径。"
|
||||
company_travel_rule.config_json = {
|
||||
**(company_travel_rule.config_json or {}),
|
||||
"severity": "medium",
|
||||
"enabled": True,
|
||||
"tag": "财务规则",
|
||||
"detail_mode": "spreadsheet",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_template_label": "差旅报销 Excel 模板",
|
||||
}
|
||||
company_travel_rule_meta = self._ensure_company_travel_rule_spreadsheet_seed(
|
||||
company_travel_rule,
|
||||
version=str(company_travel_rule.current_version or COMPANY_TRAVEL_RULE_VERSION),
|
||||
actor_name="系统初始化",
|
||||
)
|
||||
self._ensure_asset_version(
|
||||
company_travel_rule,
|
||||
version=str(company_travel_rule.current_version or COMPANY_TRAVEL_RULE_VERSION),
|
||||
content=AgentAssetSpreadsheetManager.build_version_markdown(
|
||||
rule_name=company_travel_rule.name,
|
||||
version=str(company_travel_rule.current_version or COMPANY_TRAVEL_RULE_VERSION),
|
||||
metadata=company_travel_rule_meta,
|
||||
),
|
||||
content_type=AgentAssetContentType.MARKDOWN.value,
|
||||
change_note="初始化差旅费报销 Excel 规则表。",
|
||||
created_by="系统初始化",
|
||||
)
|
||||
if str(company_travel_rule.current_version or "").strip() == COMPANY_TRAVEL_RULE_VERSION:
|
||||
self._ensure_asset_review(
|
||||
company_travel_rule,
|
||||
version=COMPANY_TRAVEL_RULE_VERSION,
|
||||
reviewer="顾承宇",
|
||||
review_status=AgentReviewStatus.APPROVED.value,
|
||||
review_note="首版 Excel 规则表已确认,可作为财务规则使用。",
|
||||
reviewed_at=datetime.now(UTC),
|
||||
)
|
||||
|
||||
if "skill.ar.aging_summary" not in existing_codes:
|
||||
asset = self._create_seed_asset(
|
||||
asset_type=AgentAssetType.SKILL.value,
|
||||
code="skill.ar.aging_summary",
|
||||
name="应收账龄汇总技能",
|
||||
@@ -1207,10 +1372,10 @@ class AgentFoundationService:
|
||||
created_by="系统初始化",
|
||||
)
|
||||
|
||||
if "task.hermes.knowledge_index_sync" not in existing_codes:
|
||||
asset = self._create_seed_asset(
|
||||
asset_type=AgentAssetType.TASK.value,
|
||||
code="task.hermes.knowledge_index_sync",
|
||||
if "task.hermes.knowledge_index_sync" not in existing_codes:
|
||||
asset = self._create_seed_asset(
|
||||
asset_type=AgentAssetType.TASK.value,
|
||||
code="task.hermes.knowledge_index_sync",
|
||||
name="Hermes ??????",
|
||||
description="?????????? LightRAG ???????",
|
||||
domain=AgentAssetDomain.SYSTEM.value,
|
||||
@@ -1234,14 +1399,112 @@ class AgentFoundationService:
|
||||
}
|
||||
),
|
||||
content_type=AgentAssetContentType.JSON.value,
|
||||
change_note="初始化制度知识与规则草稿形成任务。",
|
||||
created_by="系统初始化",
|
||||
)
|
||||
|
||||
def _create_seed_asset(
|
||||
self,
|
||||
*,
|
||||
asset_type: str,
|
||||
change_note="初始化制度知识与规则草稿形成任务。",
|
||||
created_by="系统初始化",
|
||||
)
|
||||
|
||||
def _ensure_company_travel_rule_spreadsheet_seed(
|
||||
self,
|
||||
asset: AgentAsset,
|
||||
*,
|
||||
version: str,
|
||||
actor_name: str,
|
||||
):
|
||||
manager = AgentAssetSpreadsheetManager()
|
||||
manager.ensure_rule_library_dirs()
|
||||
live_document = manager.store_rule_library_spreadsheet(
|
||||
library=FINANCE_RULES_LIBRARY,
|
||||
file_name=COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,
|
||||
content=self._read_or_build_company_travel_rule_file(manager),
|
||||
actor_name=actor_name,
|
||||
source="rule-library",
|
||||
)
|
||||
existing_document = (
|
||||
asset.config_json.get("rule_document")
|
||||
if isinstance(asset.config_json, dict)
|
||||
else None
|
||||
)
|
||||
storage_key = (
|
||||
str(existing_document.get("storage_key") or "").strip()
|
||||
if isinstance(existing_document, dict)
|
||||
else ""
|
||||
)
|
||||
if storage_key:
|
||||
try:
|
||||
existing_path = manager.resolve_storage_path(storage_key)
|
||||
except FileNotFoundError:
|
||||
existing_path = None
|
||||
if existing_path is not None and existing_path.exists():
|
||||
metadata = RuleSpreadsheetMeta(
|
||||
file_name=str(existing_document.get("file_name") or COMPANY_TRAVEL_EXPENSE_RULE_FILENAME),
|
||||
storage_key=storage_key,
|
||||
mime_type=str(existing_document.get("mime_type") or "").strip()
|
||||
or "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
size_bytes=int(existing_document.get("size_bytes") or existing_path.stat().st_size),
|
||||
checksum=hashlib.sha256(existing_path.read_bytes()).hexdigest(),
|
||||
updated_at=str(existing_document.get("updated_at") or "").strip()
|
||||
or datetime.now(UTC).isoformat(),
|
||||
updated_by=str(existing_document.get("updated_by") or actor_name).strip()
|
||||
or actor_name,
|
||||
source=str(existing_document.get("source") or "seed").strip() or "seed",
|
||||
)
|
||||
asset.config_json = {
|
||||
**(asset.config_json or {}),
|
||||
"detail_mode": "spreadsheet",
|
||||
"tag": "财务规则",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_document": {
|
||||
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||||
live_document,
|
||||
asset_version=version,
|
||||
),
|
||||
"storage_key": live_document.storage_key,
|
||||
},
|
||||
}
|
||||
return metadata
|
||||
|
||||
live_content = manager.resolve_storage_path(live_document.storage_key).read_bytes()
|
||||
metadata = manager.store_spreadsheet(
|
||||
asset_id=asset.id,
|
||||
version=version,
|
||||
file_name=COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,
|
||||
content=live_content,
|
||||
actor_name=actor_name,
|
||||
source="seed",
|
||||
)
|
||||
asset.config_json = {
|
||||
**(asset.config_json or {}),
|
||||
"detail_mode": "spreadsheet",
|
||||
"tag": "财务规则",
|
||||
"rule_library": FINANCE_RULES_LIBRARY,
|
||||
"rule_document": {
|
||||
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||||
live_document,
|
||||
asset_version=version,
|
||||
),
|
||||
"storage_key": live_document.storage_key,
|
||||
},
|
||||
}
|
||||
return metadata
|
||||
|
||||
@staticmethod
|
||||
def _read_or_build_company_travel_rule_file(
|
||||
manager: AgentAssetSpreadsheetManager,
|
||||
) -> bytes:
|
||||
live_key = (
|
||||
Path("rules")
|
||||
/ FINANCE_RULES_LIBRARY
|
||||
/ COMPANY_TRAVEL_EXPENSE_RULE_FILENAME
|
||||
).as_posix()
|
||||
live_path = manager.resolve_storage_path(live_key)
|
||||
if live_path.exists():
|
||||
return live_path.read_bytes()
|
||||
return AgentAssetSpreadsheetManager.build_blank_rule_workbook("差旅费报销规则")
|
||||
|
||||
def _create_seed_asset(
|
||||
self,
|
||||
*,
|
||||
asset_type: str,
|
||||
code: str,
|
||||
name: str,
|
||||
description: str,
|
||||
@@ -1262,10 +1525,12 @@ class AgentFoundationService:
|
||||
scenario_json=scenario_json,
|
||||
owner=owner,
|
||||
reviewer=reviewer,
|
||||
status=status,
|
||||
current_version=current_version,
|
||||
config_json=config_json,
|
||||
)
|
||||
status=status,
|
||||
current_version=current_version,
|
||||
published_version=current_version if status == AgentAssetStatus.ACTIVE.value else None,
|
||||
working_version=current_version,
|
||||
config_json=config_json,
|
||||
)
|
||||
self.db.add(asset)
|
||||
self.db.flush()
|
||||
return asset
|
||||
@@ -1331,7 +1596,7 @@ class AgentFoundationService:
|
||||
)
|
||||
)
|
||||
|
||||
def _remove_legacy_rule_assets(self) -> None:
|
||||
def _remove_legacy_rule_assets(self) -> None:
|
||||
assets = list(
|
||||
self.db.scalars(
|
||||
select(AgentAsset).where(AgentAsset.code.in_(LEGACY_RULE_CODES))
|
||||
@@ -1345,8 +1610,38 @@ class AgentFoundationService:
|
||||
select(AuditLog).where(AuditLog.resource_id.in_(LEGACY_RULE_CODES))
|
||||
).all()
|
||||
)
|
||||
for log in obsolete_logs:
|
||||
self.db.delete(log)
|
||||
for log in obsolete_logs:
|
||||
self.db.delete(log)
|
||||
|
||||
def _ensure_agent_asset_schema(self) -> None:
|
||||
bind = self.db.get_bind()
|
||||
inspector = inspect(bind)
|
||||
if "agent_assets" not in inspector.get_table_names():
|
||||
return
|
||||
|
||||
column_names = {column["name"] for column in inspector.get_columns("agent_assets")}
|
||||
migration_statements: list[str] = []
|
||||
if "published_version" not in column_names:
|
||||
migration_statements.append("ALTER TABLE agent_assets ADD COLUMN published_version VARCHAR(30)")
|
||||
if "working_version" not in column_names:
|
||||
migration_statements.append("ALTER TABLE agent_assets ADD COLUMN working_version VARCHAR(30)")
|
||||
|
||||
for statement in migration_statements:
|
||||
self.db.execute(text(statement))
|
||||
|
||||
self.db.execute(
|
||||
text(
|
||||
"UPDATE agent_assets "
|
||||
"SET working_version = COALESCE(working_version, current_version), "
|
||||
"published_version = CASE "
|
||||
"WHEN published_version IS NOT NULL THEN published_version "
|
||||
"WHEN status = 'active' THEN current_version "
|
||||
"ELSE published_version END"
|
||||
)
|
||||
)
|
||||
|
||||
if migration_statements:
|
||||
self.db.commit()
|
||||
|
||||
def _attachment_submission_requirement_markdown(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user