refactor(backend): update services and register OCR router
- router.py: register ocr_router with OCR tag - ontology.py: update ontology service logic - orchestrator.py: update orchestrator service logic - user_agent.py: update user agent schema and service - schemas/user_agent.py: update user agent data schemas
This commit is contained in:
@@ -8,6 +8,7 @@ from app.api.v1.endpoints.bootstrap import router as bootstrap_router
|
||||
from app.api.v1.endpoints.employees import router as employees_router
|
||||
from app.api.v1.endpoints.health import router as health_router
|
||||
from app.api.v1.endpoints.knowledge import router as knowledge_router
|
||||
from app.api.v1.endpoints.ocr import router as ocr_router
|
||||
from app.api.v1.endpoints.ontology import router as ontology_router
|
||||
from app.api.v1.endpoints.orchestrator import router as orchestrator_router
|
||||
from app.api.v1.endpoints.reimbursements import router as reimbursements_router
|
||||
@@ -21,6 +22,7 @@ router.include_router(agent_assets_router, tags=["agent-assets"])
|
||||
router.include_router(agent_runs_router, tags=["agent-runs"])
|
||||
router.include_router(audit_logs_router, tags=["audit-logs"])
|
||||
router.include_router(knowledge_router, tags=["knowledge"])
|
||||
router.include_router(ocr_router, tags=["ocr"])
|
||||
router.include_router(ontology_router, tags=["ontology"])
|
||||
router.include_router(orchestrator_router, tags=["orchestrator"])
|
||||
router.include_router(employees_router, prefix="/employees", tags=["employees"])
|
||||
|
||||
@@ -29,6 +29,9 @@ class UserAgentDraftPayload(BaseModel):
|
||||
title: str = Field(description="草稿标题。")
|
||||
body: str = Field(description="草稿正文。")
|
||||
confirmation_required: bool = Field(default=True, description="是否需要人工确认。")
|
||||
claim_id: str | None = Field(default=None, description="关联的报销草稿 ID。")
|
||||
claim_no: str | None = Field(default=None, description="关联的报销草稿单号。")
|
||||
status: str | None = Field(default=None, description="当前报销草稿状态。")
|
||||
|
||||
|
||||
class UserAgentRequest(BaseModel):
|
||||
|
||||
@@ -666,6 +666,8 @@ class SemanticOntologyService:
|
||||
"entry_source": payload.context_json.get("entry_source"),
|
||||
"attachment_names": payload.context_json.get("attachment_names", []),
|
||||
"attachment_count": payload.context_json.get("attachment_count", 0),
|
||||
"ocr_summary": payload.context_json.get("ocr_summary", ""),
|
||||
"ocr_documents": payload.context_json.get("ocr_documents", []),
|
||||
"request_context": payload.context_json.get("request_context"),
|
||||
"role_codes": payload.context_json.get("role_codes", []),
|
||||
},
|
||||
@@ -690,6 +692,7 @@ class SemanticOntologyService:
|
||||
"即使没有明确说“生成草稿”,也优先使用 expense + draft。"
|
||||
"出现“客户”不等于应收,出现“供应商”不等于应付,必须结合动作词和业务目标判断。"
|
||||
"只有明确查询、统计、列出、多少、明细、对比时才优先使用 query 或 compare。"
|
||||
"附件名称和 OCR 摘要只作为辅助证据,不能编造未出现的事实。"
|
||||
"信息不足时 clarification_required=true,并给出一句简短中文追问。"
|
||||
"missing_slots 使用简短 snake_case,例如 expense_type, amount, "
|
||||
"customer_name, participants, attachments。"
|
||||
|
||||
@@ -32,6 +32,7 @@ from app.schemas.orchestrator import (
|
||||
)
|
||||
from app.schemas.user_agent import UserAgentRequest, UserAgentResponse
|
||||
from app.services.agent_assets import AgentAssetService
|
||||
from app.services.expense_claims import ExpenseClaimService
|
||||
from app.services.agent_foundation import AgentFoundationService
|
||||
from app.services.agent_runs import AgentRunService
|
||||
from app.services.ontology import SemanticOntologyService
|
||||
@@ -61,6 +62,7 @@ class OrchestratorService:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
self.asset_service = AgentAssetService(db)
|
||||
self.expense_claim_service = ExpenseClaimService(db)
|
||||
self.run_service = AgentRunService(db)
|
||||
self.ontology_service = SemanticOntologyService(db)
|
||||
self.user_agent_service = UserAgentService(db)
|
||||
@@ -475,23 +477,43 @@ class OrchestratorService:
|
||||
failed_tool_count=1 if degraded else 0,
|
||||
)
|
||||
|
||||
tool_type = AgentToolType.LLM.value
|
||||
tool_name = "user_agent.draft_placeholder"
|
||||
executor = lambda: {
|
||||
"message": (
|
||||
f"已生成 {ontology.scenario} 场景草稿,"
|
||||
"占位能力后续由 Day 5 User Agent 接管。"
|
||||
),
|
||||
"draft_only": True,
|
||||
}
|
||||
fallback_factory = lambda exc: {
|
||||
"message": f"草稿生成暂时不可用,请稍后再试:{exc}",
|
||||
"degraded": True,
|
||||
}
|
||||
|
||||
if ontology.scenario == "expense":
|
||||
tool_type = AgentToolType.DATABASE.value
|
||||
tool_name = "database.expense_claims.upsert_draft"
|
||||
executor = lambda: self.expense_claim_service.upsert_draft_from_ontology(
|
||||
run_id=run_id,
|
||||
user_id=payload.user_id,
|
||||
message=payload.message or "",
|
||||
ontology=ontology,
|
||||
context_json=payload.context_json,
|
||||
)
|
||||
fallback_factory = lambda exc: {
|
||||
"message": f"报销草稿落库失败,请稍后再试:{exc}",
|
||||
"degraded": True,
|
||||
}
|
||||
|
||||
tool_payload, degraded = self._invoke_tool(
|
||||
run_id=run_id,
|
||||
tool_type=AgentToolType.LLM.value,
|
||||
tool_name="user_agent.draft_placeholder",
|
||||
tool_type=tool_type,
|
||||
tool_name=tool_name,
|
||||
request_json=self._build_ontology_json(ontology),
|
||||
context_json=payload.context_json,
|
||||
executor=lambda: {
|
||||
"message": (
|
||||
f"已生成 {ontology.scenario} 场景草稿,"
|
||||
"占位能力后续由 Day 5 User Agent 接管。"
|
||||
),
|
||||
"draft_only": True,
|
||||
},
|
||||
fallback_factory=lambda exc: {
|
||||
"message": f"草稿生成暂时不可用,请稍后再试:{exc}",
|
||||
"degraded": True,
|
||||
},
|
||||
executor=executor,
|
||||
fallback_factory=fallback_factory,
|
||||
)
|
||||
result = self._build_user_agent_result(
|
||||
self.user_agent_service.respond(
|
||||
|
||||
@@ -142,8 +142,11 @@ class UserAgentService:
|
||||
return self._build_implicit_expense_draft_guidance(payload)
|
||||
|
||||
attachment_names = self._resolve_attachment_names(payload)
|
||||
ocr_summary = str(payload.context_json.get("ocr_summary") or "").strip()
|
||||
attachment_hint = ""
|
||||
if attachment_names:
|
||||
if ocr_summary:
|
||||
attachment_hint = f" 我已读取附件 OCR 摘要:{ocr_summary}"
|
||||
elif attachment_names:
|
||||
attachment_hint = (
|
||||
f" 我已带入 {len(attachment_names)} 份附件名称,但目前还不能直接读取附件内容,"
|
||||
"仍需要你补充关键信息。"
|
||||
@@ -238,6 +241,8 @@ class UserAgentService:
|
||||
"request_context": payload.context_json.get("request_context"),
|
||||
"attachment_count": payload.context_json.get("attachment_count"),
|
||||
"attachment_names": self._resolve_attachment_names(payload),
|
||||
"ocr_summary": payload.context_json.get("ocr_summary", ""),
|
||||
"ocr_documents": payload.context_json.get("ocr_documents", []),
|
||||
},
|
||||
"tool_payload": payload.tool_payload,
|
||||
"citations": [item.model_dump(mode="json") for item in citations],
|
||||
@@ -348,7 +353,11 @@ class UserAgentService:
|
||||
def _build_draft_payload(self, payload: UserAgentRequest) -> UserAgentDraftPayload:
|
||||
scenario_label = SCENARIO_LABELS.get(payload.ontology.scenario, "业务")
|
||||
subject = self._resolve_subject(payload)
|
||||
claim_no = str(payload.tool_payload.get("claim_no") or "").strip() or None
|
||||
claim_status = str(payload.tool_payload.get("status") or "").strip() or None
|
||||
title = f"{scenario_label}处理意见草稿"
|
||||
if claim_no:
|
||||
title = f"{scenario_label}草稿 {claim_no}"
|
||||
body = (
|
||||
f"主题:{subject}\n"
|
||||
"结论:已根据当前语义解析结果生成草稿,尚未自动执行。\n"
|
||||
@@ -360,6 +369,9 @@ class UserAgentService:
|
||||
title=title,
|
||||
body=body,
|
||||
confirmation_required=True,
|
||||
claim_id=str(payload.tool_payload.get("claim_id") or "").strip() or None,
|
||||
claim_no=claim_no,
|
||||
status=claim_status,
|
||||
)
|
||||
|
||||
def _build_suggested_actions(
|
||||
|
||||
Reference in New Issue
Block a user