法律内容生成器(集成小米大模型)代码
python运行功能:定义法律内容生成器的核心类,封装所有功能方法与配置属性,实现「高内聚、低耦合」的代码设计。设计思路:将案例分析、脚本生成、文章创作等功能封装为类方法,配置属性统一管理,方便实例化与复用。核心流程环境准备→案例读取→AI分析→内容生成→文件保存→批量报告,逻辑闭环,功能完整,兼顾在线与离线模式。设计亮点并发控制与重试机制,提升 API 调用的稳定性与效率。结构化提示词设计,保证
·
这份代码实现了一个集成小米大模型的法律内容自动化生成工具,核心功能包括法律案例分析、短视频脚本生成、微信普法文章创作,支持在线 API 调用与离线本地备份,还具备并发控制和重试机制。下面按「模块划分 + 逐行逻辑」进行详细说明,兼顾代码功能与设计思路。
一、导入模块(第 1-12 行)
python
运行
import os
- 功能:导入 Python 内置
os模块,用于环境变量读取、文件路径操作。 - 用途:后续读取
.env配置、拼接文件路径等依赖该模块。
python
运行
import json
- 功能:导入 Python 内置
json模块,用于JSON 数据的序列化与反序列化。 - 用途:解析小米 API 返回的 JSON 格式案例结果、保存视频制作指令、生成批量处理报告。
python
运行
import time
- 功能:导入 Python 内置
time模块,用于时间延迟、计时控制。 - 用途:API 重试间隔、并发等待、批量处理案例间的延迟。
python
运行
from datetime import datetime
- 功能:从
datetime模块导入datetime类,用于生成时间戳、格式化日期时间。 - 用途:创建唯一文件名(如批量报告、视频文件)、记录处理时间。
python
运行
from pathlib import Path
- 功能:导入
pathlib模块的Path类,用于面向对象的文件路径操作(比os.path更简洁、更易读)。 - 用途:创建工作目录、拼接文件路径、判断文件是否存在,后续所有路径操作均基于此类。
python
运行
import re
- 功能:导入 Python 内置正则表达式模块,用于文本匹配、提取、清理。
- 用途:从 API 返回结果中提取 JSON、从案例文本中提取当事人 / 判决结果等关键信息。
python
运行
from openai import OpenAI
- 功能:从
openai库导入OpenAI客户端类,用于调用兼容 OpenAI 接口的小米大模型 API。 - 用途:初始化 API 客户端、发送聊天完成请求,获取 AI 生成的法律内容。
python
运行
from dotenv import load_dotenv
- 功能:从
python-dotenv库导入load_dotenv函数,用于加载.env文件中的环境变量。 - 用途:读取小米 API 密钥、基础 URL、模型参数等配置,避免硬编码敏感信息。
python
运行
import random
- 功能:导入 Python 内置
random模块,用于生成随机数(本代码中未实际使用,预留用于后续扩展,如随机延迟避免 API 限流)。 - 用途:可扩展为随机化重试延迟、随机选择视频模板等。
python
运行
import threading
- 功能:导入 Python 内置
threading模块,用于多线程编程、线程同步控制。 - 用途:实现并发 API 调用的槽位控制、线程安全的活跃线程数统计。
python
运行
from queue import Queue
- 功能:从
queue模块导入Queue类,用于线程安全的队列操作(本代码中未实际使用,预留用于批量任务队列化处理)。 - 用途:可扩展为生产者 - 消费者模型,批量处理大量案例时解耦任务提交与执行。
python
运行
import requests
- 功能:导入
requests库,用于发送 HTTP 请求(本代码中未实际使用,预留用于直接调用 HTTP API、下载视频素材等扩展功能)。 - 用途:可扩展为绕过
OpenAI客户端,直接调用小米大模型的 HTTP 接口,或下载普法图片素材。
二、核心类定义:LegalContentGenerator(第 14 行)
python
运行
class LegalContentGenerator:
- 功能:定义法律内容生成器的核心类,封装所有功能方法与配置属性,实现「高内聚、低耦合」的代码设计。
- 设计思路:将案例分析、脚本生成、文章创作等功能封装为类方法,配置属性统一管理,方便实例化与复用。
三、类初始化方法:init(第 15-68 行)
python
运行
def __init__(self, api_key=None, base_url=None, max_concurrent=3, retry_delay=5, max_retries=3):
- 功能:类的构造方法,初始化实例属性与配置,完成环境准备。
- 入参说明:
api_key:可选,手动传入小米 API 密钥,优先级高于环境变量。base_url:可选,手动传入小米 API 基础 URL,优先级高于环境变量。max_concurrent:最大并发数,默认 3,控制同时调用 API 的线程数。retry_delay:重试间隔,默认 5 秒,API 调用失败后等待时间。max_retries:最大重试次数,默认 3 次,API 调用失败后的重试上限。
python
运行
# 加载环境变量
load_dotenv()
- 功能:调用
load_dotenv()函数,加载当前目录下.env文件中的环境变量,存入系统环境变量字典。 - 注意:若
.env文件不存在,该函数无副作用,不会报错。
python
运行
# 从环境变量获取配置,如果没有传入则从环境变量读取
self.api_key = api_key or os.getenv("XIAOMI_API_KEY", "your_xiaomi_api_key_here")
self.base_url = base_url or os.getenv("XIAOMI_BASE_URL", "https://api.xiaomimimo.com/v1")
- 功能:初始化 API 配置属性,实现「手动传入优先,环境变量次之,默认值兜底」的配置优先级。
- 逻辑解析:
or运算符:左侧为真则取左侧,否则取右侧(先判断是否手动传入api_key/base_url,无则读取环境变量)。os.getenv(key, default):读取指定环境变量,不存在则返回默认值,避免None导致后续报错。self.api_key:实例属性,存储小米 API 密钥,默认值为占位符。self.base_url:实例属性,存储小米 API 基础 URL,默认值为小米 MiMo 接口地址。
python
运行
# 验证必要配置
if self.api_key == "your_xiaomi_api_key_here" or not self.api_key or self.api_key == "sk-cf3c9b4ioxd0e1wi6n5fi9ylk3nn2yuph0g7ta1j66xk7ssp":
print("警告: 未设置有效的XIAOMI_API_KEY环境变量,请在.env文件中配置")
self.use_offline_mode = True
else:
self.use_offline_mode = False
try:
# 初始化OpenAI客户端
self.client = OpenAI(
api_key=self.api_key,
base_url=self.base_url
)
except Exception as e:
print(f"初始化API客户端失败: {str(e)},切换到离线模式")
self.use_offline_mode = True
- 功能:验证 API 密钥有效性,初始化 OpenAI 客户端,切换在线 / 离线模式。
- 逐行逻辑:
- 条件判断:检查 API 密钥是否为默认占位符、空值,或测试密钥,若满足则判定为无效配置。
self.use_offline_mode = True:实例属性,标记为离线模式,后续将使用本地规则生成内容,不调用远程 API。- 反之,标记为在线模式(
self.use_offline_mode = False),尝试初始化 OpenAI 客户端。 self.client = OpenAI(...):创建 OpenAI 客户端实例,传入小米 API 密钥与基础 URL(小米大模型兼容 OpenAI 接口规范)。try...except:捕获客户端初始化过程中的异常(如网络错误、配置错误),捕获后切换为离线模式,保证程序不崩溃。
python
运行
# 从环境变量获取模型参数
self.model_name = os.getenv("MODEL_NAME", "mimo-v2-flash")
self.max_tokens = int(os.getenv("MAX_TOKENS", "1024"))
self.temperature = float(os.getenv("TEMPERATURE", "0.3"))
self.top_p = float(os.getenv("TOP_P", "0.95"))
- 功能:初始化小米大模型的生成参数,均从环境变量读取,无则使用默认值。
- 参数说明:
self.model_name:模型名称,默认mimo-v2-flash(小米轻量版大模型,兼顾速度与效果)。self.max_tokens:生成内容的最大令牌数(近似字数),默认 1024,转换为整数类型。self.temperature:生成随机性,默认 0.3(低随机性,保证法律内容的准确性与严谨性),转换为浮点型。self.top_p:核采样参数,默认 0.95(控制生成内容的多样性,避免极端结果),转换为浮点型。
python
运行
# 并发和重试配置
self.max_concurrent = max_concurrent # 最大并发数
self.retry_delay = retry_delay # 重试间隔
self.max_retries = max_retries # 最大重试次数
self.active_threads = 0 # 当前活跃线程数
self.thread_lock = threading.Lock() # 线程锁
- 功能:初始化并发与重试相关的实例属性,保证多线程调用 API 时的安全性与可控性。
- 关键属性说明:
self.active_threads:统计当前正在运行的 API 调用线程数,初始值为 0。self.thread_lock:创建线程锁实例,用于保护self.active_threads的修改操作,避免多线程竞争导致的数据不一致(线程安全)。
python
运行
# 创建工作目录
self.work_dir = Path("./legal_content_workflow").resolve()
self.cases_dir = self.work_dir / "cases"
self.temp_dir = self.work_dir / "temp"
self.audio_dir = self.work_dir / "audio"
self.video_dir = self.work_dir / "video"
self.output_dir = self.work_dir / "output"
self.wechat_dir = self.work_dir / "wechat_articles"
- 功能:使用
Path类创建工作目录及其子目录的路径对象,实现清晰的目录结构管理。 - 逐行逻辑:
self.work_dir:根工作目录,Path("./legal_content_workflow")创建相对路径对象,.resolve()转换为绝对路径,避免路径歧义。- 后续子目录:使用
/运算符拼接路径(Path类的便捷特性),分别对应案例文件、临时文件、音频、视频、输出结果、微信文章的存储目录。
- 目录设计思路:按功能划分目录,方便用户查找与管理生成的内容,保持项目结构整洁。
python
运行
# 确保所有目录存在
directories = [self.work_dir, self.cases_dir, self.temp_dir, self.audio_dir, self.video_dir, self.output_dir, self.wechat_dir]
for directory in directories:
directory.mkdir(parents=True, exist_ok=True)
- 功能:遍历所有目录路径,创建不存在的目录,保证后续文件操作不会因目录不存在而报错。
- 方法说明:
directory.mkdir():创建目录。parents=True:递归创建父目录(若legal_content_workflow不存在,会自动创建)。exist_ok=True:若目录已存在,不抛出异常,避免重复创建导致的错误。
四、并发控制方法:wait_for_slot 与 release_slot(第 70-83 行)
python
运行
def wait_for_slot(self):
"""等待并发槽位"""
while True:
with self.thread_lock:
if self.active_threads < self.max_concurrent:
self.active_threads += 1
break
time.sleep(0.5)
- 功能:等待可用的并发槽位,控制同时调用 API 的线程数不超过
max_concurrent,避免触发 API 限流。 - 逐行逻辑:
while True:无限循环,直到获取到可用槽位。with self.thread_lock:上下文管理器,自动获取与释放线程锁,保证self.active_threads的修改是原子操作(多线程安全)。- 条件判断:若当前活跃线程数小于最大并发数,说明有可用槽位。
self.active_threads += 1:活跃线程数加 1,标记该槽位已被占用。break:退出循环,继续执行后续 API 调用。time.sleep(0.5):若无可用槽位,等待 0.5 秒后再次尝试,避免 CPU 空转。
python
运行
def release_slot(self):
"""释放并发槽位"""
with self.thread_lock:
self.active_threads -= 1
- 功能:API 调用完成(成功或失败)后,释放占用的并发槽位,让其他线程可以获取槽位进行 API 调用。
- 逻辑解析:通过线程锁保护
self.active_threads的减 1 操作,确保线程安全,避免数据混乱。
五、带重试机制的 API 调用:call_xiaomi_api_with_retry(第 85-156 行)
python
运行
def call_xiaomi_api_with_retry(self, prompt, max_tokens=None, temperature=None, top_p=None):
"""带重试机制的API调用"""
tokens = max_tokens or self.max_tokens
temp = temperature or self.temperature
p = top_p or self.top_p
- 功能:封装小米 API 调用,实现自动重试、并发控制,返回 AI 生成的内容。
- 入参说明:
prompt:用户提示词,传递给 AI 模型的生成指令。max_tokens/temperature/top_p:可选,覆盖实例默认的模型参数,实现个性化生成。
- 逻辑解析:初始化生成参数,优先使用传入的参数,无则使用实例默认参数。
python
运行
for attempt in range(self.max_retries + 1):
- 功能:循环执行 API 调用,最多尝试
max_retries + 1次(包含首次调用与max_retries次重试)。
python
运行
try:
# 检查是否达到最大并发限制
self.wait_for_slot()
- 功能:进入内层异常捕获块,先调用
wait_for_slot()获取可用并发槽位,再执行 API 调用。
python
运行
try:
completion = self.client.chat.completions.create(
model=self.model_name,
messages=[
{
"role": "system",
"content": "You are MiMo, an AI assistant developed by Xiaomi. You are a legal expert specialized in analyzing Chinese legal cases and creating educational content. Please provide clear, accurate, and professional analysis."
},
{
"role": "user",
"content": prompt
}
],
max_completion_tokens=tokens,
temperature=temp,
top_p=p,
stream=False,
stop=None,
frequency_penalty=0,
presence_penalty=0,
extra_body={
"thinking": {"type": "disabled"}
}
)
- 功能:调用 OpenAI 客户端的
chat.completions.create()方法,发送请求到小米大模型 API,获取生成结果。 - 关键参数解析:
model:指定使用的模型名称(self.model_name)。messages:消息列表,包含系统角色与用户角色:system:系统提示,定义 AI 的人设(小米 MiMo、法律专家),规范生成内容的风格与专业性。user:用户提示,传递具体的生成任务(如案例分析、脚本生成)。
max_completion_tokens:生成内容的最大令牌数,控制内容长度。temperature/top_p:生成随机性与多样性参数,保证法律内容的严谨性。stream=False:关闭流式返回,等待完整结果后再返回。extra_body:小米大模型扩展参数,禁用思考过程,提升生成速度。
python
运行
# 提取返回的内容
content = completion.choices[0].message.content
result = content.strip()
# 释放并发槽位
self.release_slot()
return result
- 功能:提取 API 返回的核心内容,释放并发槽位,返回清理后的结果。
- 逻辑解析:
completion.choices[0].message.content:获取第一个生成结果的文本内容(OpenAI 接口返回格式)。content.strip():去除首尾空白字符,清理结果。- 调用
release_slot()释放槽位,返回结果。
python
运行
except Exception as e:
# 释放并发槽位
self.release_slot()
error_msg = str(e).lower()
if "rate limit" in error_msg or "concurrent" in error_msg or "too many requests" in error_msg:
print(f"速率限制错误,等待 {self.retry_delay}s 后重试... (尝试 {attempt + 1}/{self.max_retries + 1})")
if attempt < self.max_retries:
time.sleep(self.retry_delay)
continue
raise e
- 功能:捕获 API 调用过程中的异常,处理限流错误,释放槽位,决定是否重试。
- 逐行逻辑:
- 捕获异常后,首先调用
release_slot()释放并发槽位(避免槽位被永久占用)。 error_msg = str(e).lower():将错误信息转为小写,方便匹配关键词。- 匹配限流相关关键词(速率限制、并发限制、请求过多),若为限流错误:
- 打印重试提示,若未达到最大重试次数,等待
retry_delay秒后,continue进入下一次循环重试。
- 打印重试提示,若未达到最大重试次数,等待
- 若非限流错误,直接
raise e抛出异常,终止当前重试循环,进入外层异常捕获块。
- 捕获异常后,首先调用
python
运行
except Exception as e:
if attempt < self.max_retries:
print(f"API调用失败,等待 {self.retry_delay}s 后重试... (尝试 {attempt + 1}/{self.max_retries + 1})")
time.sleep(self.retry_delay)
else:
print(f"API调用最终失败: {str(e)}")
return self.simulate_api_response(prompt)
- 功能:捕获外层异常(如槽位获取失败、非限流类 API 错误),实现通用重试逻辑。
- 逻辑解析:
- 若未达到最大重试次数,打印提示,等待后重试。
- 若已达到最大重试次数,打印最终失败提示,调用
simulate_api_response()返回模拟结果,保证程序继续运行。
python
运行
return self.simulate_api_response(prompt)
- 功能:所有重试均失败后,返回模拟 API 响应,兜底保障程序不中断。
六、模拟 API 响应:simulate_api_response(第 158-206 行)
python
运行
def simulate_api_response(self, prompt):
"""模拟API响应"""
print("使用离线模式处理...")
- 功能:离线模式下,根据提示词类型返回对应的模拟内容,避免因 API 不可用导致程序崩溃。
- 设计思路:按提示词关键词匹配任务类型(案例分析、视频脚本、微信文章),返回结构化的模拟结果,保持与在线 API 返回格式的一致性。
python
运行
# 根据提示类型返回不同的模拟响应
if "分析以下法律案例" in prompt or ("标题" in prompt and "当事人" in prompt):
# 模拟案例分析
...
elif "生成一个适合短视频的解说脚本" in prompt or ("开场" in prompt and "案情介绍" in prompt):
# 模拟视频脚本生成
...
elif "为微信公众号生成一篇普法文章" in prompt or "微信普法文章" in prompt:
# 模拟微信普法文章生成
...
else:
# 通用模拟响应
...
- 功能:通过关键词匹配提示词类型,分发到对应的模拟结果生成逻辑。
- 逻辑解析:针对不同的任务类型,返回符合格式要求的模拟内容,保证后续处理流程(如 JSON 解析、脚本保存)可以正常执行。
七、核心业务方法(案例分析 / 脚本生成 / 文章创作)
1. 法律案例分析:analyze_legal_case(第 208-252 行)
python
运行
def analyze_legal_case(self, case_text):
"""使用小米API分析法律案例"""
prompt = f"""
请仔细分析以下法律案例,并提取以下关键信息:
1. 案例标题
2. 当事人信息(原告、被告等)
3. 案件基本事实
4. 主要争议焦点
5. 法院判决结果
6. 适用的法律条文
案例原文:
{case_text}
请按照JSON格式返回分析结果:
...
"""
- 功能:构造案例分析的提示词,调用 API 获取分析结果,解析 JSON 并返回结构化数据。
- 设计思路:通过详细的提示词规范 AI 的返回格式(JSON),方便后续解析与使用,提升结果的结构化程度。
python
运行
result = self.call_xiaomi_api_with_retry(prompt, max_tokens=1500)
- 功能:调用带重试机制的 API 方法,获取案例分析结果,最大令牌数设为 1500(足够容纳详细的分析内容)。
python
运行
# 提取JSON部分
json_match = re.search(r'\{.*\}', result, re.DOTALL)
if json_match:
json_str = json_match.group()
return json.loads(json_str)
- 功能:使用正则表达式从 API 返回结果中提取 JSON 字符串,解析为 Python 字典并返回。
- 关键参数:
re.DOTALL让.匹配包括换行符在内的所有字符,保证完整提取多行 JSON。
python
运行
except json.JSONDecodeError:
# 解析失败,返回兜底结构
...
else:
# 调用本地规则分析
return self.local_analyze_case(case_text)
- 功能:异常处理与兜底,JSON 解析失败时返回默认结构,API 调用失败时调用
local_analyze_case()使用本地规则分析案例。
2. 本地案例分析:local_analyze_case(第 254-316 行)
python
运行
def local_analyze_case(self, case_text):
"""使用本地规则分析案例"""
# 使用正则表达式提取案例信息
title_match = re.search(r'(判决书|裁定书|案例).*?$', case_text.split('\n')[0] if case_text.split('\n') else ['案例'])
title = title_match.group() if title_match else "法律案例分析"
- 功能:离线模式下,使用本地正则表达式与关键词匹配,提取案例关键信息,返回结构化结果。
- 设计思路:针对中文法律文书的固定格式,使用关键词(如「经审理查明」「争议焦点」)与正则表达式,提取核心信息,保证离线模式的可用性。
3. 视频脚本生成:generate_video_script(第 318-352 行)
4. 微信文章生成:generate_wechat_article(第 374-408 行)
- 核心逻辑与
analyze_legal_case一致:- 构造针对性的提示词,规范生成格式(如短视频脚本的分场景结构、微信文章的分段结构)。
- 调用带重试机制的 API 方法,获取生成结果。
- API 调用失败时,调用对应的本地生成方法(
local_generate_script/local_generate_article),返回兜底内容。
- 设计亮点:生成结果符合短视频 / 微信公众号的传播特性,语言通俗易懂,结构清晰,兼顾专业性与可读性。
八、辅助方法(文件操作 / 语音合成 / 视频制作)
1. 文件读取与保存:read_case_from_file /save_case_to_file(第 458-498 行)
- 功能:读取案例文件(支持 UTF-8 与 GBK 编码,解决中文乱码问题),保存案例内容到指定目录。
- 亮点:自动适配文件编码,避免中文法律文书读取时出现乱码;生成唯一时间戳文件名,避免文件覆盖。
2. 模拟语音合成与视频制作:convert_to_speech /create_video_with_scenes(第 410-456 行)
- 功能:模拟文字转语音(保存语音脚本),分割视频脚本为场景,生成视频制作指令(JSON 格式)。
- 设计思路:本代码为框架实现,实际应用中可集成 TTS 服务(如百度语音、阿里云语音)与视频编辑库(如
moviepy),替换模拟实现,实现真正的语音合成与视频生成。
九、批量处理方法:process_single_case /batch_process_all_cases(第 500-598 行)
1. 处理单个案例:process_single_case(第 500-572 行)
- 功能:串联「读取案例→分析案例→生成脚本→生成文章→语音合成→视频制作」的完整流程,处理单个案例文件,返回处理结果。
- 亮点:步骤清晰,每一步都有异常处理与状态反馈,生成的文件按功能分类存储,方便后续管理。
2. 批量处理所有案例:batch_process_all_cases(第 574-598 行)
- 功能:递归查找所有案例文件,批量处理,生成处理报告(JSON 格式),统计成功 / 失败数量。
- 亮点:
- 生成详细的批量处理报告,方便用户追溯处理结果。
- 案例间添加 2 秒延迟,避免触发 API 限流。
- 统计成功与失败数量,提供清晰的处理总结。
十、辅助函数与程序入口(第 600-680 行)
1. 环境配置:setup_environment(第 600-622 行)
- 功能:创建
.env配置文件(若不存在),填充默认配置,提醒用户替换 API 密钥。
2. 依赖检查:check_dependencies(第 624-644 行)
- 功能:检查必要的依赖包(
openai、python-dotenv)是否安装,缺失则提示用户安装。
3. 主函数:main(第 646-680 行)
- 功能:程序入口,串联「检查依赖→创建环境配置→初始化生成器→创建示例案例→批量处理案例」的完整流程。
- 亮点:若未找到案例文件,自动创建示例案例,方便用户快速测试程序功能,降低使用门槛。
python
运行
if __name__ == "__main__":
main()
- 功能:Python 程序入口判断,直接运行该脚本时执行
main()函数,作为模块导入时不执行,保证代码的可复用性。
总结
- 核心流程:
环境准备→案例读取→AI分析→内容生成→文件保存→批量报告,逻辑闭环,功能完整,兼顾在线与离线模式。 - 设计亮点:
- 并发控制与重试机制,提升 API 调用的稳定性与效率。
- 结构化提示词设计,保证 AI 生成结果的规范性。
- 完善的异常处理与兜底方案,程序鲁棒性强。
- 清晰的目录结构与文件管理,方便用户使用与扩展。
——————————————————————————————
完整代码
import os
import json
import time
from datetime import datetime
from pathlib import Path
import re
from openai import OpenAI
from dotenv import load_dotenv
import random
import threading
from queue import Queue
import requests
class LegalContentGenerator:
def __init__(self, api_key=None, base_url=None, max_concurrent=3, retry_delay=5, max_retries=3):
# 加载环境变量
load_dotenv()
# 从环境变量获取配置,如果没有传入则从环境变量读取
self.api_key = api_key or os.getenv("XIAOMI_API_KEY", "your_xiaomi_api_key_here")
self.base_url = base_url or os.getenv("XIAOMI_BASE_URL", "https://api.xiaomimimo.com/v1")
# 验证必要配置
if self.api_key == "your_xiaomi_api_key_here" or not self.api_key or self.api_key == "sk-cf3c9b4ioxd0e1wi6n5fi9ylk3nn2yuph0g7ta1j66xk7ssp":
print("警告: 未设置有效的XIAOMI_API_KEY环境变量,请在.env文件中配置")
self.use_offline_mode = True
else:
self.use_offline_mode = False
try:
# 初始化OpenAI客户端
self.client = OpenAI(
api_key=self.api_key,
base_url=self.base_url
)
except Exception as e:
print(f"初始化API客户端失败: {str(e)},切换到离线模式")
self.use_offline_mode = True
# 从环境变量获取模型参数
self.model_name = os.getenv("MODEL_NAME", "mimo-v2-flash")
self.max_tokens = int(os.getenv("MAX_TOKENS", "1024"))
self.temperature = float(os.getenv("TEMPERATURE", "0.3"))
self.top_p = float(os.getenv("TOP_P", "0.95"))
# 并发和重试配置
self.max_concurrent = max_concurrent # 最大并发数
self.retry_delay = retry_delay # 重试间隔
self.max_retries = max_retries # 最大重试次数
self.active_threads = 0 # 当前活跃线程数
self.thread_lock = threading.Lock() # 线程锁
# 创建工作目录
self.work_dir = Path("./legal_content_workflow").resolve()
self.cases_dir = self.work_dir / "cases"
self.temp_dir = self.work_dir / "temp"
self.audio_dir = self.work_dir / "audio"
self.video_dir = self.work_dir / "video"
self.output_dir = self.work_dir / "output"
self.wechat_dir = self.work_dir / "wechat_articles"
# 确保所有目录存在
directories = [self.work_dir, self.cases_dir, self.temp_dir, self.audio_dir, self.video_dir, self.output_dir, self.wechat_dir]
for directory in directories:
directory.mkdir(parents=True, exist_ok=True)
def wait_for_slot(self):
"""等待并发槽位"""
while True:
with self.thread_lock:
if self.active_threads < self.max_concurrent:
self.active_threads += 1
break
time.sleep(0.5)
def release_slot(self):
"""释放并发槽位"""
with self.thread_lock:
self.active_threads -= 1
def call_xiaomi_api_with_retry(self, prompt, max_tokens=None, temperature=None, top_p=None):
"""带重试机制的API调用"""
tokens = max_tokens or self.max_tokens
temp = temperature or self.temperature
p = top_p or self.top_p
for attempt in range(self.max_retries + 1):
try:
# 检查是否达到最大并发限制
self.wait_for_slot()
try:
completion = self.client.chat.completions.create(
model=self.model_name,
messages=[
{
"role": "system",
"content": "You are MiMo, an AI assistant developed by Xiaomi. You are a legal expert specialized in analyzing Chinese legal cases and creating educational content. Please provide clear, accurate, and professional analysis."
},
{
"role": "user",
"content": prompt
}
],
max_completion_tokens=tokens,
temperature=temp,
top_p=p,
stream=False,
stop=None,
frequency_penalty=0,
presence_penalty=0,
extra_body={
"thinking": {"type": "disabled"}
}
)
# 提取返回的内容
content = completion.choices[0].message.content
result = content.strip()
# 释放并发槽位
self.release_slot()
return result
except Exception as e:
# 释放并发槽位
self.release_slot()
error_msg = str(e).lower()
if "rate limit" in error_msg or "concurrent" in error_msg or "too many requests" in error_msg:
print(f"速率限制错误,等待 {self.retry_delay}s 后重试... (尝试 {attempt + 1}/{self.max_retries + 1})")
if attempt < self.max_retries:
time.sleep(self.retry_delay)
continue
raise e
except Exception as e:
if attempt < self.max_retries:
print(f"API调用失败,等待 {self.retry_delay}s 后重试... (尝试 {attempt + 1}/{self.max_retries + 1})")
time.sleep(self.retry_delay)
else:
print(f"API调用最终失败: {str(e)}")
return self.simulate_api_response(prompt)
return self.simulate_api_response(prompt)
def simulate_api_response(self, prompt):
"""模拟API响应"""
print("使用离线模式处理...")
# 根据提示类型返回不同的模拟响应
if "分析以下法律案例" in prompt or ("标题" in prompt and "当事人" in prompt):
# 模拟案例分析
case_text = prompt.split("案例原文:")[1] if "案例原文:" in prompt else prompt
title = "模拟案例分析结果"
parties = "原告: 未知, 被告: 未知"
facts = case_text[:300] + "..." if len(case_text) > 300 else case_text
dispute_points = "争议焦点: 合同履行问题"
ruling = "法院判决: 支持原告部分诉讼请求"
legal_basis = "法律依据: 《民法典》相关条款"
return f"""{{
"title": "{title}",
"parties": "{parties}",
"facts": "{facts}",
"dispute_points": "{dispute_points}",
"ruling": "{ruling}",
"legal_basis": "{legal_basis}"
}}"""
elif "生成一个适合短视频的解说脚本" in prompt or ("开场" in prompt and "案情介绍" in prompt):
# 模拟视频脚本生成
return """【开场】
大家好,今天为大家解读一个重要的法律案例。
【案情介绍】
本案涉及合同纠纷,具体情况是...
【争议焦点】
案件的核心争议在于...
【法院判决】
最终法院认定...
【法条解读】
相关法律条文规定...
【结语】
这个案例提醒我们..."""
elif "为微信公众号生成一篇普法文章,人设是中年女律师,高智慧高情商" in prompt or "微信普法文章" in prompt:
# 模拟微信普法文章生成
return """# 微信普法 | 重要法律案例解读
## 案例背景
今日为您解读一起重要法律案例...
## 争议焦点
案件核心争议在于...
## 专业分析
作为专业律师,我们认为...
## 法律启示
此案例提醒大家注意...
*本文由专业律师团队撰写,仅供参考*"""
else:
# 通用模拟响应
return f"模拟API响应:由于离线模式,返回基于'{prompt[:50]}...'的分析结果"
def analyze_legal_case(self, case_text):
"""使用小米API分析法律案例"""
prompt = f"""
请仔细分析以下法律案例,并提取以下关键信息:
1. 案例标题
2. 当事人信息(原告、被告等)
3. 案件基本事实
4. 主要争议焦点
5. 法院判决结果
6. 适用的法律条文
案例原文:
{case_text}
请按照JSON格式返回分析结果:
{{
"title": "...",
"parties": "...",
"facts": "...",
"dispute_points": "...",
"ruling": "...",
"legal_basis": "..."
}}
"""
result = self.call_xiaomi_api_with_retry(prompt, max_tokens=1500)
if result and not result.startswith("API调用失败") and not result.startswith("模拟API响应"):
try:
# 提取JSON部分
json_match = re.search(r'\{.*\}', result, re.DOTALL)
if json_match:
json_str = json_match.group()
return json.loads(json_str)
else:
# 如果无法提取JSON,返回模拟结构
return {
"title": "案例分析结果",
"parties": "当事人信息分析中",
"facts": result[:500],
"dispute_points": "争议焦点分析中",
"ruling": "判决结果分析中",
"legal_basis": "法律依据分析中"
}
except json.JSONDecodeError:
print("JSON解析失败,使用文本分析结果")
return {
"title": "案例分析结果",
"parties": "分析结果",
"facts": result[:500],
"dispute_points": "争议焦点待分析",
"ruling": "判决结果待分析",
"legal_basis": "法律依据待分析"
}
else:
# 使用本地规则分析
print("使用本地规则分析案例...")
return self.local_analyze_case(case_text)
def local_analyze_case(self, case_text):
"""使用本地规则分析案例"""
# 使用正则表达式提取案例信息
title_match = re.search(r'(判决书|裁定书|案例).*?$', case_text.split('\n')[0] if case_text.split('\n') else ['案例'])
title = title_match.group() if title_match else "法律案例分析"
# 查找当事人信息
parties = "当事人信息待提取"
parties_patterns = [
r'原告.*?与.*?被告',
r'申请人.*?与.*?被申请人',
r'上诉人.*?与.*?被上诉人'
]
for pattern in parties_patterns:
match = re.search(pattern, case_text, re.IGNORECASE)
if match:
parties = match.group()
break
# 提取案件事实
facts_keywords = ['经审理查明', '事实如下', '查明事实', '案件事实']
facts = "案件事实待提取"
for keyword in facts_keywords:
if keyword in case_text:
start_pos = case_text.find(keyword) + len(keyword)
end_pos = case_text.find('\n\n', start_pos)
if end_pos == -1:
end_pos = start_pos + 500 # 默认取500字符
facts = case_text[start_pos:end_pos].strip()
break
# 提取争议焦点
dispute_keywords = ['争议焦点', '争议问题', '争议所在']
dispute_points = "争议焦点待提取"
for keyword in dispute_keywords:
if keyword in case_text:
start_pos = case_text.find(keyword) + len(keyword)
end_pos = case_text.find('\n\n', start_pos)
if end_pos == -1:
end_pos = start_pos + 300 # 默认取300字符
dispute_points = case_text[start_pos:end_pos].strip()
break
# 提取判决结果
ruling_keywords = ['判决如下', '裁定如下', '法院认为', '法院判决']
ruling = "判决结果待提取"
for keyword in ruling_keywords:
if keyword in case_text:
start_pos = case_text.find(keyword) + len(keyword)
end_pos = case_text.find('\n\n', start_pos)
if end_pos == -1:
end_pos = start_pos + 300 # 默认取300字符
ruling = case_text[start_pos:end_pos].strip()
break
# 提取法律依据
legal_basis = "法律依据待提取"
legal_patterns = [
r'《.*?》第.*?条',
r'根据.*?规定',
r'依据.*?法律'
]
for pattern in legal_patterns:
matches = re.findall(pattern, case_text)
if matches:
legal_basis = ', '.join(matches[:5]) # 最多取5个
break
return {
"title": title,
"parties": parties,
"facts": facts,
"dispute_points": dispute_points,
"ruling": ruling,
"legal_basis": legal_basis
}
def generate_video_script(self, analysis_result):
"""生成视频解说脚本"""
prompt = f"""
基于以下法律案例分析结果,生成一个适合短视频的解说脚本。
要求:
1. 语言通俗易懂,适合普通观众
2. 结构清晰,分为开场、案情介绍、争议焦点、法院判决、法条解读、结语
3. 总长度控制在2-3分钟(约300-500字)
4. 突出重点,便于配合画面展示
分析结果:
标题:{analysis_result.get('title', '')}
当事人:{analysis_result.get('parties', '')}
事实:{analysis_result.get('facts', '')}
争议焦点:{analysis_result.get('dispute_points', '')}
判决:{analysis_result.get('ruling', '')}
法律依据:{analysis_result.get('legal_basis', '')}
返回格式:
【开场】
...
【案情介绍】
...
【争议焦点】
...
【法院判决】
...
【法条解读】
...
【结语】
...
"""
script = self.call_xiaomi_api_with_retry(prompt, max_tokens=1000)
if script and not script.startswith("视频脚本生成失败") and not script.startswith("模拟API响应"):
return script
else:
# 使用本地规则生成脚本
print("使用本地规则生成视频脚本...")
return self.local_generate_script(analysis_result)
def local_generate_script(self, analysis_result):
"""使用本地规则生成视频脚本"""
title = analysis_result.get('title', '法律案例解读')
parties = analysis_result.get('parties', '当事人信息')
facts = analysis_result.get('facts', '案件事实')
disputes = analysis_result.get('dispute_points', '争议焦点')
ruling = analysis_result.get('ruling', '判决结果')
basis = analysis_result.get('legal_basis', '法律依据')
script = f"""【开场】
大家好,今天为您解读一起重要的法律案例——《{title}》。
【案情介绍】
本案涉及{parties}。案件的基本情况是:{facts[:200]}...
【争议焦点】
案件的核心争议在于:{disputes}
【法院判决】
法院最终认定:{ruling[:150]}...
【法条解读】
相关法律依据是:{basis}
【结语】
通过这个案例,我们可以了解到...希望对您有所帮助!"""
return script
def generate_wechat_article(self, analysis_result):
"""生成微信普法文章"""
prompt = f"""
基于以下法律案例分析结果,为微信公众号生成一篇专业的普法文章。
要求:
1. 文章标题吸引人,突出争议焦点
2. 内容专业但通俗易懂
3. 重点突出争议焦点和法律分析
4. 体现律师专业性
5. 结构清晰,适合手机阅读
6. 总长度控制在800-1200字
分析结果:
标题:{analysis_result.get('title', '')}
当事人:{analysis_result.get('parties', '')}
事实:{analysis_result.get('facts', '')}
争议焦点:{analysis_result.get('dispute_points', '')}
判断:{analysis_result.get('ruling', '')}
法律依据:{analysis_result.get('legal_basis', '')}
返回格式:
# 文章标题
## 案例背景
...
## 争议焦点
...
## 专业分析
...
## 法律启示
...
*本文由专业律师团队撰写,仅供参考*
"""
article = self.call_xiaomi_api_with_retry(prompt, max_tokens=1500)
if article and not article.startswith("微信文章生成失败") and not article.startswith("模拟API响应"):
return article
else:
# 使用本地规则生成文章
print("使用本地规则生成微信普法文章...")
return self.local_generate_article(analysis_result)
def local_generate_article(self, analysis_result):
"""使用本地规则生成微信普法文章"""
title = analysis_result.get('title', '法律案例解读')
parties = analysis_result.get('parties', '当事人信息')
facts = analysis_result.get('facts', '案件事实')
disputes = analysis_result.get('dispute_points', '争议焦点')
ruling = analysis_result.get('ruling', '判决结果')
basis = analysis_result.get('legal_basis', '法律依据')
article = f"""# 【以案说法】{title}
## 案例背景
近日,我们关注到一起涉及{parties}的重要法律案例。案件基本情况如下:{facts[:300]}...
## 争议焦点
本案的核心争议在于:{disputes}。这一争议点涉及到法律适用的关键问题,值得我们深入分析。
## 专业分析
作为专业律师,我们对此案例进行如下分析:
1. **法律关系认定**:根据{basis},本案中的法律关系明确...
2. **争议焦点剖析**:{disputes}的实质在于...
3. **判决合理性**:{ruling}体现了法律对此类问题的处理原则...
4. **实务启示**:此类案件提醒我们在实践中应注意...
## 法律启示
此案例具有重要的实践意义,特别是对于{parties}而言,提醒我们在类似情况下应当...
*本文由专业律师团队撰写,仅供参考。如需专业法律咨询,请联系执业律师。*"""
return article
def convert_to_speech(self, text, output_path):
"""模拟文字转语音(实际应用中应使用TTS服务)"""
# 这里简化处理,保存文本作为语音脚本
with open(output_path, 'w', encoding='utf-8') as f:
f.write(text)
print(f"语音脚本已保存至: {output_path}")
def create_video_with_scenes(self, script, output_path):
"""创建分场景的视频脚本"""
# 分割脚本为不同场景
scenes = script.split('【')
scene_descriptions = []
for i, scene in enumerate(scenes):
if scene.strip():
scene_name = scene.split('】')[0] if '】' in scene else f"场景{i+1}"
scene_content = scene.split('】')[1] if '】' in scene else scene
scene_info = {
"scene_number": i,
"scene_name": scene_name,
"content": scene_content.strip(),
"duration": min(len(scene_content) // 10, 30) # 估计时长
}
scene_descriptions.append(scene_info)
# 生成视频制作说明
video_instructions = {
"title": "法律案例解读视频",
"scenes": scene_descriptions,
"total_duration": sum(s['duration'] for s in scene_descriptions),
"bg_music": True,
"font_size": "medium",
"color_scheme": "professional"
}
# 保存视频制作指令
instruction_file = output_path.with_suffix('.json')
with open(instruction_file, 'w', encoding='utf-8') as f:
json.dump(video_instructions, f, ensure_ascii=False, indent=2)
print(f"视频制作指令已保存至: {instruction_file}")
# 模拟生成视频文件
with open(output_path, 'wb') as f:
f.write(b"dummy_video_file") # 模拟视频内容
return str(output_path)
def read_case_from_file(self, file_path):
"""从文件读取法律案例"""
file_path = Path(file_path)
if not file_path.exists():
print(f"错误: 文件不存在 - {file_path}")
return None
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
print(f"成功读取案例文件: {file_path}")
return content
except UnicodeDecodeError:
# 如果UTF-8解码失败,尝试其他编码
try:
with open(file_path, 'r', encoding='gbk') as f:
content = f.read()
print(f"成功读取案例文件: {file_path} (使用GBK编码)")
return content
except UnicodeDecodeError:
print(f"错误: 无法解码文件 - {file_path}")
return None
except Exception as e:
print(f"读取文件时发生错误: {str(e)}")
return None
def save_case_to_file(self, case_content, filename=None):
"""保存案例到文件"""
if filename is None:
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f"case_{timestamp}.txt"
file_path = self.cases_dir / filename
with open(file_path, 'w', encoding='utf-8') as f:
f.write(case_content)
print(f"案例已保存到: {file_path}")
return str(file_path)
def list_available_cases(self):
"""递归列出cases文件夹及子文件夹下的所有案例文件"""
case_files = []
for root, dirs, files in os.walk(self.cases_dir):
for file in files:
if file.lower().endswith(('.txt', '.md')):
file_path = Path(root) / file
case_files.append(file_path)
if not case_files:
print("未找到案例文件")
return []
print(f"找到 {len(case_files)} 个案例文件:")
for i, file_path in enumerate(case_files, 1):
# 计算相对于cases_dir的路径
rel_path = file_path.relative_to(self.cases_dir)
print(f" {i}. {rel_path} ({file_path.stat().st_size} 字节)")
return case_files
def process_single_case(self, case_file):
"""处理单个案例文件"""
try:
print(f"开始处理案例: {case_file}")
# 读取案例内容
case_text = self.read_case_from_file(case_file)
if case_text is None:
print(f"无法读取案例文件: {case_file}")
return {
"case_file": str(case_file),
"result": None,
"status": "failed",
"error": "无法读取文件"
}
# 使用文件名作为标题前缀
title_prefix = case_file.stem.replace(' ', '_')
# 步骤1: 分析法律案例
print(f"[{title_prefix}] 步骤1: 正在分析法律案例...")
analysis = self.analyze_legal_case(case_text)
if not analysis:
print(f"[{title_prefix}] 案例分析失败")
return {
"case_file": str(case_file),
"result": None,
"status": "failed",
"error": "案例分析失败"
}
print(f"[{title_prefix}] 案例分析完成")
# 步骤2: 生成视频脚本
print(f"[{title_prefix}] 步骤2: 正在生成视频脚本...")
script = self.generate_video_script(analysis)
if not script or script.startswith("视频脚本生成失败"):
print(f"[{title_prefix}] 视频脚本生成失败")
return {
"case_file": str(case_file),
"result": None,
"status": "failed",
"error": "视频脚本生成失败"
}
script_path = self.temp_dir / f"{title_prefix}_video_script.txt"
with open(script_path, 'w', encoding='utf-8') as f:
f.write(script)
print(f"[{title_prefix}] 视频脚本生成完成")
# 步骤3: 生成微信普法文章
print(f"[{title_prefix}] 步骤3: 正在生成微信普法文章...")
article = self.generate_wechat_article(analysis)
if not article or article.startswith("微信文章生成失败"):
print(f"[{title_prefix}] 微信文章生成失败")
return {
"case_file": str(case_file),
"result": None,
"status": "failed",
"error": "微信文章生成失败"
}
article_path = self.wechat_dir / f"{title_prefix}_wechat_article.md"
with open(article_path, 'w', encoding='utf-8') as f:
f.write(article)
print(f"[{title_prefix}] 微信普法文章生成完成")
# 步骤4: 语音合成
print(f"[{title_prefix}] 步骤4: 正在生成语音...")
speech_path = self.audio_dir / f"{title_prefix}_speech.txt"
self.convert_to_speech(script, speech_path)
print(f"[{title_prefix}] 语音生成完成")
# 步骤5: 创建视频制作指令
print(f"[{title_prefix}] 步骤5: 正在创建视频制作指令...")
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
video_output_path = self.output_dir / f"{title_prefix}_{timestamp}.mp4"
final_video = self.create_video_with_scenes(script, video_output_path)
print(f"[{title_prefix}] 视频制作指令生成完成: {final_video}")
# 返回处理结果
result = {
"video_path": final_video,
"script_path": str(script_path),
"article_path": str(article_path),
"speech_path": str(speech_path),
"analysis": analysis,
"processing_time": time.time()
}
return {
"case_file": str(case_file),
"result": result,
"status": "success",
"error": None
}
except Exception as e:
print(f"处理案例 {case_file} 时发生错误: {str(e)}")
return {
"case_file": str(case_file),
"result": None,
"status": "failed",
"error": str(e)
}
def batch_process_all_cases(self):
"""批量处理所有案例文件"""
print("开始批量处理所有案例文件...")
case_files = self.list_available_cases()
if not case_files:
print("未找到任何案例文件,无法批量处理")
return []
results = []
completed = 0
failed = 0
for i, case_file in enumerate(case_files, 1):
print(f"\n--- 处理第 {i}/{len(case_files)} 个案例 ---")
# 计算相对于cases_dir的路径作为显示名
rel_path = case_file.relative_to(self.cases_dir)
print(f"案例文件: {rel_path}")
# 处理单个案例
result = self.process_single_case(case_file)
results.append(result)
if result['status'] == 'success':
completed += 1
print(f"✓ 成功处理: {rel_path}")
else:
failed += 1
print(f"✗ 失败处理: {rel_path}, 错误: {result['error']}")
# 添加延迟以避免API限制
if i < len(case_files):
print("等待2秒后继续处理下一个案例...")
time.sleep(2)
# 生成批量处理报告
report_path = self.output_dir / f"batch_processing_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
report = {
"batch_start_time": datetime.now().isoformat(),
"total_cases": len(case_files),
"successful_cases": completed,
"failed_cases": failed,
"results": results
}
with open(report_path, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"\n批量处理完成!")
print(f"总案例数: {report['total_cases']}")
print(f"成功处理: {report['successful_cases']}")
print(f"处理失败: {report['failed_cases']}")
print(f"报告保存至: {report_path}")
return results
def setup_environment():
"""创建环境配置文件"""
env_path = Path(".env")
if not env_path.exists():
env_content = '''
# 小米大模型API配置
XIAOMI_API_KEY=your_actual_xiaomi_api_key_here
XIAOMI_BASE_URL=https://api.xiaomimimo.com/v1
# 可选配置
MODEL_NAME=mimo-v2-flash
MAX_TOKENS=1024
TEMPERATURE=0.3
TOP_P=0.95
'''
with open(env_path, 'w', encoding='utf-8') as f:
f.write(env_content.strip())
print(f"已创建环境配置文件: {env_path.absolute()}")
else:
print(f"环境配置文件已存在: {env_path.absolute()}")
# 检查配置内容
with open(env_path, 'r', encoding='utf-8') as f:
content = f.read()
if 'sk-cf3c9b4ioxd0e1wi6n5fi9ylk3nn2yuph0g7ta1j66xk7ssp' in content:
print("警告: 发现测试密钥,请替换为实际的API密钥")
def check_dependencies():
"""检查必要的依赖包"""
missing_deps = []
try:
import openai
print("✓ openai 已安装")
except ImportError:
missing_deps.append("openai")
print("✗ 缺少依赖包: openai")
try:
import dotenv
print("✓ python-dotenv 已安装")
except ImportError:
missing_deps.append("python-dotenv")
print("✗ 缺少依赖包: python-dotenv")
if missing_deps:
print(f"请运行: pip install {' '.join(missing_deps)}")
return False
return True
def main():
# 检查依赖
if not check_dependencies():
return
# 创建环境配置文件
setup_environment()
# 初始化生成器(降低并发数以避免API限制)
generator = LegalContentGenerator(max_concurrent=2, retry_delay=3, max_retries=2)
# 显示当前配置
print(f"当前配置:")
print(f" API Key: {'已配置' if generator.api_key != 'your_xiaomi_api_key_here' else '未配置'}")
print(f" Base URL: {generator.base_url}")
print(f" Model: {generator.model_name}")
print(f" Max Tokens: {generator.max_tokens}")
print(f" Temperature: {generator.temperature}")
print(f" Top P: {generator.top_p}")
print(f" 最大并发数: {generator.max_concurrent}")
print(f" 重试延迟: {generator.retry_delay}s")
print(f" 最大重试次数: {generator.max_retries}")
print(f" 工作目录: {generator.work_dir.absolute()}")
print(f" 案例目录: {generator.cases_dir.absolute()}")
print(f" 模式: {'在线模式' if not generator.use_offline_mode else '离线模式'}")
# 列出并处理所有可用案例
print("\n--- 开始处理所有可用案例文件 ---")
available_cases = generator.list_available_cases()
if not available_cases:
print("未找到案例文件,创建示例案例...")
sample_case = """
北京市朝阳区人民法院民事判决书
原告张三与被告李四房屋租赁合同纠纷一案
经审理查明:2023年3月1日,原告张三与被告李四签订房屋租赁合同,
约定被告将其位于北京市朝阳区某小区房屋出租给原告,租期一年,
月租金5000元,押金10000元。合同签订后,原告依约支付了租金和押金。
2023年5月,被告无故要求解除合同并收回房屋,原告不同意,
被告强行换锁导致原告无法进入房屋。原告认为被告构成根本违约,
要求解除合同并返还押金、赔偿损失。
被告辩称:因家庭原因需要收回房屋自住,同意退还部分租金,
但认为不应全额退还押金。
争议焦点:1. 被告是否有权单方解除合同;2. 押金是否应当返还
法院认为:双方签订的租赁合同合法有效,被告无正当理由单方解约
构成根本违约,应承担违约责任。判决:1. 解除租赁合同;
2. 被告返还原告押金10000元;3. 被告赔偿原告损失3000元。
适用法律:《中华人民共和国民法典》第563条、第584条
"""
# 确保示例案例目录存在
sample_dir = generator.cases_dir / "sample"
sample_dir.mkdir(exist_ok=True)
case_file = generator.save_case_to_file(sample_case, "sample/sample_case.txt")
print(f"已创建示例案例文件: {case_file}")
available_cases = [Path(case_file)]
# 自动批量处理所有案例
print(f"\n自动开始批量处理所有 {len(available_cases)} 个案例...")
results = generator.batch_process_all_cases()
if __name__ == "__main__":
main()
更多推荐


所有评论(0)