Commit 5e91ed5f by ccran

feat: add party role check;

parent 447b8d67
...@@ -46,10 +46,11 @@ FILE_SUFFIX = "-审核批注" ...@@ -46,10 +46,11 @@ FILE_SUFFIX = "-审核批注"
## 关键参数** ## 关键参数**
use_non_fastgpt_llm = False use_non_fastgpt_llm = False
use_lufa = False use_lufa = True
use_jp_machine = True use_jp_machine = True
## 关键参数** ## 关键参数**
max_model_len = 131072
ocr_url = 'http://192.168.252.71:8202/openapi/ocrUploadFile' ocr_url = 'http://192.168.252.71:8202/openapi/ocrUploadFile'
if use_lufa: if use_lufa:
outer_backend_url = "http://znkf.lgfzgroup.com:48081" outer_backend_url = "http://znkf.lgfzgroup.com:48081"
......
from __future__ import annotations
from typing import Dict
from loguru import logger
from core.tool import tool, tool_func
from core.tools.segment_llm import LLMTool
PARTY_ROLE_SYSTEM_PROMPT = """
你是一名专业合同审查律师。
"""
PARTY_ROLE_USER_PROMPT = """
请分析指定公司在合同中的实际商业角色。
注意:
- 不要仅依据“甲方/乙方”字样判断
- 必须结合实际权利义务关系判断
重点考虑:
- 谁提出需求
- 谁支付费用
- 谁接受服务或交付成果
- 谁拥有验收、管理、监督权
- 谁负责提供产品或服务
输出要求:
只输出一个 JSON:
{{
"party_role": "demand_side | supplier_side | unclear",
"reason": ""
}}
其中:
- demand_side:需求方、委托人、采购方、客户、甲方等
- supplier_side:供应商、服务方、承包方、实施方、乙方等
- unclear:无法明确判断
reason 要用一句话简要说明核心依据。
禁止输出任何Markdown、代码块或额外文本。
待分析公司:
{company_name}
合同内容:
{contract_text}
"""
VALID_PARTY_ROLES = {"demand_side", "supplier_side", "mixed", "unclear"}
PARTY_ROLES_MAP={
"demand_side": "需求方、委托人、采购方、客户、甲方等",
"supplier_side": "供应商、服务方、承包方、实施方、乙方等",
"unclear": "",
}
@tool("party_role", "识别指定公司在合同中的实际商业角色")
class PartyRoleTool(LLMTool):
def __init__(self) -> None:
super().__init__(PARTY_ROLE_SYSTEM_PROMPT)
@tool_func(
{
"type": "object",
"properties": {
"company_name": {"type": "string"},
"contract_text": {"type": "string"},
},
"required": ["company_name", "contract_text"],
}
)
def run(self, company_name: str, contract_text: str) -> Dict[str, str]:
company_name = (company_name or "").strip()
contract_text = (contract_text or "").strip()
if not company_name or not contract_text:
return {"party_role": "", "reason": ""}
user_content = PARTY_ROLE_USER_PROMPT.format(
company_name=company_name,
contract_text=contract_text,
)
try:
resp = self.run_with_loop(self.chat_async(self.build_messages(user_content)))
data = self.parse_first_json(resp)
party_role = str(data.get("party_role", "")).strip()
reason = str(data.get("reason", "")).strip()
if party_role in VALID_PARTY_ROLES:
return {"party_role": PARTY_ROLES_MAP.get(party_role, ""), "reason": reason}
except Exception as exc:
logger.error("Party role detection LLM failed: %s", exc)
return {"party_role": "", "reason": ""}
if __name__ == "__main__":
tool = PartyRoleTool()
print(
tool.run(
company_name="麓发公司",
contract_text="麓发委托乙公司开发系统,甲公司支付费用并负责验收。",
)
)
...@@ -19,6 +19,7 @@ from core.config import ( ...@@ -19,6 +19,7 @@ from core.config import (
pdf_support_formats, pdf_support_formats,
MERGE_RULE_PROMPT, MERGE_RULE_PROMPT,
use_lufa, use_lufa,
max_model_len
) )
from core.tools.segment_summary import SegmentSummaryTool from core.tools.segment_summary import SegmentSummaryTool
from core.tools.segment_review import SegmentReviewTool from core.tools.segment_review import SegmentReviewTool
...@@ -29,6 +30,7 @@ from core.tools.reflect_retry import ReflectRetryTool ...@@ -29,6 +30,7 @@ from core.tools.reflect_retry import ReflectRetryTool
from core.tools.segment_merger import SegmentMergerTool from core.tools.segment_merger import SegmentMergerTool
from core.tools.fact_merger import FactMergerTool from core.tools.fact_merger import FactMergerTool
from core.tools.ruleset_router import RulesetRouterTool from core.tools.ruleset_router import RulesetRouterTool
from core.tools.party_role import PartyRoleTool
from core.memory import Finding from core.memory import Finding
from core.memory import FINDING_KEY_MERGE, FINDING_KEY_REFLECT, FINDING_KEY_REVIEW from core.memory import FINDING_KEY_MERGE, FINDING_KEY_REFLECT, FINDING_KEY_REVIEW
...@@ -44,6 +46,7 @@ reflect_tool = ReflectRetryTool() ...@@ -44,6 +46,7 @@ reflect_tool = ReflectRetryTool()
merger_tool = SegmentMergerTool() merger_tool = SegmentMergerTool()
fact_merger_tool = FactMergerTool() fact_merger_tool = FactMergerTool()
ruleset_router_tool = RulesetRouterTool() ruleset_router_tool = RulesetRouterTool()
party_role_tool = PartyRoleTool()
@app.post("/sleep") @app.post("/sleep")
...@@ -84,6 +87,17 @@ class RulesetRouteResponse(BaseModel): ...@@ -84,6 +87,17 @@ class RulesetRouteResponse(BaseModel):
reason: str = "" reason: str = ""
class PartyRoleRequest(BaseModel):
company_name: str = Field(..., description="待分析公司名称")
contract_text: str = Field(..., description="合同全文或合同片段内容")
class PartyRoleResponse(BaseModel):
company_name: str
party_role: str
reason: Optional[str] = ""
@app.post("/rulesets/route", response_model=RulesetRouteResponse) @app.post("/rulesets/route", response_model=RulesetRouteResponse)
def route_ruleset(payload: RulesetRouteRequest) -> RulesetRouteResponse: def route_ruleset(payload: RulesetRouteRequest) -> RulesetRouteResponse:
question = (payload.question or "").strip() question = (payload.question or "").strip()
...@@ -98,6 +112,26 @@ def route_ruleset(payload: RulesetRouteRequest) -> RulesetRouteResponse: ...@@ -98,6 +112,26 @@ def route_ruleset(payload: RulesetRouteRequest) -> RulesetRouteResponse:
) )
@app.post("/contracts/party-role", response_model=PartyRoleResponse)
def detect_party_role(payload: PartyRoleRequest) -> PartyRoleResponse:
company_name = (payload.company_name or "").strip()
contract_text = (payload.contract_text or "").strip()
if not company_name:
raise HTTPException(status_code=400, detail="company_name cannot be empty")
if not contract_text:
raise HTTPException(status_code=400, detail="contract_text cannot be empty")
result = party_role_tool.run(
company_name=company_name,
contract_text=contract_text[:max_model_len], # TODO 合同文本过长时,优先保留包含公司名称的片段内容,后续优化
)
return PartyRoleResponse(
company_name=company_name,
party_role=result.get("party_role", ""),
reason=result.get("reason", ""),
)
@app.post("/documents/parse", response_model=DocumentParseResponse) @app.post("/documents/parse", response_model=DocumentParseResponse)
async def parse_document(payload: DocumentParseRequest) -> DocumentParseResponse: async def parse_document(payload: DocumentParseRequest) -> DocumentParseResponse:
if not payload.urls: if not payload.urls:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment