2024-12-11 17:42:51 +08:00
|
|
|
|
import json
|
2024-12-25 14:35:52 +08:00
|
|
|
|
import os
|
2024-12-11 17:42:51 +08:00
|
|
|
|
import re
|
|
|
|
|
import time
|
2025-01-08 17:34:50 +08:00
|
|
|
|
from typing import Any, Dict
|
2025-02-12 14:55:35 +08:00
|
|
|
|
from flask_app.general.llm.大模型通用函数 import read_txt_to_string
|
2024-12-26 17:20:27 +08:00
|
|
|
|
from flask_app.general.file2markdown import convert_file_to_markdown
|
|
|
|
|
from flask_app.general.format_change import get_pdf_page_count, pdf2docx
|
2025-01-07 17:35:11 +08:00
|
|
|
|
from flask_app.general.json_utils import extract_content_from_json
|
2025-02-06 14:39:58 +08:00
|
|
|
|
from flask_app.general.llm.model_continue_query import process_continue_answers
|
2025-02-12 14:55:35 +08:00
|
|
|
|
from flask_app.general.llm.通义千问long import upload_file, qianwen_long, qianwen_long_stream
|
|
|
|
|
from flask_app.general.llm.qianwen_plus import qianwen_plus
|
2024-12-11 17:42:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def remove_unknown_scores(data):
|
|
|
|
|
if isinstance(data, dict):
|
|
|
|
|
return {
|
|
|
|
|
k: remove_unknown_scores(v)
|
|
|
|
|
for k, v in data.items()
|
|
|
|
|
if not (k == "评分" and v in ["未知", "/", ""])
|
|
|
|
|
}
|
|
|
|
|
elif isinstance(data, list):
|
|
|
|
|
return [remove_unknown_scores(item) for item in data]
|
|
|
|
|
else:
|
|
|
|
|
return data
|
2025-02-12 14:55:35 +08:00
|
|
|
|
|
2025-01-07 17:35:11 +08:00
|
|
|
|
def combine_technical_and_business(data):
|
2025-02-12 14:55:35 +08:00
|
|
|
|
'''
|
|
|
|
|
后处理,区分技术评分和商务评分,给外键添加总的评分
|
|
|
|
|
'''
|
2024-12-30 17:32:24 +08:00
|
|
|
|
data = remove_unknown_scores(data)
|
2025-01-07 17:35:11 +08:00
|
|
|
|
extracted_data = {
|
2025-01-08 17:34:50 +08:00
|
|
|
|
'技术评分': {},
|
|
|
|
|
'商务评分': {}
|
2025-01-07 17:35:11 +08:00
|
|
|
|
}
|
2024-12-11 17:42:51 +08:00
|
|
|
|
|
2025-01-08 17:34:50 +08:00
|
|
|
|
# 用于累加 '其他评分' 的总分
|
|
|
|
|
other_total_score = 0
|
|
|
|
|
other_items = {}
|
|
|
|
|
def process_data(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
|
|
"""
|
|
|
|
|
处理给定的数据字典,如果存在外键 '技术评分' 且其子键符合 '技术评分-<数字>' 格式,
|
|
|
|
|
则删除外键 '技术评分' 并将其子键提升到更高层级。即'技术评分-1' '技术评分-2'作为外键
|
|
|
|
|
Args:
|
|
|
|
|
data (Dict[str, Any]): 输入的数据字典。
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dict[str, Any]: 处理后的数据字典。
|
|
|
|
|
"""
|
|
|
|
|
# 定义匹配 '技术评分-数字' 的正则表达式
|
|
|
|
|
pattern = re.compile(r'^技术评分-\d+$')
|
|
|
|
|
|
|
|
|
|
# 创建一个新的字典以避免在遍历时修改原字典
|
|
|
|
|
new_data = {}
|
|
|
|
|
|
|
|
|
|
for key, value in data.items():
|
|
|
|
|
if key == "技术评分" and isinstance(value, dict):
|
|
|
|
|
# 检查所有子键是否符合 '技术评分-<数字>' 格式
|
|
|
|
|
if all(pattern.match(child_key) for child_key in value.keys()):
|
|
|
|
|
# 如果符合,提升子键到更高层级
|
|
|
|
|
new_data.update(value)
|
|
|
|
|
else:
|
|
|
|
|
# 如果不符合,保留原键值
|
|
|
|
|
new_data[key] = process_data(value) if isinstance(value, dict) else value
|
|
|
|
|
elif isinstance(value, dict):
|
|
|
|
|
# 递归处理子字典
|
|
|
|
|
new_data[key] = process_data(value)
|
|
|
|
|
else:
|
|
|
|
|
# 对于非字典类型的值,直接保留
|
|
|
|
|
new_data[key] = value
|
|
|
|
|
|
|
|
|
|
return new_data
|
|
|
|
|
|
2025-01-07 17:35:11 +08:00
|
|
|
|
def extract_nested(data):
|
2025-01-08 17:34:50 +08:00
|
|
|
|
nonlocal other_total_score, other_items
|
2024-12-30 17:32:24 +08:00
|
|
|
|
if isinstance(data, dict):
|
|
|
|
|
for key, value in data.items():
|
2025-01-07 17:35:11 +08:00
|
|
|
|
# 区分 '技术评分'
|
2025-01-08 17:34:50 +08:00
|
|
|
|
if key.startswith('技术评分'):
|
|
|
|
|
total_score = compute_total_score({key: value})
|
|
|
|
|
if 0 < total_score <= 100:
|
|
|
|
|
new_key = f'{key}({total_score}分)'
|
|
|
|
|
else:
|
|
|
|
|
new_key = f'{key}'
|
|
|
|
|
extracted_data['技术评分'][new_key] = value
|
2025-01-07 17:35:11 +08:00
|
|
|
|
# 匹配到后,不再递归处理其子项
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# 区分 '商务评分'
|
|
|
|
|
elif key == '商务评分':
|
2025-01-08 17:34:50 +08:00
|
|
|
|
total_score = compute_total_score({key: value})
|
|
|
|
|
if 0 < total_score <= 100:
|
|
|
|
|
new_key = f'商务评分({total_score}分)'
|
|
|
|
|
else:
|
|
|
|
|
new_key = '商务评分'
|
|
|
|
|
extracted_data['商务评分'][new_key] = value
|
2025-01-07 17:35:11 +08:00
|
|
|
|
# 匹配到后,不再递归处理其子项
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# 区分 '投标报价评分'
|
|
|
|
|
elif key == '投标报价评分':
|
2025-01-08 17:34:50 +08:00
|
|
|
|
total_score = compute_total_score({key: value})
|
|
|
|
|
if 0 < total_score <= 100:
|
|
|
|
|
new_key = f'投标报价评分({total_score}分)'
|
|
|
|
|
else:
|
|
|
|
|
new_key = '投标报价评分'
|
|
|
|
|
extracted_data['商务评分'][new_key] = value
|
2025-01-07 17:35:11 +08:00
|
|
|
|
# 匹配到后,不再递归处理其子项
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# 其他键名归为 '其他评分'
|
2024-12-30 17:32:24 +08:00
|
|
|
|
else:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
# 计算当前项的总分
|
|
|
|
|
current_score = compute_total_score({key: value})
|
|
|
|
|
other_total_score += current_score
|
|
|
|
|
# 保持原键名,不添加(分数)
|
|
|
|
|
other_items[key] = value
|
|
|
|
|
processed_data=process_data(data)
|
|
|
|
|
extract_nested(processed_data)
|
|
|
|
|
|
|
|
|
|
# 如果有 '其他评分',替换为包含总分的键名
|
|
|
|
|
if other_items:
|
|
|
|
|
if other_total_score > 0:
|
|
|
|
|
new_key = f'其他评分({other_total_score}分)'
|
|
|
|
|
else:
|
|
|
|
|
new_key = '其他评分'
|
|
|
|
|
extracted_data['商务评分'][new_key] = other_items
|
2024-12-11 17:42:51 +08:00
|
|
|
|
|
2024-12-30 17:32:24 +08:00
|
|
|
|
return extracted_data
|
2024-12-11 17:42:51 +08:00
|
|
|
|
|
2025-01-08 17:34:50 +08:00
|
|
|
|
|
2024-12-11 17:42:51 +08:00
|
|
|
|
# 防止外键只有一个'一包'的情况
|
|
|
|
|
def process_data_based_on_key(data):
|
|
|
|
|
exclude_word = ["包", "未知", "评分因素"]
|
|
|
|
|
# 获取字典的键列表
|
|
|
|
|
keys = list(data.keys())
|
|
|
|
|
# 检查键的数量是否为1并且 exclude_word 中的任何词包含在 keys[0] 中
|
|
|
|
|
if len(keys) == 1 and any(word in keys[0] for word in exclude_word):
|
|
|
|
|
# 返回内层的字典
|
|
|
|
|
return data[keys[0]]
|
|
|
|
|
|
|
|
|
|
# 如果条件不满足,则返回原始字典
|
|
|
|
|
return data
|
|
|
|
|
|
2025-01-07 17:35:11 +08:00
|
|
|
|
def compute_total_score(data):
|
|
|
|
|
"""
|
|
|
|
|
计算传入字典的总分。
|
|
|
|
|
|
|
|
|
|
规则:
|
|
|
|
|
- 输入字典只有一个外层键。
|
|
|
|
|
- 遍历该外层键的所有子键:
|
|
|
|
|
- 如果子键名中包含 '(XX分)' 或 '(XX分)',提取 XX 并累加到总分中,不再处理其子项。
|
|
|
|
|
- 如果子键名中不包含这样的分数,遍历其子项,查找键名为 '评分' 的键,提取分数并累加。
|
|
|
|
|
- '评分' 的值可以是 'XX分' 或整数。
|
|
|
|
|
- 如果没有找到 '评分' 键,则该项分数为 0。
|
|
|
|
|
"""
|
|
|
|
|
total = 0
|
|
|
|
|
|
|
|
|
|
# 确保输入数据为字典且只有一个外层键
|
|
|
|
|
if not isinstance(data, dict) or len(data) != 1:
|
|
|
|
|
raise ValueError("输入数据必须是一个只有一个外层键的字典。")
|
|
|
|
|
|
|
|
|
|
# 获取唯一的外层键和值
|
|
|
|
|
outer_key, outer_value = next(iter(data.items()))
|
|
|
|
|
|
2025-01-08 17:34:50 +08:00
|
|
|
|
# 更新后的正则表达式,匹配中英文括号中的分数,如 '(24分)' 或 '(24分)' #考虑了空格的影响
|
|
|
|
|
score_pattern = re.compile(r'[((]\s*(\d+)\s*分\s*[))]')
|
2025-01-07 17:35:11 +08:00
|
|
|
|
|
|
|
|
|
def process_node(node):
|
|
|
|
|
nonlocal total
|
|
|
|
|
if isinstance(node, dict):
|
|
|
|
|
for key, value in node.items():
|
|
|
|
|
# 检查键名中是否包含 '(XX分)' 或 '(XX分)'
|
|
|
|
|
match = score_pattern.search(key)
|
|
|
|
|
if match:
|
|
|
|
|
score = int(match.group(1))
|
|
|
|
|
total += score
|
|
|
|
|
# 匹配到后,不再递归处理其子项
|
|
|
|
|
continue
|
|
|
|
|
elif key == '评分':
|
|
|
|
|
if isinstance(value, str):
|
|
|
|
|
# 提取 '评分' 键的值中的数字,如 '20分'
|
2025-01-08 17:34:50 +08:00
|
|
|
|
match_score = re.match(r'\s*(\d+)\s*分\s*', value)
|
2025-01-07 17:35:11 +08:00
|
|
|
|
if match_score:
|
|
|
|
|
score = int(match_score.group(1))
|
|
|
|
|
total += score
|
|
|
|
|
else:
|
|
|
|
|
# 如果 '评分' 值不符合格式,默认加 0
|
|
|
|
|
total += 0
|
|
|
|
|
elif isinstance(value, int):
|
|
|
|
|
# 如果 '评分' 键的值是整数,直接累加
|
|
|
|
|
total += value
|
|
|
|
|
else:
|
|
|
|
|
# 如果 '评分' 键的值既不是字符串也不是整数,默认加 0
|
|
|
|
|
total += 0
|
|
|
|
|
else:
|
|
|
|
|
# 如果键名不包含分数,递归处理其子项
|
|
|
|
|
process_node(value)
|
|
|
|
|
elif isinstance(node, list):
|
|
|
|
|
for item in node:
|
|
|
|
|
process_node(item)
|
|
|
|
|
else:
|
|
|
|
|
# 如果是其他类型的数据,忽略
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# 开始递归处理外层键的值
|
|
|
|
|
process_node(outer_value)
|
|
|
|
|
|
|
|
|
|
return total
|
|
|
|
|
|
2024-12-11 17:42:51 +08:00
|
|
|
|
def reorganize_data(input_dict, include=None):
|
|
|
|
|
"""
|
|
|
|
|
重组输入字典,将“技术评分”和“商务评分”提升为最外层键,
|
|
|
|
|
并将包含在 include 列表中的包名的数据嵌套在相应的评分类别下。
|
|
|
|
|
|
|
|
|
|
如果 input_dict 的顶层键不包含任何 include 列表中的项,则返回原始字典。
|
|
|
|
|
|
|
|
|
|
:param input_dict: 原始输入字典
|
|
|
|
|
:param include: 包名列表,例如 ['一包', '二包', '三包']
|
|
|
|
|
:return: 重组后的字典
|
|
|
|
|
"""
|
|
|
|
|
if include is None:
|
|
|
|
|
include = []
|
|
|
|
|
|
|
|
|
|
# 检查是否有任何顶层键包含在 include 列表中
|
|
|
|
|
has_include = any(key in include for key in input_dict.keys())
|
|
|
|
|
|
|
|
|
|
if not has_include:
|
|
|
|
|
# 没有包含任何指定的包名,直接返回原始字典
|
|
|
|
|
return input_dict
|
|
|
|
|
|
|
|
|
|
# 初始化新的字典结构
|
|
|
|
|
reorganized = {
|
|
|
|
|
"技术评分": {},
|
|
|
|
|
"商务评分": {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 遍历每一个包(如 "一包", "二包")
|
|
|
|
|
for package, categories in input_dict.items():
|
|
|
|
|
# 处理技术评分
|
|
|
|
|
if "技术评分" in categories:
|
|
|
|
|
reorganized["技术评分"][package] = categories["技术评分"]
|
|
|
|
|
|
|
|
|
|
# 处理商务评分
|
|
|
|
|
if "商务评分" in categories:
|
|
|
|
|
reorganized["商务评分"][package] = categories["商务评分"]
|
|
|
|
|
|
|
|
|
|
return reorganized
|
2025-01-17 10:47:43 +08:00
|
|
|
|
|
2024-12-11 17:42:51 +08:00
|
|
|
|
def combine_evaluation_standards(evaluation_method_path,invalid_path,zb_type):
|
2025-02-06 17:27:29 +08:00
|
|
|
|
'''
|
|
|
|
|
传入截取后的评分章节,输出评分细则
|
|
|
|
|
'''
|
2024-12-11 17:42:51 +08:00
|
|
|
|
# 定义默认的评审结果字典
|
|
|
|
|
DEFAULT_EVALUATION_REVIEW = {
|
2025-01-17 10:47:43 +08:00
|
|
|
|
"技术评分": "未解析到'技术评分'项!",
|
|
|
|
|
"商务评分": "未解析到'商务评分'项!"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
}
|
|
|
|
|
# 如果 truncate_file 是空字符串,直接返回包含空字符串的字典
|
|
|
|
|
if not evaluation_method_path:
|
|
|
|
|
return DEFAULT_EVALUATION_REVIEW.copy()
|
|
|
|
|
|
2025-01-08 17:34:50 +08:00
|
|
|
|
def generate_prompt(zb_type):
|
|
|
|
|
common_prompt="""你是一个对招投标业务非常熟悉的专家。根据该文档中的评标办法表格,请你列出该文件的技术评分,商务评分,投标报价评分以及它们对应的具体评分要求,请以JSON格式返回结果。
|
|
|
|
|
**格式要求**:
|
2024-12-25 14:35:52 +08:00
|
|
|
|
1.总体结构:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
-JSON 的最外层包含三个键:'技术评分'、'商务评分' 和 '投标报价评分'。
|
2024-12-25 14:35:52 +08:00
|
|
|
|
-每个大项(如技术评分、商务评分)下包含具体的评分项,评分项按以下规则表示。
|
|
|
|
|
2.评分项表示规则:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
-每个评分项由评分因素和评分标准组成。
|
|
|
|
|
-评分因素:
|
|
|
|
|
若评分因素(内容、项)存在嵌套关系(通过表格结构判断):主评分因素需附加括号,括号中注明该主评分因素的总分,若无具体评分,则无需添加该括号,例如:产品技术响应(8分);子评分因素作为嵌套键名,列在主评分因素之下,无需再附加括号表示评分。
|
2024-12-30 17:32:24 +08:00
|
|
|
|
-注意:禁止通过归纳、推测或自行总结来生成子评分因素,尤其不可根据'评分标准'中的打分要求来反向总结'子评分因素'。
|
2025-01-08 17:34:50 +08:00
|
|
|
|
若评分因素(内容、项)不存在嵌套关系:键名直接为评分因素,无需附加括号表示总分。
|
|
|
|
|
-评分标准:
|
2025-01-09 14:59:28 +08:00
|
|
|
|
-评分因素的键值为评分标准,它是列表形式,列表中包含描述分值及评分细则的字典。
|
2024-12-26 17:20:27 +08:00
|
|
|
|
-字典个数:
|
2025-01-09 14:59:28 +08:00
|
|
|
|
默认为1个字典,若某评分因素包括多个评分细则(通过表格结构判断,一个单元格视为一个评分细则),字典个数等于评分细则数。
|
2024-12-26 17:20:27 +08:00
|
|
|
|
-字典结构如下:
|
2025-01-09 14:59:28 +08:00
|
|
|
|
评分:一个打分点的分值即最高分(如'8分'),字符串类型;不能是一个范围数字(如0-8分);若为定性指标(如“合格制”),可标明相应的定性指标;无评分时可删去'评分'键值对。
|
|
|
|
|
要求:一个打分点的评分细则,即一个表格单元格内的所有内容,禁止拆分。
|
|
|
|
|
例:"拥有一级证书得3分,拥有二级证书得1分,其他不得分。"
|
|
|
|
|
示例输出:{
|
|
|
|
|
"评分": "3分",
|
|
|
|
|
"要求": "拥有一级证书得3分,拥有二级证书得1分,其他不得分。"
|
|
|
|
|
}
|
|
|
|
|
禁止拆分出三个字典:
|
|
|
|
|
{
|
|
|
|
|
"评分": "3分",
|
|
|
|
|
"要求": "拥有一级证书得3分"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"评分": "1分",
|
|
|
|
|
"要求": "拥有二级证书得1分"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"评分": "0分",
|
|
|
|
|
"要求": "其他不得分。"
|
|
|
|
|
}
|
2024-12-26 17:20:27 +08:00
|
|
|
|
-禁止情况:
|
2025-01-09 14:59:28 +08:00
|
|
|
|
禁止将同个单元格内的内容拆分至多个字典中;禁止遗漏单元格内任何信息,包括注释的内容。
|
2024-12-25 14:35:52 +08:00
|
|
|
|
3.备注信息:
|
|
|
|
|
-若评分部分包含附加信息(如大项评分的整体要求,未直接归属于具体评分项),需添加一个 备注 键,值为该附加信息。
|
2024-12-11 17:42:51 +08:00
|
|
|
|
|
2025-01-08 17:34:50 +08:00
|
|
|
|
**要求与指南**:
|
|
|
|
|
1. 请首先定位评分表,请不要遗漏任何一个评分表的信息,不要回答有关资格审查、符合审查的内容,也不要从评标办法正文中提取回答。
|
2025-01-02 11:28:38 +08:00
|
|
|
|
2. 若表中的评分大项不是这三个,或者有额外的评分大项:
|
|
|
|
|
-请你根据语义及你对招投标业务的熟悉,分别映射到'技术评分'、'商务评分'、'投标报价评分',而不必按照表格中大项名称。
|
|
|
|
|
-映射关系可以是“一对一”或“多对一”,例如两个评分大项可以同时归类到“技术评分”。关键是不遗漏表中的评分大项,确保每个大项都被映射。
|
|
|
|
|
若表中只有评分因素而无评分大项:
|
|
|
|
|
-请将评分因素正确地归类到'技术评分'、'商务评分'、'投标报价评分',不要遗漏任何一个评分因素。
|
|
|
|
|
3. 若表格中商务和技术评分混合一起,或者有部分评分因素无法通过表格结构明确归类:
|
|
|
|
|
-请根据你对招投标业务的熟悉,对表格中的评分因素进行准确分类。关键是确保每个评分因素都能被归类到'技术评分'或'商务评分'或'投标报价评分',不要遗漏任何一个评分因素。
|
2025-01-08 17:34:50 +08:00
|
|
|
|
|
|
|
|
|
**特殊情况**:
|
2025-02-13 15:42:52 +08:00
|
|
|
|
1. 缺失评分项:若大项的'xx评分'要求未在文中说明,则键名'xx评分'的键值设为字符串'本项目无xx评分项',例如"技术评分":"本项目无技术评分项",而非默认的字典格式,请基于提供的实际招标文件内容,禁止捏造回答。
|
2025-01-08 17:34:50 +08:00
|
|
|
|
2. 其他评分:默认情况大项评分仅有'技术评分''商务评分''投标报价评分',若在充分归类之后,表格中仍有评分因素未被归类,才添加大项评分'其他评分'保存该内容。
|
|
|
|
|
3. 多包评分:默认只有一包,最外层键名为各大评分项,而不是'一包';但是如果该招标、采购活动有多个分包且每个分包有独自的评分表,则最外层键名为对应的包名,如'一包''二包',内部才是各大评分项。
|
|
|
|
|
4. 多张技术评分表:若同一包下有多张技术评分表,请不要遗漏任何一个评分表的信息,此时最外层键名'技术评分'替换为'技术评分-d',d为自然数,从1开始,分别保存每张技术评分表的信息。
|
|
|
|
|
-例如有'技术评分标准(1)其他项目','技术评分标准(2)施工类',算作两个技术评分表,最外层的键名分别为'技术评分-1''技术评分-2',替换默认的'技术评分'
|
|
|
|
|
|
|
|
|
|
**禁止内容**:
|
2025-02-13 15:42:52 +08:00
|
|
|
|
1. 确保所有输出内容均基于提供的实际招标文件内容(除了最外层的三个评分大项名称),不使用任何预设的示例作为回答,也禁止捏造评分标准。
|
2024-12-12 18:03:04 +08:00
|
|
|
|
2. 不得擅自添加不属于评审因素的键名以及 `'备注'` 之外的其他键名。
|
2025-01-02 11:28:38 +08:00
|
|
|
|
3. 不得遗漏评分表中的任何评分因素,确保每个评分因素都被正确归类到评分大项下。
|
2025-01-08 17:34:50 +08:00
|
|
|
|
"""
|
|
|
|
|
if zb_type == 1:
|
|
|
|
|
example_output="""
|
|
|
|
|
以下为示例输出,仅供格式参考:
|
|
|
|
|
{
|
|
|
|
|
"技术评分": {
|
|
|
|
|
"实施方案(16分)":{
|
|
|
|
|
"总体实施方案":[
|
|
|
|
|
{
|
|
|
|
|
"评分":8,
|
|
|
|
|
"要求":"根据投标人总体实施方案进行评分"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"项目实施要点":[
|
|
|
|
|
{
|
|
|
|
|
"评分":8,
|
|
|
|
|
"要求":"根据投标人对项目实施要点、难点进行评分。"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"设计创意": [
|
|
|
|
|
{
|
|
|
|
|
"评分": "10分",
|
|
|
|
|
"要求": "主题突出,形式多样,内容与形式完美统一,得10分,其他酌情打分。"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"备注": "技术标采用暗标形式,暗标不得出现投标人名称、人员姓名。"
|
|
|
|
|
},
|
|
|
|
|
"商务评分": {
|
|
|
|
|
"主要监理岗位的职责": [
|
|
|
|
|
{
|
|
|
|
|
"评分": "4分",
|
|
|
|
|
"要求": "1、总监理工程师的职责全面、清晰、合理得 2 分;一般的 1 分。2、其他主要监理人员及岗位的职责全面、清晰、合理得 2 分;一般的 1 分。"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
}
|
|
|
|
|
],
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"制造商实力": [
|
2024-12-11 17:42:51 +08:00
|
|
|
|
{
|
|
|
|
|
"评分": "3分",
|
2024-12-26 17:20:27 +08:00
|
|
|
|
"要求": "一级证书得3分,二级证书得1分,其他不得分。"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"评分": "2分",
|
2024-12-26 17:20:27 +08:00
|
|
|
|
"要求": "行业销量排名连续前 2 名,得 2 分,第 3-6 名得 0.5 分,其他不得分。"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
2025-01-08 17:34:50 +08:00
|
|
|
|
"投标报价评分": {
|
|
|
|
|
"投标报价是否出现违反计价规范": [
|
|
|
|
|
{
|
|
|
|
|
"评分": "合格制",
|
|
|
|
|
"要求": "A:投标报价未违反计价规范的,评审意见为“合格”;B:投标报价违反计价规范的,评审意见为“不合格”"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
elif zb_type == 2:
|
|
|
|
|
example_output="""
|
2024-12-11 17:42:51 +08:00
|
|
|
|
以下为示例输出,仅供格式参考:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
{
|
2025-01-07 17:35:11 +08:00
|
|
|
|
"技术评分": {
|
2024-12-31 09:22:40 +08:00
|
|
|
|
"产品技术响应(8分)":{
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"常规参数符合":[
|
|
|
|
|
{
|
|
|
|
|
"评分":"4分",
|
|
|
|
|
"要求":"未标★项为常规参数每条负偏离扣1分,本项满分4分。"
|
|
|
|
|
}
|
|
|
|
|
],
|
2024-12-26 17:20:27 +08:00
|
|
|
|
"控制系统":[
|
2024-12-25 14:35:52 +08:00
|
|
|
|
{
|
|
|
|
|
"评分":"4分",
|
2024-12-26 17:20:27 +08:00
|
|
|
|
"要求":"所投电梯控制系统技术先进、市场美誉度高,与整梯同品牌,得4分;所投电梯控制系统的技术基本先进、市场美誉度较高,与整梯不同品牌,得2分。"
|
2024-12-25 14:35:52 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"实施方案": [
|
2024-12-11 17:42:51 +08:00
|
|
|
|
{
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"评分": "10分",
|
2024-12-26 17:20:27 +08:00
|
|
|
|
"要求": "实施方案清晰、完整、合理、可行的得 10 分。实施方案一般的得5分"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
}
|
|
|
|
|
],
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"备注": "注:若不满足“与公安部、省公安厅、随州市公安局高清视频会议系统无缝对接互联互通”的要求,则本项技术部分不得分。"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
},
|
2025-01-07 17:35:11 +08:00
|
|
|
|
"商务评分": {
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"主要监理岗位的职责": [
|
2024-12-11 17:42:51 +08:00
|
|
|
|
{
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"评分": "4分",
|
|
|
|
|
"要求": "1、总监理工程师的职责全面、清晰、合理得 2 分;一般的 1 分。2、其他主要监理人员及岗位的职责全面、清晰、合理得 2 分;一般的 1 分。"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
}
|
|
|
|
|
],
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"制造商实力": [
|
2024-12-11 17:42:51 +08:00
|
|
|
|
{
|
|
|
|
|
"评分": "3分",
|
|
|
|
|
"要求": "一级证书得3分,二级证书得1分,其他不得分"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"评分": "2分",
|
2024-12-25 14:35:52 +08:00
|
|
|
|
"要求": "行业销量排名连续前 2 名,得 2 分,第 3-6 名得 0.5 分,其他不得分"
|
2024-12-11 17:42:51 +08:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"投标报价评分": {
|
|
|
|
|
"投标报价是否出现违反计价规范": [
|
|
|
|
|
{
|
|
|
|
|
"评分": "合格制",
|
|
|
|
|
"要求": "A:投标报价未违反计价规范的,评审意见为“合格”;B:投标报价违反计价规范的,评审意见为“不合格”"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"""
|
2025-01-08 17:34:50 +08:00
|
|
|
|
else:
|
|
|
|
|
raise ValueError("Invalid zb_type. 请输入 1 或 2。")
|
|
|
|
|
# 拼接最终的提示内容
|
|
|
|
|
final_prompt = common_prompt + example_output
|
|
|
|
|
return final_prompt
|
|
|
|
|
|
|
|
|
|
def run_first_query(file_path):
|
|
|
|
|
print("判断有无评分")
|
|
|
|
|
# 上传文件并获取文件ID
|
|
|
|
|
file_id = upload_file(file_path)
|
|
|
|
|
|
|
|
|
|
# 定义用户查询
|
|
|
|
|
query = (
|
2025-02-13 15:42:52 +08:00
|
|
|
|
"""请根据以下指南判断该文档是否包含关于技术评分、商务评分或投标报价的具体评分要求和标准:
|
|
|
|
|
1. 若文档中以表格形式展示了评分要求,且包含评分因素(如“技术评分”或“商务评分”或更细的评分因素)及相应的评分标准;即使评分方式为定性(无具体分值),也应视为满足要求。
|
|
|
|
|
2. 如果文档中仅描述了评标流程,但未提供具体的评分标准,则应视为不满足条件。
|
|
|
|
|
3. 虽然竞争性磋商文件通常不包含评分要求,但若文档满足第1条的内容,也应视为符合要求。
|
|
|
|
|
请仅返回“是”或“否”,不附加其他解释或内容。
|
|
|
|
|
"""
|
2025-01-08 17:34:50 +08:00
|
|
|
|
) # 应对竞争性谈判这种无评分要求的情况
|
|
|
|
|
|
|
|
|
|
# 执行查询
|
|
|
|
|
return qianwen_long(file_id, query),file_id
|
|
|
|
|
def run_second_qeury(file_id,processed_filepath,model_type):
|
|
|
|
|
print("获取评分项...")
|
|
|
|
|
# 执行 user_query 相关的逻辑
|
|
|
|
|
user_query=generate_prompt(zb_type)
|
2024-12-26 17:20:27 +08:00
|
|
|
|
if model_type==4:
|
|
|
|
|
full_text = read_txt_to_string(processed_filepath)
|
2025-02-06 14:39:58 +08:00
|
|
|
|
user_query = f"文本内容:\n{full_text}\n" + user_query
|
2024-12-20 17:24:49 +08:00
|
|
|
|
questions_to_continue = []
|
|
|
|
|
temp_final={}
|
2024-12-26 17:20:27 +08:00
|
|
|
|
if model_type==4:
|
|
|
|
|
# evaluation_res=doubao_model(user_query,True)
|
|
|
|
|
evaluation_res = qianwen_plus(user_query, True)
|
|
|
|
|
else:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
evaluation_res = qianwen_long_stream(file_id, user_query,2,1,True) # 有些重复的键名,只有qianwen_long_text能保留
|
2024-12-20 17:24:49 +08:00
|
|
|
|
message = evaluation_res[0]
|
2025-01-02 11:28:38 +08:00
|
|
|
|
# print(message)
|
2024-12-20 17:24:49 +08:00
|
|
|
|
total_tokens = evaluation_res[1]
|
2024-12-11 17:42:51 +08:00
|
|
|
|
# 清理和处理响应
|
2024-12-30 17:32:24 +08:00
|
|
|
|
cleaned_evaluation_res = extract_content_from_json(message,True) # 带上True处理重复键名的情况-》生成列表
|
2024-12-20 17:24:49 +08:00
|
|
|
|
# print(json.dumps(cleaned_evaluation_res,ensure_ascii=False,indent=4))
|
2024-12-26 17:20:27 +08:00
|
|
|
|
max_tokens = 7900 if model_type == 4 else 5900
|
|
|
|
|
if not cleaned_evaluation_res and total_tokens > max_tokens:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
print(f"total_tokens: {total_tokens}")
|
2025-01-09 14:59:28 +08:00
|
|
|
|
questions_to_continue.append((user_query, message))
|
2024-12-20 17:24:49 +08:00
|
|
|
|
else:
|
|
|
|
|
temp_final.update(cleaned_evaluation_res)
|
|
|
|
|
if questions_to_continue:
|
2024-12-26 17:20:27 +08:00
|
|
|
|
continued_results = process_continue_answers(questions_to_continue, model_type, file_id)
|
2024-12-20 17:24:49 +08:00
|
|
|
|
temp_final.update(continued_results)
|
|
|
|
|
result_data = process_data_based_on_key(temp_final) # 处理不知名外键的情况
|
2024-12-11 17:42:51 +08:00
|
|
|
|
include = ['一包', '二包', '三包', '四包', '五包']
|
|
|
|
|
updated_jsons = {}
|
|
|
|
|
# 检查是否有外层键匹配 include 列表
|
|
|
|
|
if any(key for key in result_data if
|
|
|
|
|
any(included in key for included in include)): # 检查result_data中的任何键是否包含include列表中的任意一个项。
|
|
|
|
|
# 有匹配的项,处理这些项
|
|
|
|
|
for key in result_data:
|
|
|
|
|
if any(item in key for item in include):
|
|
|
|
|
inner_dict = result_data[key]
|
2025-01-07 17:35:11 +08:00
|
|
|
|
updated_jsons[key] = combine_technical_and_business(inner_dict) # 对于分包,单独对分包内的'技术评分''商务评分'作处理
|
2024-12-11 17:42:51 +08:00
|
|
|
|
else:
|
|
|
|
|
# 没有匹配的项,对整个字典运行
|
2025-01-07 17:35:11 +08:00
|
|
|
|
updated_jsons = combine_technical_and_business(result_data)
|
2024-12-11 17:42:51 +08:00
|
|
|
|
final_res = reorganize_data(updated_jsons, include) # 重新组织字典,尤其是分包的情况
|
|
|
|
|
return final_res
|
2024-12-25 14:35:52 +08:00
|
|
|
|
|
2024-12-11 17:42:51 +08:00
|
|
|
|
try:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
judge_res, file_id = run_first_query(evaluation_method_path)
|
2025-02-13 15:42:52 +08:00
|
|
|
|
print(judge_res)
|
2024-12-25 14:35:52 +08:00
|
|
|
|
eval_path = os.path.abspath(evaluation_method_path)
|
|
|
|
|
invalid_eval_path = os.path.abspath(invalid_path)
|
2025-01-08 17:34:50 +08:00
|
|
|
|
# 获取 evaluation_method_path 所在的目录
|
|
|
|
|
eval_dir = os.path.dirname(eval_path)
|
|
|
|
|
# extract3.txt是invalid_path->markdown,不一定存在
|
|
|
|
|
extract3_path = os.path.join(eval_dir, "extract3.txt")
|
2024-12-25 14:35:52 +08:00
|
|
|
|
if '是' in judge_res:
|
2025-01-08 17:34:50 +08:00
|
|
|
|
if get_pdf_page_count(evaluation_method_path) <= 20:
|
|
|
|
|
model_type = 4 # qianwen-plus
|
|
|
|
|
processed_filepath = convert_file_to_markdown(evaluation_method_path, "extract2.txt")
|
|
|
|
|
else:
|
|
|
|
|
if eval_path == invalid_eval_path:
|
|
|
|
|
if os.path.exists(extract3_path):
|
|
|
|
|
print("qianwen-plus1:extract3.txt")
|
|
|
|
|
processed_filepath = extract3_path
|
|
|
|
|
model_type = 4 # 使用 qianwen-plus
|
|
|
|
|
else:
|
|
|
|
|
processed_filepath = ""
|
|
|
|
|
file_id = upload_file(invalid_eval_path)
|
|
|
|
|
model_type = 3 # 默认 qianwen-long-stream
|
|
|
|
|
else:
|
|
|
|
|
evaluation_method_docxpath = pdf2docx(evaluation_method_path)
|
|
|
|
|
file_id = upload_file(evaluation_method_docxpath)
|
|
|
|
|
processed_filepath = ""
|
|
|
|
|
model_type = 3 # qianwen-long_stream
|
2024-12-26 17:20:27 +08:00
|
|
|
|
return run_second_qeury(file_id, processed_filepath, model_type)
|
2025-01-08 17:34:50 +08:00
|
|
|
|
|
|
|
|
|
# 如果 judge_res 不包含 '是' 并且路径不同,处理 invalid_path
|
|
|
|
|
if eval_path != invalid_eval_path:
|
|
|
|
|
judge_res_invalid, file_id_invalid = run_first_query(invalid_path)
|
|
|
|
|
if '是' in judge_res_invalid:
|
|
|
|
|
if os.path.exists(extract3_path):
|
|
|
|
|
print("qianwen-plus2:extract3.txt")
|
|
|
|
|
processed_filepath = extract3_path
|
|
|
|
|
model_type = 4 # qianwen-plus
|
|
|
|
|
else:
|
|
|
|
|
processed_filepath = ""
|
|
|
|
|
model_type = 3 # 默认 qianwen-long_stream
|
|
|
|
|
return run_second_qeury(file_id_invalid, processed_filepath, model_type)
|
|
|
|
|
|
|
|
|
|
# 如果路径相同或未找到 '是',返回默认结果
|
|
|
|
|
return DEFAULT_EVALUATION_REVIEW.copy()
|
|
|
|
|
|
2024-12-11 17:42:51 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error in combine_evaluation_standards: {e}")
|
2025-01-08 17:34:50 +08:00
|
|
|
|
# 出错时返回默认结果
|
2024-12-11 17:42:51 +08:00
|
|
|
|
return DEFAULT_EVALUATION_REVIEW.copy()
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
start_time=time.time()
|
|
|
|
|
# truncate_file=r"C:\Users\Administrator\Desktop\招标文件-采购类\tmp2\2024-新疆-塔城地区公安局食药环分局快检实验室项目_evaluation_method.pdf"
|
2025-02-13 15:42:52 +08:00
|
|
|
|
evaluation_method_path = r'C:\Users\Administrator\Downloads\2022-广东-鹏华基金管理有限公司深圳深业上城办公室装修项目.pdf'
|
|
|
|
|
invalid_path=r'C:\Users\Administrator\Downloads\2022-广东-鹏华基金管理有限公司深圳深业上城办公室装修项目.pdf'
|
2024-12-11 17:42:51 +08:00
|
|
|
|
# truncate_file = "C:\\Users\\Administrator\\Desktop\\货物标\\output2\\2-招标文件(统计局智能终端二次招标)_evaluation_method.pdf"
|
|
|
|
|
# truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\广水市妇幼招标文件最新(W改)_evaluation_method.pdf"
|
|
|
|
|
# truncate_file = "C:\\Users\\Administrator\\Desktop\\fsdownload\\2d481945-1f82-45a5-8e56-7fafea4a7793\\ztbfile_evaluation_method.pdf"
|
|
|
|
|
# truncate_file="C:\\Users\\Administrator\\Desktop\\fsdownload\\ztbfile_evaluation_method.pdf"
|
2025-01-07 17:35:11 +08:00
|
|
|
|
res = combine_evaluation_standards(evaluation_method_path,invalid_path,2)
|
2025-01-08 17:34:50 +08:00
|
|
|
|
print("------------------------------")
|
2024-12-11 17:42:51 +08:00
|
|
|
|
print(json.dumps(res, ensure_ascii=False, indent=4))
|
|
|
|
|
end_time=time.time()
|
|
|
|
|
print("elapsed time:"+str(end_time-start_time))
|