首次提交:上传本地文件夹

This commit is contained in:
ruxia
2025-03-31 17:28:23 +08:00
commit 0de349447c
439 changed files with 36643 additions and 0 deletions
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+95
View File
@@ -0,0 +1,95 @@
import numpy as np
from typing import List, Dict, Any
from .llm import embedding as embedding_model
class TextEmbedder:
def __init__(self):
"""初始化文本嵌入器,使用自定义的embedding模型"""
pass
def get_embedding(self, text: str) -> np.ndarray:
"""
获取文本的嵌入向量
参数:
text: 输入文本
返回:
嵌入向量
"""
return embedding_model.embed(text)
def get_embeddings(self, texts: List[str]) -> List[np.ndarray]:
"""
批量获取文本的嵌入向量
参数:
texts: 输入文本列表
返回:
嵌入向量列表
"""
return [embedding_model.embed(text) for text in texts]
def compute_similarity(self, text1: str, text2: str) -> float:
"""
计算两段文本的相似度
参数:
text1: 第一段文本
text2: 第二段文本
返回:
相似度分数 (0-1)
"""
emb1 = self.get_embedding(text1)
emb2 = self.get_embedding(text2)
return self._cosine_similarity(emb1, emb2)
def find_most_similar(self, query: str, candidates: List[str], top_k: int = 5) -> List[Dict[str, Any]]:
"""
找出与查询最相似的候选文本
参数:
query: 查询文本
candidates: 候选文本列表
top_k: 返回的最相似文本数量
返回:
最相似文本及其相似度的列表
"""
query_emb = self.get_embedding(query)
candidate_embs = self.get_embeddings(candidates)
similarities = []
for i, emb in enumerate(candidate_embs):
similarity = self._cosine_similarity(query_emb, emb)
similarities.append((i, similarity))
# 按相似度降序排序
similarities.sort(key=lambda x: x[1], reverse=True)
results = []
for i, sim in similarities[:top_k]:
results.append({
"text": candidates[i],
"similarity": float(sim)
})
return results
def _cosine_similarity(self, vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
计算两个向量的余弦相似度
参数:
vec1: 第一个向量
vec2: 第二个向量
返回:
余弦相似度 (0-1)
"""
dot_product = np.dot(vec1, vec2)
norm1 = np.linalg.norm(vec1)
norm2 = np.linalg.norm(vec2)
return dot_product / (norm1 * norm2)
+124
View File
@@ -0,0 +1,124 @@
# import requests
# class LLM:
# def __init__(self, model_uid, server_url):
# self.model_uid = model_uid
# self.server_url = server_url.rstrip("/")
# def generate(self, prompt, max_tokens=32000, temperature=0.2, **kwargs):
# url = f"{self.server_url}/v1/completions"
# headers = {"Content-Type": "application/json"}
# data = {
# "model": self.model_uid,
# "prompt": prompt,
# "max_tokens": max_tokens,
# "temperature": temperature,
# **kwargs,
# }
# try:
# response = requests.post(url, headers=headers, json=data)
# response.raise_for_status()
# result = response.json()
# return result["choices"][0]["text"]
# except requests.exceptions.RequestException as e:
# raise RuntimeError(f"请求失败: {e}")
# except KeyError:
# raise ValueError("响应格式错误,无法解析生成结果")
# llm = LLM(model_uid="QwQ-32b", server_url="http://172.20.0.145:9995")
class Embedding:
def __init__(self, model_uid, server_url):
self.model_uid = model_uid
self.server_url = server_url.rstrip("/")
def embed(self, text):
url = f"{self.server_url}/v1/embeddings"
headers = {"Content-Type": "application/json"}
data = {"model": self.model_uid, "input": text}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
result = response.json()
return result["data"][0]["embedding"]
except requests.exceptions.RequestException as e:
raise RuntimeError(f"请求失败: {e}")
except KeyError:
raise ValueError("响应格式错误,无法解析嵌入结果")
embedding = Embedding(model_uid="bge-m3", server_url="http://10.1.16.39:9995")
from langchain_openai import ChatOpenAI
import requests
from typing import Any, Dict, List, Optional
from langchain_core.language_models import BaseLLM
from langchain_core.callbacks import CallbackManagerForLLMRun
from langchain_core.outputs import LLMResult, Generation
from langchain_community.llms.yi import YiLLM
from tenacity import retry, stop_after_attempt, wait_exponential
class SiliconFlowLLM(BaseLLM):
"""自定义硅基流动大模型调用类"""
api_url: str
api_key: str
model: str
def _generate(
self,
prompts: List[str],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> LLMResult:
from langchain_core.outputs import Generation, LLMResult
generations = []
for prompt in prompts:
try:
headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
payload = {
"model": self.model,
"messages": [{"role": "user", "content": prompt}],
}
response = requests.post(self.api_url, json=payload, headers=headers)
response.raise_for_status()
text = response.json()["choices"][0]["message"]["content"]
generations.append([Generation(text=text)])
except Exception as e:
raise Exception(f"调用硅基流动API失败: {str(e)}")
return LLMResult(generations=generations)
@property
def _llm_type(self) -> str:
return "siliconflow"
search_llm = SiliconFlowLLM(
api_url="https://api.siliconflow.cn/v1/chat/completions",
api_key="sk-bbeamiumkouptsrueilgufqqyuumelcsivxwjbdugqwsqhwj",
model="deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
)
keyword_llm = SiliconFlowLLM(
api_url="https://api.siliconflow.cn/v1/chat/completions",
api_key="sk-bbeamiumkouptsrueilgufqqyuumelcsivxwjbdugqwsqhwj",
model="deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
)
rewrite_llm = SiliconFlowLLM(
api_url="https://api.siliconflow.cn/v1/chat/completions",
api_key="sk-bbeamiumkouptsrueilgufqqyuumelcsivxwjbdugqwsqhwj",
model="Qwen/Qwen2.5-72B-Instruct",
)
+133
View File
@@ -0,0 +1,133 @@
PROMPTS = {}
PROMPTS["DEFAULT_ENTITY_TYPES"] = ["软件名称", "功能名称", "使用场景", "功能入口", "操作步骤", "操作影响"]
PROMPTS["DEFAULT_RELATIONSHIP_TYPES"] = ["包含", "配置", "应用于", "引用", "影响"]
PROMPTS["entity_extraction"] = """
-目标-
给定可能与此活动相关的文本文档和实体类型列表,从文本中识别出这些类型的所有实体以及所识别实体之间的所有关系。
-步骤-
1.识别所有实体。对于每个已识别的实体,提取以下信息:
-entity_name:实体的名称,使用与输入文本相同的语言。
-entity_type:以下类型之一:〔{entity_type}〕
-entity_description:对实体属性和活动的全面描述
将每个实体格式化为("entity"{tuple_delimiter}<entity_name>{tuple_delimiter}<entity_type>{tuple_delimiter}<entity_description>
2.从步骤1中识别的实体中,识别彼此*明显相关*的所有对(source_entity、target_entity)。
对于每对相关实体,提取以下信息:
-source_entity:源实体的名称,如步骤1中所标识的
-target_entity:目标实体的名称,如步骤1中所标识的
-relationship_description:解释为什么你认为源实体和目标实体是相互关联的
-relationship_strength:一个数字分数,表示源实体和目标实体之间关系的强度
-relationship_keywords:一个或多个高级关键字,总结关系的总体性质,侧重于概念或主题,而不是具体细节
将每个关系格式化为("relationship"{tuple_delimiter}<source_entity>{tuple_delimiter}<target_entity>{tuple_delimiter}<relationship_description>{tuple_delimiter}<relationship_keywords>{tuple_delimiter}<relationship_strength>)
3.识别概括整篇文章主要概念、主题或主题的高级关键字。这些应该捕捉到文件中存在的总体想法。
将内容级关键字格式化为("content_keywords"{tuple_delimiter}<high_level_keywords>)
4.返回中文输出,作为步骤1和2中识别的所有实体和关系的单个列表。使用**{record_delimiter}**作为列表分隔符。
5.完成后,输出 {completion_delimiter}
6.在输出的结果中,不能加载任何多余的字符,请严格按照给定的规范和实例的输出格式来进行输出。
-示例-
示例 1:
实体类型列表: ["软件名称", "功能名称", "使用场景", "功能入口", "操作步骤"]
关系类型列表: ["包含", "配置", "应用于", "引用", "影响"]
文本内容:
(储能C1)按项目划分分页输出
# (储能C1)按项目划分分页输出
## 使用场景
表三需要按项目划分分页显示,如何调整?
## 功能入口
【报表输出】界面——“批量设置”——“设置报表参数”
![功能入口](https://172.20.0.145/files/0bf8512b-17a0-486f-9a5c-d8ef0ed29a45/image-preview)
## 操作步骤
**设置表三按项目划分分页显示:**
在“设置报表参数”窗口,点开“按项目划分分页输出”名称前面“\+”,可单独针对建筑/安装/余物清理表三分别进行设置勾选“按项目划分分页输出”,点击确定即可。
![picture](https://172.20.0.145/files/fe488f93-9a7b-47d1-b536-6102e2ee72bf/image-preview)
![分页显示效果](https://172.20.0.145/files/8702d44b-b8f8-4d19-8db9-5487e1fb8e72/image-preview)
## 操作影响
输出:
("entity"{tuple_delimiter}"储能C1"{tuple_delimiter}"软件名称"{tuple_delimiter}"用于工程计价和报表管理的软件,支持按项目划分分页输出功能。"){record_delimiter}
("entity"{tuple_delimiter}"按项目划分分页输出"{tuple_delimiter}"功能名称"{tuple_delimiter}"针对表三报表按建筑/安装/余物清理等项目类型进行分页显示的配置功能。"){record_delimiter}
("entity"{tuple_delimiter}"表三需要按项目划分分页显示,如何调整?"{tuple_delimiter}"使用场景"{tuple_delimiter}"用户需要调整表三报表的显示方式,使其按不同项目类型分页展示。"){record_delimiter}
("entity"{tuple_delimiter}"【报表输出】界面——“批量设置”——“设置报表参数”"{tuple_delimiter}"功能入口"{tuple_delimiter}"通过报表输出界面的批量设置菜单进入分页配置功能的路径。"){record_delimiter}
("entity"{tuple_delimiter}"在“设置报表参数”窗口勾选对应表三的分页选项"{tuple_delimiter}"操作步骤"{tuple_delimiter}"通过展开设置项并勾选特定表三的分页输出复选框完成配置。"){record_delimiter}
("relationship"{tuple_delimiter}"按项目划分分页输出"{tuple_delimiter}"储能C1"{tuple_delimiter}"该功能属于储能C1软件的核心报表管理功能。"{tuple_delimiter}"功能归属, 软件功能"{tuple_delimiter}0.95){record_delimiter}
("relationship"{tuple_delimiter}"功能入口"{tuple_delimiter}"操作步骤"{tuple_delimiter}"功能入口是执行操作步骤的前置路径条件。"{tuple_delimiter}"操作流程, 功能调用"{tuple_delimiter}0.85){record_delimiter}
("content_keywords"{tuple_delimiter}"工程报表管理, 分页配置, 项目划分"){completion_delimiter}
实例 2:
实体类型列表: ["软件名称", "功能名称", "使用场景", "功能入口", "操作步骤"]
关系类型列表: ["包含", "配置", "应用于", "引用", "影响"]
文本内容:
(技改检修工程计价通T1软件) (T1)批量设置小数位数
# (技改检修工程计价通T1软件) (T1)批量设置小数位数
## 使用场景
单个工程中需要批量设置报表小数位数或者编制说明需要设置小数位数,可以使用该功能。
## 功能入口
【报表输出】界面——工具栏“批量设置小数位数”
## 操作步骤
**批量设置小数位数:**
【报表输出】界面——工具栏“批量设置小数位数”——弹出窗口中勾选上需要批量设置的报表,下拉选择设置的小数位数——点击“确定”按钮。
![批量设置小数位数](https://172.20.0.145/files/b83dac30-ad89-4cde-9b06-b52bec8d1b53/image-preview)
## 操作影响
1、设置后影响对应的报表小数位数显示。
2、设置“汇总表(表一)”小数位数可以影响编制说明中的小数位数显示(编制说明中工程费用小数位数与总表/表一相同)。
输出:
("entity"{tuple_delimiter}"技改检修工程计价通T1软件"{tuple_delimiter}"软件名称"{tuple_delimiter}"专注于技改检修工程计价的软件,支持批量设置小数位数功能。"){record_delimiter}
("entity"{tuple_delimiter}"批量设置小数位数"{tuple_delimiter}"功能名称"{tuple_delimiter}"统一设置单个工程中多个报表或编制说明的小数位数显示格式。"){record_delimiter}
("entity"{tuple_delimiter}"单个工程中需要批量设置报表小数位数或编制说明小数位数"{tuple_delimiter}"使用场景"{tuple_delimiter}"用户需要统一工程内不同报表或文档的小数精度显示需求。"){record_delimiter}
("entity"{tuple_delimiter}"【报表输出】界面——工具栏“批量设置小数位数”"{tuple_delimiter}"功能入口"{tuple_delimiter}"通过报表输出界面工具栏直接启动小数位数批量配置功能的入口。"){record_delimiter}
("entity"{tuple_delimiter}"勾选目标报表并选择小数位数后确认"{tuple_delimiter}"操作步骤"{tuple_delimiter}"在批量设置窗口选择需配置的报表列表并指定小数位数完成设置。"){record_delimiter}
("relationship"{tuple_delimiter}"批量设置小数位数"{tuple_delimiter}"技改检修工程计价通T1软件"{tuple_delimiter}"该功能是T1软件用于工程计价格式标准化的重要工具。"{tuple_delimiter}"功能实现, 数据格式"{tuple_delimiter}0.90){record_delimiter}
("relationship"{tuple_delimiter}"使用场景"{tuple_delimiter}"功能名称"{tuple_delimiter}"使用场景直接驱动该功能的开发需求。"{tuple_delimiter}"需求映射, 功能设计"{tuple_delimiter}0.88){record_delimiter}
("content_keywords"{tuple_delimiter}"数值格式标准化, 批量配置, 工程计价"){completion_delimiter}
"""
# 回答生成的提示模板
RESPONSE_TEMPLATE = """
- Role: 知识图谱应用专家和交互设计顾问
- Background: 用户需要将提问和知识图谱检索到的知识传递给大模型,大模型需按照特定规范输出回答内容。这种需求常见于复杂系统操作指导场景,用户希望通过清晰的结构化回答,快速准确地找到操作路径和方法。
- Profile: 你是一位精通知识图谱应用的专家,对复杂系统操作流程有着深入的理解和丰富的实践经验,擅长将知识图谱中的信息转化为简洁明了的操作指导。
- Skills: 你具备知识图谱分析能力、交互设计能力、自然语言处理能力以及逻辑推理能力,能够精准地提取关键信息并按照规范进行输出。
- Goals: 根据用户提问和知识图谱检索到的内容,按照规范输出功能入口、操作步骤和操作影响(如果有的话)。
- Constrains: 输出内容必须严格遵循给定的规范,确保信息的准确性和完整性。
- OutputFormat: 按照以下结构输出:
1. 功能入口:节点名称、节点类型、节点到根节点的路径。
2. 操作步骤:根据问题和知识图谱内容给出的操作指导。
3. 操作影响:节点的属性(如果有的话)。
- 例子:
用户提问:如何更新土质比例?
知识图谱检索到的内容:节点名称:更新土质;节点类型:按钮;节点到根节点的路径:【工程信息】界面——“线路参数”页签;节点属性:软件将土质比例更新到土方资源中。
输出内容:
功能入口:【工程信息】界面——“线路参数”页签——“更新土质”按钮。
操作步骤:在【工程信息】界面——“线路参数”页签中右边,设置好土质比例;点击工具栏“更新土质”按钮。
操作影响:软件将土质比例更新到土方资源中。
请根据以下信息回答用户的问题。
### 用户意图分析
{intent_info}
### 相关信息:
{context}
### 用户问题:
{query}
"""
+167
View File
@@ -0,0 +1,167 @@
def extract_names_from_json(file_path):
import json
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
# 确保数据是一个列表
if isinstance(data, list):
names = [item.get("name") for item in data if "name" in item]
return names
else:
raise ValueError("JSON 文件的格式应为包含对象的列表")
# list 生成 问题类别
def define_suffix(input_str, fields):
import re
# 定义要检测的字段列表
# fields = [
# "xzwb", "bxqd2", "bpz17", "zwqd", "bwpw", "BJGX", "bt2", "BDQ3", "BT2",
# "gec5", "BDY3", "dwg", "bwpwz", "BDD3", "bt1", "bphq18", "zwzj", "bczc2",
# "BPQ", "BPY", "BDQ3", "SXZB23", "SXZ", "xzwb2", "bpz17", "bwpw", "BJGX",
# "bt1", "bpz17", "SXZB23", ".SXZ",
# ]
# 构建正则表达式模式,匹配大小写不敏感且前面可能带有.
# 去掉 \b 以允许字段是其他字符串的一部分
pattern = r'(?:\.?)(' + '|'.join(re.escape(field) for field in fields) + r')'
# 使用 re.IGNORECASE 标志来忽略大小写
if re.search(pattern, input_str, re.IGNORECASE):
return ['后缀名问题']
else:
return ['未知']
def output_suffix(input_str, fields):
import re
pattern = r'(?:\.?)(' + '|'.join(re.escape(field) for field in fields) + r')'
matches = re.findall(pattern, input_str, re.IGNORECASE)
return matches[0]
# str提取后缀名str
def match_suffix(input_str, fields):
import re
pattern = r'(?:\.?)(' + '|'.join(re.escape(field) for field in fields) + r')'
matches = re.findall(pattern, input_str, re.IGNORECASE)
return matches[0] if matches else '未知'
# 将pydantic的str类型转为model
def str_to_pydantic(class_str):
from pydantic import BaseModel, Field
namespace = {}
# exec(class_str, globals(), namespace)
exec(class_str, {"BaseModel": BaseModel, "Field": Field}, namespace)
return namespace[list(namespace.keys())[0]]
# 将pydantic的str类型转为template的input str
def parse_pydantic_fields(class_str):
import ast
tree = ast.parse(class_str)
fields = {}
for node in ast.walk(tree):
if isinstance(node, ast.ClassDef): # 找到类定义
for body_item in node.body:
if isinstance(body_item, ast.AnnAssign): # 找到字段定义
field_name = body_item.target.id
fields[field_name] = '' # 设为空值,可改为空字符串 ""
formatted_str = "\n".join([f"'{key}': ," for key in fields.keys()])
return formatted_str
# 后缀名chains 后处理
def extract_values(json_str):
import re
# 使用正则匹配 JSON 中的值
matches = re.findall(r'"(?:一级意图|二级意图)"\s*:\s*"([^"]+)"', json_str)
# 替换双引号为单引号
return [match.replace('"', "'") for match in matches]
# 意图 槽位结构str返回
def check_and_return(intention_result, parser):
key = intention_result[0] # 获取 `intention_result` 的第一个值
value_lv1 = None
value_lv2 = None
# 检查 `pydantic_dict_lv1`
if key in parser.pydantic_dict_lv1:
temp_value_lv1 = parser.pydantic_dict_lv1[key]
if len(temp_value_lv1) >= 35:
value_lv1 = temp_value_lv1
# 检查 `pydantic_dict_lv2`
for item in parser.pydantic_dict_lv2:
for i in item:
intention_tuple = tuple(intention_result)
if intention_tuple in i:
temp_value_lv2 = i[intention_tuple]
if len(temp_value_lv2) >= 35:
value_lv2 = temp_value_lv2
return [value_lv1, value_lv2]
""" --------------------------------------------- 其他工具 --------------------------------------------- """
def transform_json(input_file: str, output_file: str):
import json
"""
读取JSON文件并转换格式后保存
:param input_file: 输入的JSON文件路径
:param output_file: 输出的JSON文件路径
"""
try:
with open(input_file, 'r', encoding='utf-8') as f:
data = json.load(f)
transformed_data = [
{
"name": item["name"],
"synonymous": [],
"description": {
"software_name": item["software_name"],
"works_category": item["works_category"]
}
} for item in data
]
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(transformed_data, f, ensure_ascii=False, indent=2)
print(f"转换完成,结果已保存至 {output_file}")
except Exception as e:
print(f"转换失败: {e}")
def extract_required_values(data, result=None):
"""
递归查找 JSON 数据中所有包含 "_required" 的键,并返回对应的值。
:param data: 输入的 JSON 数据(字典或列表)
:param result: 结果列表(用于递归)
:return: 包含所有符合条件值的列表
"""
if result is None:
result = []
if isinstance(data, dict):
for key, value in data.items():
if "name_required" in key:
result.append(value)
if isinstance(value, (dict, list)):
extract_required_values(value, result)
elif isinstance(data, list):
for item in data:
extract_required_values(item, result)
return result