feat: 增强 agent_assets 功能,支持更多资产操作

This commit is contained in:
caoxiaozhu
2026-05-18 02:48:51 +00:00
parent 68f663f2f4
commit 55e0591a5e
6 changed files with 1654 additions and 148 deletions

View File

@@ -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,