Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
ccran
/
lufa-contract
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
5e91ed5f
authored
May 25, 2026
by
ccran
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add party role check;
parent
447b8d67
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
146 additions
and
1 deletions
+146
-1
core/config.py
+2
-1
core/tools/party_role.py
+110
-0
main.py
+34
-0
No files found.
core/config.py
View file @
5e91ed5f
...
@@ -46,10 +46,11 @@ FILE_SUFFIX = "-审核批注"
...
@@ -46,10 +46,11 @@ FILE_SUFFIX = "-审核批注"
## 关键参数**
## 关键参数**
use_non_fastgpt_llm
=
False
use_non_fastgpt_llm
=
False
use_lufa
=
Fals
e
use_lufa
=
Tru
e
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"
...
...
core/tools/party_role.py
0 → 100644
View file @
5e91ed5f
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
=
"麓发委托乙公司开发系统,甲公司支付费用并负责验收。"
,
)
)
main.py
View file @
5e91ed5f
...
@@ -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
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment