refactor(server): user_agent/steward/ocr 等服务重构并适配关联任务

- user_agent 拆分 application/locations/knowledge/response/review 四个子模块,接入申请位置语义与关联草稿分支
- steward planner/runtime/slot/plan_builder 决策链路重构,travel_reimbursement_calculator/orchestrator_expense_query 适配
- ocr/document_preview/document_intelligence/receipt_folder 复用预览与资产缓存,expense_claim_draft_flow/application_handoff 适配
- pyproject.toml 新增依赖,paddleocr bootstrap 脚本与 server_start.sh 调整
- 更新差旅/交通/通信等财务规则表,同步 document_intelligence/ocr/receipt_folder/user_agent 等测试
This commit is contained in:
caoxiaozhu
2026-06-24 10:42:24 +08:00
parent 332f77389d
commit 0264a4b5b4
41 changed files with 1273 additions and 182 deletions

View File

@@ -15,6 +15,7 @@ from app.models.financial_record import ExpenseClaim
from app.schemas.ontology import OntologyParseRequest
from app.schemas.user_agent import UserAgentCitation, UserAgentRequest, UserAgentReviewRiskBrief
from app.services.agent_assets import AgentAssetService
from app.services.application_location_semantics import resolve_jieba_tokens
from app.services.ontology import SemanticOntologyService
from app.services.user_agent import UserAgentService
from app.services.user_agent_documents import UserAgentDocumentService
@@ -763,6 +764,67 @@ def test_user_agent_application_submit_blocks_overlapping_travel_dates() -> None
assert response.draft_payload is None
def test_user_agent_application_submit_normalizes_location_mixed_with_business_content() -> None:
session_factory = build_session_factory()
with session_factory() as db:
response = build_application_user_agent_response(
db,
"确认提交",
context_overrides={
"manager_name": "向万红",
"application_preview": {
"fields": {
"applicationType": "差旅费用申请",
"time": "2026-02-20 至 2026-02-23",
"location": "上海辅助国网仿生产服务器",
"reason": "辅助国网仿生产服务器部署",
"days": "4天",
"transportMode": "火车",
"amount": "2120元",
}
},
},
)
claim = application_claim_query(db).one()
assert claim.location == "上海市"
assert claim.reason == "辅助国网仿生产服务器部署"
assert "申请单据已生成" in response.answer
assert response.draft_payload is not None
def test_user_agent_application_submit_splits_location_and_reason_from_raw_sentence() -> None:
session_factory = build_session_factory()
with session_factory() as db:
response = build_application_user_agent_response(
db,
"确认提交",
history=[
{
"role": "user",
"content": "2026-02-20 至 2026-02-23去上海辅助国网仿生产服务器部署火车",
}
],
context_overrides={
"manager_name": "向万红",
"grade": "P5",
"department_name": "技术部",
},
)
claim = application_claim_query(db).one()
assert claim.location == "上海市"
assert claim.reason == "辅助国网仿生产服务器部署"
assert "申请单据已生成" in response.answer
def test_application_sentence_jieba_tokenizer_recognizes_location_boundary() -> None:
tokens = resolve_jieba_tokens("上海辅助国网仿生产服务器部署")
assert ("上海", "ns") in tokens
assert [word for word, _ in tokens] == ["上海", "辅助", "国网", "仿生产", "服务器", "部署"]
def test_user_agent_application_maps_preview_travel_type_label() -> None:
session_factory = build_session_factory()
with session_factory() as db:
@@ -2155,7 +2217,7 @@ def test_user_agent_returns_draft_limit_message_when_save_is_blocked() -> None:
context_json={"review_action": "save_draft"},
tool_payload={
"draft_limit_reached": True,
"message": "当前已保存 3 个草稿,请先完成已保存的草稿,才能再次新建草稿。",
"message": "当前已保存 3 个草稿,请先完成已保存的草稿,才能再次新建草稿。",
"status": "blocked",
},
)
@@ -2163,7 +2225,7 @@ def test_user_agent_returns_draft_limit_message_when_save_is_blocked() -> None:
assert (
response.answer
== "当前已保存 3 个草稿,请先完成已保存的草稿,才能再次新建草稿。"
== "当前已保存 3 个草稿,请先完成已保存的草稿,才能再次新建草稿。"
)