From 6934e1903017eec54d274b307d9b9c50da3130ea Mon Sep 17 00:00:00 2001 From: ZHOU Shujian Date: Wed, 15 Apr 2026 14:47:43 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat(chunking):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E5=88=86=E5=9D=97=E9=A2=84=E8=AE=BE=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=9F=BA=E4=BA=8E=E5=B5=8C=E5=85=A5=E5=92=8C?= =?UTF-8?q?=E8=81=9A=E7=B1=BB=E7=9A=84=E6=99=BA=E8=83=BD=E5=88=87=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加语义分块预设选项,使用嵌入模型和聚类算法进行语义切分 - 实现标题路径增强、表格转换和图像标题处理等语义增强功能 - 新增NLTK相关依赖并持久化数据包,避免重复下载 - 添加完整的单元测试验证语义切分逻辑 --- backend/package/pyproject.toml | 6 + .../chunking/ragflow_like/dispatcher.py | 4 +- .../chunking/ragflow_like/parsers/semantic.py | 274 ++++++++++++++++++ .../chunking/ragflow_like/presets.py | 16 + .../ragflow_like/utils/md_parser_utils.py | 122 ++++++++ .../ragflow_like/utils/semantic_utils.py | 114 ++++++++ .../ragflow_like/utils/table_utils.py | 135 +++++++++ .../unit/backends/test_semantic_chunking.py | 78 +++++ docker-compose.yml | 1 + web/src/utils/chunk_presets.js | 5 + 10 files changed, 754 insertions(+), 1 deletion(-) create mode 100644 backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py create mode 100644 backend/package/yuxi/knowledge/chunking/ragflow_like/utils/md_parser_utils.py create mode 100644 backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py create mode 100644 backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py create mode 100644 backend/test/unit/backends/test_semantic_chunking.py diff --git a/backend/package/pyproject.toml b/backend/package/pyproject.toml index c2983862..30eff0e3 100644 --- a/backend/package/pyproject.toml +++ b/backend/package/pyproject.toml @@ -76,6 +76,12 @@ dependencies = [ "typer>=0.16.0", "unstructured>=0.17.2", "wcmatch>=8.0.0", + # Markdown 解析语义切分相关依赖 + "markdown-it-py>=3.0.0", + "mdit-py-plugins>=0.4.0", + "scikit-learn>=1.3.0", + "nltk>=3.8.1", + "beautifulsoup4>=4.12.0", ] [tool.setuptools] diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/dispatcher.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/dispatcher.py index 598b96f6..a9d5971a 100644 --- a/backend/package/yuxi/knowledge/chunking/ragflow_like/dispatcher.py +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/dispatcher.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Any -from yuxi.knowledge.chunking.ragflow_like.parsers import book, general, laws, qa +from yuxi.knowledge.chunking.ragflow_like.parsers import book, general, laws, qa, semantic from yuxi.knowledge.chunking.ragflow_like.presets import map_to_internal_parser_id, normalize_chunk_preset_id @@ -42,6 +42,8 @@ def _dispatch_markdown_parser( return book.chunk_markdown(markdown_content, parser_config) if parser_id == "laws": return laws.chunk_markdown(filename, markdown_content, parser_config) + if parser_id == "semantic": + return semantic.chunk_markdown(markdown_content, parser_config) return general.chunk_markdown(markdown_content, parser_config) diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py new file mode 100644 index 00000000..91a84ab9 --- /dev/null +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py @@ -0,0 +1,274 @@ +from __future__ import annotations + +import re +from typing import Any +from markdown_it import MarkdownIt +from mdit_py_plugins.dollarmath import dollarmath_plugin +from yuxi.utils.logging_config import logger +from yuxi.knowledge.chunking.ragflow_like.nlp import count_tokens + +from ..utils.md_parser_utils import ( + infer_heading_level, + get_title_path, + extract_table_block, + split_text_by_length_and_newline +) +from ..utils.table_utils import html_table_to_key_value + +def _flush_content( + result: list, + current_content: list, + title_stack: list, + max_length: int, + embed_fn: Any, + special_element: str = None, + allow_split: bool = False + ) -> None: + if not current_content: + return + + content = '\n'.join(current_content).strip() + if not content: + current_content.clear() + return + + level = next((i + 1 for i in range(5, -1, -1) if title_stack[i]), 1) + title_path = get_title_path(title_stack) + + if special_element and not allow_split: + header = f"{'#' * level} {title_path}|{special_element}" if title_path else f"{'#' * level} {special_element}" + result.extend([header, content, '-' * 10]) + else: + if count_tokens(content) > max_length: + chunks = split_text_by_length_and_newline(content, max_length, embed_fn=embed_fn, token_count_fn=count_tokens) + for idx, chunk in enumerate(chunks, 1): + base_header = f"{'#' * level} {title_path}" if title_path else f"{'#' * level}" + if special_element: + header = f"{base_header}|{special_element}|Part {idx}" + else: + header = f"{base_header}|Part {idx}" + result.extend([header, chunk, '-' * 10]) + else: + base_header = f"{'#' * level} {title_path}" if title_path else f"{'#' * level}" + if special_element: + header = f"{base_header}|{special_element}" + else: + header = base_header + + if header: + result.append(header) + result.append("") + result.extend([content, '-' * 10]) + + current_content.clear() + +def _handle_image_caption(tokens, i, result, current_content, title_stack, max_length, embed_fn): + token = tokens[i] + if token.type != 'paragraph_open': + return False, i + + inline_token = tokens[i + 1] + if inline_token.type != 'inline': + return False, i + + content = inline_token.content.strip() + image_pattern = r'^!\[.*?\]\(.*?\)\s*$' + caption_pattern = r'^(?:Figure|图|Fig\.|表|Table)\s*[\d\w\.]+' + + img_match = re.search(r'^(!\[.*?\]\(.*?\))', content) + if img_match: + rest = content[img_match.end():].strip() + if rest and re.match(caption_pattern, rest, re.IGNORECASE): + _flush_content(result, current_content, title_stack, max_length, embed_fn) + current_content.append(content) + caption_title = rest.split('\n')[0].strip() + _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=caption_title) + return True, i + 3 + + if re.match(image_pattern, content): + next_p_idx = i + 3 + if next_p_idx + 1 < len(tokens) and tokens[next_p_idx].type == 'paragraph_open': + next_inline = tokens[next_p_idx + 1] + if next_inline.type == 'inline': + next_content = next_inline.content.strip() + if re.match(caption_pattern, next_content, re.IGNORECASE): + _flush_content(result, current_content, title_stack, max_length, embed_fn) + current_content.append(content) + current_content.append(next_content) + _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=next_content) + return True, i + 6 + + if current_content and re.match(caption_pattern, content, re.IGNORECASE): + last_item = current_content[-1].strip() + if re.match(image_pattern, last_item): + image_tag = current_content.pop() + _flush_content(result, current_content, title_stack, max_length, embed_fn) + current_content.append(image_tag) + current_content.append(content) + _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=content) + return True, i + 3 + + return False, i + +def chunk_markdown( + markdown_content: str, + parser_config: dict[str, Any] | None = None, + embed_fn: Any | None = None +) -> list[str]: + """ + 语义化切分 Markdown 内容。 + + Args: + markdown_content: 待切分的 Markdown 文本 + parser_config: 切分参数,如 chunk_token_num + embed_fn: 可选。传入用于生成向量的函数。如果不传,将从系统配置中加载模型。 + 通过注入此参数可以避免在单元测试中加载重型资源。 + """ + parser_config = parser_config or {} + max_length = int(parser_config.get("chunk_token_num", 512)) + logger.info(f"语义切分开始: max_length={max_length}, content_length={len(markdown_content)}") + + # 延迟加载重型资源,仅在没有注入 embed_fn 时触发 + if embed_fn is None: + try: + from yuxi.config.app import config + from yuxi.models.embed import select_embedding_model + + embed_model_id = parser_config.get("embed_model_id") or config.embed_model + logger.info(f"语义切分加载Embedding模型: {embed_model_id}") + embed_model = select_embedding_model(embed_model_id) + embed_fn = embed_model.encode + except Exception as e: + logger.error(f"加载 Embedding 模型失败: {e}。将退化为简单切分。") + embed_fn = None + + md = MarkdownIt('commonmark').enable('table') + md.use(dollarmath_plugin, allow_space=True, allow_digits=True) + + tokens: list = md.parse(markdown_content) + original_lines: list = markdown_content.split('\n') + + result: list = [] + current_content: list = [] + title_stack: list = [''] * 6 + + i = 0 + while i < len(tokens): + token = tokens[i] + if token.type == 'heading_open': + _flush_content(result, current_content, title_stack, max_length, embed_fn) + level = int(token.tag[1:]) if token.tag and len(token.tag) > 1 else 1 + inline_token = tokens[i + 1] + if inline_token.type == 'inline': + full_title = inline_token.content.strip() + title_stack[level - 1] = full_title + for j in range(level, 6): + title_stack[j] = '' + i += 3 + continue + elif token.type == 'table_open': + _flush_content(result, current_content, title_stack, max_length, embed_fn) + j, table_content = extract_table_block(tokens, i, original_lines) + current_content.append(table_content) + _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element='Table') + i = j + 1 if j < len(tokens) else len(tokens) + continue + elif token.type == 'paragraph_open': + handled, new_i = _handle_image_caption(tokens, i, result, current_content, title_stack, max_length, embed_fn) + if handled: + i = new_i + continue + inline_token = tokens[i + 1] + if inline_token.type == 'inline': + current_content.append(inline_token.content.strip()) + i += 3 + continue + elif token.type == 'fence': + current_content.append(f"```\n{token.content}\n```") + i += 1 + continue + elif token.type == 'ordered_list_open': + _flush_content(result, current_content, title_stack, max_length, embed_fn) + list_content = [] + j = i + 1 + list_item_counter = 1 + while j < len(tokens) and tokens[j].type != 'ordered_list_close': + if tokens[j].type == 'list_item_open': + k = j + 1 + while k < len(tokens) and tokens[k].type != 'list_item_close': + if tokens[k].type == 'paragraph_open' and k + 1 < len(tokens) and tokens[k + 1].type == 'inline': + list_content.append(f"{list_item_counter}. {tokens[k + 1].content.strip()}") + list_item_counter += 1 + k += 1 + j += 1 + if list_content: + current_content.extend(list_content) + _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=token.type) + i = j + 1 + continue + elif token.type == 'bullet_list_open': + _flush_content(result, current_content, title_stack, max_length, embed_fn) + list_content = [] + j = i + 1 + while j < len(tokens) and tokens[j].type != 'bullet_list_close': + if tokens[j].type == 'list_item_open': + k = j + 1 + while k < len(tokens) and tokens[k].type != 'list_item_close': + if tokens[k].type == 'paragraph_open' and k + 1 < len(tokens) and tokens[k + 1].type == 'inline': + list_content.append(f"- {tokens[k + 1].content.strip()}") + k += 1 + j += 1 + if list_content: + current_content.extend(list_content) + _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=token.type) + i = j + 1 + continue + elif token.type == 'html_block': + _flush_content(result, current_content, title_stack, max_length, embed_fn) + content = token.content.strip() + is_converted_table = False + if ' list[dict[str, str]]: "label": "Laws", "description": CHUNK_PRESET_DESCRIPTIONS[CHUNK_PRESET_LAWS], }, + { + "value": CHUNK_PRESET_SEMANTIC, + "label": "Semantic", + "description": CHUNK_PRESET_DESCRIPTIONS[CHUNK_PRESET_SEMANTIC], + }, ] diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/md_parser_utils.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/md_parser_utils.py new file mode 100644 index 00000000..e1b37869 --- /dev/null +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/md_parser_utils.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import re +from typing import List, Callable, Any +from .semantic_utils import semantic_chunking_with_auto_clusters + +def infer_heading_level(title: str) -> int: + """ + 根据标题文本推断其层级级别(1-6级)。 + """ + m = re.match(r'^\s*(\d+(?:\.\d+)*)[.)、]?\s*', title) + if m: + return max(1, min(len(m.group(1).split('.')), 6)) + m_zh = re.match(r'^\s*[一二三四五六七八九十百千]+[、.]\s*', title) + if m_zh: + return 1 + return 1 + +def get_title_path(stack: List[str]) -> str: + """ + 根据标题栈生成标题路径,用"|"分隔。 + """ + return '|'.join([t for t in stack if t]) + +def extract_table_block(tokens: List[Any], i: int, original_lines: List[str]) -> tuple[int, str]: + """ + 从token流和原始文本中提取完整的表格块。 + """ + token = tokens[i] + table_start = token.map[0] if token.map else 0 + j = i + 1 + while j < len(tokens) and tokens[j].type != 'table_close': + j += 1 + if j < len(tokens): + end_token = tokens[j] + if end_token.map and end_token.map[1] is not None: + table_end = end_token.map[1] + else: + table_end = None + for k in range(j + 1, len(tokens)): + if tokens[k].map and tokens[k].map[0] is not None: + table_end = tokens[k].map[0] + break + if table_end is None: + table_end = table_start + 1 + for line_idx in range(table_start, len(original_lines)): + line = original_lines[line_idx].strip() + if not line or not (line.startswith('|') or '|' in line): + table_end = line_idx + break + else: + table_end = table_start + 1 + for line_idx in range(table_start, len(original_lines)): + line = original_lines[line_idx].strip() + if not line or not (line.startswith('|') or '|' in line): + table_end = line_idx + break + return j, '\n'.join(original_lines[table_start:table_end]) + +def split_text_by_length_and_newline( + text: str, + max_length: int, + embed_fn: Callable[[List[str]], Any], + token_count_fn: Callable[[str], int] +) -> List[str]: + """ + 层次化文本切分策略。 + """ + chunks = [] + + paragraphs = text.split('\n\n') + + for paragraph in paragraphs: + paragraph = paragraph.strip() + if not paragraph: + continue + + paragraph_token_count = token_count_fn(paragraph) + + if paragraph_token_count <= max_length: + chunks.append(paragraph) + continue + + lines = paragraph.split('\n') + current_chunk_lines = [] + current_chunk_tokens = 0 + + for line in lines: + line = line.strip() + if not line: + continue + + line_token_count = token_count_fn(line) + added_tokens = line_token_count + (1 if current_chunk_lines else 0) + + if line_token_count > max_length: + if current_chunk_lines: + chunks.append('\n'.join(current_chunk_lines)) + current_chunk_lines = [] + current_chunk_tokens = 0 + + sub_chunks = semantic_chunking_with_auto_clusters( + line, + embed_fn=embed_fn, + token_count_fn=token_count_fn, + max_chunk_size=max_length + ) + chunks.extend(sub_chunks) + + elif current_chunk_tokens + added_tokens > max_length: + chunks.append('\n'.join(current_chunk_lines)) + current_chunk_lines = [line] + current_chunk_tokens = line_token_count + + else: + current_chunk_lines.append(line) + current_chunk_tokens += added_tokens + + if current_chunk_lines: + chunks.append('\n'.join(current_chunk_lines)) + + return chunks diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py new file mode 100644 index 00000000..231d7320 --- /dev/null +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py @@ -0,0 +1,114 @@ +from __future__ import annotations + +import re +import nltk +from nltk.tokenize import sent_tokenize +from sklearn.cluster import AgglomerativeClustering +from sklearn.metrics import silhouette_score +from typing import Callable, List, Any + +# Ensure punkt_tab is available +try: + nltk.data.find('tokenizers/punkt_tab') +except LookupError: + nltk.download('punkt_tab') + +def split_sentences_chinese(text: str) -> List[str]: + """ + Split sentences by Chinese punctuation while keeping the punctuation. + """ + pattern = r'(?<=[。!?])(?![”’"])|(?<=[。!?][”’"])' + sentences = re.split(pattern, text) + return [s.strip() for s in sentences if s.strip()] + +def split_mixed_sentences(text: str) -> List[str]: + """ + Handle both Chinese and English sentence splitting. + """ + chunks = re.split(r'(\n+)', text) + sentences = [] + + for ch in chunks: + if not ch.strip(): + continue + if re.search(r'[A-Za-z]', ch): + parts = sent_tokenize(ch) + sentences.extend([p.strip() for p in parts if p.strip()]) + else: + sents = split_sentences_chinese(ch) + if sents: + sentences.extend([s.strip() for s in sents if s.strip()]) + else: + parts = re.split(r'(?<=[。!?])', ch) + sentences.extend([p.strip() for p in parts if p.strip()]) + return sentences + +def find_best_num_clusters(embeddings: Any, min_clusters: int = 2, max_clusters: int = 10) -> int: + """ + Select best number of clusters using silhouette score. + """ + if len(embeddings) <= min_clusters: + return len(embeddings) + + best_score = -1 + best_k = min_clusters + + limit_k = min(max_clusters, len(embeddings)) + for k in range(min_clusters, limit_k + 1): + labels = AgglomerativeClustering(n_clusters=k, metric='cosine', linkage='average').fit_predict(embeddings) + if len(set(labels)) <= 1: + continue + score = silhouette_score(embeddings, labels, metric='cosine') + if score > best_score: + best_score = score + best_k = k + + return best_k + +def semantic_chunking_with_auto_clusters( + text: str, + embed_fn: Callable[[List[str]], Any], + token_count_fn: Callable[[str], int], + max_chunk_size: int = 512 +) -> List[str]: + """ + Semantic chunking with automatic cluster number selection. + """ + sentences = split_mixed_sentences(text) + if len(sentences) < 2: + return [text.strip()] + + # Vectorization + embeddings = embed_fn(sentences) + + # Pre-calculate token counts + sentence_token_counts = [token_count_fn(s) for s in sentences] + total_tokens = sum(sentence_token_counts) + + # Determine number of clusters + best_k = max(total_tokens // max_chunk_size, 1) + 1 + best_k = min(best_k, len(sentences)) + + # Clustering + labels = AgglomerativeClustering(n_clusters=best_k, metric='cosine', linkage='average').fit_predict(embeddings) + + chunks = [] + current_chunk = "" + current_chunk_tokens = 0 + current_label = labels[0] + + for sentence, label, token_count in zip(sentences, labels, sentence_token_counts): + if label != current_label or current_chunk_tokens + token_count > max_chunk_size: + if current_chunk.strip(): + chunks.append(current_chunk.strip()) + current_chunk = sentence + current_chunk_tokens = token_count + current_label = label + else: + current_chunk += sentence + current_chunk_tokens += token_count + + if current_chunk.strip(): + chunks.append(current_chunk.strip()) + + return chunks diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py new file mode 100644 index 00000000..9b71a4b0 --- /dev/null +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py @@ -0,0 +1,135 @@ +from __future__ import annotations + +from typing import List +from bs4 import BeautifulSoup +from yuxi.utils.logging_config import logger + +def html_table_to_markdown(html: str) -> str: + """ + 将HTML表格转换为Markdown格式的具体实现 + """ + soup = BeautifulSoup(html, 'html.parser') + table = soup.find('table') + if table is None: + return '' + + rows = table.find_all('tr') + if not rows: + return '' + + grid = [] + + for r_idx, row in enumerate(rows): + while len(grid) <= r_idx: + grid.append([]) + + cells = row.find_all(['td', 'th']) + c_idx = 0 + + for cell in cells: + while c_idx < len(grid[r_idx]) and grid[r_idx][c_idx] is not None: + c_idx += 1 + + text = cell.get_text(strip=True) + text = text.replace('\n', ' ') + + rowspan = int(cell.get('rowspan', 1)) + colspan = int(cell.get('colspan', 1)) + + for r in range(rowspan): + target_r = r_idx + r + while len(grid) <= target_r: + grid.append([]) + + for c in range(colspan): + target_c = c_idx + c + while len(grid[target_r]) <= target_c: + grid[target_r].append(None) + + grid[target_r][target_c] = text + + c_idx += colspan + + if not grid: + return '' + + markdown_lines = [] + max_cols = max(len(r) for r in grid) + for r in grid: + while len(r) < max_cols: + r.append("") + + header = grid[0] + header = [h if h is not None else "" for h in header] + markdown_lines.append('| ' + ' | '.join(header) + ' |') + markdown_lines.append('|' + '|'.join([' --- ' for _ in range(max_cols)]) + '|') + + for row in grid[1:]: + row_clean = [cell if cell is not None else "" for cell in row] + line = '| ' + ' | '.join(row_clean) + ' |' + markdown_lines.append(line) + + return '\n'.join(markdown_lines) + + +def html_table_to_key_value(html: str) -> List[str]: + """ + 将HTML表格转换为键值对格式的列表 + """ + soup = BeautifulSoup(html, 'html.parser') + table = soup.find('table') + if table is None: + return [] + + rows = table.find_all('tr') + if not rows: + return [] + + grid = [] + + for r_idx, row in enumerate(rows): + while len(grid) <= r_idx: + grid.append([]) + + cells = row.find_all(['td', 'th']) + c_idx = 0 + + for cell in cells: + while c_idx < len(grid[r_idx]) and grid[r_idx][c_idx] is not None: + c_idx += 1 + + text = cell.get_text(strip=True) + rowspan = int(cell.get('rowspan', 1)) + colspan = int(cell.get('colspan', 1)) + + for r in range(rowspan): + target_r = r_idx + r + while len(grid) <= target_r: + grid.append([]) + + for c in range(colspan): + target_c = c_idx + c + while len(grid[target_r]) <= target_c: + grid[target_r].append(None) + grid[target_r][target_c] = text + c_idx += colspan + + if not grid: + return [] + + headers = grid[0] + headers = [h if h is not None else "" for h in headers] + + kv_lines = [] + for row_values in grid[1:]: + min_len = min(len(headers), len(row_values)) + row_parts = [] + for i in range(min_len): + key = headers[i] + val = row_values[i] if row_values[i] is not None else "" + if key: + row_parts.append(f"{key}:{val}") + if row_parts: + kv_lines.append(";".join(row_parts) + ";") + + return kv_lines diff --git a/backend/test/unit/backends/test_semantic_chunking.py b/backend/test/unit/backends/test_semantic_chunking.py new file mode 100644 index 00000000..bdfe5b73 --- /dev/null +++ b/backend/test/unit/backends/test_semantic_chunking.py @@ -0,0 +1,78 @@ +import pytest +import numpy as np + +# 现在可以安全地导入了,因为顶层不再有重型依赖 +from yuxi.knowledge.chunking.ragflow_like.parsers import semantic + +@pytest.fixture +def mock_embed_fn(): + """模拟一个简单的嵌入函数""" + def encode(sentences): + if isinstance(sentences, str): + sentences = [sentences] + # 返回模拟的向量 (N, 384) + return np.random.rand(len(sentences), 384).astype(np.float32) + return encode + +@pytest.fixture +def sample_markdown(): + return """ +# 核心业务说明 + +本节主要介绍公司的核心业务流程。 + +## 第一部分:研发流程 +1. 需求分析:收集用户反馈。 +2. 系统设计:架构方案评审。 +3. 编码实现:遵循规范。 + +## 第二部分:交付标准 +| 阶段 | 交付物 | 责任人 | +| --- | --- | --- | +| 交付1 | 源码包 | 开发 | +| 交付2 | 测试报告 | 测试 | + +这里是一些额外的语义内容。 +语义内容应当被聚类在一起。 +这也是语义内容。 +""" + +def test_semantic_chunking_basic(mock_embed_fn, sample_markdown): + """测试基本的语义切分逻辑 (使用注入方式)""" + + # 配置切分参数 + parser_config = { + "chunk_token_num": 100, # 触发语义切分 + "overlapped_percent": 0 + } + + # 直接注入 mock_embed_fn,完全跳过 config 加载 + chunks = semantic.chunk_markdown( + sample_markdown, + parser_config=parser_config, + embed_fn=mock_embed_fn + ) + + # 验证结果 + assert isinstance(chunks, list) + assert len(chunks) > 0 + + # 验证标题路径增强是否生效 + has_enhanced_title = any("# 核心业务说明|第一部分:研发流程" in chunk for chunk in chunks) + assert has_enhanced_title, "标题路径增强失效" + + # 验证表格是否被正确保留 + has_table = any("交付1" in chunk and "源码包" in chunk for chunk in chunks) + assert has_table, "表格内容丢失" + + print(f"\n成功切分为 {len(chunks)} 个片段") + +def test_heading_inference(): + """测试标题层级推断工具类""" + from yuxi.knowledge.chunking.ragflow_like.utils.md_parser_utils import infer_heading_level + + assert infer_heading_level("1. 简介") == 1 + assert infer_heading_level("1.1 详细设计") == 2 + assert infer_heading_level("1.2.3 核心逻辑") == 3 + assert infer_heading_level("一、 背景") == 1 + assert infer_heading_level("普通文本") == 1 diff --git a/docker-compose.yml b/docker-compose.yml index 39242efd..5b562c23 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,7 @@ services: - ./.env:/app/.env - ${MODEL_DIR:-./models}:/models # 使用默认值处理未定义的环境变量 - /var/run/docker.sock:/var/run/docker.sock + - ~/nltk_data:/root/nltk_data # 持久化语义切分使用的NLTK数据包,避免重复下载 ports: - "5050:5050" networks: diff --git a/web/src/utils/chunk_presets.js b/web/src/utils/chunk_presets.js index ebd6285f..642dddd1 100644 --- a/web/src/utils/chunk_presets.js +++ b/web/src/utils/chunk_presets.js @@ -18,6 +18,11 @@ export const CHUNK_PRESET_OPTIONS = [ value: 'laws', label: 'Laws', description: '法规分块:按法条层级组织与合并,适合法律法规、制度规范类文本。' + }, + { + value: 'semantic', + label: 'Semantic', + description: '语义分块:利用嵌入和聚类算法进行语义切分,并自动增强标题上下文。' } ] From a2ed3d9a108101d2fc061f3c7840f2aa8f210931 Mon Sep 17 00:00:00 2001 From: ZHOU Shujian Date: Wed, 15 Apr 2026 23:23:08 +0800 Subject: [PATCH 2/6] test(semantic_chunking): replace mock embedding with real model Use a real embedding model from SiliconFlow for more realistic testing. Update test assertions to validate LaTeX formula preservation and semantic clustering of distinct chapters. --- .../unit/backends/test_semantic_chunking.py | 88 ++++++++++--------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/backend/test/unit/backends/test_semantic_chunking.py b/backend/test/unit/backends/test_semantic_chunking.py index bdfe5b73..2a5c6ae2 100644 --- a/backend/test/unit/backends/test_semantic_chunking.py +++ b/backend/test/unit/backends/test_semantic_chunking.py @@ -1,71 +1,77 @@ import pytest import numpy as np +import os # 现在可以安全地导入了,因为顶层不再有重型依赖 from yuxi.knowledge.chunking.ragflow_like.parsers import semantic +from yuxi.models import select_embedding_model @pytest.fixture -def mock_embed_fn(): - """模拟一个简单的嵌入函数""" - def encode(sentences): - if isinstance(sentences, str): - sentences = [sentences] - # 返回模拟的向量 (N, 384) - return np.random.rand(len(sentences), 384).astype(np.float32) - return encode +def embed_fn(): + """使用真实的嵌入函数 (从 SiliconFlow 获取)""" + model_id = "siliconflow/Qwen/Qwen3-Embedding-0.6B" + try: + model = select_embedding_model(model_id) + def encode(sentences): + if isinstance(sentences, str): + sentences = [sentences] + return model.encode(sentences) + return encode + except Exception as e: + pytest.skip(f"无法初始化真实嵌入模型 {model_id}: {e}") @pytest.fixture def sample_markdown(): - return """ -# 核心业务说明 - -本节主要介绍公司的核心业务流程。 - -## 第一部分:研发流程 -1. 需求分析:收集用户反馈。 -2. 系统设计:架构方案评审。 -3. 编码实现:遵循规范。 - -## 第二部分:交付标准 -| 阶段 | 交付物 | 责任人 | -| --- | --- | --- | -| 交付1 | 源码包 | 开发 | -| 交付2 | 测试报告 | 测试 | - -这里是一些额外的语义内容。 -语义内容应当被聚类在一起。 -这也是语义内容。 -""" - -def test_semantic_chunking_basic(mock_embed_fn, sample_markdown): - """测试基本的语义切分逻辑 (使用注入方式)""" + with open("test/resource/test4.md", "r", encoding="utf-8") as f: + return f.read() +def test_semantic_chunking_basic(embed_fn, sample_markdown): + """测试基本的语义切分逻辑 (使用真实嵌入模型)""" # 配置切分参数 parser_config = { - "chunk_token_num": 100, # 触发语义切分 + "chunk_token_num": 200, # 适中的 token 数以触发更多切分 "overlapped_percent": 0 } - # 直接注入 mock_embed_fn,完全跳过 config 加载 + # 直接注入 embed_fn chunks = semantic.chunk_markdown( sample_markdown, parser_config=parser_config, - embed_fn=mock_embed_fn + embed_fn=embed_fn ) # 验证结果 assert isinstance(chunks, list) assert len(chunks) > 0 - # 验证标题路径增强是否生效 - has_enhanced_title = any("# 核心业务说明|第一部分:研发流程" in chunk for chunk in chunks) - assert has_enhanced_title, "标题路径增强失效" + print(f"\n成功切分为 {len(chunks)} 个片段:") + for i, chunk in enumerate(chunks): + print(f"--- 片段 {i+1} ---") + print(chunk) - # 验证表格是否被正确保留 - has_table = any("交付1" in chunk and "源码包" in chunk for chunk in chunks) - assert has_table, "表格内容丢失" + # 验证标题路径增强是否生效 (检查是否包含 Part 或特殊元素标记) + has_enhanced_title = any("|Part" in chunk or "|Math Block" in chunk for chunk in chunks) + assert has_enhanced_title, "标题路径增强或分片标记失效" - print(f"\n成功切分为 {len(chunks)} 个片段") + # 验证是否包含特殊的 LaTeX 公式或符号 (匹配输出中的格式) + has_formula = any("f _ {0, 1}" in chunk or "\\sigma_ {y, d}" in chunk or "A _ {\\mathrm {s p}}" in chunk for chunk in chunks) + assert has_formula, "LaTeX 公式/符号在切分中丢失" + + # 验证语义聚类是否大概生效 (钢制塔架和混凝土塔架内容应该分开) + # 我们检查是否有钢制塔架相关的片段和混凝土塔架相关的片段 + # 使用 startswith 来更准确地定位章节开始,避免匹配到目次/前言中的文字 + has_steel = any(chunk.startswith("# 6 钢制塔架") for chunk in chunks) + has_concrete = any(chunk.startswith("# 7 混凝土塔架") for chunk in chunks) + + assert has_steel and has_concrete, "语义内容丢失 (钢制或混凝土塔架章节开始部分)" + + # 验证它们是否在不同的片段中 + steel_chunks = [i for i, c in enumerate(chunks) if c.startswith("# 6 钢制塔架")] + concrete_chunks = [i for i, c in enumerate(chunks) if c.startswith("# 7 混凝土塔架")] + + # 理论上语义聚类会将这些大章节分开 + if steel_chunks and concrete_chunks: + assert not set(steel_chunks).intersection(set(concrete_chunks)), "钢制塔架和混凝土塔架大章节内容被错误地聚类在同一个 chunk 中了" def test_heading_inference(): """测试标题层级推断工具类""" From edf66bbe381ca6f452dac072290f2273bf7cb008 Mon Sep 17 00:00:00 2001 From: zhou shujian Date: Thu, 16 Apr 2026 17:00:59 +0800 Subject: [PATCH 3/6] =?UTF-8?q?docs:=20=E4=B8=BA=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=88=86=E5=9D=97=E5=B7=A5=E5=85=B7=E5=87=BD=E6=95=B0=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=AF=A6=E7=BB=86=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 html_table_to_key_value 函数添加处理逻辑说明,解释如何应对过长表格的切分问题 - 为 infer_heading_level 函数添加层级推断规则说明,包括数字序号和中文序号的处理 - 为 extract_table_block 函数添加表格提取算法的详细步骤说明 - 为 split_text_by_length_and_newline 函数添加分块逻辑的详细注释,解释段落和行的处理流程 - 为语义分块相关函数添加中文文档,包括句子分割、聚类算法和整体流程说明 - 更新测试用例,使用更清晰的测试文档并添加结果保存功能 --- .../chunking/ragflow_like/parsers/semantic.py | 178 +- .../ragflow_like/utils/md_parser_utils.py | 108 +- .../ragflow_like/utils/semantic_utils.py | 103 +- .../ragflow_like/utils/table_utils.py | 96 +- backend/package/yuxi/services/oidc_service.py | 24 +- backend/test/resource/test4.md | 2211 +++++++++++++++++ .../backends/resource/semantic_chunks.json | 7 + .../unit/backends/test_semantic_chunking.py | 111 +- backend/uv.lock | 95 +- web/package.json | 1 + web/src/components/AgentChatComponent.vue | 43 +- web/src/components/AgentConfigSidebar.vue | 6 +- .../ToolCallingResult/BaseToolCall.vue | 4 +- .../components/ToolCallsGroupComponent.vue | 13 +- .../components/UserManagementComponent.vue | 5 +- web/src/views/AgentView.vue | 9 +- 16 files changed, 2720 insertions(+), 294 deletions(-) create mode 100644 backend/test/resource/test4.md create mode 100644 backend/test/unit/backends/resource/semantic_chunks.json diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py index 91a84ab9..acb25d6d 100644 --- a/backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/parsers/semantic.py @@ -2,100 +2,107 @@ from __future__ import annotations import re from typing import Any + from markdown_it import MarkdownIt from mdit_py_plugins.dollarmath import dollarmath_plugin -from yuxi.utils.logging_config import logger + from yuxi.knowledge.chunking.ragflow_like.nlp import count_tokens +from yuxi.utils.logging_config import logger from ..utils.md_parser_utils import ( - infer_heading_level, - get_title_path, - extract_table_block, - split_text_by_length_and_newline + extract_table_block, + get_title_path, + split_text_by_length_and_newline, ) from ..utils.table_utils import html_table_to_key_value + def _flush_content( - result: list, - current_content: list, - title_stack: list, - max_length: int, + result: list, + current_content: list, + title_stack: list, + max_length: int, embed_fn: Any, - special_element: str = None, - allow_split: bool = False - ) -> None: + special_element: str = None, + allow_split: bool = False, +) -> None: if not current_content: return - - content = '\n'.join(current_content).strip() + + content = "\n".join(current_content).strip() if not content: current_content.clear() return level = next((i + 1 for i in range(5, -1, -1) if title_stack[i]), 1) title_path = get_title_path(title_stack) - + if special_element and not allow_split: header = f"{'#' * level} {title_path}|{special_element}" if title_path else f"{'#' * level} {special_element}" - result.extend([header, content, '-' * 10]) + result.extend([header, content, "-" * 10]) else: if count_tokens(content) > max_length: - chunks = split_text_by_length_and_newline(content, max_length, embed_fn=embed_fn, token_count_fn=count_tokens) + chunks = split_text_by_length_and_newline( + content, max_length, embed_fn=embed_fn, token_count_fn=count_tokens + ) for idx, chunk in enumerate(chunks, 1): base_header = f"{'#' * level} {title_path}" if title_path else f"{'#' * level}" if special_element: header = f"{base_header}|{special_element}|Part {idx}" else: header = f"{base_header}|Part {idx}" - result.extend([header, chunk, '-' * 10]) + result.extend([header, chunk, "-" * 10]) else: base_header = f"{'#' * level} {title_path}" if title_path else f"{'#' * level}" if special_element: header = f"{base_header}|{special_element}" else: header = base_header - + if header: result.append(header) result.append("") - result.extend([content, '-' * 10]) - + result.extend([content, "-" * 10]) + current_content.clear() + def _handle_image_caption(tokens, i, result, current_content, title_stack, max_length, embed_fn): token = tokens[i] - if token.type != 'paragraph_open': + if token.type != "paragraph_open": return False, i - - inline_token = tokens[i + 1] - if inline_token.type != 'inline': - return False, i - - content = inline_token.content.strip() - image_pattern = r'^!\[.*?\]\(.*?\)\s*$' - caption_pattern = r'^(?:Figure|图|Fig\.|表|Table)\s*[\d\w\.]+' - img_match = re.search(r'^(!\[.*?\]\(.*?\))', content) + inline_token = tokens[i + 1] + if inline_token.type != "inline": + return False, i + + content = inline_token.content.strip() + image_pattern = r"^!\[.*?\]\(.*?\)\s*$" + caption_pattern = r"^(?:Figure|图|Fig\.|表|Table)\s*[\d\w\.]+" + + img_match = re.search(r"^(!\[.*?\]\(.*?\))", content) if img_match: - rest = content[img_match.end():].strip() + rest = content[img_match.end() :].strip() if rest and re.match(caption_pattern, rest, re.IGNORECASE): _flush_content(result, current_content, title_stack, max_length, embed_fn) current_content.append(content) - caption_title = rest.split('\n')[0].strip() + caption_title = rest.split("\n")[0].strip() _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=caption_title) return True, i + 3 if re.match(image_pattern, content): next_p_idx = i + 3 - if next_p_idx + 1 < len(tokens) and tokens[next_p_idx].type == 'paragraph_open': + if next_p_idx + 1 < len(tokens) and tokens[next_p_idx].type == "paragraph_open": next_inline = tokens[next_p_idx + 1] - if next_inline.type == 'inline': + if next_inline.type == "inline": next_content = next_inline.content.strip() if re.match(caption_pattern, next_content, re.IGNORECASE): _flush_content(result, current_content, title_stack, max_length, embed_fn) current_content.append(content) current_content.append(next_content) - _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=next_content) + _flush_content( + result, current_content, title_stack, max_length, embed_fn, special_element=next_content + ) return True, i + 6 if current_content and re.match(caption_pattern, content, re.IGNORECASE): @@ -110,14 +117,13 @@ def _handle_image_caption(tokens, i, result, current_content, title_stack, max_l return False, i + def chunk_markdown( - markdown_content: str, - parser_config: dict[str, Any] | None = None, - embed_fn: Any | None = None + markdown_content: str, parser_config: dict[str, Any] | None = None, embed_fn: Any | None = None ) -> list[str]: """ 语义化切分 Markdown 内容。 - + Args: markdown_content: 待切分的 Markdown 文本 parser_config: 切分参数,如 chunk_token_num @@ -127,7 +133,7 @@ def chunk_markdown( parser_config = parser_config or {} max_length = int(parser_config.get("chunk_token_num", 512)) logger.info(f"语义切分开始: max_length={max_length}, content_length={len(markdown_content)}") - + # 延迟加载重型资源,仅在没有注入 embed_fn 时触发 if embed_fn is None: try: @@ -142,61 +148,67 @@ def chunk_markdown( logger.error(f"加载 Embedding 模型失败: {e}。将退化为简单切分。") embed_fn = None - md = MarkdownIt('commonmark').enable('table') + md = MarkdownIt("commonmark").enable("table") md.use(dollarmath_plugin, allow_space=True, allow_digits=True) - + tokens: list = md.parse(markdown_content) - original_lines: list = markdown_content.split('\n') - + original_lines: list = markdown_content.split("\n") + result: list = [] current_content: list = [] - title_stack: list = [''] * 6 + title_stack: list = [""] * 6 i = 0 while i < len(tokens): token = tokens[i] - if token.type == 'heading_open': + if token.type == "heading_open": _flush_content(result, current_content, title_stack, max_length, embed_fn) level = int(token.tag[1:]) if token.tag and len(token.tag) > 1 else 1 inline_token = tokens[i + 1] - if inline_token.type == 'inline': + if inline_token.type == "inline": full_title = inline_token.content.strip() title_stack[level - 1] = full_title for j in range(level, 6): - title_stack[j] = '' + title_stack[j] = "" i += 3 continue - elif token.type == 'table_open': + elif token.type == "table_open": _flush_content(result, current_content, title_stack, max_length, embed_fn) j, table_content = extract_table_block(tokens, i, original_lines) current_content.append(table_content) - _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element='Table') + _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element="Table") i = j + 1 if j < len(tokens) else len(tokens) continue - elif token.type == 'paragraph_open': - handled, new_i = _handle_image_caption(tokens, i, result, current_content, title_stack, max_length, embed_fn) + elif token.type == "paragraph_open": + handled, new_i = _handle_image_caption( + tokens, i, result, current_content, title_stack, max_length, embed_fn + ) if handled: i = new_i continue inline_token = tokens[i + 1] - if inline_token.type == 'inline': + if inline_token.type == "inline": current_content.append(inline_token.content.strip()) i += 3 continue - elif token.type == 'fence': + elif token.type == "fence": current_content.append(f"```\n{token.content}\n```") i += 1 continue - elif token.type == 'ordered_list_open': + elif token.type == "ordered_list_open": _flush_content(result, current_content, title_stack, max_length, embed_fn) list_content = [] j = i + 1 list_item_counter = 1 - while j < len(tokens) and tokens[j].type != 'ordered_list_close': - if tokens[j].type == 'list_item_open': + while j < len(tokens) and tokens[j].type != "ordered_list_close": + if tokens[j].type == "list_item_open": k = j + 1 - while k < len(tokens) and tokens[k].type != 'list_item_close': - if tokens[k].type == 'paragraph_open' and k + 1 < len(tokens) and tokens[k + 1].type == 'inline': + while k < len(tokens) and tokens[k].type != "list_item_close": + if ( + tokens[k].type == "paragraph_open" + and k + 1 < len(tokens) + and tokens[k + 1].type == "inline" + ): list_content.append(f"{list_item_counter}. {tokens[k + 1].content.strip()}") list_item_counter += 1 k += 1 @@ -206,15 +218,19 @@ def chunk_markdown( _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=token.type) i = j + 1 continue - elif token.type == 'bullet_list_open': + elif token.type == "bullet_list_open": _flush_content(result, current_content, title_stack, max_length, embed_fn) list_content = [] j = i + 1 - while j < len(tokens) and tokens[j].type != 'bullet_list_close': - if tokens[j].type == 'list_item_open': + while j < len(tokens) and tokens[j].type != "bullet_list_close": + if tokens[j].type == "list_item_open": k = j + 1 - while k < len(tokens) and tokens[k].type != 'list_item_close': - if tokens[k].type == 'paragraph_open' and k + 1 < len(tokens) and tokens[k + 1].type == 'inline': + while k < len(tokens) and tokens[k].type != "list_item_close": + if ( + tokens[k].type == "paragraph_open" + and k + 1 < len(tokens) + and tokens[k + 1].type == "inline" + ): list_content.append(f"- {tokens[k + 1].content.strip()}") k += 1 j += 1 @@ -223,33 +239,41 @@ def chunk_markdown( _flush_content(result, current_content, title_stack, max_length, embed_fn, special_element=token.type) i = j + 1 continue - elif token.type == 'html_block': + elif token.type == "html_block": _flush_content(result, current_content, title_stack, max_length, embed_fn) content = token.content.strip() is_converted_table = False - if ' int: """ 根据标题文本推断其层级级别(1-6级)。 + + 逻辑说明: + 1. 数字序号推断: + - 匹配如 "1.", "1.1", "1.2.3" 等格式。 + - 根据点号分隔的数量确定层级,例如 "1.1" 为 2 级,"1.2.3" 为 3 级。 + - 层级限制在 1-6 之间。 + 2. 中文序号推断: + - 匹配如 "一、", "二." 等中文数字序号。 + - 统一归类为 1 级标题。 + 3. 默认处理: + - 若不匹配以上规则,默认返回 1 级。 """ - m = re.match(r'^\s*(\d+(?:\.\d+)*)[.)、]?\s*', title) + m = re.match(r"^\s*(\d+(?:\.\d+)*)[.)、]?\s*", title) if m: - return max(1, min(len(m.group(1).split('.')), 6)) - m_zh = re.match(r'^\s*[一二三四五六七八九十百千]+[、.]\s*', title) + return max(1, min(len(m.group(1).split(".")), 6)) + m_zh = re.match(r"^\s*[一二三四五六七八九十百千]+[、.]\s*", title) if m_zh: return 1 return 1 -def get_title_path(stack: List[str]) -> str: + +def get_title_path(stack: list[str]) -> str: """ 根据标题栈生成标题路径,用"|"分隔。 """ - return '|'.join([t for t in stack if t]) + return "|".join([t for t in stack if t]) -def extract_table_block(tokens: List[Any], i: int, original_lines: List[str]) -> tuple[int, str]: + +def extract_table_block(tokens: list[Any], i: int, original_lines: list[str]) -> tuple[int, str]: """ 从token流和原始文本中提取完整的表格块。 + + 逻辑说明: + 1. 定位起始:通过当前 token (i) 的 `map` 属性获取表格在原始行中的起始行号 `table_start`。 + 2. 查找结束 token:遍历后续 tokens 直到找到 `table_close`。 + 3. 确定结束行号 (`table_end`): + - 优先使用 `table_close` token 的 `map` 属性。 + - 若不存在,则尝试查找下一个带有 `map` 信息的 token 的起始行作为当前表格的结束。 + - 若上述均失败(如文件末尾或解析异常),则回退到基于文本内容的启发式扫描: + 从 `table_start` 开始向下扫描,直到遇到不符合 Markdown 表格特征(不以 '|' 开头且不含 '|')的行为止。 + 4. 返回结果:返回 `table_close` 的索引 `j` 以及拼接后的表格原始字符串。 """ token = tokens[i] table_start = token.map[0] if token.map else 0 j = i + 1 - while j < len(tokens) and tokens[j].type != 'table_close': + while j < len(tokens) and tokens[j].type != "table_close": j += 1 if j < len(tokens): end_token = tokens[j] @@ -45,78 +71,80 @@ def extract_table_block(tokens: List[Any], i: int, original_lines: List[str]) -> table_end = table_start + 1 for line_idx in range(table_start, len(original_lines)): line = original_lines[line_idx].strip() - if not line or not (line.startswith('|') or '|' in line): + if not line or not (line.startswith("|") or "|" in line): table_end = line_idx break else: table_end = table_start + 1 for line_idx in range(table_start, len(original_lines)): line = original_lines[line_idx].strip() - if not line or not (line.startswith('|') or '|' in line): + if not line or not (line.startswith("|") or "|" in line): table_end = line_idx break - return j, '\n'.join(original_lines[table_start:table_end]) + return j, "\n".join(original_lines[table_start:table_end]) + def split_text_by_length_and_newline( - text: str, - max_length: int, - embed_fn: Callable[[List[str]], Any], - token_count_fn: Callable[[str], int] -) -> List[str]: + text: str, max_length: int, embed_fn: Callable[[list[str]], Any], token_count_fn: Callable[[str], int] +) -> list[str]: """ 层次化文本切分策略。 """ chunks = [] - - paragraphs = text.split('\n\n') - + + paragraphs = text.split("\n\n") + for paragraph in paragraphs: paragraph = paragraph.strip() if not paragraph: continue - + paragraph_token_count = token_count_fn(paragraph) + # 如果当前段落长度未超过最大 Token 数量,直接作为独立分块放入chunks + # 否则继续尝试按行切分 if paragraph_token_count <= max_length: chunks.append(paragraph) continue - - lines = paragraph.split('\n') + + # 把段落进一步使用换行符进行切分为行 + lines = paragraph.split("\n") current_chunk_lines = [] current_chunk_tokens = 0 - + for line in lines: line = line.strip() - if not line: + if not line: # 跳过空行 continue - - line_token_count = token_count_fn(line) + + line_token_count = token_count_fn(line) # 计算当前行的 Token 数量 + # 为了考虑行之间的空格,需要在计算 Token 数量时加 1(如果当前行不是第一行,需要添加一个换行符的Token数量) added_tokens = line_token_count + (1 if current_chunk_lines else 0) - + # 如果当前行的 Token 数量超过最大 Token 数量,直接作为独立分块放入chunks if line_token_count > max_length: if current_chunk_lines: - chunks.append('\n'.join(current_chunk_lines)) + chunks.append("\n".join(current_chunk_lines)) current_chunk_lines = [] current_chunk_tokens = 0 - + sub_chunks = semantic_chunking_with_auto_clusters( - line, - embed_fn=embed_fn, - token_count_fn=token_count_fn, - max_chunk_size=max_length + line, embed_fn=embed_fn, token_count_fn=token_count_fn, max_chunk_size=max_length ) chunks.extend(sub_chunks) - + # 如果当前行的 Token 数量与当前分块的 Token 数量合并后超过最大 Token 数量,直接作为独立分块放入chunks elif current_chunk_tokens + added_tokens > max_length: - chunks.append('\n'.join(current_chunk_lines)) + # 把之前的分块内容放入chunks + chunks.append("\n".join(current_chunk_lines)) + # 重置当前分块为当前行的内容 current_chunk_lines = [line] + # 更新当前分块的 Token 数量 current_chunk_tokens = line_token_count - + # 如果当前行的内容加入当前分块后不会超过最大 Token 数量,直接加入当前分块 else: current_chunk_lines.append(line) - current_chunk_tokens += added_tokens - + current_chunk_tokens += added_tokens # 更新当前分块的 Token 数量 + # 最后的收尾,把最后一行内容放入chunks if current_chunk_lines: - chunks.append('\n'.join(current_chunk_lines)) - + chunks.append("\n".join(current_chunk_lines)) + return chunks diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py index 231d7320..fc49fc2d 100644 --- a/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/semantic_utils.py @@ -1,37 +1,62 @@ from __future__ import annotations import re +from collections.abc import Callable +from typing import Any + import nltk from nltk.tokenize import sent_tokenize from sklearn.cluster import AgglomerativeClustering from sklearn.metrics import silhouette_score -from typing import Callable, List, Any -# Ensure punkt_tab is available +# 模块加载的时候先检查是否已经下载了punkt_tab模型(用于识别句子的边界) try: - nltk.data.find('tokenizers/punkt_tab') + nltk.data.find("tokenizers/punkt_tab") except LookupError: - nltk.download('punkt_tab') + nltk.download("punkt_tab") -def split_sentences_chinese(text: str) -> List[str]: + +def split_sentences_chinese(text: str) -> list[str]: """ - Split sentences by Chinese punctuation while keeping the punctuation. + 使用正则表达式将中文文本分割成句子。 + + 逻辑: + - 匹配中文句号、感叹号、问号(。!?)作为分隔点。 + - 使用正向/反向预查处理引号:确保如果标点后面紧跟引号(”’"),该引号会被保留在当前句子末尾,而不是被切分到下一句。 + - 返回去除两端空格且非空的句子列表。 """ - pattern = r'(?<=[。!?])(?![”’"])|(?<=[。!?][”’"])' + pattern = r'(?<=[。!?])(?![”’"])|(?<=[。!?])(?![”’"])|(?<=[。!?][”’"])' sentences = re.split(pattern, text) return [s.strip() for s in sentences if s.strip()] -def split_mixed_sentences(text: str) -> List[str]: + +def split_mixed_sentences(text: str) -> list[str]: """ - Handle both Chinese and English sentence splitting. + 处理中英文混合文本的分句逻辑,支持按物理段落分发不同的分句策略。 + + 该函数采用“分而治之”的策略来处理复杂的混合文本: + 1. **物理分块**:首先按换行符 (`\\n+`) 将原始文本切分为多个物理段落(chunks),确保物理结构不被破坏。 + 2. **语言检测与分发**: + - **英文/混合路径**:若段落中包含英文字母 (`[A-Za-z]`),则视为英文或混合文本, + 调用 NLTK 的 `sent_tokenize` 进行处理。NLTK 能更好地处理英文缩写、句点等复杂情况。 + - **中文路径**:若段落不含字母,则视为纯中文文本,调用 `split_sentences_chinese`。 + 该方法通过正则精准匹配中文标点及后续引号。 + - **兜底方案**:若上述方法未产生结果,则使用简单的正则表达式按中文标点强制分割。 + 3. **清洗与过滤**:汇总所有子句,去除两端空白字符,并过滤掉空字符串。 + + Args: + text: 待分句的原始字符串。 + + Returns: + List[str]: 分割后的句子列表。 """ - chunks = re.split(r'(\n+)', text) + chunks = re.split(r"(\n+)", text) sentences = [] for ch in chunks: if not ch.strip(): continue - if re.search(r'[A-Za-z]', ch): + if re.search(r"[A-Za-z]", ch): parts = sent_tokenize(ch) sentences.extend([p.strip() for p in parts if p.strip()]) else: @@ -39,13 +64,29 @@ def split_mixed_sentences(text: str) -> List[str]: if sents: sentences.extend([s.strip() for s in sents if s.strip()]) else: - parts = re.split(r'(?<=[。!?])', ch) + parts = re.split(r"(?<=[。!?])", ch) sentences.extend([p.strip() for p in parts if p.strip()]) return sentences + def find_best_num_clusters(embeddings: Any, min_clusters: int = 2, max_clusters: int = 10) -> int: """ - Select best number of clusters using silhouette score. + 使用轮廓系数选择最佳聚类数量。让每个分段语义集中,且分段之间界限分明 + + 逻辑: + - 遍历可能的聚类数量(从 min_clusters 到 max_clusters)。 + - 对每个聚类数量,使用 `AgglomerativeClustering` 进行聚类。 + - 计算轮廓系数(Silhouette Score)。 + - 选择轮廓系数最高的聚类数量作为最佳聚类数量。 + - 如果聚类数量为 1 或更少,直接返回 1。 + + Args: + embeddings: 待聚类的向量数据(所有句子的嵌入向量列表)。 + min_clusters: 搜索的最佳聚类数量下限,默认为 2。 + max_clusters: 搜索的最佳聚类数量上限,默认为 10。 + + Returns: + int: 轮廓系数表现最好的聚类数量。 """ if len(embeddings) <= min_clusters: return len(embeddings) @@ -55,42 +96,48 @@ def find_best_num_clusters(embeddings: Any, min_clusters: int = 2, max_clusters: limit_k = min(max_clusters, len(embeddings)) for k in range(min_clusters, limit_k + 1): - labels = AgglomerativeClustering(n_clusters=k, metric='cosine', linkage='average').fit_predict(embeddings) + labels = AgglomerativeClustering(n_clusters=k, metric="cosine", linkage="average").fit_predict(embeddings) if len(set(labels)) <= 1: continue - score = silhouette_score(embeddings, labels, metric='cosine') + score = silhouette_score(embeddings, labels, metric="cosine") if score > best_score: best_score = score best_k = k return best_k + def semantic_chunking_with_auto_clusters( - text: str, - embed_fn: Callable[[List[str]], Any], - token_count_fn: Callable[[str], int], - max_chunk_size: int = 512 -) -> List[str]: + text: str, embed_fn: Callable[[list[str]], Any], token_count_fn: Callable[[str], int], max_chunk_size: int = 512 +) -> list[str]: """ - Semantic chunking with automatic cluster number selection. + 对传入的文本进行语义切分,过程中会自动选择最佳的聚集数量。 + + 逻辑: + - 先将文本中的句子按语言进行分发,英文/混合文本使用NLTK的sent_tokenize,中文文本使用split_sentences_chinese。 + - 对每个句子进行嵌入向量化。 + - 确定最佳的聚类数量(根据轮廓系数)。 + - 对句子进行聚类,将每个聚类中的句子连接起来,形成一个分块。 + """ sentences = split_mixed_sentences(text) if len(sentences) < 2: return [text.strip()] - # Vectorization + # 向量化每个句子, 得到他们的嵌入向量 embeddings = embed_fn(sentences) - - # Pre-calculate token counts + + # 计算每个句子的token数量 sentence_token_counts = [token_count_fn(s) for s in sentences] total_tokens = sum(sentence_token_counts) - # Determine number of clusters + # 决定合适的聚集数量,需要保证每个分块的token数量都不超过max_chunk_size best_k = max(total_tokens // max_chunk_size, 1) + 1 best_k = min(best_k, len(sentences)) - - # Clustering - labels = AgglomerativeClustering(n_clusters=best_k, metric='cosine', linkage='average').fit_predict(embeddings) + + # 根据指定的聚集数量、相似度判断方式、联动方式,对句子进行聚类 + # 这里返回的labels是一个每个句子的聚类标签列表,例如[0,0,1,2,2],相同ID的句子被聚类到同一个分块中 + labels = AgglomerativeClustering(n_clusters=best_k, metric="cosine", linkage="average").fit_predict(embeddings) chunks = [] current_chunk = "" diff --git a/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py index 9b71a4b0..72b6fd9c 100644 --- a/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py +++ b/backend/package/yuxi/knowledge/chunking/ragflow_like/utils/table_utils.py @@ -1,57 +1,56 @@ from __future__ import annotations -from typing import List from bs4 import BeautifulSoup -from yuxi.utils.logging_config import logger + def html_table_to_markdown(html: str) -> str: """ 将HTML表格转换为Markdown格式的具体实现 """ - soup = BeautifulSoup(html, 'html.parser') - table = soup.find('table') + soup = BeautifulSoup(html, "html.parser") + table = soup.find("table") if table is None: - return '' + return "" - rows = table.find_all('tr') + rows = table.find_all("tr") if not rows: - return '' + return "" grid = [] - + for r_idx, row in enumerate(rows): while len(grid) <= r_idx: grid.append([]) - - cells = row.find_all(['td', 'th']) + + cells = row.find_all(["td", "th"]) c_idx = 0 - + for cell in cells: while c_idx < len(grid[r_idx]) and grid[r_idx][c_idx] is not None: c_idx += 1 - + text = cell.get_text(strip=True) - text = text.replace('\n', ' ') - - rowspan = int(cell.get('rowspan', 1)) - colspan = int(cell.get('colspan', 1)) - + text = text.replace("\n", " ") + + rowspan = int(cell.get("rowspan", 1)) + colspan = int(cell.get("colspan", 1)) + for r in range(rowspan): target_r = r_idx + r while len(grid) <= target_r: grid.append([]) - + for c in range(colspan): target_c = c_idx + c while len(grid[target_r]) <= target_c: grid[target_r].append(None) - + grid[target_r][target_c] = text - + c_idx += colspan if not grid: - return '' + return "" markdown_lines = [] max_cols = max(len(r) for r in grid) @@ -61,52 +60,65 @@ def html_table_to_markdown(html: str) -> str: header = grid[0] header = [h if h is not None else "" for h in header] - markdown_lines.append('| ' + ' | '.join(header) + ' |') - markdown_lines.append('|' + '|'.join([' --- ' for _ in range(max_cols)]) + '|') + markdown_lines.append("| " + " | ".join(header) + " |") + markdown_lines.append("|" + "|".join([" --- " for _ in range(max_cols)]) + "|") for row in grid[1:]: row_clean = [cell if cell is not None else "" for cell in row] - line = '| ' + ' | '.join(row_clean) + ' |' + line = "| " + " | ".join(row_clean) + " |" markdown_lines.append(line) - return '\n'.join(markdown_lines) + return "\n".join(markdown_lines) -def html_table_to_key_value(html: str) -> List[str]: +def html_table_to_key_value(html: str) -> list[str]: """ - 将HTML表格转换为键值对格式的列表 + 将HTML表格转换为键值对格式的列表,为了应对过长的表格的切分问题。 + + 处理逻辑: + 1. **网格重建**:由于 HTML 表格可能包含 `rowspan` 和 `colspan`(合并单元格), + 函数首先构建一个完整的二维网格(grid)。 + 2. **单元格展开**:遍历 HTML 行和列,遇到合并单元格时,将其内容填充到网格中受影响的所有坐标点。 + 这确保了原本被合并的区域在逻辑网格中每个点都有对应的值。 + 3. **键值对转换**: + - 将网格的第一行视为表头(Key)。 + - 从第二行开始,将每一行与表头对应,生成 "键:值" 形式的字符串。 + + 例如: + - 输入:HTML表格,包含姓名、年龄、性别三列。 + - 输出:['姓名:张三;年龄:25;性别:男', '姓名:李四;年龄:30;性别:女'] """ - soup = BeautifulSoup(html, 'html.parser') - table = soup.find('table') + soup = BeautifulSoup(html, "html.parser") + table = soup.find("table") if table is None: return [] - rows = table.find_all('tr') + rows = table.find_all("tr") if not rows: return [] grid = [] - + for r_idx, row in enumerate(rows): while len(grid) <= r_idx: grid.append([]) - - cells = row.find_all(['td', 'th']) + + cells = row.find_all(["td", "th"]) c_idx = 0 - + for cell in cells: while c_idx < len(grid[r_idx]) and grid[r_idx][c_idx] is not None: c_idx += 1 - + text = cell.get_text(strip=True) - rowspan = int(cell.get('rowspan', 1)) - colspan = int(cell.get('colspan', 1)) - + rowspan = int(cell.get("rowspan", 1)) + colspan = int(cell.get("colspan", 1)) + for r in range(rowspan): target_r = r_idx + r while len(grid) <= target_r: grid.append([]) - + for c in range(colspan): target_c = c_idx + c while len(grid[target_r]) <= target_c: @@ -116,10 +128,10 @@ def html_table_to_key_value(html: str) -> List[str]: if not grid: return [] - + headers = grid[0] headers = [h if h is not None else "" for h in headers] - + kv_lines = [] for row_values in grid[1:]: min_len = min(len(headers), len(row_values)) @@ -131,5 +143,5 @@ def html_table_to_key_value(html: str) -> List[str]: row_parts.append(f"{key}:{val}") if row_parts: kv_lines.append(";".join(row_parts) + ";") - + return kv_lines diff --git a/backend/package/yuxi/services/oidc_service.py b/backend/package/yuxi/services/oidc_service.py index 9c154f62..a57ff60f 100644 --- a/backend/package/yuxi/services/oidc_service.py +++ b/backend/package/yuxi/services/oidc_service.py @@ -475,10 +475,7 @@ async def find_user_by_oidc_sub(db, sub: str) -> User | None: # 方法1: 检查是否有用户的 user_id 直接等于 "oidc:{sub}"(标准 OIDC 用户) standard_oidc_user_id = f"oidc:{sub}" # 占位绑定记录会被标记为 is_deleted=1,但我们仍需要查询它们来获取绑定关系 - result = await db.execute(select(User).filter( - User.user_id == standard_oidc_user_id, - User.is_deleted == 0 - )) + result = await db.execute(select(User).filter(User.user_id == standard_oidc_user_id, User.is_deleted == 0)) user = result.scalar_one_or_none() if user: return user @@ -486,10 +483,9 @@ async def find_user_by_oidc_sub(db, sub: str) -> User | None: # 方法2: 检查是否有绑定占位用户格式: "oidc:{sub}:{target_user_id}"(use_raw_username 绑定记录) # 绑定占位用户被标记为 is_deleted=1,需要包括deleted来查询 legacy_result = await db.execute( - select(User).filter( - User.user_id.like(f"{standard_oidc_user_id}:%"), - User.is_deleted.in_([0, 1]) - ).order_by(User.id.asc()) + select(User) + .filter(User.user_id.like(f"{standard_oidc_user_id}:%"), User.is_deleted.in_([0, 1])) + .order_by(User.id.asc()) ) legacy_users = list(legacy_result.scalars().all()) if legacy_users: @@ -528,10 +524,7 @@ async def find_deleted_oidc_user_by_sub(db, sub: str) -> User | None: # 检查绑定占位格式 oidc:{sub}:{target_user_id}(占位本身是deleted,需要查询目标用户) legacy_result = await db.execute( - select(User).filter( - User.user_id.like(f"{oidc_user_id}:%"), - User.is_deleted == 1 - ).order_by(User.id.asc()) + select(User).filter(User.user_id.like(f"{oidc_user_id}:%"), User.is_deleted == 1).order_by(User.id.asc()) ) legacy_users = list(legacy_result.scalars().all()) if legacy_users: @@ -585,6 +578,7 @@ async def _create_oidc_binding_placeholder(db, sub: str, target_user: User) -> N # username 使用 oidc-binding-{sub_hash} 避免冲突,sub_hash 基于完整 sub 生成 import hashlib + sub_hash = hashlib.sha256(sub.encode()).hexdigest()[:8] username = f"oidc-binding-{sub_hash}" @@ -604,8 +598,7 @@ async def _create_oidc_binding_placeholder(db, sub: str, target_user: User) -> N db.add(placeholder_user) await db.commit() logger.info( - f"Created OIDC binding placeholder (deleted) for sub {sub} -> " - f"user {target_user.id} ({target_user.user_id})" + f"Created OIDC binding placeholder (deleted) for sub {sub} -> user {target_user.id} ({target_user.user_id})" ) except IntegrityError: # 并发创建冲突,回滚后忽略 @@ -661,8 +654,7 @@ async def create_oidc_user(db, user_info: dict, department_id: int | None = None if user_by_sub and user_by_sub.id == existing_user.id: # sub 已经正确绑定到该用户,允许返回 logger.info( - f"User with raw username {user_id} already exists and " - f"bound to sub {sub}, returning existing user" + f"User with raw username {user_id} already exists and bound to sub {sub}, returning existing user" ) return existing_user elif user_by_sub is None: diff --git a/backend/test/resource/test4.md b/backend/test/resource/test4.md new file mode 100644 index 00000000..4edec64c --- /dev/null +++ b/backend/test/resource/test4.md @@ -0,0 +1,2211 @@ +# 中 华 人 民 共 和 国 国 家 标 准 + +GB/T19072—2022 +代替 GB/T19072—2010 + +# 风力发电机组 塔架 + +Windturbines—Tower + +# 国国 家家 市标 场 准 监 督 管 理 总管 理 委 员 局 会化 + +# 发 布 + +# 目 次 + +# + +前言 …… +1 范围 +规范性引用文件 +3 术语和定义 +4 符号和缩略语 55 设计原则 … 66 钢制塔架 +7 混凝土塔架… 32附录 (资料性) 塔架结构阻尼比… 62附录 (资料性) 塔架门框开口的屈曲简化分析方法 65附录 (资料性) 钢制塔架疲劳强度分析 …… 67参考文献 ……… 68 + +# 前 言 + +本文件按照 / — 《标准化工作导则 第 部分:标准化文件的结构和起草规则》的规定起草。本文件代替 / — 《风力发电机组 塔架》,与 / — 相比,除结构调整和编辑性改动外,主要技术变化如下: + +) 将标准的适用范围改为“本文件适用于水平轴风力发电机组塔架的设计、制造、施工和验收”(见第 章, 年版的第 章); +) 增加和替换了部分规范性引用文件(见第 章, 年版的第 章); +) 增加了术语和定义(见第 章); +) 增加了符号和缩略语(见第 章); +) 更改了设计原则的内容(见第 章, 年版的第 章); +) 更改了设计条件的内容,并调整了顺序(见 , 年版的第 章); +g) 更改了材料要求的内容,并调整了顺序(见 , 年版的第 章); +) 增加了设计要求的内容(见 、 ),更改了门框开口强度分析(见 , 年版的第 章); +) 更改了塔架的外观、尺寸偏差要求(见 , 年版的 ); +j) 更改了塔架的焊接要求(见 , 年版的 ); +) 增加了焊缝无损检验方法衍射时差法超声波检测( )和相控阵超声检测( )无损检验方法(见 ); +) 增加了塔架表面及结构预处理和喷砂工序(见 和 ); +) 增加了塔架的检验内容(见 ); +) 更改了塔架陆上运输要求,增加了运输的临时加固措施,海上运输,零部件运输要求(见, 年版的第 章); +) 增加了混凝土塔架设计、制造和检验的内容(见第 章)。 +请注意本文件的某些内容可能涉及专利。本文件的发布机构不承担识别专利的责任。 +本文件由中国机械工业联合会提出。 +本文件由全国风力发电标准化技术委员会( / )归口。 + +本文件起草单位:上海泰胜风能装备股份有限公司、浙江运达风电股份有限公司、新疆金风科技股份有限公司、北京鉴衡认证中心有限公司、上海电气风电集团股份有限公司、 (北京)工程技术有限公司、明阳智慧能源集团股份有限公司、重庆大学、国电联合动力技术有限公司、远景能源有限公司 中国长江三峡集团有限公司 中国船舶重工集团海装风电股份有限公司本文件主要起草人:郭文辉、柳业、陈棋、张紫平、郝华庚、李鹏、曹广启、张强、奥云、陈志刚、宋恭杰、王宇航、朱孝晗、淮凯文、周禄飞、谭继可、陆辉、孔亮、吴红艳、韩雷岩、罗伟、孔维博。本文件及其所代替文件的历次版本发布情况为:年首次发布为 / — , 年第一次修订;本次为第二次修订。 + +# 风力发电机组 塔架 + +# 1 范围 + +本文件规定了风力发电机组塔架的设计原则、设计要求、工艺要求、试验方法、检验规则、竣工资料及随机文件、标志、包装、储存及运输等要求。 + +本文件适用于水平轴风力发电机组塔架的设计、制造、施工和验收。 + +# 2 规范性引用文件 + +下列文件中的内容通过文中的规范性引用而构成本文件必不可少的条款。其中,注日期的引用文件,仅该日期对应的版本适用于本文件;不注日期的引用文件,其最新版本(包括所有的修改单)适用于本文件。通用硅酸盐水泥 + +/ 锌锭 +/ 碳素结构钢 +/ 热轧钢板和钢带的尺寸、外形、重量及允许偏差 +/ 气焊、焊条电弧焊、气体保护焊和高能束焊的推荐坡口 +/ 埋弧焊的推荐坡口 +GB/T1228 钢结构用高强度大六角头螺栓 +GB/T1231 钢结构用高强度大六角头螺栓、大六角螺母、垫圈技术条件 +/ 低合金高强度结构钢 +/ 紧固件机械性能 螺栓、螺钉和螺柱 +/ 紧固件机械性能 螺母 +/ 碳素结构钢和低合金结构钢热轧钢板和钢带 +/ 非合金钢及细晶粒钢焊条 +/ 热强钢焊条 +GB/T5210 色漆和清漆 拉开法附着力试验 +/ 预应力混凝土用钢绞线 +/ 埋弧焊用非合金钢及细晶粒钢实心焊丝、药芯焊丝和焊丝 焊剂组合分类要求 +/ — 厚度方向性能钢板 +/ 六角头螺栓 +混凝土外加剂 +/ 熔化极气体保护电弧焊用非合金钢及细晶粒钢实心焊丝 +/ 色漆和清漆 划格试验 +/ 无损检测 人员资格鉴定与认证 +/ 色漆和清漆 不含金属颜料的色漆漆膜的 、 和 镜面光泽的测定 +/ 非合金钢及细晶粒钢药芯焊丝 +/ 建筑施工机械与设备 混凝土搅拌站(楼) +/ (所有部分) 金属材料熔焊质量要求 +GB/T13306 标牌 +/ 预应力筋用锚具、夹具和连接器 +/ 预拌混凝土 +/ — 热轧钢板表面质量的一般要求 +/ (所有部分) 机械安全 接近机械的固定设施 +/ — 钢及钢产品 检验文件的类型 +/ 风力发电机组 设计要求 +/ 焊接 预热温度、道间温度及预热维持温度的测量指南 +/ 焊接结构的一般尺寸公差和形位公差 +/ 混凝土搅拌运输车 +风力发电设施防护涂装技术规范 +/ (所有部分) 预载荷高强度栓接结构连接副 +建筑结构荷载规范 +GB50010 混凝土结构设计规范 +建筑抗震设计规范 +/ 工业建筑防腐蚀设计标准 +混凝土外加剂应用技术规范 +高耸结构设计标准 +混凝土质量控制标准 +混凝土结构工程施工质量验收规范 +建筑防腐蚀工程施工规范 +/ 水泥基灌浆材料应用技术规范 +混凝土结构工程施工规范 +/ — 热切割 质量和几何技术规范 +/ 风力发电塔架 法兰锻件 +/ 无粘结预应力钢绞线 +/ 无粘结预应力筋用防腐润滑脂 +/ 混凝土泵送施工技术规程 +钢筋焊接及验收规程 +JGJ55 普通混凝土配合比设计规程 +JGJ63 混凝土用水标准 +JGJ85 预应力筋用锚具、夹具和连接器应用技术规程 +/ 预应力混凝土结构抗震设计标准 +JGJ/T281 高强混凝土应用技术规程 +JGJ369 预应力混凝土结构设计规范 +NB/T31082 风电机组塔架用高强度螺栓连接副 +/ 承压设备无损检测 第 部分:射线检测 +/ — 承压设备无损检测 第 部分:超声检测 +/ 承压设备无损检测 第 部分:磁粉检测 +/ 承压设备无损检测 第 部分:渗透检测 +/ 承压设备无损检测 第 部分:衍射时差法超声检测 +/ 承压设备焊接工艺评定 +/ 压力容器焊接规程 +/ 承压设备产品焊接试件的力学性能检验 + +ISO 2808 色漆和清漆漆膜厚度的测定(Paints and varnishes—Determination of film thick-ness)色 漆 和 清 漆 在 、 和 非 金 属 色 漆 漆 膜 镜 面 光 泽 的 测 定 (varnishes—Determination of gloss value at 2O°,6O°and 85)金属材料 熔 化 焊 的 质 量 要 求 (materials)(所有部分) 色漆和清漆 涂层老化的评定 缺陷的变化程度、数量和大小的规定(Paints and varnishes—Evaluation of degradation of coatings-Designation of quantity and size ofdefects,and of intensity of uniform changes in appearance)无损检测 人员资格鉴定与认证( —tion of NDT personnel)色漆和清漆 防护漆体系对钢结构的防腐蚀保护( —protection of steel structures by protective paint systems)焊缝的无损检验 超声波检验 自动相控阵技术的使用(welds—Ultrasonictesting—Useofautomatedphasedarraytechnology)焊接 金属材料的电弧螺柱焊( — )金属材料焊接工艺规程及评定 焊接工艺评定试验 第 部分 钢的电弧焊和气焊、镍及镍合金的电弧焊(Specification and qualification of welding procedures for metalic materials-Welding procedure test—Part 1: Arc and gas welding of steels and arc welding of nickel and nickelalloys)ISO/TR17671-2 焊接 金 属 材 料 焊 接 的 推 荐 第 2部 分:铁 素 体 钢 的 电 弧 焊 接 (Weldin —Recommendations for welding of metallic materials-Part 2:Arc welding of ferritic steels)色漆和清漆 防护涂料体系对钢结构的防腐蚀防护 粗糙面上干膜厚度的测量和验收准则(Paintsandvarnishes—Corrosionprotectionofsteelstructuresbyprotectivepaintsystems—Measurement of,and acceptance criteria for,the thickness of dry films on rough surfaces): 对结构的作用 第 部分:一般作用 风作用( —1-4:General actions—Wind actions)1-6:Strengthandstabilityofshellstructures)钢结构设计 第 部分:疲劳度( — : )焊 接 接 头 和 零 部 件 的 疲 劳 设 计 建 议 (WeldedJointsandCom onents) + +# 3 术语和定义 + +下列术语和定义适用于本文件。 + +塔架 tower风力发电机组支撑结构的一部分,连接下部结构和主机部分。 + +3.2 + +塔段 towersection两端以法兰接触面为界的钢制塔架段。 + +33纵焊缝 longitudinalweld在卷制后的钢板对接接口通过焊接形成的连续焊缝。 + +3.4 + +筒节 tube由具备特定形状的钢板经过卷制及纵焊缝焊接形成的锥筒形或者直筒形的单个部件。 + +3.5环焊缝 circumferentialweld在组对后的筒节与筒节、筒节与法兰颈部的对接接口通过焊接形成的连续焊缝。 + +3.6 + +混凝土塔架 concretetower由预应力钢筋混凝土塔为主要受力体系的用于支撑风力发电机组的结构。 + +3.7 + +# 混凝土构件 concreteelement + +根据标准规范和设计图纸,在工厂或现场预制、现浇成型,用于风力发电机组塔架的混凝土塔架构件。 + +3.8 + +混凝土筒节 concretetube在工厂或现场预先生产制作完成或在拼装平台将混凝土构件组拼而成的一节筒形构件。 + +拼接缝 splicejoint装配式混凝土塔架上下筒节间的水平接缝和左右构件间的竖向接缝。 + +3.10 + +水平接缝 horizontaljoint装配式混凝土塔架上下筒节间的拼接缝。 + +311竖向接缝 verticaljoint装配式混凝土塔架左右相邻构件间的拼接缝。 + +3.12 + +灌浆料 groutingmaterial用以连接拼接缝两侧构件的黏结材料。 + +3.13 + +座浆料 seatingmaterial涂抹于水平接缝上连接上下筒节的黏结材料。 + +3.14预埋件 insertpart预先埋置在钢筋混凝土内的构件。 + +3.15预应力钢筋 prestressingtendon用于混凝土结构构件中,施加预应力的钢筋、钢丝和钢绞线的总称。 + +# 4 符号和缩略语 + +# 4.1 符号 + +下列符号适用于本文件。 + +Asp -螺纹的应力截面积 [mm²] +b -焊颈厚度(一般指焊接的筒壁等厚度) [mm]-法兰宽度 [mm]焊缝预留坡口高度 [mm]-门框加劲肋宽度 [mm] +E -塔架材料的弹性模量 [MPa]正常运行范围内风轮最大旋转频率 [Hz]塔架(在整机状态下)第一阶固有频率 [Hz]m 个风轮叶片通过频率 [Hz]塔架(在整机状态下)第n 阶固有频率 [Hz]螺栓设计预紧力 [N] +l 焊缝预留坡口到过渡圆半径之间的距离 [mm]过渡圆半径 [mm] +t 塔筒壁厚 [mm]-法兰厚度 [mm]-门框加劲肋厚度 [mm]包含焊颈的法兰厚度 [mm] +Z -塔架筒壁轴向力 [N]总应变 [ue]-材料线弹性分析的结构应力 [N/m²]材料的设计屈服强度 [MPa]螺栓材料的设计屈服强度 [MPa]混凝土抗压强度设计值 [MPa]混凝土抗拉强度设计值 [MPa]混凝土抗压强度标准值 [MPa]混凝土抗拉强度标准值 [MPa]普通钢筋抗拉、抗压强度设计值 [MPa]预应力筋抗拉设计值 [N/m]预应力筋抗拉强度标准值 [MPa] +Rd 构件承载力设计值 [N] +C -混凝土塔架达到正常使用要求所规定的变形、应力、裂缝宽度和自振频率等的限值[N] + +# 4.2 缩略语 + +下列缩略语适用于本文件。 + +材料和几何非线性 +考虑缺陷的材料和几何非线性 +材料非线性 +磁粉检测 +相控阵超声检测 +渗透检测 +作业指导书 +射线检测 +曲线 应力 寿命曲线 +衍射时差法超声波检测 +超声波检测 +目视检测 +焊接工艺评定报告 +焊接工艺规程 + +# 5 设计原则 + +5.1 塔架的设计使用寿命不应低于风力发电机组的设计使用寿命。52 塔架在规定的设计使用寿命内应满足下列功能要求: + +在正常施工和使用时,能承受可能出现的各种载荷和作用; +在正常使用时,具有良好的工作性能; +在正常维护下,具有足够的耐久性能; +当发生偶然事件时,结构能保持整体稳定性。 + +53 塔架载荷应按照 / 的相关规定进行计算分析。54 塔架载荷除应按照 进行计算分析外,还应考虑塔架和基础倾斜引起的附加载荷,其中由安装、制造和温度等因素引起的塔架倾斜宜按 / 计算,由基础变形和不均匀沉降等因素引起的塔架倾斜宜按 / 计算。55 塔架的模态阻尼比应通过测试或分析获得,设计时可参考附录 选取。 + +56 塔架设计应选取所有设计工况中最不利的载荷工况组合进行计算分析。 +57 塔架设计应考虑塔架吊装、停机或不对风时可能产生的涡激振动对塔架的影响,可根据: 附录 的方法或其他具有相同安全水平的方法来进行计算分析,计算分析时应考虑塔架的一阶涡激振动 必要时应考虑二阶及更高阶的涡激振动 计算涡激振动时 不应考虑气动阻尼 +58 当涡激作用造成的疲劳累积损伤不大于 时,可忽略其影响;当涡激作用造成的疲劳累积损伤大于 时,应与疲劳载荷造成的累积损伤叠加。可在塔架中添加破涡结构破坏涡激形成或添加阻尼器减少涡激振动的影响。 +5.9 塔架设计应考虑耐久性影响,耐久性设计应包括抗疲劳强度、抗震(有必要时)、抗蚀、运维要求等。510 钢制塔架设计应进行极限强度分析、疲劳强度分析和屈曲稳定性;混凝土塔架设计应进行承载能力极限状态分析和正常使用极限状态分析,其中承载能力极限状态分析应包含疲劳分析。 +511 塔架设计应进行整机状态下的频率分析,若其固有频率与风轮旋转频率及叶片通过频率存在共振,应采取软件或硬件的措施,减小塔架振动和振幅。 + +512 塔架设计还应考虑下列因素: + +轮毂中心高度风轮 机舱组件质量、重心位置、转动惯量和风轮转速范围; +偏航结构对顶法兰的要求; +基础刚度、标高及接口要求; +工作温度; +运输条件; +生产能力; +施工条件; +电气及其他设备接口。 + +513 塔架设计应考虑防雷接地要求。514 塔架附件的设计应满足操作人员和设备能安全地进行安装 维护和通行的要求 包括必要的防坠落装置等安全保护设施导电轨或电缆固定设施;爬梯、护栏、平台;照明及电气设备安装附件;门的密封持久耐用;必要时应设有助爬器、提升机、免爬助力装置、升降机等。 + +# 6 钢制塔架 + +# 61 设计要求 + +# 611 总体要求 + +钢制塔架设计应以第 章为基础,并应根据 / 选取载荷局部安全系数、材料局部安全系数和失效后果局部安全系数,其中失效后果局部安全系数应满足 / 中二类零件的要求。 + +# 6.1.2 材料 + +# 6121 塔架主体钢板 + +塔架主体(包括筒体、法兰、门框、拼焊法兰)材料应考虑塔架的强度、工作温度、材料的工艺性及经济性,应符合 / 或 / 的规定。 + +钢板的尺寸、外形及允许偏差应符合 / 的规定,厚度允许偏差不应低于 级。 + +钢板表面质量应符合 / — 规定的 类 级要求。 + +如钢板厚度方向有性能要求,应符合 / — 的规定。 + +如钢板有超声波检测要求,至少应符合 / — 中质量分级 级的规定。 + +钢板材料的订货内容、技术要求、试验方法、检验规则、包装、标志、钢印位置、质量证明书等,应符合/ 的规定,钢板质量证明书应满足 / — 中 的要求。 + +塔架制造所用钢材的各项性能指标应符合设计文件要求。若需采用替代材料,应由制造单位提出申请,并通过取样试验,表明所采用的替代材料满足设计要求,经设计单位确认后方可执行。 + +# 6122 法兰 + +法兰用钢应符合 / 的规定,厚度方向性能(如要求)至少应符合 / — 规定的 要求。超声波检测要求至少应符合 / — 中质量分级 级的规定。 + +法兰用钢质量等级不应低于塔架筒体使用钢材的质量等级。法兰应为锻件,锻件用钢应采用炉外精炼、真空脱气钢锭或连铸圆坯,不准许使用连铸板坯。 + +法兰订货内容、技术要求、取样、试验方法、检验规则、标志、标签和随行文件以及包装、运输和贮存应符合 / 的规定。 + +# 6.1.2.3 焊接材料 + +用于塔架制造的焊接材料(焊条、焊丝、焊剂)应符合 / 、 / 、 / 、/ 、 / 的规定和设计要求,并应具有有效质量证明书。 + +用于塔架制造的焊接材料(焊条、焊丝、焊剂)应与母材匹配,冲击吸收能量不低于母材要求。 + +# 6.1.2.4 紧固件 + +用于塔架连接的紧固件用钢及性能等级由设计单位根据 / 、 / 、 / 、/ 、 / 、 / (所有部分)、 / 进行选用,并在设计文件中明确其性能要求。 + +# 62 塔架的固有特性分析 + +塔架的固有特性分析应考虑: + +般情况下,固有频率 f 和激振频率 f 、f 之间应有适 当 的 间 隔,可 根 据 公 式 ()和公式()计算: + +![](01K8T2JX33/images/1bcaaf813a0f2c093d2470c392f15668be450491603e55f2a86cc31e8b241280.jpg) + +…(1) + +计算固有频率时,应考虑基础的影响; +考虑到不确定性因素的影响 在计算固有频率时 应考虑 的偏差 +设计时还应考虑各种风况、波浪等引起的不同方向的振动,塔架可能存在共振情况时,可通过调整控制策略等方法来抑制振动。 + +# 63 塔架的强度分析 + +# 631 极限强度分析 + +# 6311 一般要求 + +筒节极限强度分析应符合 / 的规定。 +塔架附件的极限强度分析,应符合 / (所有部分)的要求。 + +# 6312 局部塑性要求 + +塔架极限强度分析时,若未考虑材料非线性,发生塑性变形的区域应限于较小范围。 应力超过屈服强度时,应变按 规则计算,见公式(),且不应大于 。 + +![](01K8T2JX33/images/09158d685b49ba3d5281649bcd493747628984e817a9fbe5932bb56e578b6dbb.jpg) + +……(3) + +疲劳工况最大载荷下,结构不应发生局部塑性变形。在使用疲劳工况最大载荷进行结构计算时,可不考虑焊接产生的残余应力。 + +# 6.3.2 稳定性分析 + +# 6.3.2.1 一般要求 + +塔架的稳定性分析应考虑特定类型结构的相关失效模式,即塔架的筒体屈曲。 + +若塔段两端是 型或 型法兰,可仅对各塔段的稳定性进行分析;若塔段两端采用其他型式,应证实塔段两端能提供必要的边界条件以便分析各塔段的稳定性,否则应分析塔架整体的稳定性。 + +# 6.3.2.2 分析方法 + +塔架的筒体屈曲分析时应考虑制造和安装过程中可能出现的几何、结构和材料缺陷。屈曲分析应采用以下任一方法进行。 + +依据行业认可的相关标准规范进行分析,如 EN1993-1-6。 + +——计算机辅助屈曲分析。应考虑材料非线性( )、材料和几何非线性( )以及考虑缺陷的材料和几何非线性( )等对屈曲的影响。 + +# 6323 门框/加强结构 + +对于塔段上有无加强结构的开口,都应采用以下任一方法进行屈曲分析。包含考虑缺陷的材料和几何非线性( )的数值屈曲分析。根据附录 推荐的方法进行分析验证。对于有门框的开口,门框横截面在 点钟和 点钟方位应与塔架筒壁中心对齐(如图 所示)。如有其他替代方法可保证开口屈曲的安全,则可采用替代方法的修改、简化或扩展。 + +![](01K8T2JX33/images/4bfd9d892b6cfad08f4a26bb3b8007bc3c4c07116d8c891b0bc0bcb4ff759de6.jpg) +图 1 门框开口的几何形状 + +# 633 疲劳强度分析 + +# 6.3.3.1 一般要求 + +塔架疲劳强度分析应包括以下方面: + +塔架环焊缝的疲劳强度分析; +塔架附件连接结构的疲劳强度分析。 + +对于塔架结构的连续区域(门框除外),可采用只考虑弯矩中最大疲劳载荷分量进行疲劳强度分析。 + +# 6332 分析方法 + +塔筒疲劳强度分析宜采用 线性累积损伤理论。应力计算可采用名义应力法或热点应力法进行计算。所分析结构对应的焊缝型式细节分类和 曲线应按 或选取。 + +不应使用 曲线中的疲劳截止限。 + +如使用主应力进行疲劳分析,则 曲线中应力方向与主应力方向一致,具体可参见附录 。 + +不应使用带符号的等效( )应力进行焊缝的疲劳分析。 + +应评估焊接附件或钻孔的局部应力集中的影响,具体可参见附录 。 + +# 64 环形法兰强度分析 + +# 641 环形法兰尺寸要求 + +环焊缝与法兰上表面之间的距离应满足公式(),则为有颈法兰(如图 所示);如不满足,则为无颈法兰。焊缝预留坡口到过渡圆半径之间的距离应满足公式()。 + +![](01K8T2JX33/images/84e13e75576459fdc46ce76b48bc7e9b396d37037fc377b444a494a2574b0ef3.jpg) + +·…( 4) + +![](01K8T2JX33/images/cd373bbf1ab92b921ed9e555fa150de26082967c877290e9358ecb2f90a4880c.jpg) + +…(5)单位为毫米标引序号说明:法兰厚度;包含颈高的法兰厚度;法兰宽度;过渡圆半径;焊缝预留坡口高度,宜符合 / 的要求;b 颈厚(一般和焊接的筒壁等厚度);l 焊缝预留坡口到过渡圆半径之间的距离。 + +![](01K8T2JX33/images/a7b0d9b653f8cef02fbea67156c091c95102e5e8fc16a45bafd020cfe869f892.jpg) +图2 L型法兰 + +# 642 环形法兰设计要求 + +螺栓的设计预紧力 F 宜满足公式()的要求: + +![](01K8T2JX33/images/9e12551f13bd7580a68af91e86e904f249f5bbfee24c627bbe28ee33015bbac5.jpg) + +·…( 6 ) + +单个法兰的倾角和连接表面的平面度偏差[见图 的 )和 )],不应超过表 中的值。不准许塔架法兰外倾。 + +![](01K8T2JX33/images/047e154eb721cd721f1c1e250950c5fac6d37a2d99b4dd106ed4dd99d437015d.jpg) +图3 法兰间隙 k + +# 表 1 法兰公差要求 + +| 特征 | 限制值 | +| --- | --- | +| 整个圆周面上每个法兰的平面度偏差(k/2) | ≤2.0 mm | +| 30范围内每个法兰的平面度偏差值(k/2) | ≤1.0 mm | +| 法兰连接表面内倾角(αs) | 0°~0.7° | +| 使用锥形垫圈前法兰外表面的倾角(αs) | ≤2° | + +如有超出表 的平面度偏差,则螺栓疲劳强度分析应考虑平面度偏差的影响(如可能出现的法兰张开对螺栓轴向应力和弯曲应力的影响)。疲劳强度分析的螺栓预紧力应为螺栓设计预紧力减去闭合法兰间隙所需的预紧力值。螺栓施加完预紧力后,如法兰接触面不完全接触,应采取适当措施。可选取在没有施加预紧力的情况下 采取返修 垫补或填补接触缝隙等措施垫片或其他填充物的材料应具有和法兰材料相近的弹性模量和抗压强度。在每个螺栓的附近或每个螺栓和筒壁之间的区域进行填充,填充物应保证法兰在螺栓预紧之前或在施加 的设计预紧力后填充区域充分接触。如螺栓施加完预紧力后法兰外表面的倾角 α 超过 ,则应使用不低于法兰强度的锥形垫圈填充。以下情况,可假定法兰与筒壁的焊接不受筒壁边缘变形的影响: + +法兰表面和焊颈之间的半径 r 至少为 ; +环焊缝与法兰上表面之间的距离应满足 的要求; +焊趾与法兰上表面距离要求适用于生产和修复焊接; +单个法兰内锥度最大限值为 。 + +如法兰满足上述要求,只需分析螺栓和环焊缝的疲劳强度。如不满足上述要求,则需对法兰、法兰和筒壁之间的焊接以及焊接附近的筒壁进行详细的极限强度和疲劳强度分析。焊缝疲劳细节分类应选择无焊颈法兰连接结构的值。 + +# 6.4.3 法兰和螺栓连接极限强度分析 + +对于法兰和螺栓连接的极限强度分析可采用简化计算方法,如 / 方法。而采用不考虑法兰间隙的有限元计算方法以及其他导致相似结果的计算方法都是不准许的。 + +简化计算方法至少需要考虑以下三种失效模式: + +螺栓断裂导致的失效; +塔架筒壁和/或法兰上出现塑性铰且螺栓断裂导致的失效; +塔架筒壁和/或法兰上出现塑性铰。 + +应考虑塔架筒壁轴向载荷对塔架筒壁和法兰的应力的影响。 + +# 644 法兰连接螺栓疲劳强度分析 + +如在螺栓连接的初始松弛之后重新拧紧螺栓,则螺栓疲劳计算时预紧力最大可选取 的设计预紧力(F )。重新拧紧应在 预验收后进行,但不应超过 预验收后的六个月,否则应使用的设计预紧力(F )进行疲劳强度分析。 + +螺栓疲劳强度分析应基于非线性的螺栓受力函数 F f(Z)进行计算,螺栓受力函数如图 所示,其中 Z 为塔架筒壁轴向力。如分析时考虑容许的法兰间隙,则非线性螺栓受力函数可根据/ 方法计算。 + +如采用更复杂的计算方法 如使用接触单元或弹簧单元的有限元法 来确定螺栓受力函数时 应将制造过程中容许的法兰间隙作为缺陷考虑。如采用无法兰间隙的计算方法确定螺栓受力函数,则可参考图 基于对缺陷影响的研究适当增加螺栓受力曲线的斜率,来计算考虑法兰间隙后的螺栓受力函数。 + +![](01K8T2JX33/images/294bc92ccfb53591b48abe182822a9710c21b4d8f607f9da553236f26569d2b1.jpg) +图4 螺栓力与塔架筒壁轴向力的关系图 + +计算方法未考虑螺栓中弯矩影响时(如 / 的简化计算方法),螺栓的疲劳细节分类应按 \* 考虑(见图 )。 + +对于规格大于 的螺栓, 曲线应考虑折减系数(k ),k 按公式()计算,其中 d 为螺栓的公称直径。 + +![](01K8T2JX33/images/b272b61c51c931a0d8311d6cb0eb4fb87d7a6f7c9f9892bf64850e341f7d69c8.jpg) + +![](01K8T2JX33/images/38afcc644afeb5e0072d816aa499b254f10bb6249bd870f056472886ffd5d600.jpg) +图 5 疲劳等级36\* 的SN曲线 + +# 65 塔架门框开口强度分析 + +651 门框区域的稳定性可根据 规定方法进行分析。652 对于采用纵向加劲肋的开口区域 [见图 )],可按有关规范或标准,进行简化的屈曲分析。653 对于采用环形加劲肋的开口区域 [见图 )],及未采用加劲肋的筒体可用简化的方法进行屈曲分析。654 门框开口区域还应进行极限强度和疲劳强度分析。655 门框加劲肋焊缝疲劳计算中结构应力分析应采取以下任一方法进行: + +![](01K8T2JX33/images/65b06b9b6dffb64013ef807aab45a3981cb8fdc737af32a1802ab0ad7e94f358.jpg) +图 6 加劲肋的开口示意图 + +对门框加劲肋的焊缝焊趾进行详细建模的有限元方法; +未对门框加劲肋的焊缝焊趾进行详细建模的有限元方法 此时根据 外推热点应力; +其他类似方法。 + +# 66 涂料防护体系 + +塔架的涂料防护体系应根据 的要求进行设计 塔架的涂层防护体系耐久性应满足塔架的设计寿命。 + +# 6.7 技术要求 + +# 671 塔架制造商基本要求 + +塔架制造商应建立健全的安全、质量和环境管理体系并有效运行。 + +焊工/焊接操作工应经过专门的理论和操作技能培训,取得国家或国际授权的相关部门颁发的资格证书 并且焊工焊接的钢材类别 焊接方法和焊接位置等均应与焊工 焊接操作工本人考试合格的项目相符。 + +无损检测人员应按照要求进行考核鉴定通过相关从业资格的考核,取得有效资格证书。 + +# 672 原材料检验 + +6721 原材料进厂后,塔架制造单位应对原材料(不仅限于筒体钢板、法兰、门框、焊材、涂料等)进行详细的质量及资质文件审查,合格后方可使用。所有焊材应提供冲击吸收能量、化学成分、力学性能等检验报告,并符合标准要求。 + +6722 原材料应按 进行检测并符合要求。 + +6723 塔架主体钢板进厂后需对 的炉批号进行化学成分及力学性能复验,复验不合格不准许使用。 + +6724 所选用的试验机构应具有相关资质,其资质应包含力学及化学成分的能力许可。 + +# 673 外观、尺寸偏差要求 + +# 6.7.3.1 切割 + +钢板切割表面不应有裂纹、分层、夹杂等缺陷,不应影响焊接及产品外观质量。 +筒节下料的尺寸偏差应符合下列要求:大小口弦长偏差±2mm;对角线之间长度偏差 ;斜高偏差±2mm。 +门框下料尺寸偏差 ,成型后切割面不应有任何微裂纹,切割边缘圆滑过渡。 + +# 6732 卷制成型 + +6.7.3.2.1 筒节任意截面圆度公差要求为(D -D )/D 不大于0.01,如图7所示。 + +![](01K8T2JX33/images/fd78b728e87842c5bb8e900436728fce337af0d7cbccf0b16ee215c5a195933f.jpg) +图7 任意截面圆度示意图 + +标引序号说明: + +Dmax 测量出的最大内径;-测量出的最小内径;公称内径。 + +67322 纵焊缝对口错边量 h(见图 )不大于 .t,且最大不超过 ,t 为钢板厚度( )。 + +![](01K8T2JX33/images/c706139f413ba279f487324c434d9b0cfff262ba0794b0bbe7df4794e2a21966.jpg) +图8 纵焊缝对口错边量示意图 + +67323 筒节纵焊缝棱角和环向表面局部凹凸度检测样板长度选取如表 ,筒节纵焊缝棱角和环向表面局部凹凸度检测(见图 ),凹凸度 E 不大于 L,L 为样板长度。 + +# 表2 纵焊缝棱角和环向表面局部凹凸度检测样板选取 + +单位为毫米 + +| t | 6~7 | 8~9 | 10~11 | 12~13 | 14~15 | 16~17 | 18~19 | ≥20 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| L | 150 | 200 | 250 | 300 | 350 | 400 | 450 | 500 | + +![](01K8T2JX33/images/b7985ee4ea58ff1a8cc8d157a66edc58cc464f1946b8b57cde7f0078f2ced445.jpg) +图9 纵焊缝棱角及环向局部凹凸度检测示意图 + +# 6.7.3.3 组对 + +67331 筒节与筒节对接一般采用外壁对齐或板厚中心对齐 不同厚度筒节对接时 当单边板厚差(t)不小于 时,应对较厚的板做削斜处理,建议使用 t/h 不大于 / ,见图 。 + +![](01K8T2JX33/images/caa43e48f560ae234c6c112b33fb464a7c8358b93823cce3ce60cbb90a70a05a.jpg) +图10 板厚差较大时厚板削薄处理示意图 + +67332 环焊缝对口错边量(h)(见图 )不大于 t(t 为 t 和 t 中的较小值),且最大不超过在测量对口错边量 h 时 不应计入两板厚度差值 t 和 t 为钢板厚度 单位为毫米 + +![](01K8T2JX33/images/b2911f5b27ff447f695bb3947a4217f0c3935cd423e11072bea4e39b50b3fe33.jpg) +图 11 环焊缝对口错边量示意图 + +67333 环焊缝棱角和纵向表面局部凹凸度要求如下(见图 ),用 L 为 长的直尺检查环焊缝两侧的棱角或筒体表面局部凹凸度,其凹凸度 E 值不应大于(0.1t+1)mm,且不大于5mm。 + +![](01K8T2JX33/images/7ab9f1527184ebac91a41c40c843eee1ddaea6d461244786f0fdca79203233b1.jpg) +图 12 环焊缝棱角及纵向局部凹凸度测量示意图 + +67334 塔段直线度高度公差按照 / 执行,相应等级应由设计单位确定,在没有要求时,塔段直线度(L)按下列要求: + +塔段高度 H≤20000mm时,L≤H/1000mm; +塔段高度 H>20000mm时,L≤(0.5H/1000+10)mm。 + +6.7.3.3.5 塔段两端面平行度允许偏差为5mm。 + +6.7.3.3.6 塔段垂直度V 不应大于 H/1000mm(见图13)。 + +标引序号说明: + +![](01K8T2JX33/images/19f99dc40c7ee64c1b3fbedcc1f9f68f5c365f17d4706376daf0c9833e21f197.jpg) +图 13 塔段垂直度示意图 + +———垂直度;以塔段一端法兰面为基准面,两法兰中心连线在垂直于基准面的圆柱面内区域变动,这个圆柱的直径就是垂直度。 +H—-塔段高度。 + +6.7.3.3.7 塔段高度偏差(ΔH)不应大于 H/1000mm。 + +67338 法兰焊接后的螺栓孔位置中心圆的直径偏差(见图 )。 + +在法兰表面上,与垂直成 的方位上测量两次,并记录。圆度检测时应考虑产品重量对圆度的影响。所测结果应符合设计要求。 + +![](01K8T2JX33/images/69e8a9a19c0c853af089a0defde028f6f19d51fa811a18ec2b2dfe6f01b18647.jpg) +图14 法兰焊后螺栓孔位置中心圆直径偏差测量示意图 + +67339 法兰焊接完成后,应进行平面度和法兰面内倾量的检测,检测结果应符合 的要求(见图15)。 + +![](01K8T2JX33/images/e9b93622613044b2c4b6d33511115b097cd0ea2ec9277899bdbc95002974851b.jpg) +图15 法兰内倾量示意图 + +# 674 切割工艺要求 + +塔架主体钢板下料后应有材料的可追溯标识。可采用无应力钢印标识或深度小于 的有应力钢印标识。 + +# 675 焊接要求 + +# 6751 焊接标准 + +塔架焊接按照 / (所有部分)、 / 或 相关部分要求执行。 + +# 6752 焊接计划 + +焊接计划应包括以下内容: + +焊接工艺规程,包括焊接方法及对焊材、预热温度和层间温度及焊后热处理的要求; +为避免焊中和焊后出现焊接变形应采取的措施; +焊接顺序 对起弧和收弧位置的限制或指定 包括中间停弧和起弧位置 在这些位置因焊接接头形状复杂导致焊接无法连续进行; +中间检查要求与焊接顺序相关,在焊接过程中部件的翻转; +避免层状撕裂应采取的措施; +焊缝验收标准; +焊前和焊中的检查和检验计划; +焊缝标识要求; +表面处理要求。 + +# 6753 焊接工艺评定报告 + +筒体的焊缝以及与筒体相焊的焊缝(包括门框)的焊接工艺应有相应的焊接工艺评定的支持。焊接工艺评定应按 / 、 的规定进行,并编制焊接工艺评定报告( ),该报告应包含所使用的材料质量证明书。 + +返修焊缝未采用原始焊缝相同的焊接工艺时应对返修工艺单独进行工艺评定。全焊透的 型对接焊缝的焊接工艺应单独评定。 + +# 6754 焊接准备和实施 + +# 67541 焊接接头准备 + +接头准备应与焊接方法匹配。接头准备应符合焊接工艺评定中准备形式要求。焊接工艺规程( )中应规定接头准备与组对的公差范围。 + +接头坡口应根据 / 、 / 的要求设计并经焊接工艺评定确定选择坡口形式和几何尺寸。 + +接头坡口的加工方法:机械方法、火焰切割、等离子切割等加工方法。火焰切割或等离子切割表面质量按 / — 中的 级要求,切割加工的坡口应去除坡口表面的氧化皮、熔渣及影响焊接质量的表面层。 + +接头坡口及其内外侧表面不小于 范围内的油、漆、垢、锈、毛刺及镀锌层等应清除干净,且不应有裂纹、夹层等缺陷。接头坡口出现较大缺口或其他几何尺寸问题修复时需要使用经过认可的焊接工艺规程,上述区域应及时打磨并圆滑过渡。 + +# 67542 焊材的储存和流转 + +焊材的储存、流转及使用应依据制造厂商的推荐。焊条和焊剂的烘干和存放,应满足合适的温度和时间。若焊材(焊条和焊剂)在焊接后未使用完,应按要求再次烘干。焊条最多只能烘干两次。 + +# 6755 焊接环境 + +焊接作业应在室内进行,特殊情况需露天作业,应满足图样和技术规范要求。出现下列情况之一且无有效措施时,不应施焊:风速:气体保护焊时大于2m/s,焊条电弧焊时大于5m/s,埋弧焊时10m/s; + +相对湿度大于 ;雨雪环境。 +当待焊材料低于 时,可适当加热。 +钢级不小于 的钢材焊接环境温度低于 时,焊前应预热。 + +# 6756 焊接组对 + +为保证焊接部件尺寸和变形控制 焊前用点固焊或外部设备固定位置 装配时接头组对的最终尺寸应在公差允许范围内,应为收缩和变形预留变形量。 + +焊接部件的装配和定位焊时,焊缝应可达并可见。 + +# 6757 预热 + +预热应按照 / 和 / 进行。 +预热应按照适用的 进行,包括定位焊和临时附件的焊接。 + +# 6758 临时附件 + +如果组装过程需要使用临时焊接附件(法兰、门框不准许焊接临时附件),附件所处位置应便于去除,且附件的去除不能对筒体结构造成破坏。临时附件焊接应依据 或 进行。 + +临时焊接附件去除后,母材表面应随即打磨平滑,并做 或 确保筒体临时附件的焊接部位表面没有表面裂纹。 + +# 6759 角焊缝 + +角焊缝的焊缝厚度或焊脚高度不应小于规定尺寸,焊接内附件(如平台支耳等)角焊缝应围绕拐角连续焊接 长度不小于焊脚长度的两倍 除非可达性差或结构限制 在没有其他规定时 要完成角焊缝的包角焊。由于毛细现象产生锈蚀的位置不能采用断续角焊缝。 + +# 67510 对接焊缝 + +筒体环焊缝收弧处、纵焊缝端部应保证有效焊缝厚度。纵焊缝应使用引弧板和收弧板,引弧板和收弧板的可焊性不低于母材的可焊性,引弧、收弧焊缝长度不低于80mm。 + +焊后去除引弧板、收弧板和其他辅助材料,应保证对筒体结构不造成破坏。 + +如果需要焊缝表面磨平,应清除余高以满足质量要求。 + +# 67511 背面清根 + +背面清根应有足够深度,且完全清除前面焊缝金属。 +背面清根应形成一个单面 型坡口,使其易于焊接。 + +# 67512 螺柱焊 + +螺柱焊应按照 / 或 进行。 + +# 67513 火焰矫正 + +如果需要火焰矫正来调整焊接变形,应制定合适的矫正工艺,该工艺至少应包括下列几点: + +允许的最大加热温度和冷却速度; +加热方法; +温度的测量使用方法为工艺评定进行的机械性能实验的结果; +有资质操作该工艺的操作工的标识。 + +# 67514 异种钢焊接 + +应对不同类型的不锈钢的焊接以及与其他钢种焊接的要求做出规定。 + +焊接责任人员应考虑选择适当的焊接技术、焊接方法和焊接材料。应认真考虑不锈钢污染和电化学腐蚀问题。 + +# 67515 焊后热处理 + +部件焊接后如需要焊后热处理,应有适当的工艺。 + +# 67516 焊接实施 + +应采取有效措施避免出现电弧擦伤,若出现电弧擦伤,钢板表面应稍适打磨并检查。外观检查后,应辅以 或磁粉检测 。 + +在每一道焊接前,应将前面焊道中的裂纹、孔穴及其他不准许缺陷清除。 + +在每一道焊接前,应将前面焊道表面及盖面焊道表面熔渣清除。应特别注意焊缝与母材的连接处。 + +应对焊接完成之后的打磨和修整要求做出规定。 + +# 67517 焊接修复 + +# 675171 表面修复 + +焊缝及母材表面的所有超标缺陷,应按工艺要求修复。 + +# 675172 焊缝返修 + +对需要进行返修的焊缝,应当分析内部缺陷产生的原因,提出改进措施,按评定合格的焊接返修工艺进行返修。焊缝同一部位的返修次数不应超过两次,如超过两次,返修前应经制造单位技术负责人批准,焊后焊缝、母材及热影响区要进行硬度检测(检测硬度值不大于 ),同时返修次数和返修部位应记入质量证明材料。 + +# 67518 产品焊接试板 + +675181 若需要,在施焊塔架的同时,按相同要求制作筒体纵缝焊接试板。产品焊接试板的厚度范围应在所代表的工艺评定覆盖的产品厚度范围之内。 + +675182 产品焊接试板检验项目按照 / 中的规定执行。如试板不合格,应加倍制作焊接试板。不合格的试板代表的产品焊缝不合格,应做返修或报废。 + +# 676 焊接检查 + +# 6761 焊前及焊中检查 + +焊前及焊中对以下项目进行检查: + +焊工/焊接操作证书的适用性、有效性; +的适用性; +母材和焊接材料的标识; +焊接坡口(形式及尺寸); +组对、工装及定位; +工作条件(包括环境)对焊接的适用性; +主要焊接工艺参数(焊接电流、电弧电压及焊接速度); +焊道的清理与形状,焊缝金属的层数; +根部气刨; +预热/层间温度; +焊接顺序; +焊接材料的正确使用及保管; +焊接变形的控制。 + +# 6.7.6.2 焊后检查 + +# 67621 塔架的无损检测 + +塔架的无损检测主要包括以下几种方法: + +VT; +PT; +MT; +UT; +RT; +TOFD; +PAUT。 + +# 67622 检测时机 + +焊后冷却 合格后 同时满足以下等待时间再进行 或其他方法的检测 对于有冷作校正包括热校正焊缝,无损检测可以在校正前进行,但校正后应再次检测: + +针对埋弧焊焊缝(含焊返修焊缝); +钢板厚度(t)不大于 时,等待时间执行 ; +钢板厚度(t)大于 时,等待时间执行 ; +针对 气保焊或手工焊条焊缝,等待时间执行 ; +使用 气保焊或手工焊条返修及表面缺陷修补的焊缝,冷却后 合格即可进行 (或MT)检测。 + +# 67623 目视检测(VT) + +应对焊缝的外观质量、焊接材料、焊接工艺规程的执行、焊接设备的完好状况、焊工的资格及相关记录进行检查。 + +要求检测的表面清洁,无油污、油脂、砂土、划伤或其他影响检测结果的情况。 + +# 67624 磁粉检测(MT)或渗透检测(PT) + +按 / 或 / 规定进行 或 的焊缝及区域,合格级别为 级。 + +# 67625 超声波检测(UT) + +塔架主体对接焊缝 按 / — 进行, 按照 / 进行,各部位焊缝的合格级别分别如下:) 合格级别为 级的焊缝:) 筒节与筒节焊接的环焊缝;) 门框与筒体焊接的焊缝;3) 筒节与法兰焊接的环焊缝(包括基础段筒节与上法兰的环焊缝);) 筒节的纵焊缝;) 法兰的拼接焊缝(不包括基础段底法兰拼接焊缝);6) 钢板卷制的门框对接焊缝;) 合格级别为 级的焊缝:基础段筒节与基础段底环法兰焊接的 型焊缝;) 合格级别为 级的焊缝:基础段底法兰的拼接焊缝。 + +# 67626 射线检测(RT) + +塔架产品的射线检测适用于设计有要求或对 有疑问时作抽查,按 / 执行。如焊缝厚度大于 时,可使用 代替 。 + +# 67627 重复检测 + +经 、 、 、 、 、 、 检测发现有超标缺陷的,应在缺陷清除干净后,进行必要的修磨或补焊 并对该部位采用原检测方法重新检测 直至合格 + +对于设计要求为部分抽检的焊缝,若发现超标缺陷时,应按设计要求增加检测比例。 + +# 67628 尺寸检验 + +焊接后法兰螺栓孔位置中心圆直径偏差,见图 。 + +在法兰表面上,与垂直成 的方位上测量两次,并记录。圆度检测时应考虑产品重量对圆度的影响。所测结果应符合设计要求。 + +塔架在喷砂前应对主要的几何尺寸进行检查 检查结果符合设计图纸及技术规范要求 检查内容应包括塔架法兰平面度、法兰内倾量、螺栓孔位置中心圆的直径偏差、塔段高度偏差等。 + +# 677 表面防护 + +# 6771 一般要求 + +塔架各部件(位)防护方式按照 / 、 要求执行;防护体系宜采用高固体分、低挥发性有机化合物含量的产品。 + +# 6772 涂装前表面处理 + +# 67721 塔架表面及结构预处理 + +塔架在喷砂除锈前应进行必要的结构预处理 包括焊缝应无飞溅、焊渣,表面匀顺,焊缝上深度大于 或宽度小于深度的咬边应补焊处理,并打磨匀顺;锐边用砂轮打磨成曲率半径 以上的圆角;般表面 钢板表面无麻点 结疤 辊压杂质 夹层结构等 槽 凿痕及搬运过程中可视的损伤等应打磨至半径不小于 的圆弧,必要时进行补焊。 + +# 6.7.7.2.2 清洁 + +喷砂前,塔段应进行清理去掉表面的污物。 + +# 67723 喷砂 + +喷砂在喷砂房内进行,湿度不大于75%。 + +压缩空气应干净,无油无水。 + +喷砂时使用颗粒大小为 ϕ ϕ 的钢丸和钢丝段按比例相混合,按要求进行循环清洁并定期补充。 + +塔段喷砂后的表面要用压缩空气清洁表面的灰尘。 + +喷砂后出现缺陷的表面,必要时可进行打磨或补焊,修复后应重新喷砂。 + +# 67724 涂装时机 + +处理过的塔段应立即进行防护喷涂,通常在 之内进行涂装。 + +# 6773 涂装施工 + +# 67731 施工环境要求 + +一般要求施工环境温度 ,空气相对湿度不大于 ,钢板表面温度高于露点 以上;不应在雨、雪、雾、大风和较大灰尘的条件下进行户外施工;冬季施工时宜采用相应的低温固化型涂料。 + +# 67732 热喷锌 + +喷锌环境湿度不应超过 ,应在室内施工,钢板温度至少高出露点温度 。 + +喷砂过程结束后的时间尽量缩短,塔段应保存在可控的环境中。 + +# 67733 涂料配制和使用时间 + +喷涂施工工艺及要求应严格按照涂料厂家的手册或施工指导文件规定的内容执行 涂料的存放环境应满足厂家对储存条件的要求,已凝结或变质的涂层材料不应使用。 + +涂料应充分搅拌均匀后方可施工 宜采用电动或气动搅拌装置 对于双组分或多组分涂料 应先将各组分分别搅拌均匀后,再严格按照比例混合并搅拌均匀。 + +搅拌均匀后的涂料按照产品说明书规定的时间进行熟化。 + +涂料的混合使用期限按照产品说明书规定的适用期执行。 +低温施工时,涂料混合和固化的温度应符合产品说明书的规定。 + +# 67734 涂装工艺 + +涂装工艺按照设计要求或涂料供应商提供的施工工艺执行。 +漆膜光泽度应符合 、 / 的要求。 + +# 67735 涂装间隔 + +按照设计要求和材料工艺进行配套涂层的施工。每道涂层的涂装间隔时间应符合材料供应商的有关技术要求。超过最大涂装间隔时,应进行表面拉毛处理后再涂装。 + +# 67736 热镀锌 + +可拆卸的钢制附件一般采用热镀锌进行防腐,具体要求如下:熔融锌中的杂质总含量(铁、锡除外)不应超过总质量的 ,所指杂质按照 / 的规定;热镀锌应根据各工厂设备情况制定脱脂、酸洗、水洗、镀锌、钝化、清洗、干燥等工序以及时间、温度等工艺参数;镀锌层表面应连续、完整、光滑,不应有溶剂渣、漏镀、结瘤、积锌、毛刺等缺陷,镀锌层表面一般呈灰色或暗灰色;镀锌层厚度和镀锌层附着力应符合设计文件要求。 + +# 67737 固化 + +涂装完成后应在喷漆房内停留并保温一段时间,保温的温度和时间按工艺规程要求执行。实际情况中,固化时间应随着膜厚的增加而相应延长。 + +# 678 防护检验 + +# 6781 喷砂前检查 + +喷砂前检查内容如下: + +塔段表面应无油污、油脂及各种残留物等;塔段表面应无超标凹痕、划痕、擦伤、锐边及其他缺陷;施工环境的温度、相对湿度、空气露点温度等应符合规定要求 + +# 6782 喷涂过程检查 + +喷涂过程检查内容如下: + +塔段表面清洁度、粗糙度应符合技术规范要求; +涂料的牌号及色号正确无误,与涂料工艺规范相符; +涂料的包装完好无损,产品合格证及生产日期符合要求; +施工环境的温度、相对湿度、空气露点温度等应符合规定要求; +每道涂层不准许存在漏涂、针孔、起泡、流挂、起皱等超标缺陷; +施工中及时进行湿膜厚度测量,以便控制干膜厚度。 + +# 6783 喷涂后检查 + +涂层外观应色泽均匀 平整 有光泽 表面不准许有咬底 鱼眼 裂纹 剥落 针孔 流挂 起皱 起泡等缺陷 涂层缺陷验收要求符合 所有部分 的要求干膜测厚区位置应随机选择,塔段内外表面至少每 2 为一个测区,每区至少 个测量点。测量结果平均值作为该区的涂层干膜厚度值,塔段所有测区的干膜厚的平均值作为该段塔架的干膜厚度值。漆膜厚度测量要求符合 、 的要求。塔段表面涂层各层厚度值由设计单位按下列三个规则选用一个,作为检查的依据:评定规则:所有测量点中 测点的测量值不应低于规定的干膜厚度,其余 测点的测量值不应低于规定干膜厚度的 ;评定规则:所有测量点中 测点的测量值不应低于规定的干膜厚度,其余 测点的测量值不应低于规定干膜厚度的 ;最小干膜厚度原则:所有测点的测量值不应低于设计要求的规定值。最大干膜厚度不应大于规定值的 倍。必要时应制作涂层试板,涂层附着力按照 / 或 / 进行检测。漆膜光泽度测量应符合 、 / 的要求。涂层附着力按照 / 要求:单个测试值不小于 ,平均值不小于 。 + +# 6784 返修后检查 + +返修后需对返修区域重新进行检查,检查规则同喷涂后检查。 + +# 68 检验 + +# 681 检验类别 + +产品检验分为出厂检验和型式检验。有下列情况之一时应进行型式检验: + +新产品的试制定型鉴定时; +产品的设计 工艺等方面有重大改变时出厂检验的结果与上次型式检验有较大差异时; +国家质量监督机构要求进行型式检验时; +定期对产品进行抽检时; +在使用中出现重大偏差时。 + +# 6.8.2 检验要求 + +6821 塔架实物质量应分为外部质量与内部质量。外部质量包括表面粗糙度、尺寸公差、重量公差、表面缺陷;内部质量包括材料力学性能、化学成分。 +6822 技术管理应包括工艺文件及工艺纪律管理、标准化及计量管理、检测能力及质量保证管理等。6823 检验人员应是专职质量管理人员、质检员和试验员,检测用具、仪器及设备应符合计量检定和质量检测要求,能够提供精确可靠的检测数据。 +6824 从事无损检测的人员应经过技术培训并按照 / 进行考核鉴定持证上岗或按照进行考核鉴定持证。 +6825 无损检测人员技术等级分为高级、中级、初级或 级、 级、 级,取得不同无损检测方法的各项技术等级人员只能从事与该等级相对应的无损检测工作,并负相应的技术责任。 +6826 塔架应至少由制造单位质量检验部门检查和验收。 +6827 需方要求参加供方检验时,双方应商定检验日期,若需方在商定的时间内未能到场,供方可自行检验并将检验结果提交需方。 + +# 683 检验项目与方法 + +6831 除另有规定外,塔架的检验项目和检验方法应符合表 规定。6832 塔架的型式检验要求按表 的型式检验项目进行。如各检验项目均符合本文件规定的要求时,则判定该产品的型式检验合格;如某一项目的检验结果不符合本文件规定的要求,则判定该产品的型式检验为不合格。 + +表3 检验项目和方法 + +| 序号 | 检验项目 | 型式检验 | 过程检验 | 出厂检验 | 检验要求 | 检验方法 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | 塔架主体钢板 | ○ | ■ | * | 6.1.2.1 | GB/T 1591、GB/T 700 | +| 2 | 法兰 | ○ | ■ | * | 6.1.2.2 | GB/T 1591 | +| 3 | 焊接材料 | ○ | □ | * | 6.1.2.3 | GB/T 5117、GB/T 5118、GB/T 5293、GB/T 8110、GB/T 10045 | +| 4 | 紧固件 | ○ | ■ | * | 6.1.2.4 | GB/T 5782、GB/T 1228、GB/T 1231、GB/T 3098.1、GB/T 3098.2、GB/T 32076(所有部分)、NB/T 31082 | +| 5 | 焊接工艺评定 | ○ | 一 | | 6.7.5.2 | NB/T 47014、ISO 15614-1 | +| 6 | 筒体任意截面圆度公差 | ○ | ○ | ★ | 6.7.3.2.1 | 图7 | +| 7 | 纵焊缝棱角及环向局部凹凸度 | ○ | 0 | ★ | 6.7.3.2.3 | 图9 | +| 8 | 环焊缝棱角及纵向局部凹凸度 | ○ | 0 | * | 6.7.3.3.3 | 图12 | +| 9 | 纵焊缝对口错边量 | ○ | O | * | 6.7.3.2.2 | 图8 | +| 10 | 环焊缝对口错边量 | ○ | O | ★ | 6.7.3.3.2 | 图11 | +| 11 | 塔段垂直度 | ○ | 0 | * | 6.7.3.3.6 | 图13 | +| 12 | 法兰平面度和内倾角 | 0 | 0 | * | 6.4.2 | 表1 | +| 13 | 产品焊接试板 | ○ | △10 | * | 6.7.5.18 | NB/T 47016 | +| 14 | 焊缝外观检测 | ○ | O | * | 6.7.6.2.1 | 目测 | +| 15 | 无损检测 | ○ | O | * | 6.7.6.2.1 | NB/T 47013.2、NB/T 47013.3、NB/T 47013.4、NB/T 47013.5、NB/T 47013.10、ISO 13588 | +| 16 | 涂层外观检测 | ○ | 0 | * | 6.7.8.3 | ISO 4628(所有部分) | +| 17 | 涂层厚度检测 | O | O | * | 6.7.8.3 | ISO 2808、ISO 19840 | + +6833 过程检验和出厂检验时抽检不合格 应在同一批产品中抽取双倍数量的产品 对不合格项进行复检;如仍不合格,则应对同一批产品 进行检验。 +6834 产品在过程检验和出厂检验出现不合格,或使用中出现质量问题时,需方将根据需要加大抽检比例。 +6.8.3.5 首批产品抽检,需方可根据需要加大抽检比例或全检。 +6836 如一个批次按比例抽检数量少于一套时,按一套抽检。 +6837 塔架按表 规定的出厂检验项目采用全数出厂检验,并由制造单位质检部门检验合格,出具产品合格证书并经需方监理工程师认可后方可出厂。 +6838 制造单位应根据表 ,作更详尽的生产过程控制和质量检测,做好质量记录。 + +# 69 完工资料及随机文件 + +# 691 完工资料内容 + +# 6911 资料内容说明 + +塔架完工后应及时汇编各工序的检测报告,包括但不限于以下文件。 + +# 6912 塔段的制作和焊接 + +塔段的制作和焊接的完工资料包括以下文件:主体钢材质量证明书;材料履历表;焊接日志;法兰质量检测报告;拼焊法兰的热处理报告或振动时效报告(若存在时);无损检测报告; + +塔段的几何尺寸检测报告; +减震装置的泄漏试验报告(若存在时)。 + +# 6913 表面处理 + +表面处理的完工资料包括以下文件:表面处理方案;表面处理记录表面处理返修记录。 + +# 6914 最终检验和交付 + +最终检验和交付完工资料包括以下文件: + +附件安装检验报告; +第三方监理报告(若存在时); +产品发货清单。 + +# 6.9.1.5 其他 + +制作过程中的设计变更文件(若存在时)。 + +# 692 竣工资料存档 + +制造厂对竣工资料应存档,保存期至少5年。 + +# 610 标志、包装、储存 + +# 6.10.1 标志 + +61011 生产厂家可按照 / 制作标牌,标牌内容应包括制造单位名称、产品名称、出厂编号、出厂日期,标牌材质应选用不易腐蚀的材料。 + +61012 同一台塔架上、下段对接标识,塔体下法兰与基础环上法兰对接标志按有关图样要求标注。 + +# 6.10.2 包装 + +61021 包装应根据塔架的结构尺寸、重量大小、运输方法(铁路、公路、水路)等特点进行,保证其安全可靠地运达目的地。 +61022 塔架应待涂层完全固化并经检验合格后,方可进行包装。 + +# 6.10.3 储存 + +61031 塔段成品应存放在开阔、洁净、平整的场地;塔段成品应放置在楔形垫木或支架上,楔形垫木 +或支架与塔架之间应放置缓冲物。 +61032 露天存放时,应确保储存场地有良好的排水设施,不会积水以免浸泡塔段。 +61033 塔段两端应设置防雨布,防雨布应完好。 +61034 放置塔段时,宜将爬梯放置在 点钟方向。 + +# 6.11 运输 + +# 6111 临时加固措施 + +61111 运输前,塔架内升降机护栏门、平台翻盖门、塔架门等活动部件应可靠固定。 +61112 运输前,电缆、照明等电气设备应做好防水措施并固定在爬梯等附件上。 + +# 6112 陆上运输 + +61121 装卸场地应足够开阔、平坦,满足吊装设备的正常运行与运输车辆的行驶。 +61122 运输过程中,应遵守相应的安全规范。作业时,应符合安全作业要求。 +61123 运输车辆司机应接受过必要的培训指导并考核合格。 +61124 运输车辆应有鞍型支架或 型支座,其沿塔段轴向的宽度应根据机型确定,且不宜小于 +。塔段在运输过程中应始终保持可靠固定。 + +# 6113 海上运输 + +61131 运输船舶应满足运输沿线国家和地区航运的相关要求。 +61132 船舶应具有足够的载重能力、纵强度、横强度以及扭转强度;设备积载区域应具有足够的局部 +强度,并应清洁干燥。 +61133 塔段在运输过程中应可靠固定。 +61134 运输过程中不应擅自移动设备位置,且不应拆除设备运输支架。 + +# 6114 吊运防护 + +塔段的吊运不应破坏涂层,吊装工具应采取可靠防护措施,避免与涂层直接接触。 + +# 6115 零部件运输要求 + +随机零部件应按使用部位分类包装,并采取必要的防护措施,确保在装卸及运输过程中不被磕碰损坏。 + +零部件应有标志,涵盖零部件名称与图号。标志粘贴在零部件表面,并用透明胶带进行可靠防护和固定。标志不应褪色、脱落。 + +# 6.11.6 预防变形 + +应采取有效措施防止塔段和法兰在储存、运输过程中产生变形。 + +# 7 混凝土塔架 + +# 71 基本规定 + +# 7.1.1 一般规定 + +7111 混凝土塔架采用以概率理论为基础的极限状态设计方法,以可靠指标度量结构构件的可靠度,采用分项系数的设计表达式进行设计。7112 混凝土塔架结构安全等级应为二级,结构安全重要性系数为 。7113 混凝土塔架结构疲劳设计可采用容许应力法。7114 混凝土塔架宜采用预应力混凝土结构体系。7115 根据结构类型、材料性能和受力特点等,混凝土塔架的结构可选择弹性分析、弹塑性分析、塑性极限分析和试验分析等方法进行分析。7116 混凝土塔架段的结构设计应引入预应力作用效应,考虑二阶效应的影响。7117 装配式混凝土塔架结构设计时应考虑拼接缝的影响。7118 装配式混凝土塔架拼接缝的连接应符合下列要求: + +拼接缝的承载力应保证构件之间的传力性能; +混凝土构件之间的连接应采取可靠的措施; +应考虑构件变形对连接节点及相邻结构或构件造成的影响; +宜避免竖向接缝上下贯通,当采用竖向接缝上下贯通的结构时,应保证贯通的竖向接缝不影响结构整体稳定性。 + +7119 混凝土塔架结构的地震作用计算宜采用振型分解反应谱法。71110 混凝土塔架截面抗震受剪承载力应满足 、 / 的有关规定;斜截面抗震受剪承载力应符合 的有关规定。71111 混凝土塔架除应进行承载能力极限状态计算和正常使用极限状态验算外,还应对施工工况进行验算。7.1.1.12 混凝土塔架设计所用术语“载荷”等同于 GB50009中的“荷载”。 + +# 712 承载能力极限状态计算 + +7.1.2.1 混凝土塔架的承载能力极限状态应计算下列内容: + +正截面抗压、抗弯承载力计算; +端部锚固区局部受压承载力计算; +有抗震设防要求时,应进行抗震承载计算; +拼接缝承载分析; +疲劳分析; +温度作用影响。 + +7122 极限承载状态,应采用载荷基本组合和偶然组合的效应设计值,并按公式()设计: + +![](01K8T2JX33/images/ce6fa38182bace9d9bd91c2df724bfe4a0375864acd41bec51e4c112a60713d6.jpg) + +…(8) + +式中: + +Y0 结构重要性系数,按照表4取值; +Sd 载荷组合的效应设计值; +R 结构的抗力设计值。 + +表 4 结构重要性系数(γ )取值 + +| 安全等级 | Y | +| --- | --- | +| 二级 | 不应小于1.0 | + +# 713 正常使用极限状态验算 + +7131 混凝土塔架的正常使用极限状态验算应包括下列内容: + +# GB/T19072—2022 + +) 裂缝验算; +) 应力验算。 + +7132 正常使用极限状态应采用载荷标准组合的效应设计值,并按公式()设计: + +![](01K8T2JX33/images/3c035805fcc3d4c0804f84615be631b357653de89dbf08313fe33e2674182f66.jpg) + +………( ) + +式中: + +— 正常使用极限状态载荷标准组合的效应设计值; +— 结构达到正常使用要求所规定的应力、裂缝宽度等的限值。 + +7133 混凝土塔段的最大裂缝宽度应符合 的相关规定,裂缝控制等级不应低于二级。 + +# 714 耐久性设计 + +7141 结构的耐久性应根据结构的设计使用年限 结构所处的环境类别及作用等级进行设计7142 结构的耐久性设计主要包含以下内容: + +结构的设计使用年限、环境类别及其作用等级; +有利于减轻环境作用的结构形式、布置和构造; +结构材料的耐久性质量要求; +钢筋的保护层厚度; +混凝土裂缝控制要求; +防水、排水等构造措施; +严重环境作用下采取合理防腐附加措施或多重防护策略; +耐久性所需的施工养护制度与保护层厚度的施工质量验收要求; +结构使用阶段的维护、修理与检测要求。 + +7143 预应力钢筋应根据具体情况采取表面防护 孔道灌浆 加大混凝土保护层厚度等措施 外露的锚具应采取封锚等有效措施 此外尚应符合 的要求 + +# 715 施工阶段验算 + +7151 在张拉 运输及安装等施工阶段 应对混凝土塔架进行承载能力极限状态计算 +7152 混凝土塔架应进行局部承压验算。混凝土强度应按张拉时的实际强度确定。 +7153 施工阶段验算时,应考虑混凝土塔架自重、施工载荷和施工路径对预应力的影响等。混凝土构 +件和筒节的吊装验算,应将其自重乘以动力系数,动力系数宜取 。 +7154 对于预应力分批施加的混凝土塔架,应根据不同的张拉工况分别进行施工验算。 +7155 施工阶段计入构件自重后的应力限值应按表 的规定采用。 + +表 5 施工阶段计入构件自重后的应力限值 + +| 项目 | 不准许出现裂缝的构件 | +| --- | --- | +| 混凝土压应力 | C30~C80 | 0.8fck | +| 超张拉时 | +| 混凝土拉应力 | 1.0fk | +| 注:fck- -混凝土抗压强度标准值;fk--混凝土抗拉强度标准值。 | + +# 7.2 材料 + +# 7.2.1 混凝土 + +7211 混凝土强度等级应该按照立方体抗压强度标准值确定 立方体抗压强度标准值是指按标准方法制作、养护的边长为 的立方体试件,在 或设计规定龄期以标准试验方法测得的具有保证率的抗压强度值。 + +7212 混凝土的轴心抗压、抗拉强度标准值(f 、f )和设计值(f 、f )应按照表 确定。 + +# 表6 混凝土强度标准值、设计值 + +单位为牛每平方毫米 + +| 种类 | 符号 | 混凝土强度等级 | +| --- | --- | --- | +| C40 | C45 | C50 | C55 | C60 | C65 | C70 | C75 | C80 | +| 抗压强度标准值 | fck | 26.8 | 29.6 | 32.4 | 35.5 | 38.5 | 41.5 | 44.5 | 47.4 | 50.2 | +| 抗拉强度标准值 | fk | 2.39 | 2.51 | 2.64 | 2.74 | 2.85 | 2.93 | 2.99 | 3.05 | 3.11 | +| 抗压强度设计值 | f | 19.1 | 21.1 | 23.1 | 25.3 | 27.5 | 29.7 | 31.8 | 33.8 | 35.9 | +| 抗拉强度设计值 | f | 1.71 | 1.80 | 1.89 | 1.96 | 2.04 | 2.09 | 2.14 | 2.18 | 2.22 | + +7213 混凝土的受压、受拉弹性模量(E )应该按照表 确定。混凝土的剪切变形模量(G )可按照相应的弹性模量(E )的 进行折减。混凝土泊松比(ν )可按 采用。 + +# 表7 混凝土的弹性模量 + +单位为万牛每平方毫米 + +| 混凝土强度等级 | C40 | C45 | C50 | C55 | C60 | C65 | C70 | C75 | C80 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| E | 3.25 | 3.35 | 3.45 | 3.55 | 3.60 | 3.65 | 3.70 | 3.75 | 3.80 | + +7214 混凝土疲劳变形模量(Ef)应按表 确定。 + +# 表8 混凝土的疲劳模量 + +单位为万牛每平方毫米 + +| 混凝土强度等级 | C40 | C45 | C50 | C55 | C60 | C65 | C70 | C75 | C80 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| E | 3.25 | 3.35 | 3.45 | 3.55 | 3.60 | 3.65 | 3.70 | 3.75 | 3.80 | + +7.2.1.5 预应力混凝土塔架的混凝土强度等级不宜低于 C40。 + +7216 预应力混凝土塔架宜采用后张法的预应力混凝土结构,并配置一定量的非预应力钢筋。 + +7217 混凝土宜采用硅酸盐水泥、普通硅酸盐水泥或者矿渣硅酸盐水泥进行配置。当采用其他品种或者标号的水泥时应进行对比试验分析。 + +7218 为了节约水泥 改善混凝土性能 在拌制混凝土时可掺入矿物粉质拌合料 常用的有粉煤灰硅粉、磨细矿渣粉、烧黏土、天然火山灰质材料一级磨细自燃煤矸石,其中粉煤灰的应用最为普遍。 + +7.2.1.9 当温度在0℃\~100℃范围内时,混凝土的热工参数可按照下列规定取值: + +线性膨胀系数(α ): -5/ ; +热导系数(λ): /( · · ); +比热容(c):0.96kJ/(k ·℃)。 + +# 7.2.2 钢筋 + +7221 钢筋的强度标准值应具有不小于 的保证率。普通钢筋的屈服强度标准值(f )及极限强度标准值(f )应按表 采用。预应力钢丝、钢绞线、预应力螺纹钢筋的极限强度标准值(f )及极限强度标准值(f )应按表 采用。 + +表 9 普通钢筋强度标准值 + +| 牌号 | 符号 | 公称直径mm | 屈服强度标准值(fyk)N/mm² | 极限强度标准值(fstk)N/mm² | +| --- | --- | --- | --- | --- | +| HPB300 | A | 6~22 | 300 | 420 | +| HRB400HRBF400RRB400 | CCFCR | 6~50 | 400 | 540 | +| HRB500HRBF500 | CDF | 6~50 | 500 | 630 | + +表 10 预应力钢筋强度标准值 + +| 种类 | 符号 | 公称直径mm | 屈服强度标准值(fyk)N/mm | 极限强度标准值(fstk)N/mm² | +| --- | --- | --- | --- | --- | +| 中强度预应力钢丝 | 光面螺旋肋 | APMAHM | 5、7、9 | 620 | 800 | +| 780 | 970 | +| 980 | 1 270 | +| 预应力螺纹钢筋 | 螺纹 | AT | 18、25、32、40、50 | 785 | 980 | +| 930 | 1080 | +| 1080 | 1230 | +| 消除应力钢筋 | 光面螺旋肋 | APAH | 5 | 一 | 1570 | +| | 1860 | +| 7 | | 1 570 | +| 9 | | 1470 | +| | 1570 | + +7222 普通钢筋的抗拉强度设计值(f )、抗压强度设计值(f')应按表 采用。预应力钢筋的抗拉强度设计值(f )、抗压强度实际值(f' )应按表 采用。 + +表11 普通钢筋强度设计值 + +| 牌号 | 抗拉强度设计值(fy)N/mm² | 抗压强度设计值(fy)N/mm² | +| --- | --- | --- | +| HPB300 | 270 | 270 | +| HRB400、HRBF400、RRB400 | 360 | 360 | +| HRB500、HRBF500 | 435 | 410 | + +表 12 预应力钢筋强度设计值 + +| 种类 | 极限强度标准值(fprk)N/mm | 抗拉强度设计值(fpy)N/mm² | 抗压强度设计值(fpy)N/mm | +| --- | --- | --- | --- | +| 中强度预应力钢丝 | 800 | 510 | 410 | +| 970 | 650 | +| 1270 | 810 | +| 消除应力钢丝 | 1470 | 1040 | 410 | +| 1570 | 1110 | +| 1860 | 1320 | +| 预应力钢绞线 | 1570 | 1 110 | 390 | +| 1670 | 1180 | +| 1720 | 1220 | +| 1 770 | 1250 | +| 1860 | 1320 | +| 1960 | 1390 | + +7223 普通钢筋和预应力钢筋的弹性模量(E )可按表 采用。 + +表13 钢筋的弹性模量 + +| 牌号或种类 | 弹性模量105 N/mm² | +| --- | --- | +| HPB300 | 2.10 | +| HRB400、HRB500HRBF400、RRB400、HRBF500预应力螺纹钢筋 | 2.00 | +| 消除应力钢丝、中强度预应力钢丝 | 2.05 | +| 预应力钢绞线 | 1.95 | + +7224 灌浆料的选用、施工、养护应按照 / 执行,材料性能要求应符合表 规定,灌浆料性能应符合表 规定。 + +# 表 14 灌浆料材料质量要求 + +| 序号 | 项目参数 | 性能指标 | +| --- | --- | --- | +| 1 | 流动度mm | 初始值 | ≥290 | +| 30 min | ≥260 | +| 2 | 抗压强度(23℃)MPa | 1d | ≥35 | +| 3d | ≥60 | +| 28d | ≥80 | +| 3 | 膨胀率% | 0h~3h | 0.1~3.5 | +| 24h与3h之差 | 0.02~0.5 | +| 4 | 对钢筋有无锈蚀作用 | 无 | +| 5 | 沁水率/% | 0 | + +表 15 座浆料性能要求 + +| 检验项目 | 性能指标 | 备注 | +| --- | --- | --- | +| 流动性/mm | 初始 | 130~200 | 黏稠状,不易流淌 | +| 45 min | | 具可塑性、可挤压性 | +| 抗压强度/MPa | 1d | ≥d强 | | +| 3d | ≥d强 | | +| 28d | ≥8d | | +| 抗折强度/MPa | 28d | ≥8d | | +| 竖向膨胀率/% | 3h | ≥h膨胀率 | | +| 24h与3h差值 | 0.02~0.5 | | +| 氯离子含量/% | ≤离子含量 | | +| 泌水率/% | 0 | | +| 注1:水泥基座浆材料的验收以实验室为标准,检验项目包括流动度、竖向膨胀率、抗压强度。注2:为方便现场施工可操作性,材料搅拌铺浆后不少于45 min具有良好的触变性和保水性。注3:上表相关性能适用环境温度为5℃~35℃。注4:若温度低于5℃时,采取必要的加热、保温措施。 | + +# 73 载荷和载荷组合 + +# 731 载荷与作用 + +7.3.1.1 混凝土塔架结构上的载荷与作用可分为下列三类: + +永久载荷,包括结构自重、固定设备自重、预应力,以及由混凝土收缩徐变、安装误差、基础变位引起的结构载荷效应等; +可变载荷,包括塔架风载荷、机组运行载荷、安装检修载荷、平台活载荷、多遇地震作用、温度作用等; +偶然载荷,包括冲击力、罕遇地震作用等。 + +7312 混凝土塔架载荷除机组运行载荷由主机设计方提供外,其余载荷均应按 、和 的规定进行计算。 + +# 7.3.2 载荷组合 + +7321 混凝土塔架结构设计应按正常运行工况、极限工况、疲劳工况、多遇地震工况、罕遇地震工况、施工工况进行分析计算。 + +7322 作用于塔架上可能同时出现的载荷效应应分别按承载能力极限状态和正常使用极限状态进行组合。对于承载能力极限状态应采用载荷效应的基本组合;对于正常使用极限状态应采用载荷效应的标准组合。 + +7323 承载能力极限状态下,混凝土塔架结构及构件应按载荷效应的基本组合进行如下设计。 + +a) 基本组合应采用公式(10)极限状态设计表达式中最不利值确定: + +![](01K8T2JX33/images/d0ad34a15e48abc64adade39e208ee7d499bcf703b86ff93ab5a86eb4560b755.jpg) + +式中:第j个永久载荷的分项系数,按表16取值;第j个永久载荷效应的标准值;第1个可变载荷的分项系数;第1个可变载荷效应的标准值;第i个可变载荷的分项系数;第i个可变载荷效应的标准值;第i个可变载荷的组合值系数;m 参与组合的永久载荷数;n 参与组合的可变载荷数。) 混凝土塔架结构及构件抗震设计时,应采用地震作用效应和其他载荷效应的偶然组合,设计值应按公式( )计算: + +表 16 载荷基本组合分项系数、组合值系数汇总表 + +| 系数 | 工况 | 取值 | +| --- | --- | --- | +| 永久载荷分项系数YG | 可变载荷控制组合 | 1.2 | +| 永久载荷控制组合 | 1.35 | +| 永久载荷对结构有利 | 1.0 | +| 疲劳验算 | 1.0 | +| 短暂状况验算 | 1.0 | +| 可变载荷分项系数YQ | 一般情况(除温度) | 1.4 | +| 温度作用 | 1.0 | +| 可变载荷对结构有利 | 0 | +| 疲劳验算 | 1.0 | +| 短暂状况验算 | 1.0 | +| 可变载荷组合值系数 | 一般情况(除温度、风载荷) | 0.7 | +| 温度作用 | 1.0 | +| 不与风载荷组合 | 1.0 | +| 短暂状况验算 | 1.0 | + +![](01K8T2JX33/images/b4ac23a5d051806636719e0e925fd3186c0ddd9db0c509a806b373bd246bfcc6.jpg) + +………(11) 式中: + +YEh、YEv 分别为水平和竖向地震作用的分项系数,按表17取值; +SEhk 、S Evk 分别为水平和竖向地震作用效应的标准值;塔架风载荷效应的组合值系数;塔架风载荷效应的分项系数;-塔架风载荷效应的标准值;风机运行载荷效应的组合值系数; +风机运行载荷效应的分项系数; +风机运行载荷效应的标准值。 + +表17 地震组合分项系数、组合值系数及承载力调整系数汇总表 + +| 永久载荷分项系数 | 可变载荷控制组合 | YG=1.2 | +| --- | --- | --- | +| 永久载荷对结构有利 | YG=1.0 | +| 可变载荷分项系数 | 风机运行载荷 | Y,=1.4 | +| 塔架风载荷 | Yw=1.4 | +| 地震作用分项系数 | 单独水平作用单独竖向作用 | YEh=1.3、YEv =0YEh=0、YEv =1.3 | +| 水平和竖向同时作用 | YEh =1.3、YEv =0.5 | +| 可变载荷组合值系数 | 风机运行载荷 | iE=0.7 | +| 塔架风载荷 | wE=0.2 | +| 承载力抗震调整系数 | 轴压比小于0.15 | YRE=0.75 | +| 轴压比不小于0.15 | YRE=0.8 | + +7324 正常使用极限状态下 混凝土塔架结构及构件应根据不同的设计要求 采用载荷效应的标准组合进行设计,设计值应按公式( )计算: + +![](01K8T2JX33/images/d737a397a359b3d6f2bff5adfe7fcbc08c5d76a03ef2379d3aedd16a421a6236.jpg) + +…( 12 ) + +混凝土塔架结构极限状态、载荷效应组合、计算内容、载荷工况和主要载荷应按表 的规定选用。 + +| 极限状态 | 载荷效应组合 | 设计内容 | 载荷工况 | 主要载荷 | +| --- | --- | --- | --- | --- | +| 永久载荷 | 可变载荷 | 地震作用 | +| 施工安装 | 正常运行 | 极端载荷 | 疲劳验算 | 多遇地震 | 罕遇地震 | 竖向载荷 | 预应力 | 基础变位 | 安装误差 | 正常工况载荷 | 极限工况载荷 | 疲劳工况载荷 | 风载荷 | 温度作用 | 多遇地震作用 | 罕遇地震 | +| 承载能力极限状态 | 基本组合 | 承载力计算 | | | : | | | | : | : | : | · | | · | | : | · | | | +| | | | : | | | : | : | : | : | | | : | : | : | | | +| 疲劳验算 | | | | | | | : | : | | | | | | : | | | | +| 承载力计算 | | | | | : | | : | : | | | : | | | · | | : | | +| 偶然组合 | 承载力验算 | | | | | | : | : | : | | | | | | : | | | : | +| | : | | | | | : | · | : | . | : | | | : | : | | | +| 正常使用极限状态 | 标准组合 | 应力、变形、抗裂性验算 | | | : | | | | : | · | · | : | | · | | : | : | | | + +# 74 承载能力极限状态计算 + +# 7.4.1 一般规定 + +7411 混凝土塔架由于设置悬挑平台、牛腿、挑梁、支撑托架等而受到局部载荷作用时,载荷组合和设计控制条件等应根据实际情况按 确定。 +7412 对混凝土塔架施加预应力时,所需的混凝土立方体抗压强度应经计算确定。 +7413 预应力钢筋的张拉控制应力、由预应力产生的混凝土法向应力及相应阶段预应力钢筋的应力等计算,应按 的规定。 + +# 742 预应力损失值计算 + +预应力混凝土塔架中预应力钢筋的应力损失主要包括张拉端锚具变形和预应力钢筋内缩、预应力钢筋的摩擦、预应力钢筋的应力松弛和混凝土的收缩和徐变等引起的预应力损失。预应力钢筋中的预应力损失值可按 的方法计算。 + +# 743 正截面抗压、抗弯承载力计算 + +7431 混凝土塔架正截面抗压、抗弯承载力应满足公式( )和公式( )设计表达式: + +![](01K8T2JX33/images/523cdd90122287d69f6a6765a2dea7961b30bbfa2feb0e32eb8db4baa3b164a2.jpg) + +…(13) …( 14) 式中: + +F 截面轴向力设计值; +Mxy 截面弯矩设计值;截面抗压承载力; +M. 截面抗弯承载力。 + +7432 混凝土塔架正截面抗压、抗弯承载力按照 的方法计算。 + +# 744 局部受压承载力分析 + +7441 预应力混凝土塔架端部锚固区局部压力设计值应满足公式( ): + +![](01K8T2JX33/images/6e1b4f1298dfed9f3d652ca047ff3767668503bca9554ee2818ce16a87b65c5a.jpg) + +·…(15 ) + +式中: + +F -端部锚固区局部受压面上作用的局部压力设计值; +FIR -端部锚固区局部受压承载力。 + +7442 预应力混凝土塔架端部锚固区局部受压承载力按照 的方法计算。 + +# 745 塔架截面抗震分析 + +7451 在抗震设防烈度为 度及以上的地区 混凝土塔架需进行多遇地震作用下的截面抗震承载力验算,塔架截面抗震验算应采用公式( )设计表达式: + +![](01K8T2JX33/images/55da435ff5a837b7963695dcaeed1165494d500aac622651fd97789a89549846.jpg) + +……( ) + +式中: + +S 塔架内力组合的设计值,包括组合的弯矩、轴向力和剪力设计值等; + +YRE 承载力抗震调整系数,按GB50011中选用; +R 塔架截面承载力设计值。 + +7452 混凝土塔架的地震作用效应和其他载荷效应的基本组合,应按公式( )计算: + +…( 17 )式中:-重力载荷分项系数,一般情况应采用1.2,当重力载荷效应对塔架截面承载力有利时,不应大于 ; + +YEh、YEv 分别为水平、竖直地震作用分项系数,应按表19采用;-塔架风载荷分项系数,可取1.4;风机运行载荷分项系数,可取1.4; +SGE 重力载荷代表值的效应,应按GB50011规定;水平地震作用标准值的效应,尚应乘以相应的增大系数或调整系数;-竖直地震作用标准值的效应,尚应乘以相应的增大系数或调整系数;-塔架风载荷标准值的效应; +Stk -风机运行载荷效应的标准值; +wE -塔架风载荷效应的组合值系数; +E -风机运行载荷效应的组合值系数。 + +表 19 地震作用分项系数 + +| 地震作用 | YEh | YEv | +| --- | --- | --- | +| 仅计算水平地震作用 | 1.3 | 0.0 | +| 仅计算竖直地震作用 | 0.0 | 1.3 | +| 同时计算水平与竖直地震作用(水平地震为主) | 1.3 | 0.5 | +| 同时计算水平与竖直地震作用(竖直地震为主) | 0.5 | 1.3 | + +7453 混凝土塔架截面抗震验算一般仅考虑水平地震作用,应按 采用振型分解反应谱法计算其水平地震作用和作用效应。 + +# 7.4.6 疲劳分析 + +混凝土塔架的疲劳分析可按 规范中的 累积损伤法或 规定的方法进行。 + +# 75 正常使用极限状态分析 + +# 7.5.1 裂缝分析 + +7511 结构构件正截面的受力裂缝控制等级划分及要求,应符合 的有关规定。 + +7512 一级裂缝控制等级的混凝土段,在载荷标准组合下,受拉边缘应力应符合公式( )规定: + +![](01K8T2JX33/images/ee571517440df25ac47a34532277c8e982b1f35cc353278162d97d376d6ca325.jpg) + +……( 18) + +式中: + +Oc 抗裂验算边缘的混凝土法向应力; + +Opc 扣除全部预应力损失后在抗裂验算边缘混凝土的预压应力。 + +7513 二级裂缝控制等级的混凝土段,在载荷标准组合下,受拉边缘应力应符合公式( )规定: + +![](01K8T2JX33/images/3ffada3ad9f0b6413af8dab9510cd63e9df0e2f80a6882a66e4390c17aa9e8ea.jpg) + +……( 19 ) + +# 7.5.2 应力分析 + +7521 对于正常使用极限状态,应对正截面的压应力进行验算,压应力(σ')应符合公式( )规定: + +![](01K8T2JX33/images/e0c2a31438aa93145bf5b8946a03c158083401dfe8f34306fcd6ff4dc36de6da.jpg) + +522 在载荷标准组合下,应分别对截面上混凝土的主拉应力和主压应力进行验算: + +) 对于一级裂缝控制等级构件,混凝土主拉应力应符合公式( )规定: + +……( 21 ) + +) 对于一、二级裂缝控制等级构件,混凝土主压应力应符合公式( )规定: + +![](01K8T2JX33/images/ff8584018ef04808f3d48a85a6ebbe52a1ab66ec2ff1121572315780cfd479ab.jpg) + +·…( 22 ) + +式中: -塔架截面主拉应力、主压应力,按照GB50010确定。 + +7523 在载荷标准组合下,预应力混凝土塔架正截面的应力应按照e r 和e r (图 )两种偏心情况计算。 + +![](01K8T2JX33/images/100d8481a47e6302cb8683a27a56669dca6077f880e9a912a71339aae255d05e.jpg) +图 16 水平截面在载荷标准组合作用下的计算 + +) 轴向力和截面圆心的偏心距 e 和截面核心距 r 应分别按公式( )和公式( )计算: + +![](01K8T2JX33/images/987259463fe6ff0926a76b1d2386b6af180e2e318774cca2cac6d3f73d2cd872.jpg) + +………( ) + +式中: + +Nk、Mk 载荷效应标准组合下的截面轴力和弯矩; +a 截面形心轴至圆心轴的距离,a=rm ;有效预应力,预应力钢筋对构件产生的轴向力。 + +) 受压区的混凝土应力(σ')应按公式( ) 公式( )计算: + +![](01K8T2JX33/images/0c68779bf235b28a7aedf0762ea932f0efc1a19d5687b49b31ec5ab10eeb4a68.jpg) + +式中: + +塔架截面的换算面面积; +Whs、Whp 塔架水平截面的特征系数;钢筋、预应力钢筋和混凝土弹性模量之比;非预应力纵筋和预应力钢筋配筋率; +截面受压区半角,对于大偏心受压情况 e 大于 r ,按公式( )计算; +0、0 塔架截面受压、受拉区的孔洞半角,单位为弧度(rad);当无孔洞时,取0。 + +) 受拉区的混凝土应力(σ )应按公式( )计算: + +![](01K8T2JX33/images/4d5fb243d1a20564bebb53dd02fcf3ec7130628a58e46edce6e154ef73846973.jpg) + +……(33) + +# 76 构造规定 + +# 761 混凝土筒节壁厚与直径 + +7.6.1.1 混凝土筒节的最小壁厚 t (mm)可按公式(34)计算,但不应小于180mm: + +![](01K8T2JX33/images/1c07921cdcf5aa1e17e6fb81359d49432544b23a69517cb9ad411206020426fe.jpg) + +·…( 34 ) + +式中: + +d——混凝土筒节外直径。 + +7612 塔架外表面沿高度坡度可连续变化,也可分段采用不同的坡度。 + +# 762 混凝土保护层 + +7621 预应力混凝土塔架中的普通钢筋及预应力钢筋的混凝土保护层厚度应满足下列要求:46 + +受力钢筋的混凝土保护层厚度不应小于钢筋的公称直径;最外层钢筋的保护层厚度(c)应符合表 的规定。环境类别按 的规定选取。 + +# 表 20 混凝土保护层最小厚度 c + +单位为毫米 + +| 环境类别 | | 二a | 二b | 三a | 三b | +| --- | --- | --- | --- | --- | --- | +| C | 15 | 20 | 25 | 30 | 40 | + +7622 当结构中纵向受力钢筋的保护层厚度大于 时,宜对保护层采取有效的构造措施。当在保护层内配置防裂、防剥落的钢筋网片时,网片钢筋的保护层厚度不应小于 。 + +# 763 钢筋 + +7631 混凝土塔架应配置双排纵向钢筋和双层环向钢筋,且纵向普通钢筋宜采用带肋钢筋,其最小配筋率应符合表 的规定。在后张法预应力塔架中,应配置适当的非预应力构造钢筋,如有较多的非预应力受力钢筋,则可代替构造钢筋。 + +# 表 21 环向钢筋的最小配筋率 + +% + +| 塔架配筋类别 | 最小配筋百分率(p min) | +| --- | --- | +| 纵向钢筋 | 外排 | 0.25 | +| 内排 | 0.20 | +| 环向钢筋 | 外排 | 0.20 | +| 内排 | 0.20 | + +7632 纵向钢筋和环向钢筋的最小直径和最大间距应符合表 的规定。 + +# 表 22 钢筋最小直径和钢筋最大间距 + +单位为毫米 + +| 配筋类别 | 钢筋最小直径 | 钢筋最大间距 | +| --- | --- | --- | +| 纵向钢筋 | 10 | 外侧 250,内侧 300 | +| 环向钢筋 | 8 | 250,且不大于塔架壁厚 | + +# 764 预应力钢筋 + +7641 先张法预应力钢筋之间的净间距不宜小于其公称直径的 倍和混凝土粗骨料最大粒径的倍,且应符合下列规定:预应力钢丝,不应小于 ;三股钢绞线,不应小于 ;七股钢绞线,不应小于 。当混凝土振捣密实性具有可靠保证时,净间距可放宽为最大粗骨料粒径的 倍。7.6.4.2 先张法预应力钢筋混凝土塔架端部宜采用下列构造措施:单根配置的预应力钢筋,其端部宜设置螺旋筋;分散布置的多根预应力钢筋,在结构端部 d 且不小于 长度范围内,宜设置 片片与预应力钢筋垂直的钢筋网片,此处 d 为预应力钢筋的公称直径。7643 后张法预应力钢筋预留孔道布置应符合下列构造规定:混凝土塔架中预留孔道之间的净间距不宜小于 ,且不宜小于粗骨料粒径的 倍;孔道至结构边缘的净间距不宜小于 ,且不宜小于孔道直径的 ;预留孔道的内径宜比预应力束外径及需穿过孔道的连接器外径大 ,且孔道的截面积宜为穿入预应力束截面积的 倍 倍7644 在后张法有黏结预应力混凝土塔架两端及中部应设置灌浆孔,间距不宜大于 。孔道灌浆应密实 水泥浆强度等级不应低于 其水灰比宜为 宜渗入 水泥用量的铝粉 筒壁端部应设排气孔。7645 配置钢丝、钢绞线的后张法预应力筒壁的端部,在预应力钢筋的锚具下和张拉设备的支撑处应进行局部加强,一般附加横向钢筋网或螺旋式钢筋,其配筋数量由计算确定,应根据 中的相应条款计算,且体积配筋率不应小于 ,必要时构件端部锚固区的混凝土截面应适当加大。7646 后张法预应力钢筋所用的锚具、夹具和连接器等的形式和质量应符合相关标准要求。7647 预应力混凝土塔架端部尺寸应考虑锚具的布置、张拉设备的尺寸和局部受压要求,必要时应适当加大。7648 后张法预应力混凝土塔架外露金属锚具,应采取可靠的防腐及防火措施,并应符合的规定。 + +# 765 孔洞布置与配筋 + +7651 混凝土塔架的筒壁上设有孔洞时,应符合下列规定: + +筒壁上的孔洞应规整,同一截面上开多个孔洞时,其圆心角总和不应超过 ,单个孔洞的圆心角不应大于 ; +孔洞宜设计成圆形,矩形孔洞的转角宜设计成弧形。 + +7652 孔洞处应设置补强钢筋,补强钢筋伸过孔洞边缘的长度不应小于 倍钢筋直径。 + +# 766 抗震构造要求 + +7661 预应力混凝土塔架可用于抗震设防烈度 度区、 度区和 度区,当在 度区应用预应力混凝土塔架时,应进行专门研究。 +7662 预应力混凝土塔架的抗震构造措施可参考 中的规定。 +7663 当选用 中的多遇地震、设防地震和罕遇地震的地震作用校核结构抗震承载力满足要求时,可不采取抗震构造措施。 + +# 767 连接节点构造要求 + +7671 混凝土构件的形状、尺寸和重量应满足制作、运输、安装各环节的要求。 + +7672 预制预应力混凝土塔架中,接缝及节点应进行承载能力极限状态及正常使用极限状态设计,并应符合国家现行相关标准。 + +7673 预制预应力混凝土塔架的拼接应符合下列规定: + +预制混凝土塔架拼接部位的混凝土强度等级不应低于塔架主体的混凝土强度等级; +拼接位置宜设置在受力较小部位,拼接构造应满足结构传递内力的要求; + +预制混凝土塔架的拼接应考虑温度作用和混凝土收缩徐变的不利影响,宜适当增加构造配筋。7674 节点及接缝处的纵向钢筋连接应根据接头受力、施工工艺等要求选用套筒灌浆连接、机械连接、浆锚搭接连接、焊接连接、绑扎连接等连接方式,并应符合国家现行有关标准的规定。 + +# 768 过渡段设计与施工 + +7681 过渡段应进行承载能力极限状态、正常使用极限状态设计,并进行疲劳分析。 +7682 过渡段顶面平整度应符合相关标准要求。 +7.6.8.3 过渡段构造应满足结构传递内力要求,宜适当增加构造配筋。 +7.6.8.4 过渡段预制前应对过渡垫板顶部的平整度按照设计要求进行检查 +7685 过渡段采用现浇形式时,过渡垫板与过渡段之间的连接采用灌浆料,灌浆料强度不应低于塔架 +主体的混凝土强度。 + +# 77 混凝土构件预制 + +# 7.7.1 一般规定 + +7711 原材料进场时,应按 、 / 和 的规定进行检验,合格后方可使用。 +7712 钢筋的品种 级别 规格 数量和保护层厚度应符合设计要求 +7713 钢筋下料时 应采用砂轮锯或切断机切断 不应采用电弧切割 +7714 混凝土强度等级应符合设计要求。 +7.7.1.5 模板、台座应满足强度、刚度和稳定性要求。 +7716 模具几何尺寸应准确,安装应牢固,密封应严密。 +7717 模具、台座应保持清洁,隔离剂应涂刷均匀。 +7718 混凝土构件预制前应编制生产方案,生产方案宜包括生产计划及生产工艺,模具方案、技术质 +量控制措施、成品存贮,运输和养护方案等。 +7719 预埋件加工制造应符合设计要求,并应符合 的有关规定。 +77110 预制构件用混凝土原材料及配合比应符合 、 和 / 的相关规定。 +77111 预制构件的钢筋加工、连接与安装应符合 和 的有关规定。 + +# 772 钢筋工程 + +7721 钢筋接头的方式、位置,同一截面受力钢筋的接头百分率,钢筋的搭接长度及锚固长度等应符合 的规定和设计要求。 +7722 钢筋表面不应有油污及锈蚀。 +7723 钢筋加工形状、尺寸应符合设计要求,其允许偏差应符合表 的规定。 + +表 23 钢筋加工工程质量标准和检验方法 + +| 序号 | 检查项目 | 允许偏差/mm | 检验方法及工具 | +| --- | --- | --- | --- | +| 1 | 钢筋加工偏差 | 受力钢筋顺长度方向全长的净尺寸 | ±10 | 钢尺检查 | +| 2 | 弯起钢筋的弯折位置 | ±20 | 钢尺检查 | +| 3 | 箍筋内径尺寸 | ±5 | 钢尺检查 | + +# 7724 钢筋安装的允许偏差应符合表 的规定。 + +表 24 钢筋安装工程质量标准和检验方法 + +| 序号 | 检查项目 | 允许偏差/mm | 检验方法及工具 | +| --- | --- | --- | --- | +| 1 | 钢筋网 | 网眼尺寸偏差 | ±20 | 钢尺检查,尺量连续三挡,取最大值 | +| 网片尺寸偏差 | ±10 | 钢尺检查 | +| 2 | 钢筋骨架 | 长度偏差 | ±10 | 钢尺检查 | +| 宽、高度偏差 | ±5 | 钢尺检查 | +| 3 | 受力钢筋 | 间距偏差 | ±10 | 钢尺量两端、中间各一点,取最大值 | +| 排距偏差 | ±5 | +| 保护层厚度偏差 | ±3 | 钢尺检查 | +| 4 | 箍筋、横向钢筋间距偏差 | ±20 | 钢尺检查,尺量连续三挡,取最大值 | +| 5 | 钢筋弯起点位置 | ≤20 | 钢尺检查 | +| 6 | 插筋 | 中心位置 | ≤5 | 钢尺检查 | +| 外露长度偏差 | +10 | 钢尺检查 | + +7725 钢筋网片放置于模板内之后,应对钢筋间距及保护层厚度进行调整,钢筋间距及保护层厚度应符合设计要求。 + +# 773 模具与模板工程 + +7731 模具与模板工程应编制专项方案。 +7732 模具、模板及支架应根据施工过程中的各种工况进行设计,应具有足够的承载力和刚度,并应保证其整体稳定性。 +7733 模具、模板及支架应保证混凝土构件的形状、尺寸和位置准确,模板安装应牢固、严密、不漏浆,且应便于钢筋安装和混凝土浇筑、养护。 +7734 模板与混凝土的接触面应平整,并应具有良好的耐磨性和硬度。 +7735 模板拆除后应将其表面清理干净,对变形和损伤部位应进行修复。 +7.7.3.6 模具安装与钢筋绑扎配合进行,预制构件顶部端模宜在钢筋绑扎完毕后安装。 +7737 模板安装应保证混凝土构件各部分形状、尺寸和相对位置准确,并应防止漏浆。 +7.7.3.8 模板安装后应检查尺寸偏差。固定在模具上的预埋件应检查其数量和尺寸。 +7739 模具、模板应根据预制构件及拼装施工要求预留孔洞、吊件或固定件,预留位置应符合设计或施工方案的要求。 +77310 预制构件接缝处宜采用与预制构件可靠连接的定型模板。定型模板与预制构件之间应粘贴密封条,在混凝土浇筑时节点处模板不应产生明显变形和漏浆。 + +# 774 混凝土工程 + +7741 混凝土结构施工宜采用预拌混凝土。7742 混凝土制备应符合下列规定:预拌混凝土应符合 / 的有关规定;现场搅拌混凝土宜采用具有自动计量装置的设备集中搅拌。7743 混凝土原材料的主要技术指标应符合 的规定。7744 水泥的选用应符合下列规定。水泥品种与强度等级应根据设计、施工要求,以及工程所处环境条件确定。有抗渗、抗冻融要求的混凝土,宜选用硅酸盐水泥或普通硅酸盐水泥。配置高强混凝土宜选用强度等级不低于 级的硅酸盐水泥或普通硅酸盐水泥。水泥应符合 的规定。同一工程的水泥宜采用同一厂家、同一品种、同一强度等级。水泥不应采用结块的水泥 也不宜采用出厂超过 个月的水泥水泥中氯离子含量不应大于 ,碱含量低于 。7745 粉煤灰:混凝土宜采用 级粉煤灰。 + +7746 细骨料:砂宜采用细度模数为 的 区中砂,砂的含泥量和泥块含量应分别不大于 +2.0%和0.5%。 + +7747 粗骨料的要求如下: + +岩石抗压强度应比混凝土强度等级标准值高 ; +粗骨料应采用连续级配,最大粒径不宜超过壁厚的 / ,且不宜超过 ;粗骨料宜采用二级级配; +粗骨料的含泥量不应大于 ,泥块含量不应大于 ; +粗骨料的针片状颗粒含量不宜大于 ,且不应大于 。 + +7748 外加剂的要求如下:应符合 和 的规定;应与水泥和矿物掺合料有良好的适应性,并应经试验验证。7749 水的要求如下: + +混凝土拌合用水和养护用水应符合现行行业 的规定; +混凝土搅拌与运输设备洗刷水不宜用于混凝土; +混凝土拌合用水氯离子含量不应超过350m/L。 + +77410 混凝土配置强度的确定:) 当设计强度等级低于 时,配置强度应按照公式( )确定: + +![](01K8T2JX33/images/4f04faea56303f28fe6127c7733567f8a980d2c2c45689afb39e9dc0ec000792.jpg) + +·…(35 ) + +b) 当设计强度等级不小于 C60时,配置强度应按照公式(36)确定: + +![](01K8T2JX33/images/e11df091fb75b5d7ec93ab69cc840dd7834f4a83fb2802e7f3fc26a39f565346.jpg) + +……( 36 )式中:-混凝土配置强度,单位为兆帕(MPa);-混凝土立方体抗压强度标准值,单位为兆帕(MPa);0 混凝土强度标准差,单位为兆帕( ),应按照 确定。77411 混凝土计量与搅拌: + +原材料计量应采用电子计量设备,其精度应满足 / 的要求。每一工作班开始前,应对计量设备进行零点校准; +各种计量器具、计量设备应在检定的周期内,搅拌设备运行正常;严格按照设计的混凝土配合比计量搅拌混凝土,确保混凝土质量; +原材料计量过程中,应根据粗、细骨料的含水率的变化及时调整水和粗、细骨料的称量; + +搅拌应保证预拌混凝土拌合物质量均匀,同一盘混凝土的搅拌匀质性应符合 的有关规定。77412 运输要求如下。 + +运输混凝土的搅拌车应符合 / 的规定,翻斗车应仅限于现场运送坍落度小于的混凝土拌合物。 +搅拌运输车装料前,搅拌罐内应无积水或积浆。 +搅拌罐车到达浇筑现场时,应使搅拌罐高速旋转 后再将混凝土拌合物卸出。如混凝土拌合物因稠度原因出罐困难时,可加入适量减水剂(应记录加入减水剂的情况),并使搅拌罐高速旋转不小于 后,将混凝土拌合物卸出。外加剂掺量应有经试验确定的预案。混凝土从搅拌机卸入搅拌运输车至卸料时的运输时间不宜大于 ;当采用翻斗车时,运输时间不宜大于 ;运输应保证混凝土浇筑的连续性。 + +77413 浇筑要求如下。 + +浇筑混凝土前,应检查模板支撑的稳定以及接缝的密合情况,并应保证模板在混凝土浇筑过程中不失稳、不跑模和不漏浆;天气炎热时,宜采取遮挡措施避免阳光照射金属模板,或从金属模板外侧进行浇水降温。 +当暑期施工时,混凝土拌合物入模温度不应高于 ,宜选择温度较低时段浇筑混凝土;当冬期施工时,高强混凝土拌合物入模温度不应低于 ,并应有保温措施。 +泵送设备和管道的选择、布置及其泵送操作按 / 的有关规定执行。 +当缺乏高强混凝土泵送经验时,施工前宜进行高强混凝土试泵。 +当混凝土自由倾落高度大于 ,且结构配筋较密时,宜采用导管等辅助设备。 +混凝土浇筑的分层厚度不宜大于 ,上下层同一位置浇筑的间隔时间不宜超过120min。 +混凝土施工过程中应有防风措施;当室外气温低于 时,不应浇筑混凝土。 +混凝土拌合物从搅拌机卸出后到浇筑完毕的延续时间不宜超过表 的规定。 + +# 表25 混凝土拌合物从搅拌机卸出后到浇筑完毕的延续时间 + +单位为分77414 混凝土振捣要求如下:混凝土振捣应能使模具内各个部位混凝土密实均匀,不应漏振、欠振、过振;混凝土振捣采用插入式振捣棒或附着式振捣器 必要时可采用人工辅助振捣77415 振动棒振捣混凝土应符合下列规定:应按分层浇筑厚度分别进行振捣,振捣棒的前端应插入前一层混凝土中,插入深度不应小于50mm;振捣棒应垂直于混凝土表面并快插慢拔均匀振捣;当混凝土表面无明显塌陷、有水泥浆出现、不再冒气泡时,应结束该部位振捣;振捣插入点距离不应大于振捣棒的作用半径的 倍。77416 附着式振捣器振捣混凝土应符合下列规定: + +| 混凝土施工情况 | 气温 | +| --- | --- | +| ≤25℃ | >25°℃ | +| 泵送混凝土 | 150 | 120 | +| 非泵送混凝土 | 施工现场 | 120 | 90 | +| 预制场 | 60 | 45 | + +附着式振捣器应与模具紧密连接,设置间距应通过试验确定; +附着式振捣器应根据混凝土浇筑高度和浇筑速度,依次从下往上振捣; +模具上同时使用多台附着式振捣器时,应使各振捣器的频率一致,并应交错设置在相对面的模具上。 + +77417 钢筋密集区域,应选择小型振捣棒辅助振捣、加密振捣点,并应适当延长振捣时间。77418 用于检查混凝土质量的试件,应在混凝土的浇筑地点随机抽取,取样与试件留置为: 标准养护强度试件和同条件养护试件每工作班 试生产或生产量不大时宜每天每个塔架构件 各留置 组个 试件,所有试件拆模前随塔架构件一起同条件养护,拆模后应按规定在实验室进行标准养护 若冬季施工混凝土强度试件的留置除应符合 的规定外 还应增设与塔架同条件养护试件不少于两组。同条件养护试件应在解冻后进行试验。 + +# 775 混凝土构件养护 + +7751 混凝土浇筑后应及时进行保湿养护,保湿养护可采用洒水、覆盖、喷涂养护剂等方式。养护方式应根据现场条件、环境温度湿度、塔架构件特点、技术要求、施工操作等因素确定。7752 混凝土养护时间应符合下列规定: + +采用硅酸盐水泥、普通硅酸盐水泥配制的混凝土不应少于 ,且强度不低于 ; +抗渗混凝土、强度等级 及以上的混凝土,不应少于 。 + +7753 洒水养护应符合下列规定: + +洒水养护宜在混凝土裸露表面覆盖麻袋或草帘后进行,也可采用直接洒水、蓄水养护方式;洒水养护应保证混凝土表面处于湿润状态; +当日最低温度低于 时,不应采用洒水养护。 + +7754 覆盖养护应符合下列规定: 覆盖养护宜在混凝土裸露表面覆盖塑料薄膜、塑料薄膜加麻袋、塑料薄膜加草帘进行; 塑料薄膜应紧贴混凝土裸露表面,塑料薄膜内部应保持有凝结水; 覆盖物应严密 覆盖物的层数应按照施工方案确定 7755 喷涂养护剂养护应符合下列规定: + +应在混凝土裸露表面喷涂覆盖致密的养护剂进行养护; +养护剂应均匀喷涂在结构表面,不应漏喷;养护剂应具有可靠的保湿效果,保湿效果可通过试验检验; +养护剂使用方法应符合产品说明书的有关要求 + +7756 当冬期施工时,高强混凝土养护应符合下列规定: + +宜采用带模养护; +混凝土受冻前的强度不应低于 ; +模板和保温层应在混凝土冷却到 以下再拆除,或在混凝土表面温度与外界温度相差不大于 时再拆除,拆模后的混凝土应及时覆盖; +混凝土强度达到设计强度等级标准值的 时,可撤除养护措施; +同条件养护试块的养护条件与实体结构部位的养护条件相同,并应妥善保管。 + +7757 蒸汽养护应符合下列规定: + +静停期间应保持环境温度不低于 ,灌筑结束 且混凝土终凝后方可升温; +升温速度不宜大于 / ; +恒温期间混凝土内部温度不宜超过 ,恒温养护时间应根据构件脱模强度要求、混凝土配合比情况以及环境条件等通过试验确定; +降温速度不宜大于 / 。 + +# 776 混凝土构件储存与厂内运输 + +7761 制定混凝土构件和混凝土筒节的运输与堆放方案,其内容应包括运输时间、次序、堆放场地、运输路线 规定要求 堆放支垫及成品保护措施等 对于混凝土构件和混凝土筒节超高 超宽的运输和堆放应有专门的质量安全保证措施。7762 运输车辆应满足构件的尺寸和载重要求,装卸和运输时应符合下列要求: + +装卸时,应采取保证车体平衡的措施; +运输时,应采取防止被运输物移动、倾倒、变形等的固定措施; +运输时,应采取防止被运输物损坏的措施,被运输物的边角部位或链索接触处的混凝土,宜设置保护衬垫。 + +7763 混凝土构件和混凝土筒节堆放应符合下列要求: + +堆放场地应平整、坚实,并应有排水措施; +预埋吊件应朝上,标识宜朝向堆垛间的通道; +重叠堆放时,混凝土构件或混凝土筒节之间设置一定数量的垫块; +支垫应坚实。 + +# 7.7.7 质量检查 + +7771 混凝土构件缺陷可分为尺寸偏差缺陷和外观缺陷。尺寸偏差缺陷和外观偏差缺陷可分为一般缺陷和严重缺陷。混凝土构件结构尺寸偏差超过规范规定,但尺寸偏差对结构性能和使用功能未构成影响时,应属于一般缺陷;而尺寸偏差对结构性能和使用功能构成影响时,应属于严重缺陷。混凝土构件的外观质量不应有严重缺陷,且不宜有一般缺陷,对已经出现的一般缺陷,应按技术方案进行处理,并应重新检验。外观质量缺陷等级划分应符合表 的规定。7772 混凝土筒节的外观质量允许尺寸偏差及检验方法应符合表 的规定。 + +表 26 混凝土构件外观质量缺陷等级划分 + +| 缺陷 | 缺陷描述 | 严重缺陷 | 一般缺陷 | +| --- | --- | --- | --- | +| 露筋 | 构件内钢筋未被混凝土包裹而外露 | 纵向受力钢筋有露筋 | 其他钢筋有少量露筋 | +| 蜂窝 | 混凝土表面缺少水泥砂浆而形成石子外露 | 构件主要受力部位有蜂窝 | 其他部位有少量蜂窝 | +| 孔洞 | 混凝土中孔穴深度和长度均超过保护层厚度 | 构件主要受力部位有孔洞;混凝土内孔穴深度和长度均超过保护层厚度 | 其他部位有少量孔洞 | + +# 表27 混凝土筒节外观及尺寸偏差质量标准和检验 + +| 序号 | 检查项目 | 质量标准 | 检验方法及器具 | +| --- | --- | --- | --- | +| 1 | 外观质量 | 不宜有一般缺陷 | 观察,检查处理记录 | +| 2 | 粗糙面质量 | 混凝土筒节顶面不宜有浮浆、松动的石子 | 观察检查 | +| 3 | 筒壁厚度偏差 | ±5mm | 钢尺检查 | +| 4 | 混凝土筒节直径 | ±6 mm | 钢尺检查 | +| 5 | 预留门洞中心线 | ≤5 mm | 经纬仪和钢尺检查 | + +7773 混凝土筒节进场时应对构件主要受力钢筋数量、规格、间距及混凝土强度等验收记录或强度检测报告进行检验。 + +7774 混凝土构件应按设计要求和 的有关规定进行结构性能检验。 + +7775 混凝土构件外观一般缺陷修整应符合下列规定: + +露筋、蜂窝、孔洞、夹渣、疏松、外观缺陷,应凿除胶结不牢固部分混凝土,应清理表面,洒水湿润后应用高强灌浆料抹平或环氧修补料填充; +应封闭裂缝。 + +7776 混凝土构件外观严重缺陷修整应符合下列规定: + +露筋、蜂窝、孔洞、夹渣、疏松、外表缺陷,应凿除胶结不牢固的部分混凝土至密实部位,清理表面,支设模板洒水湿润,涂抹混凝土界面剂,应采用不低于构件本体强度等级的灌浆料浇筑密 + +实,养护时间不应小于 ; + +—严重缺陷应会同设计单位共同制定专项修整方案,结构修整后应重新检查验收,若修整后无法满足设计要求,应予以报废处理。 + +# 78 混凝土塔架吊装工程 + +# 7.8.1 一般规定 + +7811 混凝土构件应根据生产加工图纸进行加工制作,生产加工图纸应能满足构件预留孔洞、预埋件设置、制作、运输、吊装等各环节的综合要求。 + +7812 混凝土构件生产单位应提供构件质量证明文件。 + +7813 混凝土构件应具有生产企业名称、生产日期、规格、编号等出厂信息标识,标识应设置在施工现场易于识别的位置。 + +7814 混凝土构件进入现场应进行质量检查,不合格的构件不应使用。 + +7815 混凝土构件和混凝土筒节运输和吊装应采取保护措施,并应编制运输和吊装方案。 + +7816 混凝土构件和混凝土筒节拼装现场应建立精密测量控制网,构件上应设置测量控制点。 + +7817 混凝土构件拼装前,防水密封材料的粘贴验收合格。 + +# 782 运输与存放 + +7821 混凝土构件和混凝土筒节运输应有稳定的支撑及固定措施,应考虑道路、桥梁承载能力,并综合考虑道路净空和宽度。 + +7822 对混凝土构件和混凝土筒节边角部或吊索接触处的混凝土,宜采用垫衬加以保护。 + +7823 混凝土构件和混凝土筒节应进行运输过程中的稳定性和强度分析。 + +824 混凝土构件和混凝土筒节应进行翻转、吊装工况的验算,必要时应采取防护措施。 + +7825 混凝土构件和混凝土筒节堆放时 每层间应设置橡胶垫或垫块 橡胶垫或垫块宜在同一垂直线上。 + +# 783 混凝土筒节拼装与吊装 + +7831 混凝土构件拼装应按设计要求进行,拼装前应进行下列准备工作: + +应确认混凝土构件各部位尺寸偏差符合设计要求; +宜用吹风机或高压水枪对其各部位进行清扫; +混凝土构件拼装用材料及工具应准备齐全。 + +7832 混凝土筒节竖向接缝部位,应使用密封材料对混凝土筒节竖向接缝进行密封处理。 + +7833 混凝土构件拼装应按设计要求进行,当混凝土构件表面出现缺棱掉角、混凝土剥落、宽度大于的裂缝或厚度方向贯穿性裂缝等缺陷时,应进行修补。构件修补时,应分析破损原因及程度,制定修补方案。修补材料强度不应低于混凝土强度。 + +7834 拼环后的混凝土筒节上口、下口尺寸应符合设计要求。采用体内索时,拼装后的预应力孔道在径向、环向尺寸应符合设计要求。 + +7835 混凝土筒节竖向接缝应满足设计强度,水平接缝连接应紧密牢靠并采取密封处理。 + +7836 存在下列情况不宜进行混凝土筒节安装作业: + +阵风风力六级(风速10m/s)以上; + +二级或二级以上高温作业; +作业场地有冰、雪、霜、水、油等易滑物; +其他对施工安全和施工质量存在较大隐患情况。 + +# 784 混凝土塔架防腐 + +7841 混凝土塔架应根据所处环境进行防腐蚀耐久性设计,保证其在设计使用年限内的安全和正常使用。 +7842 混凝土塔架防腐蚀耐久性设计,应针对结构预定功能和所处环境条件,选择合理的结构构造和抗腐蚀性、抗渗性良好的优质混凝土,同时采用特殊防腐蚀措施。 +7843 预埋件暴露在空气中的表面应进行防腐处理。 +7844 防腐蚀材料的施工符合 和 / 的规定。 + +# 785 质量检查与控制措施 + +7851 混凝土构件拼装成混凝土筒节后竖向接缝错边不应大于 混凝土筒节吊装就位后 其与上下相邻筒节的水平接缝错边不应大于 。每段混凝土筒节的竖向接缝错边检查数量不宜少于个点,水平接缝错边检查数量至少 个点。 +7852 吊装过程中的配件、连接件、配套材料的性能,应符合设计文件及相关标准的规定。 +7853 吊装完成后,应做好记录,并进行检验,其质量应符合表 规定。塔架中心线垂直度的测定工作,应在风载荷和日照温差较小的情况下进行。 + +表 28 混凝土塔架吊装允许偏差 + +| 类别 | 序号 | 检查项目 | 质量标准 | 单位 | +| --- | --- | --- | --- | --- | +| 主控项目 | 1 | 混凝土强度评定结合试块组数 | 应符合GB 50204 的规定 | | +| 2 | 横向接缝材料 | 应符合设计相关规定 | | +| 一般项目 | 1 | 外观质量 | 不宜有一般缺陷 | | +| 2 | 筒壁厚度偏差 | ±20 | mm | +| 3 | 塔架 | 高度偏差 | ±1/1 000H | mm | +| 中心线垂直度偏差 | ±1/1 000H | mm | +| 注:H- 混凝土段总高度。 | + +# 7.9 预应力工程 + +# 7.9.1 一般规定 + +7911 混凝土塔架应对预应力张拉过程进行分析。7.9.1.2 锚具及预应力体系应根据结构类型进行专项设计。7913 预应力钢筋应分类、分规格装运和堆放。堆放时应符合下列要求:在室外存放时不应直接堆放在地面上,应垫枕木并用防水布覆盖;长期存放时应置于仓库内,仓库应干燥、防潮、通风良好、无腐蚀气体和介质;在潮湿环境中存放 宜采用防锈包装产品 防潮纸内包装 涂敷水溶性防锈材料等 + +预应力钢筋盘卷存放时,应确保其盘径不致过小而影响预应力钢筋的力学性能; +无粘结预应力钢筋存放时,严禁放置在受热影响的场所,且不应直接堆放在地面上。 + +预应力钢筋在制造和安装过程中,应采取措施预防腐蚀、受热、磨损和其他伤害。 + +7914 预应力钢筋安装前应对索体或组件的所有损伤进行鉴定和修复。损坏的预应力钢筋均应更换。 +7915 预应力钢筋的工作温度环境、施工温度环境、预应力钢筋的布置及保护、张拉端的设置、张拉方法等应符合国家现行预应力标准的规定。 +7916 预应力施工的方法及措施应满足国家现行规范、标准、法规、安全和绿色施工的要求。7917 对特种材料、特种施工工艺或设计有特殊要求的预应力结构,应制定专门的施工安装方案。 + +# 792 材料与锚具系统 + +7921 制作预应力钢筋的钢绞线性能应符合 的规定 常用预应力钢筋的尺寸及允许偏差应符合表 的规定。 + +表 29 1×7结构钢绞线尺寸及允许偏差 + +| 钢绞线结构 | 公称直径mm | 直径允许偏差mm | 钢绞线公称截面积mm² | 每米钢绞线参考质量g/m | 中心钢丝直径加大范围% | +| --- | --- | --- | --- | --- | --- | +| 1×7 | 9.50(9.53) | +0.30-0.15 | 54.8 | 430 | ≥2.5 | +| 11.10(11.11) | 74.2 | 582 | +| 12.70 | | 98.7 | 775 | +| 15.20(15.24) | 140 | 1101 | +| 15.70 | +0.40-0.15 | 150 | 1 178 | +| 17.80(17.78) | 191(189.7) | 1 500 | +| 18.90 | | 220 | 1727 | +| 21.60 | | 285 | 2237 | +| 1×7I | 12.70 | +0.40-0.15 | 98.7 | 775 | +| 15.20(15.24) | 140 | 1 101 | +| (1×7)C | 12.70 | | 112 | 890 | +| 15.20(15.24) | +0.40-0.15 | 165 | 1295 | +| 18.00 | | 223 | 1750 | + +7922 预应力钢筋、无粘结预应力钢筋的外包层材料性能及涂包质量应符合 / 的规定,防腐油脂质量应符合 / 的规定。 + +7923 预应力钢筋用锚具应符合 / 及 的有关规定。 + +7924 锚具的静载锚固性能,应由预应力钢筋 锚具组装件静载试验测定的锚具效率系数和达到实测极限拉力时组装件受力长度的总应变确定 锚具的静载锚固性能应同时满足以下两项要求 锚具效率系数不小于 ,试件破断时的总应变不小于 。 + +7925 预应力钢筋锚具组装件除应满足静载锚固性能要求外,尚应满足循环次数为 万次的疲劳性能试验要求。疲劳应力上限:对钢丝、钢绞线应为抗拉强度标准值的 。 + +# 793 制作与安装 + +7931 预应力钢筋下料应选用长度适宜的平整 清洁场地 制作好的预应力钢筋应做好标识分类堆放,并做好防锈、防污染、防破损措施,以确保使用前的预应力钢筋处于完好状态。 + +7932 穿束方法可采用人工单根穿束、专用机械穿束及吊车集束穿束,施工单位需要根据现场情况确定穿束方法。 + +7933 后张法有粘结预应力钢筋穿入孔道及其防护,应符合下列规定。 + +预应力钢筋穿入孔道后至孔道灌浆的时间间隔不宜过长,当环境相对湿度大于 或处于近海环境时,不宜超过 ;当环境相对湿度不大于 时,不宜超过 。当不能满足前一项的规定时,宜对预应力钢筋采取防锈措施。 + +# 794 预应力张拉 + +7941 混凝土塔架预应力钢筋张拉前应编制专项施工方案 确定张拉顺序 张拉设备 张拉程序 人员组织 张拉平台布置 安全措施等内容7942 预应力钢筋张拉设备应由专人使用和管理,并应定期维护和校验。张拉设备的校验应符合下列规定。 + +张拉设备应配套校验。压力表的精度不应低于 级;校验张拉设备用的试验机或测力设备测力示值的不确定度不应大于 ;校验时千斤顶活塞的运行方向,应与实际张拉工作状态一致。 +张拉设备的校验期限,不应超过半年。当张拉设备出现反常现象时或千斤顶检修后,应重新校验。 + +7943 施加预应力时,混凝土强度应符合设计要求,且同条件养护的混凝土立方体抗压强度应达到设计强度的 ,预制构件的拼缝灌浆材料强度达到主体结构设计强度的 。7944 当采用超张拉 的方法减少预应力钢筋的损失时,预应力钢筋的张拉程序宜为从应力为零开始张拉至 倍预应力钢筋的张拉控制应力并锚固。7945 采用应力控制方法张拉时 应校核最大张拉力下预应力钢筋伸长值 实测伸长值与计算伸长值的偏差应控制在 之内,否则应查明原因并采取措施后再张拉。必要时,宜进行现场孔道摩擦系数测定,并可根据实测结果调整张拉控制力。7946 张拉施工应采用中心对称张拉。单根钢绞线应分级循环张拉,且全部张拉完成后宜对前期张拉的钢绞线进行补张拉。7947 预应力钢筋张拉过程中不应出现钢绞线滑脱或断丝 发生滑脱和断丝时 滑脱和断丝的钢绞线数量不应超过构件同一截面钢绞线总根数的 ;发生断丝时,且每束钢绞线不应超过一丝。7948 锚固阶段张拉端预应力钢筋的内缩量应符合设计要求。当设计无具体要求时,应符合表 的规定。 + +# 表30 锚固阶段张拉端预应力钢筋的内缩量 + +单位为毫米 + +| 锚具类别 | 内缩量限值 | +| --- | --- | +| 支撑式锚具 | 螺母缝隙 | 1 | +| 每块后加垫板的缝隙 | 1 | +| 夹片式锚具 | 有顶压 | 5 | +| 无顶压 | 6~8 | + +7949 预应力张拉需搭设可靠的操作平台,可采用独立脚手架、电动吊篮、升降梯等形式。张拉操作平台应能承受操作人员和张拉设备等施工载荷,并装有防护栏杆、安全网等防护设施。 + +79410 张拉后宜采用砂轮锯或其他机械方法切割多余的预应力钢筋,不应采用电弧焊切割,其切断后露出锚具夹片外的长度不宜小于预应力钢筋直径的 倍,且不应小于 。 + +79411 预应力钢筋张拉锚固后,如遇到特殊情况需要放张,宜在工作锚上安装退锚器,采用千斤顶放张。应有专项预应力放张退锚方案,采取可靠措施避免预应力钢筋放张退锚时断裂,防止高应力状态的预应力钢筋弹出伤人。 + +9412 预应力钢筋待切筋完成后尽快封锚,外露钢绞线采用专用防腐油脂进行涂抹。 + +79413 锚具处应安装封锚罩,有粘结预应力钢筋待灌浆完成后直接安装封锚罩。封锚罩与结构主体应有可靠的连接并密封处理。 + +# 795 质量验收 + +7951 预应力钢筋、锚具、灌浆水泥、外加剂、成孔管道等材料质量、性能应符合设计及现行国家或行业标准要求。7952 预应力工程材料进场检查应符合下列规定。 + +应检查规格、外观、尺寸及其质量证明文件。 +应按现行国家有关标准的规定进行力学性能的抽样检验 +经产品认证符合要求的产品,其检验批量可扩大一倍。 +在同一工程中,同一厂家、同一品种、同一规格的产品连续三次进场检验均一次检验合格时,其后的检验批量可扩大一倍。 +预应力钢筋中钢绞线应按批验收,每批由同一钢号、同一规格、同一生产工艺生产的钢绞线组成。每批质量不大于 ;每批随机抽取 根钢绞线进行检验。 +对静载锚固性能试验,多孔锚具不应超过 套(单孔锚具为 套)、为一个检验批。对于锚具用量不足检验批规定数量的 的工程,如供货方能提供有效的静载锚固性能试验合格的证明文件,可不做锚具静载锚固性能试验。 + +7953 预应力钢筋、预留孔道、锚垫板和锚固区加强钢筋的安装应进行下列检查: + +预应力钢筋的外观、品种、级别、规格、数量和位置等; +预留孔道的外观、规格、数量、位置、形状以及灌浆孔、排气兼泌水孔等; +锚垫板和局部加强钢筋的外观、品种、级别、规格、数量和位置等; + +预应力钢筋锚具和连接器的外观、品种、规格、数量和位置等。7954 预应力钢筋张拉应进行下列检查: + +预应力钢筋张拉时的同条件养护混凝土试块的强度; +预应力钢筋张拉记录。 + +7955 灌浆用水泥浆及灌浆应进行下列检查: + +配合比设计阶段检查稠度、泌水率、自由膨胀率、氯离子含量和试块强度; +现场搅拌后检查稠度、泌水率,并根据验收规定检查试块强度; +灌浆记录。 + +7956 预应力钢筋、锚垫板安装水平、垂直方向尺寸偏差不大于 。装配式混凝土塔架中预应力孔道的水平位置及直径尺寸偏差不大于±5mm。7957 预应力钢筋张拉力符合设计要求,偏差不大于 。7.9.5.8 张拉端锚具保护层厚度不应低于50mm。 + +# 附 录 A(资料性)塔架结构阻尼比 + +# A1 引言 + +参考文献[]、[]和[]中塔架结构阻尼比推荐值相差很大。本附录为根据塔架固有频率估算结构阻尼比提供了指导。 + +# A2 一阶结构阻尼比 + +本附录提到的结构阻尼比不考虑以下因素: + +结构气动阻尼; +气动或者机电控制动作; +机械阻尼器; +基础阻尼。 + +表 给出了推荐的一阶阻尼比的最大值。 + +# 表 A1 阻尼参数 + +| 材料形式 | 阻尼比 | 对数阻尼衰减率 | +| --- | --- | --- | +| 钢制塔架 | 0.002 5 | 0.015 | +| 预应力混凝土塔架 | 0.006 5 | 0.04 | + +钢制塔架的一阶结构阻尼比 ξ 可根据公式(A.1)计算。 + +![](01K8T2JX33/images/c82621473b2b7bf6f270a5c5cc4c0c11e8ee1099d3138f6c220c15ae81cf98c2.jpg) + +………(A.1) + +式中: + +钢制塔架的一阶固有周期。 + +图 中给出了参考文献[]、[]和[]中钢制塔架的一阶阻尼比和公式( )的拟合曲线。 + +![](01K8T2JX33/images/4953ea8170c0c6149076d14cb6bde9d31faccd940d8459cdc9b54a36be72337e.jpg) +图 A1 钢制塔架的一阶阻尼比 + +# A3 二阶结构阻尼比 + +钢制塔架的二阶结构阻尼比可采用公式( )的值[3]。 + +![](01K8T2JX33/images/ccf8b4ee682525e18b556a856ae3978117b83ac2dbbefab77ad7ff93b4fd7c0b.jpg) + +…( A.2 ) + +# A4 高阶结构阻尼比 + +高阶结构阻尼比可通过公式(A.3)和公式(A.4)瑞利阻尼模型进行估算。=α M+α K …( .)2ω ω ω ξ - ω ξ 2 ω ξ - ω ξα1 - ω2 ,α 2 - ω21 ( .)式中: + +C 阻尼矩阵; +M 质量矩阵; +K 刚度矩阵; 一阶角频率; 二阶角频率; +ξ1 一阶阻尼比; +2 二阶阻尼比。 + +# A5 参考文献 + +[1] AmericanWindEner AssociationandAmericanSociet ofCivilEnineers,RecommendedPracticeforComlianceofLareOn-landWindTurbineSu ortStructuresrecommendedbest ractice,2011. + +[2] DeutschesInstitutfürBautechnik,RichtliniefürWindenergieanlagen,2012.(InGerman). [3] JapanSocietyofCivilEngineers,GuidelinesforDesignofWindTurbineSupportStructures + +andFoundations,2010.(InJaanese). + +[] , , , , , , , , sonseofa65kW WindTurbine,JournalofEarthuakeEnineerin ,12(8),1172-1190,2009. + +[5] T.Ishihara,P.V.Phuc,Y.Fuino,K.TakaharaandT.Mekaru,AFieldTestandFull Dynamic Simulation on a Stall Regulated Wind Turbine,Proceedings of APCWE VI,pp.599- 612,2005. + +[6] S.OhandT.Ishihara,Astud onstructure arametersofanoffshorewindturbineb excitationtestusingactivemassdamper,OFFSHORE2013,2013. + +# 附 录 B + +(资料性) + +# 塔架门框开口的屈曲简化分析方法 + +该简化分析方法基于 中的分析方法形成。 + +对于采用环形加劲肋的开口区域 见图 和图 可以采用简化方法进行屈曲分析 使用公式( )得到的轴向设计屈曲应力替代 的轴向设计屈曲应力: + +![](01K8T2JX33/images/fe534aa71643d93265d964296e71b86e46b356c52a61d3407a414ba7d368710b.jpg) + +·……( B.1) + +式中: + +-EN 1993-1-6 中的轴向设计屈曲应力; + +C 开口的缩减系数,见公式(B.2): + +![](01K8T2JX33/images/cdaf3d5830b06d7d9b3769045b40e32ecdb2e44a6c65cb238091c3bf3f6b4ee7.jpg) + +……( B.2 ) 式中: + +A.和B—参考表B.1,其中中间值线性插值得到,不准许外推。 +以上计算方法的适用条件如下(r/t) ;开口的角度 δ ;h /b 。 + +其中门框开口的角度和尺寸以塔架筒壁切口为参考,不考虑环形加劲肋的尺寸,见图 和图 。同时门框环形加劲肋还需满足以下条件: + +其截面是不变的,如截面是变化的将按照最小截面来考虑; +门框加劲肋截面积至少为整个开口区域面积的 / ; +加劲肋的中线位置应布置在开口筒壁的中面(见图 和图 ); +加劲肋的截面尺寸 (c /t )(如图 所示)需满足 中 的要求,即满足 c /t小于或等于 。 + +表 B.1 公式(B.2)的参数 + +| 开口角度(δ) | Q235 | Q355 | +| --- | --- | --- | +| A | B | A | B | +| δ= 20° | 1.00 | 0.001 9 | 0.95 | 0.002 1 | +| = 30° | 0.90 | 0.0019 | 0.85 | 0.0021 | +| δ= 60° | 0.75 | 0.002 2 | 0.70 | 0.002 4 | + +![](01K8T2JX33/images/dd8e00abf25d52a438d5d60e9e8a4e8d0d9fced9ccfaeb8a5c0168cffef1125d.jpg) +图 B1 门框环形加劲肋截面图 + +![](01K8T2JX33/images/426278c6c332cda9478db096a5483ab72ccc53cb4fd9196207c3e04960598fec.jpg) +图 B2 门框环形加劲肋的周向图 + +# 附 录 C + +# (资料性) + +# 钢制塔架疲劳强度分析 + +# C1 一般情况 + +疲劳强度分析可以依据 或 文件进行。 + +若使用 进行疲劳强度分析,考虑以下内容: + +分类; + +—参照 : 附录 的几何(热点)应力可替代名义应力。厚度 t 超过 的钢板或焊缝的疲劳细节分类考虑折减系数 k ,见公式( )。 + +![](01K8T2JX33/images/ce71deef7069814ea2ab6793279effe952401bb4545baf853b96d5cb5e90d535.jpg) + +…( C.1) + +# C.2 特殊情况 + +对于受单轴应力的轧制钢板中的孔,孔直径 D 不大于 ,且不大于 t,则疲劳细节分类可选取 。孔需要用机器气割或钻孔后进行修整,不准许有裂纹和明显的质量缺陷。应力可以采用净横截面计算。孔离端部或边缘的距离小于 D,孔之间的间距小于 D。 + +# 参 考 文 献 + +[1] EN1990 结构设计基础(BasisofStructuralDesign) + +[] 钢结构的设计 第 部分:一般规则和建筑物规则(tures—Part 1-1:General rules and rules for buildings) + +[3] EN1993-3-2:2006 钢结构设计 第3-2部分:塔、桅杆和烟囱 烟囱(Desinofsteelstruc-tures—Part 3-2:Towers,masts and chimneys—Chimneys) + +中 华 人 民 共 和 国 + +# 国 家 标 准 + +# 风力发电机组 塔架 + +GB/T 19072—2022 + +中 国 标 准 出 版 社 出 版 发 行北京市朝阳区和平里西街甲 号( )北京市西城区三里河北街 号( ) + +网址:www.sc.or .cn服务热线:年 月第一版 + +书号: · + +# 版权专有 侵权必究 \ No newline at end of file diff --git a/backend/test/unit/backends/resource/semantic_chunks.json b/backend/test/unit/backends/resource/semantic_chunks.json new file mode 100644 index 00000000..a334c5a8 --- /dev/null +++ b/backend/test/unit/backends/resource/semantic_chunks.json @@ -0,0 +1,7 @@ +[ + "## 风力发电机组 塔架设计规范|1 概述\n\n本标准规定了风力发电机组塔架的设计、制造、运输和安装要求。", + "### 风力发电机组 塔架设计规范|6 钢制塔架|6.1 一般要求\n\n钢制塔架的设计应考虑极端载荷和疲劳载荷。在计算连接强度时,需要用到螺纹截面积 Asp 以及承载力设计值 Rd。\n此外,结构应力 σ _ {y, d} 的计算必须符合相关标准要求。", + "### 风力发电机组 塔架设计规范|6 钢制塔架|6.2 疲劳极限状态\n\n疲劳计算应基于 Miner 线性累积损伤理论。", + "### 风力发电机组 塔架设计规范|7 混凝土塔架|7.1 材料特性\n\n混凝土强度等级不应低于 C50。", + "### 风力发电机组 塔架设计规范|7 混凝土塔架|7.2 施工工艺\n\n混凝土塔架可采用现浇或预制拼装方式。对于预制片段,应严格控制拼装精度。" +] \ No newline at end of file diff --git a/backend/test/unit/backends/test_semantic_chunking.py b/backend/test/unit/backends/test_semantic_chunking.py index 2a5c6ae2..fa753aaf 100644 --- a/backend/test/unit/backends/test_semantic_chunking.py +++ b/backend/test/unit/backends/test_semantic_chunking.py @@ -1,6 +1,7 @@ import pytest import numpy as np import os +import json # 现在可以安全地导入了,因为顶层不再有重型依赖 from yuxi.knowledge.chunking.ragflow_like.parsers import semantic @@ -22,57 +23,87 @@ def embed_fn(): @pytest.fixture def sample_markdown(): - with open("test/resource/test4.md", "r", encoding="utf-8") as f: - return f.read() + """提供一个包含多章节、公式符号和复杂结构的临时 Markdown 样本""" + return """ +# 风力发电机组 塔架设计规范 + +## 1 概述 +本标准规定了风力发电机组塔架的设计、制造、运输和安装要求。 + +## 6 钢制塔架 +### 6.1 一般要求 +钢制塔架的设计应考虑极端载荷和疲劳载荷。在计算连接强度时,需要用到螺纹截面积 Asp 以及承载力设计值 Rd。 +此外,结构应力 σ _ {y, d} 的计算必须符合相关标准要求。 + +### 6.2 疲劳极限状态 +疲劳计算应基于 Miner 线性累积损伤理论。 + +## 7 混凝土塔架 +### 7.1 材料特性 +混凝土强度等级不应低于 C50。 + +### 7.2 施工工艺 +混凝土塔架可采用现浇或预制拼装方式。对于预制片段,应严格控制拼装精度。 +""" def test_semantic_chunking_basic(embed_fn, sample_markdown): """测试基本的语义切分逻辑 (使用真实嵌入模型)""" - + # 配置切分参数 parser_config = { - "chunk_token_num": 200, # 适中的 token 数以触发更多切分 - "overlapped_percent": 0 + "chunk_token_num": 300, # 针对标准文档调整 token 数 + "overlapped_percent": 0.1 } - - # 直接注入 embed_fn + + # 执行语义切分 chunks = semantic.chunk_markdown( - sample_markdown, - parser_config=parser_config, + sample_markdown, + parser_config=parser_config, embed_fn=embed_fn ) - - # 验证结果 + + # 1. 基础验证 assert isinstance(chunks, list) - assert len(chunks) > 0 - - print(f"\n成功切分为 {len(chunks)} 个片段:") - for i, chunk in enumerate(chunks): - print(f"--- 片段 {i+1} ---") - print(chunk) - - # 验证标题路径增强是否生效 (检查是否包含 Part 或特殊元素标记) - has_enhanced_title = any("|Part" in chunk or "|Math Block" in chunk for chunk in chunks) - assert has_enhanced_title, "标题路径增强或分片标记失效" - - # 验证是否包含特殊的 LaTeX 公式或符号 (匹配输出中的格式) - has_formula = any("f _ {0, 1}" in chunk or "\\sigma_ {y, d}" in chunk or "A _ {\\mathrm {s p}}" in chunk for chunk in chunks) - assert has_formula, "LaTeX 公式/符号在切分中丢失" + assert len(chunks) >= 5, f"预期至少切分为 5 个片段,实际仅有 {len(chunks)} 个" - # 验证语义聚类是否大概生效 (钢制塔架和混凝土塔架内容应该分开) - # 我们检查是否有钢制塔架相关的片段和混凝土塔架相关的片段 - # 使用 startswith 来更准确地定位章节开始,避免匹配到目次/前言中的文字 - has_steel = any(chunk.startswith("# 6 钢制塔架") for chunk in chunks) - has_concrete = any(chunk.startswith("# 7 混凝土塔架") for chunk in chunks) - - assert has_steel and has_concrete, "语义内容丢失 (钢制或混凝土塔架章节开始部分)" - - # 验证它们是否在不同的片段中 - steel_chunks = [i for i, c in enumerate(chunks) if c.startswith("# 6 钢制塔架")] - concrete_chunks = [i for i, c in enumerate(chunks) if c.startswith("# 7 混凝土塔架")] - - # 理论上语义聚类会将这些大章节分开 - if steel_chunks and concrete_chunks: - assert not set(steel_chunks).intersection(set(concrete_chunks)), "钢制塔架和混凝土塔架大章节内容被错误地聚类在同一个 chunk 中了" + # 2. 验证标题路径增强 (检查 RAGFlow 风格的层级标记) + # 标准文档通常会被赋予类似 "风力发电机组 塔架 | 6 钢制塔架" 的标题路径 + has_enhanced_title = any("|" in chunk and ("钢制塔架" in chunk or "混凝土塔架" in chunk) for chunk in chunks) + assert has_enhanced_title, "标题路径增强失效:未在 chunk 中发现层级分隔符 '|' 或核心章节标题" + # 3. 验证公式与符号识别 (匹配 test4.md 4.1 节中的符号) + # 重点检查文档中出现的符号:Asp (螺纹截面积), σ _ {y, d} (结构应力), Rd (承载力设计值) + symbols_to_check = ["Asp", "Rd", "σ _ {y, d}"] + found_symbols = [s for s in symbols_to_check if any(s in chunk for chunk in chunks)] + assert len(found_symbols) > 0, f"在切分结果中未找到关键符号: {symbols_to_check}" + + # 4. 验证核心章节内容是否存在 + has_steel_section = any("6 钢制塔架" in chunk for chunk in chunks) + has_concrete_section = any("7 混凝土塔架" in chunk for chunk in chunks) + assert has_steel_section, "未找到第 6 章 '钢制塔架' 相关内容" + assert has_concrete_section, "未找到第 7 章 '混凝土塔架' 相关内容" + + # 5. 验证语义聚类是否将不同主题分开 + # 钢制塔架和混凝土塔架是两个独立的大章节,语义聚类应该避免将它们的核心内容混在一个 chunk 中 + steel_start_chunks = [i for i, c in enumerate(chunks) if "# 6 钢制塔架" in c] + concrete_start_chunks = [i for i, c in enumerate(chunks) if "# 7 混凝土塔架" in c] + + if steel_start_chunks and concrete_start_chunks: + # 确保起始片段不重合 + assert not set(steel_start_chunks).intersection(set(concrete_start_chunks)), \ + "语义聚类错误:钢制塔架与混凝土塔架的章节头部被挤在了同一个 chunk 中" + + print(f"\n[测试成功] 文档成功切分为 {len(chunks)} 个片段") + print(f"识别到的关键符号: {found_symbols}") + + # 将切分后的内容写入到 resource 目录 + output_dir = os.path.join(os.path.dirname(__file__), "resource") + os.makedirs(output_dir, exist_ok=True) + + output_path = os.path.join(output_dir, "semantic_chunks.json") + with open(output_path, "w", encoding="utf-8") as f: + json.dump(chunks, f, ensure_ascii=False, indent=2) + print(f"切分内容已保存至: {output_path}") + def test_heading_inference(): """测试标题层级推断工具类""" from yuxi.knowledge.chunking.ragflow_like.utils.md_parser_utils import infer_heading_level diff --git a/backend/uv.lock b/backend/uv.lock index f1bd375b..11bc21b6 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -2521,6 +2521,18 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/46/f6b4ad632c67ef35209a66127e4bddc95759649dd595f71f13fba11bdf9a/mcp-1.27.0-py3-none-any.whl", hash = "sha256:5ce1fa81614958e267b21fb2aa34e0aea8e2c6ede60d52aba45fd47246b4d741", size = 215967, upload-time = "2026-04-02T14:48:07.24Z" }, ] +[[package]] +name = "mdit-py-plugins" +version = "0.5.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -4337,6 +4349,38 @@ torch = [ { name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" }, ] +[[package]] +name = "scikit-learn" +version = "1.8.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, +] + [[package]] name = "scipy" version = "1.17.1" @@ -4756,6 +4800,15 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/15/a11f7bb3cbc97dfecf32a90552f5a8f8a5c99316a99c6c17bdabf5baf256/thinc-8.3.13-cp313-cp313-win_arm64.whl", hash = "sha256:723949cab11d1925c15447928513a718276316cec6e0de28337cca0a62be0521", size = 1644606, upload-time = "2026-03-23T07:22:21.339Z" }, ] +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + [[package]] name = "tiktoken" version = "0.12.0" @@ -4878,9 +4931,9 @@ dependencies = [ { name = "typing-extensions", marker = "sys_platform == 'darwin'" }, ] wheels = [ - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:a47b7986bee3f61ad217d8a8ce24605809ab425baf349f97de758815edd2ef54" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:fbe2e149c5174ef90d29a5f84a554dfaf28e003cb4f61fa2c8c024c17ec7ca58" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:057efd30a6778d2ee5e2374cd63a63f63311aa6f33321e627c655df60abdd390" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:a47b7986bee3f61ad217d8a8ce24605809ab425baf349f97de758815edd2ef54" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:fbe2e149c5174ef90d29a5f84a554dfaf28e003cb4f61fa2c8c024c17ec7ca58" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:057efd30a6778d2ee5e2374cd63a63f63311aa6f33321e627c655df60abdd390" }, ] [[package]] @@ -4903,19 +4956,19 @@ dependencies = [ { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] wheels = [ - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-linux_s390x.whl", hash = "sha256:0e34e276722ab7dd0dffa9e12fe2135a9b34a0e300c456ed7ad6430229404eb5" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:610f600c102386e581327d5efc18c0d6edecb9820b4140d26163354a99cd800d" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:cb9a8ba8137ab24e36bf1742cb79a1294bd374db570f09fc15a5e1318160db4e" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:2be20b2c05a0cce10430cc25f32b689259640d273232b2de357c35729132256d" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-win_arm64.whl", hash = "sha256:99fc421a5d234580e45957a7b02effbf3e1c884a5dd077afc85352c77bf41434" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-linux_s390x.whl", hash = "sha256:8b5882276633cf91fe3d2d7246c743b94d44a7e660b27f1308007fdb1bb89f7d" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a5064b5e23772c8d164068cc7c12e01a75faf7b948ecd95a0d4007d7487e5f25" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8f81dedb4c6076ec325acc3b47525f9c550e5284a18eae1d9061c543f7b6e7de" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:e1ee1b2346ade3ea90306dfbec7e8ff17bc220d344109d189ae09078333b0856" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:64c187345509f2b1bb334feed4666e2c781ca381874bde589182f81247e61f88" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af81283ac671f434b1b25c95ba295f270e72db1fad48831eb5e4748ff9840041" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a9dbb6f64f63258bc811e2c0c99640a81e5af93c531ad96e95c5ec777ea46dab" }, - { url = "https://download.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:6d93a7165419bc4b2b907e859ccab0dea5deeab261448ae9a5ec5431f14c0e64" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-linux_s390x.whl", hash = "sha256:0e34e276722ab7dd0dffa9e12fe2135a9b34a0e300c456ed7ad6430229404eb5" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:610f600c102386e581327d5efc18c0d6edecb9820b4140d26163354a99cd800d" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:cb9a8ba8137ab24e36bf1742cb79a1294bd374db570f09fc15a5e1318160db4e" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:2be20b2c05a0cce10430cc25f32b689259640d273232b2de357c35729132256d" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp312-cp312-win_arm64.whl", hash = "sha256:99fc421a5d234580e45957a7b02effbf3e1c884a5dd077afc85352c77bf41434" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-linux_s390x.whl", hash = "sha256:8b5882276633cf91fe3d2d7246c743b94d44a7e660b27f1308007fdb1bb89f7d" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a5064b5e23772c8d164068cc7c12e01a75faf7b948ecd95a0d4007d7487e5f25" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8f81dedb4c6076ec325acc3b47525f9c550e5284a18eae1d9061c543f7b6e7de" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:e1ee1b2346ade3ea90306dfbec7e8ff17bc220d344109d189ae09078333b0856" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:64c187345509f2b1bb334feed4666e2c781ca381874bde589182f81247e61f88" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af81283ac671f434b1b25c95ba295f270e72db1fad48831eb5e4748ff9840041" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a9dbb6f64f63258bc811e2c0c99640a81e5af93c531ad96e95c5ec777ea46dab" }, + { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.8.0%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:6d93a7165419bc4b2b907e859ccab0dea5deeab261448ae9a5ec5431f14c0e64" }, ] [[package]] @@ -5616,6 +5669,7 @@ dependencies = [ { name = "aiosqlite" }, { name = "argon2-cffi" }, { name = "asyncpg" }, + { name = "beautifulsoup4" }, { name = "chardet" }, { name = "colorlog" }, { name = "dashscope" }, @@ -5642,11 +5696,14 @@ dependencies = [ { name = "llama-index" }, { name = "llama-index-readers-file" }, { name = "loguru" }, + { name = "markdown-it-py" }, { name = "markdownify" }, { name = "mcp" }, + { name = "mdit-py-plugins" }, { name = "minio" }, { name = "neo4j" }, { name = "networkx" }, + { name = "nltk" }, { name = "openai" }, { name = "opencv-python-headless" }, { name = "pillow" }, @@ -5664,6 +5721,7 @@ dependencies = [ { name = "readability-lxml" }, { name = "redis" }, { name = "rich" }, + { name = "scikit-learn" }, { name = "sqlalchemy", extra = ["asyncio"] }, { name = "tabulate" }, { name = "tavily-python" }, @@ -5689,6 +5747,7 @@ requires-dist = [ { name = "aiosqlite", specifier = ">=0.20.0" }, { name = "argon2-cffi", specifier = ">=25.1.0" }, { name = "asyncpg", specifier = ">=0.30.0" }, + { name = "beautifulsoup4", specifier = ">=4.12.0" }, { name = "chardet", specifier = ">=5.0.0" }, { name = "colorlog", specifier = ">=6.9.0" }, { name = "dashscope", specifier = ">=1.23.2" }, @@ -5715,11 +5774,14 @@ requires-dist = [ { name = "llama-index", specifier = ">=0.14" }, { name = "llama-index-readers-file", specifier = ">=0.4.7" }, { name = "loguru", specifier = ">=0.7.3" }, + { name = "markdown-it-py", specifier = ">=3.0.0" }, { name = "markdownify", specifier = ">=1.1.0" }, { name = "mcp", specifier = ">=1.20" }, + { name = "mdit-py-plugins", specifier = ">=0.4.0" }, { name = "minio", specifier = ">=7.2.7" }, { name = "neo4j", specifier = ">=5.28.1" }, { name = "networkx", specifier = ">=3.5" }, + { name = "nltk", specifier = ">=3.8.1" }, { name = "openai", specifier = ">=1.109" }, { name = "opencv-python-headless", specifier = ">=4.11.0.86" }, { name = "pillow", specifier = ">=10.5.0" }, @@ -5737,6 +5799,7 @@ requires-dist = [ { name = "readability-lxml", specifier = ">=0.8.1" }, { name = "redis", specifier = ">=5.2.0" }, { name = "rich", specifier = ">=13.7.1" }, + { name = "scikit-learn", specifier = ">=1.3.0" }, { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.0" }, { name = "tabulate", specifier = ">=0.9.0" }, { name = "tavily-python", specifier = ">=0.7.0" }, diff --git a/web/package.json b/web/package.json index 4d64cec5..0ec86226 100644 --- a/web/package.json +++ b/web/package.json @@ -2,6 +2,7 @@ "name": "yuxi-web", "version": "0.6.0", "private": true, + "type": "module", "scripts": { "dev": "vite", "server": "vite serve --host", diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index 83730d74..e5bee707 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -183,8 +183,13 @@ size="16" class="agent-switcher-menu-icon" /> - {{ agent.name || 'Unknown' }} - + {{ + agent.name || 'Unknown' + }} + 当前 @@ -605,7 +610,10 @@ const conversationRows = computed(() => { if (currentThreadConfigNotice.value) { const insertAfterCount = Math.max( 0, - Math.min(Number(currentThreadConfigNotice.value.insertAfterConversationCount) || 0, rows.length) + Math.min( + Number(currentThreadConfigNotice.value.insertAfterConversationCount) || 0, + rows.length + ) ) rows.splice(insertAfterCount, 0, { type: 'notice', @@ -646,7 +654,10 @@ const showStartAgentSelector = computed(() => { }) const showStartAgentDropdown = computed(() => { - return showStartAgentSelector.value && (startAgents.value.length >= 4 || localUIState.chatMainWidth < 380) + return ( + showStartAgentSelector.value && + (startAgents.value.length >= 4 || localUIState.chatMainWidth < 380) + ) }) const showStartAgentSegment = computed(() => { @@ -859,7 +870,11 @@ const queuePendingThreadConfigNotice = (threadId) => { } const flushPendingThreadConfigNotice = (threadId) => { - if (!threadId || !currentThreadHasHistory.value || !threadPendingConfigNoticeMap.value[threadId]) { + if ( + !threadId || + !currentThreadHasHistory.value || + !threadPendingConfigNoticeMap.value[threadId] + ) { return } @@ -1419,7 +1434,11 @@ const selectChat = async (chatId) => { // 先更新当前线程,确保底部智能体名称与选中项即时同步。 chatState.currentThreadId = chatId - if (!props.singleMode && targetChat?.agent_id && targetChat.agent_id !== currentAgentId.value) { + if ( + !props.singleMode && + targetChat?.agent_id && + targetChat.agent_id !== currentAgentId.value + ) { await agentStore.selectAgent(targetChat.agent_id) } @@ -1912,10 +1931,10 @@ const hasVisibleAssistantBody = (message) => { const { content, reasoningContent } = extractAssistantMessageBody(message) return Boolean( content || - reasoningContent || - message.error_type || - message.extra_metadata?.error_type || - message.isStoppedByUser + reasoningContent || + message.error_type || + message.extra_metadata?.error_type || + message.isStoppedByUser ) } @@ -1997,9 +2016,7 @@ const isDisplayMessageProcessing = (conv, displayItem) => { const isToolGroupActive = (conv, itemIndex, displayItems) => { return ( - isReplyLoading.value && - conv?.status === 'streaming' && - itemIndex === displayItems.length - 1 + isReplyLoading.value && conv?.status === 'streaming' && itemIndex === displayItems.length - 1 ) } diff --git a/web/src/components/AgentConfigSidebar.vue b/web/src/components/AgentConfigSidebar.vue index 1ed66c02..f822b5b9 100644 --- a/web/src/components/AgentConfigSidebar.vue +++ b/web/src/components/AgentConfigSidebar.vue @@ -57,11 +57,7 @@ />