From f320682c49aff6e29711c114e383a5e2a1218e2d Mon Sep 17 00:00:00 2001 From: martsforever Date: Mon, 8 Sep 2025 21:27:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8F=90=E4=BA=A4=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E5=8D=95=E7=9A=84=E6=97=B6=E5=80=99=E9=80=9A=E8=BF=87AI?= =?UTF-8?q?=E6=A0=B8=E9=AA=8C=E6=8A=A5=E9=94=80=E6=98=AF=E5=90=A6=E7=AC=A6?= =?UTF-8?q?=E5=90=88=E6=8A=A5=E9=94=80=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controller/add_reimburse_route.py | 3 + app/model/ApproveModel.py | 3 + app/utils/llm_approve_utils.py | 90 +++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 app/utils/llm_approve_utils.py diff --git a/app/controller/add_reimburse_route.py b/app/controller/add_reimburse_route.py index 6aecb19..df4b812 100644 --- a/app/controller/add_reimburse_route.py +++ b/app/controller/add_reimburse_route.py @@ -6,6 +6,7 @@ from langgraph.graph import StateGraph from app.model.ApproveModel import ApproveService, ApproveModel from app.model.ReimburseModel import ReimburseService, ReimburseModel from app.utils.db_utils import AsyncSessionDep +from app.utils.llm_approve_utils import get_llm_approve_result from app.utils.postgres_checkpointer import AsyncPostgresSaverDep from app.workflow.approve import create_approve_graph, ApproveGraphSchema @@ -36,6 +37,7 @@ def add_reimburse_route(app: FastAPI): if reimburse_cls.approve.status == 'approved': return {"message": "报销单已通过审批,请勿重复提交!"} + llm_response_dict = await get_llm_approve_result(session, reimburse_cls) # 1,4,5 的情况下新建审批单 # 创建审批单,使用新的审批单走审批流程 @@ -49,6 +51,7 @@ def add_reimburse_route(app: FastAPI): "apply_user_id": reimburse_cls.user_id, "proj_id": reimburse_cls.proj_id, "approve_from": "reimburse", + **llm_response_dict, } insert_approve_cls: ApproveModel = await ApproveService.item_insert(session=session, row_dict=new_approve_dict) diff --git a/app/model/ApproveModel.py b/app/model/ApproveModel.py index 22d7f33..dd7dc29 100644 --- a/app/model/ApproveModel.py +++ b/app/model/ApproveModel.py @@ -14,6 +14,9 @@ class ApproveModel(PublicApproveModel, table=True): __tablename__ = "pl_approve" __table_args__ = {'extend_existing': True} + llm_flag: Optional[bool] = Field(default=None, description="LLM审核是否通过表示") + llm_reason: Optional[str] = Field(default=None, description="LLM审核不通过原因") + # /*---------------------------------------project-------------------------------------------*/ proj_id: str = Field( diff --git a/app/utils/llm_approve_utils.py b/app/utils/llm_approve_utils.py new file mode 100644 index 0000000..8440758 --- /dev/null +++ b/app/utils/llm_approve_utils.py @@ -0,0 +1,90 @@ +import json + +from langchain_core.output_parsers import StrOutputParser +from langchain_core.prompts import ChatPromptTemplate +from pydantic import BaseModel, Field + +from app.model.KnowledgeDoc import KnowledgeDocServiceWithCreator, KnowledgeDocModel +from app.model.ReimburseModel import ReimburseModel +from app.utils.db_utils import AsyncSessionDep +from app.utils.llm_utils import create_llm + + +class LlmApproveResult(BaseModel): + llm_flag: bool = Field(..., description='AI审核是否通过') + llm_reason: str | None = Field(default=None, description="审核不通过时的原因") + + +async def get_llm_approve_result(session: AsyncSessionDep, reimburse_cls: ReimburseModel): + try: + reimburse_document: KnowledgeDocModel = await KnowledgeDocServiceWithCreator.query_item(session, {"code": "REIMBURSE"}) + except Exception as e: + raise Exception(f"获取报销规范文档失败: {e}") + + chain = ChatPromptTemplate.from_template(""" + 你是一名专业的报销单审核专家,你需要根据报销规范文档审核报销单信息,报销规范如标签中的内容所示: + + {document_content} + + 报销单信息如下标签中的内容所示: + + {reimburse_content} + + 你需要返回一个json对象,类型为:{response_description},其中llm_flag表示审核是否通过,为布尔值;llm_reason为审核不通过的原因,审核通过reason为空; + """) | create_llm() | StrOutputParser() + + print(":::::::::::::::报销规则:::::::::::::::::::") + print(reimburse_document.content) + + print(":::::::::::::::报销单描述::::::::::::::::::::::::") + reimburse_description = get_reimburse_description(reimburse_cls) + print(reimburse_description) + + llm_response_content = await chain.ainvoke({ + "document_content": reimburse_document.content, + "reimburse_content": reimburse_description, + "response_description": '{"llm_flag":true, "llm_reason":""}' + }) + + print("::::::::::::::LLM审核结果:::::::::::::::::::::::") + print(llm_response_content) + + llm_response_dict = json.loads(llm_response_content) + return llm_response_dict + + +def get_reimburse_description(reimrbuse_cls: ReimburseModel): + if not reimrbuse_cls.project: + raise Exception("报销单关联项目不存在") + + reimbruse_content = f""" + 报销申请人:{reimrbuse_cls.user.full_name} + 报销单标题:{str(reimrbuse_cls.title)} + 报销单金额:{str(reimrbuse_cls.amount)} + 报销单备注信息:{str(reimrbuse_cls.remarks)} + 报销项目:{str(reimrbuse_cls.project.name)} + """ + + for index, travel_item in enumerate(reimrbuse_cls.travel_list): + reimbruse_content += f""" + + 第{index + 1}条差旅费用信息: + 差旅费用名称:{travel_item.title} + 差旅费用类型:{travel_item.type} + 差旅出发时间:{travel_item.depart_time} + 差旅出发地点:{travel_item.depart_city} + 差旅到达时间:{travel_item.arrive_time} + 差旅到达地点:{travel_item.arrive_city} + 差旅报销金额:{travel_item.amount} + """ + + for index, other_item in enumerate(reimrbuse_cls.other_list): + reimbruse_content += f""" + + 第{index + 1}条费用信息: + 费用名称:{other_item.title} + 费用类型:{other_item.type} + 报销金额:{other_item.amount} + 票据类型:{other_item.recipe_type} + """ + return reimbruse_content