From 320248749ca216aa9f309495762e6d62b1205073 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=91=86=E5=91=86=E7=9A=84=E7=8B=97=E7=86=8A?=
<386303472@qq.com>
Date: Wed, 24 Dec 2025 20:19:32 +0800
Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=90=8C=E5=AE=A1=E6=A0=B8=E6=99=BA?=
=?UTF-8?q?=E8=83=BDAgent?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main.py | 333 ++++++++++++
requirements.txt | 18 +
sample_contract.txt | 53 ++
static/css/animations.css | 395 ++++++++++++++
static/css/style.css | 481 ++++++++++++++++++
static/js/main.js | 435 ++++++++++++++++
templates/index.html | 86 ++++
...00\346\234\257\346\226\207\346\241\243.md" | 234 +++++++++
8 files changed, 2035 insertions(+)
create mode 100644 main.py
create mode 100644 requirements.txt
create mode 100644 sample_contract.txt
create mode 100644 static/css/animations.css
create mode 100644 static/css/style.css
create mode 100644 static/js/main.js
create mode 100644 templates/index.html
create mode 100644 "\346\212\200\346\234\257\346\226\207\346\241\243.md"
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..968f541
--- /dev/null
+++ b/main.py
@@ -0,0 +1,333 @@
+import os
+from lazyllm import OnlineChatModule, WebModule, TrainableModule
+
+# 创建合同审核智能Agent
+class ContractReviewAgent:
+ def __init__(self):
+ # 初始化模型,参考Agent2实现,提供更完善的错误处理
+ self.model = self._init_model()
+
+ def _create_mock_chat_module(self):
+ """创建模拟聊天模块作为后备方案"""
+ class MockChatModule:
+ def __call__(self, prompt):
+ # 简单的模拟响应
+ return "我是合同审核智能Agent,我可以帮助您分析合同内容、识别风险点并提供专业的审核意见。请上传合同文件进行审核。"
+
+ return MockChatModule()
+
+ def _init_model(self):
+ """初始化模型,参考Agent2的实现,提供更完善的模型加载和错误处理"""
+ try:
+ # 首先尝试使用API密钥(豆包或OpenAI)
+ api_key = os.environ.get("DOUBAO_API_KEY") or os.environ.get("LAZYLLM_DOUBAO_API_KEY") or os.environ.get("OPENAI_API_KEY")
+ if api_key:
+ print("检测到环境变量中的API密钥,使用在线模型")
+ try:
+ # 优先尝试豆包模型
+ if os.environ.get("DOUBAO_API_KEY") or os.environ.get("LAZYLLM_DOUBAO_API_KEY"):
+ model = OnlineChatModule(
+ source="doubao",
+ api_key=api_key
+ )
+ else:
+ # 如果没有豆包密钥但有OpenAI密钥,尝试OpenAI
+ model = OnlineChatModule(
+ source="openai",
+ api_key=api_key
+ )
+
+ # 测试API密钥是否有效
+ try:
+ test_response = model("测试")
+ print("API密钥验证成功,使用在线模型")
+ return model
+ except Exception as test_error:
+ print(f"API密钥验证失败: {str(test_error)}")
+ print("将尝试其他模型选项")
+ # 继续尝试其他选项
+ except Exception as api_error:
+ print(f"使用API密钥初始化在线模型失败: {str(api_error)}")
+ print("将尝试其他模型选项")
+ # 继续尝试其他选项
+
+ # 尝试加载本地模型
+ local_model_path = os.environ.get("LOCAL_MODEL_PATH")
+ if local_model_path:
+ if os.path.exists(local_model_path):
+ print(f"尝试加载本地模型: {local_model_path}")
+ try:
+ model = TrainableModule(local_model_path)
+ # 尝试启动模型
+ try:
+ model.start()
+ print("本地模型启动成功")
+ return model
+ except Exception as start_error:
+ print(f"本地模型启动失败: {str(start_error)}")
+ print("将尝试使用在线模型作为后备")
+ # 继续尝试其他选项
+ except Exception as e:
+ print(f"加载本地模型失败: {str(e)}")
+ else:
+ print(f"本地模型路径不存在: {local_model_path}")
+
+ # 尝试使用常见的本地模型路径
+ common_model_paths = [
+ os.path.expanduser("~/.cache/huggingface/hub"),
+ "/models",
+ "./models"
+ ]
+
+ for path in common_model_paths:
+ if os.path.exists(path):
+ print(f"尝试从常见路径加载模型: {path}")
+ try:
+ # 尝试列出目录中的模型
+ model_dirs = [d for d in os.listdir(path)
+ if os.path.isdir(os.path.join(path, d))]
+ if model_dirs:
+ # 尝试第一个模型目录
+ model_path = os.path.join(path, model_dirs[0])
+ print(f"找到模型目录: {model_path}")
+ model = TrainableModule(model_path)
+ # 尝试启动模型
+ try:
+ model.start()
+ print("本地模型启动成功")
+ return model
+ except Exception as start_error:
+ print(f"本地模型启动失败: {str(start_error)}")
+ print("将尝试下一个模型或在线模型")
+ continue
+ except Exception as e:
+ print(f"从常见路径加载模型失败: {str(e)}")
+ continue
+
+ # 尝试使用在线模型(不指定API密钥,使用默认配置)
+ print("尝试使用在线模型...")
+ try:
+ model = OnlineChatModule()
+ # 测试模型是否可用
+ try:
+ test_response = model("测试")
+ print("在线模型验证成功")
+ return model
+ except Exception as test_error:
+ print(f"在线模型验证失败: {str(test_error)}")
+ # 使用模拟聊天模块作为后备
+ print("使用模拟聊天模块作为后备")
+ model = self._create_mock_chat_module()
+ return model
+ except Exception as online_error:
+ print(f"在线模型初始化失败: {str(online_error)}")
+ # 使用模拟聊天模块作为后备
+ print("使用模拟聊天模块作为后备")
+ model = self._create_mock_chat_module()
+ return model
+ except Exception as e:
+ print(f"初始化模型失败: {str(e)}")
+ # 使用模拟聊天模块作为后备
+ print("使用模拟聊天模块作为后备")
+ model = self._create_mock_chat_module()
+ return model
+
+ def load_contract(self, contract_path):
+ """加载合同文档"""
+ try:
+ # 简单的文件读取实现
+ with open(contract_path, 'r', encoding='utf-8') as f:
+ content = f.read()
+ return [{'page_content': content}]
+ except UnicodeDecodeError:
+ # 尝试其他编码
+ with open(contract_path, 'r', encoding='gbk') as f:
+ content = f.read()
+ return [{'page_content': content}]
+
+ def analyze_contract(self, documents):
+ """分析合同内容"""
+ # 合同审核提示词
+ prompt = """
+ 作为一名专业的合同审核律师,请对以下合同内容进行详细分析,并提供以下几个方面的审核意见:
+ 1. 合同条款完整性检查
+ 2. 潜在风险点识别
+ 3. 法律合规性评估
+ 4. 建议修改的条款
+ 5. 整体风险评级(低、中、高)
+
+ 请提供专业、详细且有针对性的审核意见。
+ """
+
+ # 将文档内容合并
+ contract_content = "\n".join([doc['page_content'] for doc in documents])
+
+ # 使用本地模型进行合同分析
+ response = self.model(f"{prompt}\n\n合同内容:\n{contract_content}")
+ return response
+
+ def summarize_review(self, result):
+ """总结审核结果"""
+ # 总结提示词
+ prompt = """
+ 请对以下合同审核意见进行简洁明了的总结,突出重点风险和建议,便于快速了解合同的主要问题和改进方向。
+ """
+
+ # 使用本地模型进行总结
+ response = self.model(f"{prompt}\n\n合同审核意见:\n{result}")
+ return response
+
+ def review_contract(self, contract_path):
+ """审核合同的入口方法"""
+ try:
+ # 按顺序执行审核步骤,替代原有的pipeline功能
+ documents = self.load_contract(contract_path)
+ analysis_result = self.analyze_contract(documents)
+ summary_result = self.summarize_review(analysis_result)
+ return summary_result
+ except Exception as e:
+ return f"审核过程中出现错误:{str(e)}"
+
+# 创建Web应用
+class ContractReviewWebApp:
+ def __init__(self):
+ self.agent = ContractReviewAgent()
+
+ def create_web_module(self):
+ """创建Web模块"""
+ # 创建WebModule
+ web_module = WebModule(
+ inputs=[
+ {
+ "name": "contract_path",
+ "type": "text",
+ "label": "合同文件路径",
+ "placeholder": "请输入合同文件的绝对路径或相对路径"
+ }
+ ],
+ outputs=[
+ {
+ "name": "review_result",
+ "type": "markdown",
+ "label": "合同审核结果"
+ }
+ ],
+ func=self.agent.review_contract,
+ title="合同审核智能Agent",
+ description="基于大模型的合同审核工具,能够自动识别合同中的风险点并提供专业的审核意见"
+ )
+
+ return web_module
+
+# 创建命令行界面
+class ContractReviewCLI:
+ def __init__(self):
+ self.agent = ContractReviewAgent()
+
+ def run(self):
+ """运行命令行界面"""
+ print("=== 合同审核智能Agent ===")
+ while True:
+ print("\n请选择操作:")
+ print("1. 审核合同文件")
+ print("2. 退出")
+
+ choice = input("请输入选项 (1-2): ").strip()
+
+ if choice == "1":
+ contract_path = input("请输入合同文件路径: ").strip()
+ print("正在审核合同...")
+ try:
+ result = self.agent.review_contract(contract_path)
+ print("\n合同审核结果:")
+ print(result)
+ except Exception as e:
+ print(f"审核失败: {str(e)}")
+ elif choice == "2":
+ print("感谢使用合同审核智能Agent,再见!")
+ break
+ else:
+ print("无效的选项,请重新输入。")
+
+# 创建Flask Web服务
+def create_flask_app():
+ from flask import Flask, request, render_template, jsonify
+ app = Flask(__name__)
+
+ # 创建合同审核智能Agent实例
+ agent = ContractReviewAgent()
+
+ @app.route('/', methods=['GET'])
+ def index():
+ return render_template('index.html')
+
+ # 处理Vite客户端请求(避免404错误)
+ @app.route('/@vite/client')
+ def vite_client():
+ return '', 204
+
+ @app.route('/review', methods=['POST'])
+ def review():
+ # 处理文件上传
+ if 'contract_file' not in request.files:
+ return jsonify({'error': '没有上传文件'}), 400
+
+ file = request.files['contract_file']
+
+ if file.filename == '':
+ return jsonify({'error': '没有选择文件'}), 400
+
+ if file:
+ # 保存上传的文件到临时目录
+ import tempfile
+ temp_dir = tempfile.gettempdir()
+ temp_path = os.path.join(temp_dir, file.filename)
+ file.save(temp_path)
+
+ try:
+ # 审核合同
+ review_result = agent.review_contract(temp_path)
+
+ # 删除临时文件
+ os.remove(temp_path)
+
+ return jsonify({'success': True, 'result': review_result})
+ except Exception as e:
+ # 删除临时文件
+ if os.path.exists(temp_path):
+ os.remove(temp_path)
+
+ return jsonify({'error': str(e)}), 500
+
+ return jsonify({'error': '文件处理失败'}), 400
+
+ return app
+
+# 主函数
+def main():
+ import sys
+
+ if len(sys.argv) > 1:
+ if sys.argv[1] == "web":
+ # 启动Web服务
+ app = create_flask_app()
+ port = int(sys.argv[2]) if len(sys.argv) > 2 else 5000
+ print(f"启动Web服务,访问地址: http://localhost:{port}")
+ app.run(debug=True, host='0.0.0.0', port=port)
+ elif sys.argv[1] == "cli":
+ # 启动命令行界面
+ cli = ContractReviewCLI()
+ cli.run()
+ else:
+ print("未知的命令参数")
+ print("可用命令:")
+ print(" python main.py web [端口] - 启动Web服务")
+ print(" python main.py cli - 启动命令行界面")
+ else:
+ # 默认启动Web服务
+ app = create_flask_app()
+ print("启动Web服务,访问地址: http://localhost:5000")
+ app.run(debug=True, host='0.0.0.0', port=5000)
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..d119c62
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,18 @@
+lazyllm>=0.1.0
+transformers>=4.30.0
+pydantic>=2.0.0
+requests>=2.31.0
+pandas>=2.0.0
+flask>=2.0.0
+numpy>=1.24.0
+matplotlib>=3.7.0
+openai>=1.0.0 # 用于真实API调用
+tiktoken>=0.5.0 # 用于token计算
+aiohttp>=3.8.0 # 用于异步HTTP请求
+asyncio # 用于异步处理
+torch>=2.0.0 # 本地模型所需
+accelerate>=0.20.0 # 本地模型加速
+sentencepiece>=0.1.99 # 本地模型分词
+protobuf>=3.20.0 # 本地模型依赖
+scipy>=1.10.0 # 本地模型依赖
+modelscope>=1.30.0 # 本地模型下载和管理
\ No newline at end of file
diff --git a/sample_contract.txt b/sample_contract.txt
new file mode 100644
index 0000000..f3a7647
--- /dev/null
+++ b/sample_contract.txt
@@ -0,0 +1,53 @@
+# 服务合同
+
+## 合同双方
+甲方(委托方):[公司名称]
+地址:[地址]
+法定代表人:[姓名]
+
+乙方(服务方):[服务公司名称]
+地址:[地址]
+法定代表人:[姓名]
+
+## 服务内容
+1. 乙方为甲方提供[具体服务内容]服务
+2. 服务范围包括但不限于:[详细服务范围]
+3. 服务标准应符合行业规范和甲方要求
+
+## 服务期限
+本合同自双方签字盖章之日起生效,有效期为[X]年。
+
+## 服务费用
+1. 本合同项下服务费用总计为人民币[金额]元
+2. 支付方式:甲方应在合同签订后[X]个工作日内支付全部费用
+3. 若甲方逾期支付,每逾期一日,应按未支付金额的3%向乙方支付违约金
+
+## 双方权利义务
+### 甲方权利义务
+1. 按时支付服务费用
+2. 提供必要的协助和配合
+3. 有权对服务质量提出异议
+
+### 乙方权利义务
+1. 按照合同约定提供服务
+2. 保守甲方的商业秘密
+3. 定期向甲方汇报服务进度
+
+## 违约责任
+1. 任何一方违反合同约定,应向对方支付合同总金额50%的违约金
+2. 如因乙方原因导致服务质量不达标,乙方应无偿返工直至达标
+
+## 争议解决
+本合同履行过程中如发生争议,双方应首先协商解决;协商不成的,任何一方均有权向甲方所在地人民法院提起诉讼。
+
+## 其他条款
+1. 本合同一式两份,甲乙双方各执一份,具有同等法律效力
+2. 本合同未尽事宜,可由双方另行签订补充协议
+
+甲方(盖章):__________________
+法定代表人(签字):____________
+日期:______年____月____日
+
+乙方(盖章):__________________
+法定代表人(签字):____________
+日期:______年____月____日
diff --git a/static/css/animations.css b/static/css/animations.css
new file mode 100644
index 0000000..be25a98
--- /dev/null
+++ b/static/css/animations.css
@@ -0,0 +1,395 @@
+/* 动画效果 */
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+@keyframes slideIn {
+ from { transform: translateX(-20px); opacity: 0; }
+ to { transform: translateX(0); opacity: 1; }
+}
+
+@keyframes pulse {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.05); }
+ 100% { transform: scale(1); }
+}
+
+@keyframes slideDown {
+ from { max-height: 0; opacity: 0; }
+ to { max-height: 1000px; opacity: 1; }
+}
+
+@keyframes highlight {
+ 0% { background-color: #ffeb3b; }
+ 100% { background-color: transparent; }
+}
+
+/* 增强的页面元素动画 */
+.fade-in {
+ animation: fadeIn 0.6s ease-out;
+}
+
+.slide-in {
+ animation: slideIn 0.5s ease-out;
+}
+
+.slide-down {
+ animation: slideDown 0.5s ease-out;
+}
+
+/* 卡片悬停效果 */
+.card {
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+}
+
+.card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 10px 25px rgba(0,0,0,0.1);
+}
+
+/* 按钮点击效果 */
+.btn {
+ position: relative;
+ overflow: hidden;
+ transition: all 0.3s ease;
+}
+
+.btn:after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.5);
+ transform: translate(-50%, -50%);
+ transition: width 0.6s, height 0.6s;
+}
+
+.btn:active:after {
+ width: 300px;
+ height: 300px;
+}
+
+/* 进度条样式 */
+.progress-bar {
+ height: 4px;
+ background: #e0e0e0;
+ border-radius: 2px;
+ overflow: hidden;
+ margin: 20px 0;
+ display: none;
+}
+
+.progress-bar.active {
+ display: block;
+}
+
+.progress-bar .progress {
+ height: 100%;
+ background: linear-gradient(90deg, #4CAF50, #8BC34A);
+ width: 0;
+ border-radius: 2px;
+ animation: progress 2s ease-in-out infinite;
+}
+
+@keyframes progress {
+ 0% { width: 0%; }
+ 50% { width: 70%; }
+ 100% { width: 100%; }
+}
+
+/* 结果区域增强样式 */
+.result-section {
+ position: relative;
+ overflow: hidden;
+}
+
+.result-section:before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
+ animation: shimmer 2s infinite;
+}
+
+@keyframes shimmer {
+ 0% { left: -100%; }
+ 100% { left: 100%; }
+}
+
+/* 报告区块动画 */
+.report-container {
+ animation: fadeIn 0.6s ease-out;
+}
+
+.report-header {
+ animation: slideDown 0.7s ease-out;
+}
+
+section {
+ animation: slideUp 0.8s ease-out;
+ animation-fill-mode: both;
+}
+
+section:nth-child(2) {
+ animation-delay: 0.1s;
+}
+
+section:nth-child(3) {
+ animation-delay: 0.2s;
+}
+
+section:nth-child(4) {
+ animation-delay: 0.3s;
+}
+
+section:nth-child(5) {
+ animation-delay: 0.4s;
+}
+
+/* 列表项动画 */
+.list-item {
+ animation: slideInRight 0.5s ease-out;
+ animation-fill-mode: both;
+}
+
+.list-item:nth-child(1) {
+ animation-delay: 0.1s;
+}
+
+.list-item:nth-child(2) {
+ animation-delay: 0.2s;
+}
+
+.list-item:nth-child(3) {
+ animation-delay: 0.3s;
+}
+
+.list-item:nth-child(4) {
+ animation-delay: 0.4s;
+}
+
+.list-item:nth-child(5) {
+ animation-delay: 0.5s;
+}
+
+.list-item:nth-child(n+6) {
+ animation-delay: 0.6s;
+}
+
+/* 数字列表项动画 */
+.numbered-item {
+ animation: slideInRight 0.5s ease-out;
+ animation-fill-mode: both;
+}
+
+.numbered-item:nth-child(1) {
+ animation-delay: 0.1s;
+}
+
+.numbered-item:nth-child(2) {
+ animation-delay: 0.2s;
+}
+
+.numbered-item:nth-child(3) {
+ animation-delay: 0.3s;
+}
+
+.numbered-item:nth-child(4) {
+ animation-delay: 0.4s;
+}
+
+.numbered-item:nth-child(5) {
+ animation-delay: 0.5s;
+}
+
+/* 段落动画 */
+.content-paragraph {
+ animation: fadeIn 0.6s ease-out;
+ animation-fill-mode: both;
+}
+
+/* 风险徽章动画 */
+.risk-badge {
+ animation: pulse 2s infinite;
+}
+
+/* 新增动画关键帧 */
+@keyframes slideUp {
+ from {
+ opacity: 0;
+ transform: translateY(30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideInRight {
+ from {
+ opacity: 0;
+ transform: translateX(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+/* 高亮动画 */
+.highlight-animation {
+ animation: highlight 1.5s ease-in-out;
+}
+
+@keyframes highlight {
+ 0% {
+ background-color: rgba(67, 97, 238, 0.2);
+ }
+ 50% {
+ background-color: rgba(67, 97, 238, 0.4);
+ }
+ 100% {
+ background-color: transparent;
+ }
+}
+
+/* 标签云效果 */
+.tag-cloud {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin: 15px 0;
+}
+
+.tag {
+ padding: 5px 12px;
+ background: #f0f0f0;
+ border-radius: 20px;
+ font-size: 0.8em;
+ transition: all 0.2s ease;
+}
+
+.tag:hover {
+ background: #e0e0e0;
+ transform: scale(1.05);
+}
+
+/* 滚动条美化 */
+::-webkit-scrollbar {
+ width: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: #f1f1f1;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #c1c1c1;
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #a1a1a1;
+}
+
+/* 提示框增强 */
+.tooltip {
+ position: relative;
+ display: inline-block;
+ cursor: help;
+}
+
+.tooltip .tooltip-text {
+ visibility: hidden;
+ width: 200px;
+ background-color: #333;
+ color: #fff;
+ text-align: center;
+ border-radius: 6px;
+ padding: 8px;
+ position: absolute;
+ z-index: 1;
+ bottom: 125%;
+ left: 50%;
+ margin-left: -100px;
+ opacity: 0;
+ transition: opacity 0.3s;
+ font-size: 0.8em;
+}
+
+.tooltip:hover .tooltip-text {
+ visibility: visible;
+ opacity: 1;
+}
+
+/* 响应式表格 */
+.responsive-table {
+ overflow-x: auto;
+}
+
+.responsive-table table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 20px 0;
+}
+
+.responsive-table th, .responsive-table td {
+ padding: 12px 15px;
+ text-align: left;
+ border-bottom: 1px solid #ddd;
+}
+
+.responsive-table th {
+ background-color: #f2f2f2;
+ font-weight: bold;
+}
+
+.responsive-table tr:hover {
+ background-color: #f5f5f5;
+}
+
+/* 深色模式支持 */
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #121212;
+ color: #e0e0e0;
+ }
+
+ .card {
+ background-color: #1e1e1e;
+ color: #e0e0e0;
+ }
+
+ .upload-form {
+ background: #2a2a2a;
+ }
+
+ .result {
+ background: #1a1a2e;
+ color: #e0e0e0;
+ }
+
+ .tag {
+ background: #333;
+ color: #e0e0e0;
+ }
+
+ .tag:hover {
+ background: #444;
+ }
+
+ .responsive-table th {
+ background-color: #333;
+ }
+
+ .responsive-table tr:hover {
+ background-color: #2a2a2a;
+ }
+}
\ No newline at end of file
diff --git a/static/css/style.css b/static/css/style.css
new file mode 100644
index 0000000..ef711cb
--- /dev/null
+++ b/static/css/style.css
@@ -0,0 +1,481 @@
+/* 合同审核智能Agent样式表 */
+
+/* 全局样式 */
+* {
+ box-sizing: border-box;
+}
+
+body {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+ line-height: 1.6;
+ color: #333;
+ background-color: #f8f9fa;
+}
+
+/* 标题样式 */
+h1 {
+ text-align: center;
+ margin-bottom: 10px;
+ font-size: 2.5rem;
+ font-weight: 700;
+}
+
+h2 {
+ color: #34495e;
+ padding-bottom: 10px;
+ margin-top: 30px;
+}
+
+/* 容器和卡片样式 */
+.container {
+ background-color: #fff;
+ border-radius: 10px;
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+ padding: 30px;
+ margin-bottom: 30px;
+}
+
+.card {
+ background-color: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
+ padding: 20px;
+ margin-bottom: 20px;
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+}
+
+.card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
+}
+
+/* 表单样式 */
+.form-group {
+ margin-bottom: 20px;
+}
+
+label {
+ display: block;
+ margin-bottom: 8px;
+ font-weight: 600;
+ color: #2c3e50;
+}
+
+input[type="file"] {
+ width: 100%;
+ padding: 12px;
+ border: 2px dashed #ddd;
+ border-radius: 5px;
+ background-color: #f9f9f9;
+ transition: border-color 0.3s ease;
+}
+
+input[type="file"]:focus {
+ border-color: #3498db;
+ outline: none;
+}
+
+/* 高亮样式 */
+.highlight {
+ background-color: #ffeb3b;
+ padding: 2px 4px;
+ border-radius: 3px;
+ font-weight: bold;
+}
+
+/* 按钮样式 */
+.btn {
+ display: inline-block;
+ padding: 12px 24px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-weight: 600;
+ text-align: center;
+ text-decoration: none;
+ transition: background-color 0.3s ease, transform 0.2s ease;
+}
+
+.btn-primary {
+ background-color: #3498db;
+ color: white;
+}
+
+.btn-primary:hover {
+ background-color: #2980b9;
+ transform: translateY(-2px);
+}
+
+.btn-success {
+ background-color: #2ecc71;
+ color: white;
+}
+
+.btn-success:hover {
+ background-color: #27ae60;
+ transform: translateY(-2px);
+}
+
+.btn-block {
+ display: block;
+ width: 100%;
+}
+
+/* 按钮样式增强 */
+.primary-btn {
+ background-color: #4CAF50;
+ color: white;
+}
+
+.secondary-btn {
+ background-color: #f44336;
+ color: white;
+}
+
+.secondary-btn:hover {
+ background-color: #d32f2f;
+}
+
+/* 结果操作按钮区域 */
+.result-actions {
+ margin-top: 20px;
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+/* 结果区域样式 */
+.result-container {
+ background: white;
+ border-radius: 12px;
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+ padding: 0;
+ margin: 20px 0;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ border: 1px solid #eaeaea;
+ display: none;
+}
+
+/* 报告容器样式 */
+.report-container {
+ max-width: 100%;
+ margin: 0 auto;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ color: #333;
+ line-height: 1.6;
+}
+
+/* 报告头部样式 */
+.report-header {
+ background: #4361ee;
+ color: white !important;
+ padding: 30px 40px;
+ text-align: center;
+}
+
+.report-title {
+ margin: 0;
+ font-size: 2.2rem;
+ font-weight: 600;
+ letter-spacing: -0.5px;
+}
+
+/* 区块通用样式 */
+section {
+ margin-bottom: 0;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+section:last-child {
+ border-bottom: none;
+}
+
+.section-header {
+ padding: 20px 30px;
+ border-bottom: 1px solid #f5f5f5;
+ display: flex;
+ align-items: center;
+}
+
+.section-title {
+ margin: 0;
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: #2d3436;
+ display: flex;
+ align-items: center;
+}
+
+.section-title .icon {
+ margin-right: 12px;
+ font-size: 1.8rem;
+}
+
+.section-content {
+ padding: 25px 0;
+}
+
+/* 评估部分样式 */
+.assessment-section .section-header {
+ background-color: #f8f9fa;
+}
+
+.assessment-section .section-title {
+ color: #0984e3;
+}
+
+/* 风险部分样式 */
+.risks-section .section-header {
+ background-color: #fff5f5;
+}
+
+.risks-section .section-title {
+ color: #e74c3c;
+}
+
+/* 建议部分样式 */
+.suggestions-section .section-header {
+ background-color: #f0f7ff;
+}
+
+.suggestions-section .section-title {
+ color: #3498db;
+}
+
+/* 结论部分样式 */
+.conclusion-section .section-header {
+ background-color: #f8fff8;
+}
+
+.conclusion-section .section-title {
+ color: #27ae60;
+}
+
+/* 列表样式 */
+.content-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.list-item {
+ margin-bottom: 16px;
+ padding: 16px 20px;
+ border-radius: 8px;
+ background-color: #f9f9f9;
+ border-left: 4px solid #ddd;
+ transition: all 0.2s ease;
+}
+
+.list-item:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+}
+
+.list-item.high-risk {
+ border-left-color: #e74c3c;
+ background-color: #fff5f5;
+}
+
+.list-item.medium-risk {
+ border-left-color: #f39c12;
+ background-color: #fffdf5;
+}
+
+.list-item.low-risk {
+ border-left-color: #27ae60;
+ background-color: #f5fff9;
+}
+
+.item-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.risk-badge {
+ padding: 4px 10px;
+ border-radius: 20px;
+ font-size: 0.8rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.risk-badge.high-risk {
+ background-color: #e74c3c;
+ color: white;
+}
+
+.risk-badge.medium-risk {
+ background-color: #f39c12;
+ color: white;
+}
+
+.risk-badge.low-risk {
+ background-color: #27ae60;
+ color: white;
+}
+
+/* 数字列表项样式 */
+.numbered-item {
+ display: flex;
+ margin-bottom: 16px;
+ align-items: flex-start;
+}
+
+.number-marker {
+ width: 28px;
+ height: 28px;
+ background-color: #4361ee;
+ color: white;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 600;
+ margin-right: 16px;
+ flex-shrink: 0;
+}
+
+.item-text {
+ flex: 1;
+ padding-top: 2px;
+}
+
+/* 段落样式 */
+.content-paragraph {
+ margin-bottom: 16px;
+ line-height: 1.7;
+ color: #444;
+}
+
+/* 子标题样式 */
+.subsection-title {
+ margin: 25px 0 15px;
+ font-size: 1.2rem;
+ font-weight: 600;
+ color: #2d3436;
+ border-bottom: 1px solid #eee;
+ padding-bottom: 8px;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .report-header {
+ padding: 20px;
+ }
+
+ .report-title {
+ font-size: 1.8rem;
+ }
+
+ .section-header {
+ padding: 15px 20px;
+ }
+
+ .section-content {
+ padding: 20px;
+ }
+
+ .item-content {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .risk-badge {
+ margin-top: 8px;
+ }
+}
+
+/* 加载动画 */
+.loading {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 40px;
+}
+
+.spinner {
+ width: 50px;
+ height: 50px;
+ border: 5px solid #f3f3f3;
+ border-top: 5px solid #3498db;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* 错误消息样式 */
+.error-message {
+ background-color: #f8d7da;
+ color: #721c24;
+ padding: 15px;
+ border-radius: 5px;
+ margin-top: 20px;
+ border-left: 4px solid #dc3545;
+}
+
+/* 成功消息样式 */
+.success-message {
+ background-color: #d4edda;
+ color: #155724;
+ padding: 15px;
+ border-radius: 5px;
+ margin-top: 20px;
+ border-left: 4px solid #28a745;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ body {
+ padding: 10px;
+ }
+
+ h1 {
+ font-size: 2rem;
+ }
+
+ .container {
+ padding: 20px;
+ }
+
+ .card {
+ padding: 15px;
+ }
+}
+
+/* 功能特性样式 */
+.features {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 20px;
+ margin-top: 30px;
+}
+
+.feature-item {
+ text-align: center;
+ padding: 20px;
+}
+
+.feature-icon {
+ font-size: 2rem;
+ color: #3498db;
+ margin-bottom: 10px;
+}
+
+/* 页脚样式 */
+.footer {
+ text-align: center;
+ margin-top: 40px;
+ padding: 20px;
+ color: #7f8c8d;
+ font-size: 0.9rem;
+}
\ No newline at end of file
diff --git a/static/js/main.js b/static/js/main.js
new file mode 100644
index 0000000..7537183
--- /dev/null
+++ b/static/js/main.js
@@ -0,0 +1,435 @@
+// 合同审核智能Agent前端交互脚本
+
+document.addEventListener('DOMContentLoaded', function() {
+ // 获取DOM元素
+ const reviewForm = document.getElementById('review-form');
+ const fileInput = document.getElementById('contract_file');
+ const submitBtn = document.getElementById('submit-btn');
+ const resultContainer = document.getElementById('result-container');
+ const resultContent = document.getElementById('result-content');
+ const fileLabel = document.getElementById('file-label');
+ const progressBar = document.getElementById('progress-bar');
+
+ // 文件选择事件处理
+ fileInput.addEventListener('change', function() {
+ const fileName = this.files[0]?.name || '选择合同文件';
+ fileLabel.textContent = fileName;
+
+ // 验证文件类型
+ if (this.files[0]) {
+ const file = this.files[0];
+ const validTypes = ['.txt', '.pdf', '.docx'];
+ const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
+
+ if (!validTypes.includes(fileExtension)) {
+ showError('请选择有效的文件格式 (.txt, .pdf, .docx)');
+ this.value = '';
+ fileLabel.textContent = '选择合同文件';
+ return;
+ }
+
+ // 检查文件大小 (限制为10MB)
+ if (file.size > 10 * 1024 * 1024) {
+ showError('文件大小不能超过10MB');
+ this.value = '';
+ fileLabel.textContent = '选择合同文件';
+ return;
+ }
+ }
+ });
+
+ // 表单提交事件处理
+ reviewForm.addEventListener('submit', async function(e) {
+ e.preventDefault();
+
+ // 验证是否选择了文件
+ if (!fileInput.files[0]) {
+ showError('请选择要审核的合同文件');
+ return;
+ }
+
+ // 显示进度条
+ progressBar.classList.add('active');
+
+ // 禁用提交按钮
+ submitBtn.disabled = true;
+ submitBtn.textContent = '审核中...';
+
+ // 隐藏之前的结果
+ resultContainer.style.display = 'none';
+
+ // 创建FormData对象
+ const formData = new FormData();
+ formData.append('contract_file', fileInput.files[0]);
+
+ try {
+ // 发送请求
+ const response = await fetch('/review', {
+ method: 'POST',
+ body: formData
+ });
+
+ const data = await response.json();
+
+ if (data.error) {
+ showError(data.error);
+ } else {
+ showResult(data.result);
+ extractRiskTags(data.result);
+ }
+ } catch (error) {
+ showError('审核过程中出现错误:' + error.message);
+ } finally {
+ // 隐藏进度条
+ progressBar.classList.remove('active');
+
+ // 启用提交按钮
+ submitBtn.disabled = false;
+ submitBtn.textContent = '开始审核';
+ }
+ });
+
+ // 显示加载状态
+ function showLoading() {
+ submitBtn.disabled = true;
+ submitBtn.textContent = '审核中...';
+ resultContainer.style.display = 'block';
+ resultContent.innerHTML = `
+
+ `;
+ resultContent.className = 'result-content';
+ }
+
+ // 显示错误消息
+ function showError(message) {
+ submitBtn.disabled = false;
+ submitBtn.textContent = '开始审核';
+ resultContainer.style.display = 'block';
+ resultContent.innerHTML = `${message}
`;
+ resultContent.className = 'result-content';
+ }
+
+ // 显示审核结果
+ function showResult(result) {
+ submitBtn.disabled = false;
+ submitBtn.textContent = '开始审核';
+ resultContainer.style.display = 'block';
+
+ // 格式化Markdown内容
+ const formattedResult = formatMarkdown(result);
+ resultContent.innerHTML = formattedResult;
+ resultContent.className = 'result-content';
+
+ // 显示操作按钮
+ document.getElementById('result-actions').style.display = 'flex';
+
+ // 滚动到结果区域
+ resultContainer.scrollIntoView({ behavior: 'smooth' });
+ }
+
+ // 完全重写的Markdown到HTML转换函数
+ function formatMarkdown(text) {
+ // 首先将文本按行分割,便于处理
+ const lines = text.split('\n');
+ let html = '';
+ let currentSection = '';
+ let inList = false;
+
+ // 创建一个容器来存放所有内容
+ html += '';
+
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i].trim();
+
+ // 跳过空行
+ if (line === '') {
+ if (inList) {
+ html += '';
+ inList = false;
+ }
+ continue;
+ }
+
+ // 处理一级标题 (合同审核报告)
+ if (line.match(/^#\s/)) {
+ const title = line.replace(/^#\s/, '');
+ html += ``;
+ continue;
+ }
+
+ // 处理二级标题 (主要部分)
+ if (line.match(/^##\s/)) {
+ // 先关闭之前的列表
+ if (inList) {
+ html += '';
+ inList = false;
+ }
+
+ const title = line.replace(/^##\s/, '');
+
+ // 识别不同的部分并应用相应的样式
+ if (title.includes('合同整体评估')) {
+ currentSection = 'assessment';
+ html += `
+
+ `;
+ } else if (title.includes('主要风险点')) {
+ currentSection = 'risks';
+ html += `
+
+ `;
+ } else if (title.includes('修改建议')) {
+ currentSection = 'suggestions';
+ html += `
+
+ `;
+ } else if (title.includes('最终结论')) {
+ currentSection = 'conclusion';
+ html += `
+
+ `;
+ } else {
+ // 默认部分
+ currentSection = 'default';
+ html += `
+
+ `;
+ }
+ continue;
+ }
+
+ // 处理三级标题
+ if (line.match(/^###\s/)) {
+ const title = line.replace(/^###\s/, '');
+ html += `
${title}
`;
+ continue;
+ }
+
+ // 处理列表项
+ if (line.match(/^-\s/)) {
+ if (!inList) {
+ html += '
';
+ inList = true;
+ }
+
+ const itemText = line.replace(/^-\s/, '');
+
+ // 检测风险等级并添加相应样式
+ let riskLevel = '';
+ let riskClass = '';
+
+ if (itemText.includes('违约金比例过高') || itemText.includes('严重')) {
+ riskLevel = '高风险';
+ riskClass = 'high-risk';
+ } else if (itemText.includes('支付条款过于严格') || itemText.includes('缺少关键条款')) {
+ riskLevel = '中风险';
+ riskClass = 'medium-risk';
+ } else if (itemText.includes('服务标准描述模糊') || itemText.includes('争议解决方式单一')) {
+ riskLevel = '低风险';
+ riskClass = 'low-risk';
+ }
+
+ // 处理粗体文本
+ const formattedText = itemText.replace(/\*\*(.+?)\*\*/g, '$1');
+
+ html += `-
+
+ ${formattedText}
+ ${riskLevel ? `${riskLevel}` : ''}
+
+ `;
+ continue;
+ } else if (inList) {
+ html += '
';
+ inList = false;
+ }
+
+ // 处理数字列表项
+ if (line.match(/^\d+\.\s/)) {
+ if (inList) {
+ html += '';
+ inList = false;
+ }
+
+ const itemText = line.replace(/^\d+\.\s/, '');
+ // 处理粗体文本
+ const formattedText = itemText.replace(/\*\*(.+?)\*\*/g, '
$1');
+
+ html += `
`;
+ continue;
+ }
+
+ // 处理普通段落
+ if (currentSection) {
+ html += `
${line}
`;
+ }
+ }
+
+ // 关闭未闭合的标签
+ if (inList) html += '';
+ if (currentSection) html += '
';
+
+ // 关闭容器
+ html += '
';
+
+ return html;
+ }
+
+ // 处理行内Markdown格式
+ function formatInlineMarkdown(text) {
+ return text
+ // 粗体
+ .replace(/\*\*(.+?)\*\*/g, '$1')
+ // 斜体
+ .replace(/\*(.+?)\*/g, '$1')
+ // 数字标记
+ .replace(/(\d+\.\s)/g, '$1');
+ }
+
+ // 提取风险标签
+ function extractRiskTags(result) {
+ const riskTags = document.getElementById('risk-tags');
+ riskTags.innerHTML = '';
+
+ // 定义常见风险关键词
+ const riskKeywords = [
+ '违约金', '支付条款', '保密条款', '知识产权', '不可抗力',
+ '争议解决', '法律风险', '合同条款', '责任限制', '赔偿',
+ '终止条件', '续约', '服务质量', '验收标准', '交付时间'
+ ];
+
+ // 从结果中提取包含风险关键词的短语
+ const tags = [];
+ riskKeywords.forEach(keyword => {
+ if (result.includes(keyword)) {
+ tags.push(keyword);
+ }
+ });
+
+ // 创建标签元素
+ tags.forEach(tag => {
+ const tagElement = document.createElement('span');
+ tagElement.className = 'tag';
+ tagElement.textContent = tag;
+ tagElement.title = `点击查看关于"${tag}"的更多信息`;
+
+ // 添加点击事件
+ tagElement.addEventListener('click', function() {
+ highlightKeywordInResult(tag);
+ });
+
+ riskTags.appendChild(tagElement);
+ });
+
+ // 如果没有找到风险标签,显示默认提示
+ if (tags.length === 0) {
+ const noTagsElement = document.createElement('span');
+ noTagsElement.className = 'tag';
+ noTagsElement.textContent = '未识别特定风险点';
+ noTagsElement.style.opacity = '0.7';
+ riskTags.appendChild(noTagsElement);
+ }
+ }
+
+ // 在结果中高亮显示关键词
+ function highlightKeywordInResult(keyword) {
+ const resultContent = document.getElementById('result-content');
+ const content = resultContent.innerHTML;
+
+ // 移除之前的高亮
+ const cleanContent = content.replace(/]*>([^<]*)<\/mark>/gi, '$1');
+
+ // 添加新的高亮
+ const highlightedContent = cleanContent.replace(
+ new RegExp(`(${keyword})`, 'gi'),
+ '$1'
+ );
+
+ resultContent.innerHTML = highlightedContent;
+
+ // 滚动到第一个高亮位置
+ const firstHighlight = resultContent.querySelector('.highlight');
+ if (firstHighlight) {
+ firstHighlight.scrollIntoView({ behavior: 'smooth', block: 'center' });
+ }
+ }
+
+ // 下载报告功能
+ document.getElementById('download-btn').addEventListener('click', function() {
+ const resultContent = document.getElementById('result-content').innerText;
+ const blob = new Blob([resultContent], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `合同审核报告_${new Date().toISOString().slice(0, 10)}.txt`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ });
+
+ // 分享结果功能
+ document.getElementById('share-btn').addEventListener('click', async function() {
+ if (navigator.share) {
+ try {
+ const resultContent = document.getElementById('result-content').innerText;
+ await navigator.share({
+ title: '合同审核报告',
+ text: resultContent.substring(0, 200) + '...',
+ });
+ } catch (error) {
+ console.log('分享失败:', error);
+ }
+ } else {
+ // 如果不支持Web Share API,复制到剪贴板
+ const resultContent = document.getElementById('result-content').innerText;
+ navigator.clipboard.writeText(resultContent).then(() => {
+ showMessage('审核结果已复制到剪贴板');
+ }).catch(() => {
+ showMessage('复制失败,请手动复制');
+ });
+ }
+ });
+
+ // 开始新审核功能
+ document.getElementById('new-review-btn').addEventListener('click', function() {
+ // 重置表单
+ reviewForm.reset();
+
+ // 隐藏结果区域
+ resultContainer.style.display = 'none';
+
+ // 滚动到上传区域
+ reviewForm.scrollIntoView({ behavior: 'smooth' });
+ });
+});
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..c3199f9
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,86 @@
+
+
+
+
+
+ 合同审核智能Agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
审核结果
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 功能特点
+
+
+
📄
+
多格式支持
+
支持PDF、Word、文本等多种格式的合同文档
+
+
+
🔍
+
全面分析
+
对合同内容进行条款完整性、风险点、合规性等多维度分析
+
+
+
💡
+
智能建议
+
提供具体的修改建议和风险评级
+
+
+
🖥️
+
用户友好
+
简洁直观的Web界面,操作便捷
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\346\212\200\346\234\257\346\226\207\346\241\243.md" "b/\346\212\200\346\234\257\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..37d41bc
--- /dev/null
+++ "b/\346\212\200\346\234\257\346\226\207\346\241\243.md"
@@ -0,0 +1,234 @@
+# 合同审核智能Agent技术文档
+
+## 1. 项目介绍
+
+### 1.1 项目背景
+
+本项目是一款基于AI技术的合同审核智能Agent,旨在为企业和个人用户提供自动化、智能化的合同文档分析和风险评估服务。该Agent能够快速识别合同中的潜在风险点,评估法律合规性,并提供专业的修改建议,大幅提高合同审核效率,降低法律风险。
+
+### 1.2 项目目标
+
+- 实现合同文档的自动加载与解析
+- 提供多维度的合同内容分析(条款完整性、风险点识别、合规性评估等)
+- 生成详细的审核报告和修改建议
+- 支持Web和命令行两种交互方式
+- 保证审核过程的高效性和准确性
+- 提供健壮的模型加载机制,支持多种模型选择
+
+### 1.3 应用场景
+
+- 企业合同管理部门的日常合同审核
+- 法律从业人员的辅助工具
+- 个人用户在签署合同前的风险自查
+- 金融机构的贷款合同、保险合同合规性检查
+
+## 2. 成员详情
+
+
+| 姓名 | 角色 | 职责描述 | 联系方式 |
+| ------ | --------------------- | ------------------------------------------------------ | -------- |
+| 李能容 | 项目负责人/开发工程师 | 负责整体架构设计、核心算法实现、前端界面开发和系统集成 | - |
+
+## 3. 技术栈
+
+### 3.1 核心框架
+
+- **Flask**:轻量级Web框架,用于构建用户友好的Web界面
+- **LazyLLM**:商汤大装置开源的低代码AI应用开发框架,主要用于模型集成
+
+### 3.2 编程语言
+
+- **Python**:项目主要开发语言
+
+### 3.3 主要依赖
+
+- `requests`:用于API调用和网络请求
+- `json`:用于数据序列化和反序列化
+- `flask`:Web服务框架
+- `lazyllm`:AI应用开发框架,主要用于OnlineChatModule和模型集成
+- `tempfile`:临时文件处理
+- `os`:用于环境变量获取和路径处理
+- `sys`:用于系统参数和退出控制
+
+### 3.4 其他依赖(用于未来扩展)
+
+requirements.txt中还包含了一些当前实现中未直接使用但为未来功能扩展准备的依赖,包括:
+
+- 模型相关:transformers, torch, accelerate, sentencepiece, protobuf, scipy, modelscope
+- 数据处理:pandas, numpy
+- 可视化:matplotlib
+- API集成:openai, tiktoken
+- 异步处理:aiohttp, asyncio
+
+### 3.4 文件处理
+
+- 支持多种编码格式(UTF-8、GBK)的文本文件读取
+- 支持临时文件的创建和管理
+
+## 4. 系统架构
+
+### 4.1 整体架构
+
+系统采用模块化设计,主要包含以下核心模块:
+
+- **Agent核心模块**:实现合同审核的主要逻辑,包括模型初始化、合同加载、分析和总结
+- **用户界面模块**:提供Web和命令行两种交互方式
+- **模型管理模块**:实现多模型支持和智能降级机制
+
+### 4.2 模块关系
+
+```
+用户 -> 用户界面模块(Web/CLI) -> Agent核心模块 -> 模型管理模块 -> 结果返回
+```
+
+### 4.3 数据流向
+
+1. 用户通过界面上传合同文件或指定文件路径
+2. Agent加载并解析合同内容
+3. 调用模型进行合同分析和总结
+4. 生成审核报告并返回给用户
+
+## 5. 接口设计
+
+### 5.1 核心类接口
+
+#### ContractReviewAgent类
+
+- **初始化方法**:`__init__()`
+
+ - 功能:初始化Agent,初始化模型
+ - 参数:无
+ - 返回值:无
+- **模型初始化**:`_init_model()`
+
+ - 功能:实现多模型加载和降级机制
+ - 参数:无
+ - 返回值:初始化后的模型对象
+- **创建模拟聊天模块**:`_create_mock_chat_module()`
+
+ - 功能:创建模拟聊天模块作为后备方案
+ - 参数:无
+ - 返回值:MockChatModule对象
+- **加载合同**:`load_contract(contract_path)`
+
+ - 功能:从文件系统读取合同内容
+ - 参数:`contract_path` - 合同文件路径
+ - 返回值:包含合同内容的列表
+- **分析合同**:`analyze_contract(documents)`
+
+ - 功能:对合同内容进行多维度分析
+ - 参数:`documents` - 包含合同内容的列表
+ - 返回值:合同分析结果字符串
+- **总结审核**:`summarize_review(result)`
+
+ - 功能:将详细分析结果总结为结构化报告
+ - 参数:`result` - 合同分析结果
+ - 返回值:总结后的审核报告
+- **审核合同**:`review_contract(contract_path)`
+
+ - 功能:执行合同审核的主入口,按顺序调用上述方法
+ - 参数:`contract_path` - 合同文件路径
+ - 返回值:审核结果字符串
+
+#### ContractReviewWebApp类
+
+- **初始化方法**:`__init__()`
+
+ - 功能:初始化Web应用,创建Agent实例
+ - 参数:无
+ - 返回值:无
+- **创建Web模块**:`create_web_module()`
+
+ - 功能:创建WebModule对象(当前代码中未使用)
+ - 参数:无
+ - 返回值:WebModule对象
+
+#### ContractReviewCLI类
+
+- **初始化方法**:`__init__()`
+
+ - 功能:初始化命令行界面,创建Agent实例
+ - 参数:无
+ - 返回值:无
+- **运行命令行**:`run()`
+
+ - 功能:启动命令行交互界面
+ - 参数:无
+ - 返回值:无
+
+### 5.2 Web API接口
+
+- **首页**:`/` (GET)
+
+ - 功能:提供合同上传和审核界面
+ - 请求方法:GET
+ - 返回值:HTML页面
+- **Vite客户端处理**:`/@vite/client` (GET)
+
+ - 功能:处理Vite客户端请求,避免404错误
+ - 请求方法:GET
+ - 返回值:空响应(HTTP 204)
+- **审核接口**:`/review` (POST)
+
+ - 功能:接收上传的合同文件并执行审核
+ - 请求方法:POST
+ - 请求体:multipart/form-data,包含合同文件
+ - 返回值:JSON格式的审核结果,包含成功状态和审核内容
+
+## 6. 功能模块详解
+
+### 6.1 模型管理模块
+
+- **功能**:实现多模型加载和智能降级机制
+- **支持的模型类型**:
+ - API密钥模型(优先尝试豆包模型,其次OpenAI模型)
+ - 本地模型(通过LOCAL_MODEL_PATH环境变量指定)
+ - 常见路径模型(自动扫描常见模型路径)
+ - 在线模型(使用默认配置)
+ - 模拟聊天模块(作为最终后备方案)
+- **实现**:采用层级尝试机制,确保系统在不同环境下都能正常运行
+
+### 6.2 合同加载模块
+
+- **功能**:负责从文件系统读取合同内容
+- **实现**:支持多种编码格式的文本文件读取,处理编码异常
+ - 优先使用UTF-8编码
+ - 遇到编码错误时自动尝试GBK编码
+- **输出**:格式化的文档内容列表
+
+### 6.3 合同分析模块
+
+- **功能**:对合同内容进行多维度分析
+- **分析维度**:
+ - 合同条款完整性检查
+ - 潜在风险点识别
+ - 法律合规性评估
+ - 建议修改的条款
+ - 整体风险评级
+- **实现**:使用Prompt Engineering技术,引导模型生成专业的审核意见
+- **输出**:详细的审核意见文本
+
+### 6.4 结果总结模块
+
+- **功能**:将详细分析结果总结为结构化报告
+- **报告内容**:
+ - 合同整体评估
+ - 主要风险点
+ - 优先修改建议
+ - 最终结论
+- **实现**:使用Prompt Engineering技术,将详细分析结果提炼为简洁明了的总结报告
+- **输出**:格式化的审核报告
+
+### 6.5 用户界面模块
+
+- **Web界面**:
+
+ - 使用Flask框架实现
+ - 提供文件上传、审核启动和结果展示功能
+ - 支持文件大小和类型验证
+ - 提供直观的操作进度和结果展示
+- **命令行界面**:
+
+ - 提供交互式的合同审核操作
+ - 支持文件路径输入和结果展示
+ - 提供菜单式操作选择
--
Gitee