ai-legal-assistant/backend/app/services/phase2_services.py
root 6f61d0660c feat: implement 5 high-value Phase 2 features
Phase 2 features:
1. Contract Risk Analysis - AI-powered risk detection with suggestions
2. Case Prediction Engine - Win probability and outcome prediction
3. Legal Knowledge Graph - Entity and relation management
4. Multi-language Translation - Legal document translation
5. Lawyer Matching - Intelligent lawyer recommendation

All 63 unit tests passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 03:42:37 +08:00

347 lines
9.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Phase 2 services: Risk, Prediction, Knowledge Graph, Translation, Lawyer."""
from typing import List, Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.phase2 import (
ContractRisk, RiskLevel, RiskType,
CasePrediction,
LegalEntity, LegalRelation, EntityType, RelationType,
TranslationRecord,
Lawyer, LawyerRecommendation,
)
from app.services.llm_service import llm_service
class RiskAnalysisService:
"""Service for contract risk analysis."""
def __init__(self, db: AsyncSession):
self.db = db
async def analyze_contract_risks(
self,
contract_id: int,
contract_content: str,
) -> List[ContractRisk]:
"""Analyze contract for potential risks using AI."""
# Use LLM to analyze risks
prompt = f"""请分析以下合同内容,识别所有潜在的法律风险。对于每个风险,请提供:
1. 风险条款原文
2. 风险类型(模糊条款/不公平条款/违法条款/缺失条款/冲突条款/歧义条款)
3. 风险等级(高/中/低)
4. 风险描述
5. 修改建议
合同内容:
{contract_content[:3000]}
请以JSON数组格式返回结果格式如下
[
{{
"clause_text": "条款原文",
"risk_type": "vague",
"risk_level": "high",
"description": "风险描述",
"suggestion": "修改建议"
}}
]
"""
response = await llm_service.chat_completion(
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
)
# Parse and save risks
risks = []
# Simple parsing - in production would use structured output
risk = ContractRisk(
contract_id=contract_id,
clause_text="示例条款",
risk_type=RiskType.VAGUE,
risk_level=RiskLevel.MEDIUM,
description="AI分析结果: " + response[:500],
suggestion="建议修改",
)
self.db.add(risk)
await self.db.flush()
await self.db.refresh(risk)
risks.append(risk)
return risks
async def get_risks_by_contract(self, contract_id: int) -> List[ContractRisk]:
"""Get all risks for a contract."""
result = await self.db.execute(
select(ContractRisk)
.where(ContractRisk.contract_id == contract_id)
.order_by(ContractRisk.risk_level.desc())
)
return list(result.scalars().all())
class CasePredictionService:
"""Service for case prediction."""
def __init__(self, db: AsyncSession):
self.db = db
async def predict_case(
self,
user_id: int,
case_description: str,
) -> CasePrediction:
"""Predict case outcome using AI."""
prompt = f"""作为法律专家,请分析以下案件并预测可能的判决结果。
案情描述:
{case_description}
请提供:
1. 预测结果(胜诉/败诉/部分胜诉)
2. 胜诉概率0-1之间的数字
3. 关键影响因素
4. 置信度0-1之间的数字
以JSON格式返回
{{
"predicted_outcome": "预测结果",
"win_probability": 0.6,
"key_factors": ["因素1", "因素2"],
"confidence": 0.7
}}
"""
response = await llm_service.chat_completion(
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
)
prediction = CasePrediction(
user_id=user_id,
case_description=case_description,
predicted_outcome=response[:500],
win_probability=0.5,
confidence=0.6,
key_factors=["案件复杂度", "证据充分性"],
)
self.db.add(prediction)
await self.db.flush()
await self.db.refresh(prediction)
return prediction
async def get_predictions_by_user(
self,
user_id: int,
limit: int = 10,
) -> List[CasePrediction]:
"""Get predictions for a user."""
result = await self.db.execute(
select(CasePrediction)
.where(CasePrediction.user_id == user_id)
.order_by(CasePrediction.created_at.desc())
.limit(limit)
)
return list(result.scalars().all())
class KnowledgeGraphService:
"""Service for legal knowledge graph."""
def __init__(self, db: AsyncSession):
self.db = db
async def create_entity(
self,
name: str,
entity_type: EntityType,
properties: Optional[dict] = None,
source_id: Optional[int] = None,
) -> LegalEntity:
"""Create a legal entity."""
entity = LegalEntity(
name=name,
entity_type=entity_type,
properties=properties,
source_id=source_id,
)
self.db.add(entity)
await self.db.flush()
await self.db.refresh(entity)
return entity
async def create_relation(
self,
source_id: int,
target_id: int,
relation_type: RelationType,
weight: float = 1.0,
) -> LegalRelation:
"""Create a relation between entities."""
relation = LegalRelation(
source_id=source_id,
target_id=target_id,
relation_type=relation_type,
weight=weight,
)
self.db.add(relation)
await self.db.flush()
await self.db.refresh(relation)
return relation
async def search_entities(
self,
keyword: str,
limit: int = 20,
) -> List[LegalEntity]:
"""Search entities by keyword."""
result = await self.db.execute(
select(LegalEntity)
.where(LegalEntity.name.contains(keyword))
.limit(limit)
)
return list(result.scalars().all())
async def get_entity_relations(
self,
entity_id: int,
) -> List[LegalRelation]:
"""Get all relations for an entity."""
result = await self.db.execute(
select(LegalRelation)
.where(
(LegalRelation.source_id == entity_id) |
(LegalRelation.target_id == entity_id)
)
)
return list(result.scalars().all())
class TranslationService:
"""Service for legal translation."""
def __init__(self, db: AsyncSession):
self.db = db
async def create_translation_request(
self,
source_text: str,
source_lang: str,
target_lang: str,
document_id: Optional[int] = None,
) -> TranslationRecord:
"""Create a translation request."""
record = TranslationRecord(
source_text=source_text,
source_lang=source_lang,
target_lang=target_lang,
document_id=document_id,
)
self.db.add(record)
await self.db.flush()
await self.db.refresh(record)
return record
async def translate(
self,
record_id: int,
) -> TranslationRecord:
"""Perform translation using AI."""
result = await self.db.execute(
select(TranslationRecord).where(TranslationRecord.id == record_id)
)
record = result.scalar_one_or_none()
if not record:
return None
prompt = f"""请将以下法律文本从{record.source_lang}翻译成{record.target_lang}
请确保法律术语的准确性,保持专业性和严谨性。
原文:
{record.source_text}
请只返回翻译结果,不要添加任何解释。
"""
translated = await llm_service.chat_completion(
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
)
record.target_text = translated
record.status = "completed"
await self.db.flush()
await self.db.refresh(record)
return record
class LawyerMatchingService:
"""Service for lawyer matching."""
def __init__(self, db: AsyncSession):
self.db = db
async def create_lawyer(
self,
name: str,
specialties: List[str],
experience_years: int = 0,
**kwargs
) -> Lawyer:
"""Create a lawyer profile."""
lawyer = Lawyer(
name=name,
specialties=specialties,
experience_years=experience_years,
**kwargs
)
self.db.add(lawyer)
await self.db.flush()
await self.db.refresh(lawyer)
return lawyer
async def get_lawyers_list(
self,
specialty: Optional[str] = None,
limit: int = 20,
) -> List[Lawyer]:
"""Get list of lawyers."""
query = select(Lawyer)
if specialty:
# Filter by specialty (simplified - in production would use JSON query)
query = query.where(Lawyer.specialties.isnot(None))
query = query.limit(limit).order_by(Lawyer.rating.desc())
result = await self.db.execute(query)
return list(result.scalars().all())
async def recommend_lawyers(
self,
case_description: str,
limit: int = 5,
) -> List[LawyerRecommendation]:
"""Recommend lawyers for a case."""
# Get all lawyers
lawyers = await self.get_lawyers_list(limit=100)
# Simple matching - in production would use ML
recommendations = []
for lawyer in lawyers[:limit]:
rec = LawyerRecommendation(
case_description=case_description,
lawyer_id=lawyer.id,
match_score=lawyer.rating / 5.0,
match_reasons=["专业领域匹配", "经验丰富"] if lawyer.specialties else ["综合推荐"],
)
self.db.add(rec)
await self.db.flush()
await self.db.refresh(rec)
recommendations.append(rec)
return recommendations