语义分析功能小改进
Browse files- backend/runtime_config.py +1 -1
- backend/semantic_analyzer.py +15 -14
- client/src/index.html +1 -1
- client/src/ts/api/GLTR_API.ts +1 -1
- client/src/ts/lang/translations.ts +1 -2
- client/src/ts/start.ts +15 -1
- client/src/ts/utils/settingsMenuManager.ts +4 -0
- client/src/ts/utils/signalThresholdDetector.ts +1 -1
- client/src/ts/utils/topkChartUtils.ts +10 -2
- client/src/ts/utils/visualizationConfigs.ts +1 -1
- client/src/ts/utils/visualizationUpdater.ts +24 -0
- client/src/ts/vis/ToolTip.ts +12 -5
- model_paths.py +6 -5
- run.py +1 -1
- scripts/cases/eval_cases_long.json +110 -9
- scripts/{eval_semantic_submodes.py → eval_semantic.py} +112 -63
- server.yaml +1 -1
backend/runtime_config.py
CHANGED
|
@@ -91,7 +91,7 @@ SEMANTIC_RUNTIME_CONFIGS = {
|
|
| 91 |
"default_cpu_machine": {"max_token_length": 500},
|
| 92 |
"cloud_cpu_16g": {"max_token_length": 500},
|
| 93 |
"cloud_cpu_32g": {"max_token_length": 2000},
|
| 94 |
-
"cloud_cuda": {"max_token_length":
|
| 95 |
"local_mps": {"max_token_length": 500},
|
| 96 |
}
|
| 97 |
|
|
|
|
| 91 |
"default_cpu_machine": {"max_token_length": 500},
|
| 92 |
"cloud_cpu_16g": {"max_token_length": 500},
|
| 93 |
"cloud_cpu_32g": {"max_token_length": 2000},
|
| 94 |
+
"cloud_cuda": {"max_token_length": 2000},
|
| 95 |
"local_mps": {"max_token_length": 500},
|
| 96 |
}
|
| 97 |
|
backend/semantic_analyzer.py
CHANGED
|
@@ -3,7 +3,7 @@ Semantic analysis:基于 instruct 模型提取原文 token 与 query 的相关
|
|
| 3 |
|
| 4 |
使用 logits_gradient 梯度归因策略(与预测更一致),子策略由 --logits_gradient_submode 指定:
|
| 5 |
- count:top-10 logits 梯度(排除 0),prompt 引导「数量」。0.6b下只适合用于判断文章整体是否有关联,1.7b下全能
|
| 6 |
-
- match_score:目标 token logit 梯度,prompt 引导「相关度打分」。0.6b/1.7b下都不太有竞争力
|
| 7 |
- fill_blank:填空式,top-10 logits 梯度(排除 无),prompt 引导「最相关的一个词」。0.6b下只适合用于给token打分,1.7b下全能
|
| 8 |
|
| 9 |
count/fill_blank 按概率加权(Σ pᵢ·zᵢ)。
|
|
@@ -24,7 +24,7 @@ from .runtime_config import get_semantic_max_token_length
|
|
| 24 |
|
| 25 |
|
| 26 |
def _get_logits_gradient_submode() -> str:
|
| 27 |
-
"""logits_gradient 子策略:count / match_score / fill_blank"""
|
| 28 |
try:
|
| 29 |
from backend.app_context import get_args
|
| 30 |
return getattr(get_args(), "logits_gradient_submode", "fill_blank")
|
|
@@ -63,7 +63,7 @@ def _analyze_logits_gradient(
|
|
| 63 |
) -> Dict:
|
| 64 |
"""
|
| 65 |
梯度归因:logits 对输入 embedding 的梯度。
|
| 66 |
-
子策略:count / match_score / fill_blank,由 --logits_gradient_submode 指定。
|
| 67 |
submode_override: 评估时可选覆盖,用于同一进程内测试不同子模式。
|
| 68 |
"""
|
| 69 |
TOTAL_STEPS = 4
|
|
@@ -74,9 +74,10 @@ def _analyze_logits_gradient(
|
|
| 74 |
if progress_callback:
|
| 75 |
progress_callback(1, TOTAL_STEPS, "encoding", None)
|
| 76 |
# 根据submodule来决定不同的instruction
|
|
|
|
| 77 |
if submode == "count":
|
| 78 |
instruction = f"以下是一篇文章,请问原文中有多少个词和查询主题({query})相关?文章内容:\n\n"
|
| 79 |
-
elif submode == "match_score":
|
| 80 |
instruction = f"以下是一篇文章,请问原文和查询主题({query})的相关程度是多少?请回答0/1/2(2为最高相关)。文章内容:\n\n"
|
| 81 |
elif submode == "fill_blank":
|
| 82 |
instruction = f"以下是一篇文章,请问原文中哪个词与查询主题({query})最相关?如无相关词则回答“无”。文章内容:\n\n"
|
|
@@ -94,7 +95,7 @@ def _analyze_logits_gradient(
|
|
| 94 |
# 生成引导词:chat template 只支持完整消息,引导词需追加到 formatted
|
| 95 |
if submode == "count":
|
| 96 |
generation_guide = f"**原文中**出现的,和查询主题({query})相关的词的数量 = **"
|
| 97 |
-
elif submode == "match_score":
|
| 98 |
generation_guide = f"文章和查询主题({query})的相关程度(0-2)打分为:**"
|
| 99 |
elif submode == "fill_blank":
|
| 100 |
# “引号是特意为了防止模型生成引号
|
|
@@ -109,6 +110,10 @@ def _analyze_logits_gradient(
|
|
| 109 |
idx = formatted.find(instruction)
|
| 110 |
instruction_start_char = idx if idx >= 0 else 0
|
| 111 |
text_start_char = instruction_start_char + len(instruction)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
enc = tokenizer(
|
| 114 |
formatted,
|
|
@@ -131,10 +136,6 @@ def _analyze_logits_gradient(
|
|
| 131 |
embed_layer = model.get_input_embeddings()
|
| 132 |
embeds = embed_layer(input_ids).detach().clone().requires_grad_(True)
|
| 133 |
|
| 134 |
-
text_end_char = text_start_char + len(truncated_text)
|
| 135 |
-
lines = truncated_text.splitlines()
|
| 136 |
-
abbrev_text = truncated_text if len(lines) <= 2 else f"{lines[0]}\n...\n{lines[-1]}"
|
| 137 |
-
abbrev = formatted[:text_start_char] + abbrev_text + formatted[text_end_char:]
|
| 138 |
use_gc = _get_gradient_checkpointing()
|
| 139 |
print(f"📌 logits_gradient: 推理原文 (tokens={len(offset_mapping)}, gradient_checkpointing={use_gc}):\n{abbrev}")
|
| 140 |
if progress_callback:
|
|
@@ -143,7 +144,7 @@ def _analyze_logits_gradient(
|
|
| 143 |
if use_gc:
|
| 144 |
model.gradient_checkpointing_enable()
|
| 145 |
try:
|
| 146 |
-
with torch.set_grad_enabled(
|
| 147 |
outputs = model(
|
| 148 |
inputs_embeds=embeds,
|
| 149 |
attention_mask=attention_mask,
|
|
@@ -164,11 +165,11 @@ def _analyze_logits_gradient(
|
|
| 164 |
|
| 165 |
neg_token = "无" if submode == "fill_blank" else "0"
|
| 166 |
neg_id = tokenizer.encode(neg_token, add_special_tokens=False)[0]
|
| 167 |
-
# 全文匹配度:count/match_score 用 1-P("0"),fill_blank 用 1-P("无")
|
| 168 |
p_neg = probs[0, neg_id].item()
|
| 169 |
full_match_degree = round(1.0 - p_neg, 4)
|
| 170 |
|
| 171 |
-
if full_match_degree_only
|
| 172 |
return {
|
| 173 |
"model": get_semantic_model_display_name(),
|
| 174 |
"token_attention": [],
|
|
@@ -186,7 +187,7 @@ def _analyze_logits_gradient(
|
|
| 186 |
w[topk_ids[0] == neg_id] = 0
|
| 187 |
|
| 188 |
target_logit = (w * vals).sum()
|
| 189 |
-
elif submode == "match_score":
|
| 190 |
target_ids = tokenizer.encode("2", add_special_tokens=False)
|
| 191 |
if not target_ids:
|
| 192 |
raise ValueError("tokenizer 无法编码 '2'")
|
|
@@ -253,7 +254,7 @@ def analyze_semantic(
|
|
| 253 |
Args:
|
| 254 |
query: 查询主题
|
| 255 |
text: 原文
|
| 256 |
-
submode_override: 评估时可选覆盖子模式(count/match_score/fill_blank)
|
| 257 |
progress_callback: 可选进度回调 (step, total_steps, stage, percentage)
|
| 258 |
debug_info: 为 True 时返回 debug_abbrev(推理原文缩写);topk_tokens、topk_probs 始终在结果中
|
| 259 |
|
|
|
|
| 3 |
|
| 4 |
使用 logits_gradient 梯度归因策略(与预测更一致),子策略由 --logits_gradient_submode 指定:
|
| 5 |
- count:top-10 logits 梯度(排除 0),prompt 引导「数量」。0.6b下只适合用于判断文章整体是否有关联,1.7b下全能
|
| 6 |
+
- match_score:目标 token logit 梯度,prompt 引导「相关度打分」。0.6b/1.7b下都不太有竞争力。【已废弃】
|
| 7 |
- fill_blank:填空式,top-10 logits 梯度(排除 无),prompt 引导「最相关的一个词」。0.6b下只适合用于给token打分,1.7b下全能
|
| 8 |
|
| 9 |
count/fill_blank 按概率加权(Σ pᵢ·zᵢ)。
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
def _get_logits_gradient_submode() -> str:
|
| 27 |
+
"""logits_gradient 子策略:count / match_score(已废弃) / fill_blank"""
|
| 28 |
try:
|
| 29 |
from backend.app_context import get_args
|
| 30 |
return getattr(get_args(), "logits_gradient_submode", "fill_blank")
|
|
|
|
| 63 |
) -> Dict:
|
| 64 |
"""
|
| 65 |
梯度归因:logits 对输入 embedding 的梯度。
|
| 66 |
+
子策略:count / match_score(已废弃) / fill_blank,由 --logits_gradient_submode 指定。
|
| 67 |
submode_override: 评估时可选覆盖,用于同一进程内测试不同子模式。
|
| 68 |
"""
|
| 69 |
TOTAL_STEPS = 4
|
|
|
|
| 74 |
if progress_callback:
|
| 75 |
progress_callback(1, TOTAL_STEPS, "encoding", None)
|
| 76 |
# 根据submodule来决定不同的instruction
|
| 77 |
+
# 文档前用 \n\n 分隔,避免 tokenizer 将首字符与空格合并,导致 offset_mapping 计算错误
|
| 78 |
if submode == "count":
|
| 79 |
instruction = f"以下是一篇文章,请问原文中有多少个词和查询主题({query})相关?文章内容:\n\n"
|
| 80 |
+
elif submode == "match_score": # 已废弃
|
| 81 |
instruction = f"以下是一篇文章,请问原文和查询主题({query})的相关程度是多少?请回答0/1/2(2为最高相关)。文章内容:\n\n"
|
| 82 |
elif submode == "fill_blank":
|
| 83 |
instruction = f"以下是一篇文章,请问原文中哪个词与查询主题({query})最相关?如无相关词则回答“无”。文章内容:\n\n"
|
|
|
|
| 95 |
# 生成引导词:chat template 只支持完整消息,引导词需追加到 formatted
|
| 96 |
if submode == "count":
|
| 97 |
generation_guide = f"**原文中**出现的,和查询主题({query})相关的词的数量 = **"
|
| 98 |
+
elif submode == "match_score": # 已废弃
|
| 99 |
generation_guide = f"文章和查询主题({query})的相关程度(0-2)打分为:**"
|
| 100 |
elif submode == "fill_blank":
|
| 101 |
# “引号是特意为了防止模型生成引号
|
|
|
|
| 110 |
idx = formatted.find(instruction)
|
| 111 |
instruction_start_char = idx if idx >= 0 else 0
|
| 112 |
text_start_char = instruction_start_char + len(instruction)
|
| 113 |
+
text_end_char = text_start_char + len(truncated_text)
|
| 114 |
+
lines = truncated_text.splitlines()
|
| 115 |
+
abbrev_text = truncated_text if len(lines) <= 2 else f"{lines[0]}\n...\n{lines[-1]}"
|
| 116 |
+
abbrev = formatted[:text_start_char] + abbrev_text + formatted[text_end_char:]
|
| 117 |
|
| 118 |
enc = tokenizer(
|
| 119 |
formatted,
|
|
|
|
| 136 |
embed_layer = model.get_input_embeddings()
|
| 137 |
embeds = embed_layer(input_ids).detach().clone().requires_grad_(True)
|
| 138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
use_gc = _get_gradient_checkpointing()
|
| 140 |
print(f"📌 logits_gradient: 推理原文 (tokens={len(offset_mapping)}, gradient_checkpointing={use_gc}):\n{abbrev}")
|
| 141 |
if progress_callback:
|
|
|
|
| 144 |
if use_gc:
|
| 145 |
model.gradient_checkpointing_enable()
|
| 146 |
try:
|
| 147 |
+
with torch.set_grad_enabled(not full_match_degree_only):
|
| 148 |
outputs = model(
|
| 149 |
inputs_embeds=embeds,
|
| 150 |
attention_mask=attention_mask,
|
|
|
|
| 165 |
|
| 166 |
neg_token = "无" if submode == "fill_blank" else "0"
|
| 167 |
neg_id = tokenizer.encode(neg_token, add_special_tokens=False)[0]
|
| 168 |
+
# 全文匹配度:count/match_score(已废弃) 用 1-P("0"),fill_blank 用 1-P("无")
|
| 169 |
p_neg = probs[0, neg_id].item()
|
| 170 |
full_match_degree = round(1.0 - p_neg, 4)
|
| 171 |
|
| 172 |
+
if full_match_degree_only:
|
| 173 |
return {
|
| 174 |
"model": get_semantic_model_display_name(),
|
| 175 |
"token_attention": [],
|
|
|
|
| 187 |
w[topk_ids[0] == neg_id] = 0
|
| 188 |
|
| 189 |
target_logit = (w * vals).sum()
|
| 190 |
+
elif submode == "match_score": # 已废弃
|
| 191 |
target_ids = tokenizer.encode("2", add_special_tokens=False)
|
| 192 |
if not target_ids:
|
| 193 |
raise ValueError("tokenizer 无法编码 '2'")
|
|
|
|
| 254 |
Args:
|
| 255 |
query: 查询主题
|
| 256 |
text: 原文
|
| 257 |
+
submode_override: 评估时可选覆盖子模式(count/match_score已废弃/fill_blank)
|
| 258 |
progress_callback: 可选进度回调 (step, total_steps, stage, percentage)
|
| 259 |
debug_info: 为 True 时返回 debug_abbrev(推理原文缩写);topk_tokens、topk_probs 始终在结果中
|
| 260 |
|
client/src/index.html
CHANGED
|
@@ -154,9 +154,9 @@
|
|
| 154 |
<label class="semantic-submode-label" for="semantic_submode_select">submode: </label>
|
| 155 |
<select id="semantic_submode_select" class="semantic-submode-select">
|
| 156 |
<option value="count">count</option>
|
| 157 |
-
<option value="match_score">match_score</option>
|
| 158 |
<option value="fill_blank">fill_blank</option>
|
| 159 |
<option value="hybrid">hybrid</option>
|
|
|
|
| 160 |
</select>
|
| 161 |
</span>
|
| 162 |
<span class="semantic-submode-group semantic-submode-group-right">
|
|
|
|
| 154 |
<label class="semantic-submode-label" for="semantic_submode_select">submode: </label>
|
| 155 |
<select id="semantic_submode_select" class="semantic-submode-select">
|
| 156 |
<option value="count">count</option>
|
|
|
|
| 157 |
<option value="fill_blank">fill_blank</option>
|
| 158 |
<option value="hybrid">hybrid</option>
|
| 159 |
+
<option value="match_score">match_score (废弃)</option>
|
| 160 |
</select>
|
| 161 |
</span>
|
| 162 |
<span class="semantic-submode-group semantic-submode-group-right">
|
client/src/ts/api/GLTR_API.ts
CHANGED
|
@@ -249,7 +249,7 @@ export class TextAnalysisAPI {
|
|
| 249 |
* @param query 查询主题
|
| 250 |
* @param text 原文
|
| 251 |
* @param onProgress 可选进度回调,传入时启用 SSE 流式响应
|
| 252 |
-
* @param submode 可选子模式:count/
|
| 253 |
*/
|
| 254 |
public async analyzeSemantic(
|
| 255 |
query: string,
|
|
|
|
| 249 |
* @param query 查询主题
|
| 250 |
* @param text 原文
|
| 251 |
* @param onProgress 可选进度回调,传入时启用 SSE 流式响应
|
| 252 |
+
* @param submode 可选子模式:count/fill_blank/hybrid;match_score 已废弃
|
| 253 |
*/
|
| 254 |
public async analyzeSemantic(
|
| 255 |
query: string,
|
client/src/ts/lang/translations.ts
CHANGED
|
@@ -188,8 +188,7 @@ export const translations: Translations = {
|
|
| 188 |
'information per token histogram': 'token信息量直方图',
|
| 189 |
'information per token progress': 'token信息量进度图',
|
| 190 |
'token index': 'token索引',
|
| 191 |
-
'
|
| 192 |
-
'semantic signal prob histogram': '语义信号概率直方图',
|
| 193 |
'signal prob': 'signal概率',
|
| 194 |
'signal ratio': '信号比',
|
| 195 |
'pw score': 'pw 分数',
|
|
|
|
| 188 |
'information per token histogram': 'token信息量直方图',
|
| 189 |
'information per token progress': 'token信息量进度图',
|
| 190 |
'token index': 'token索引',
|
| 191 |
+
'semantic score histogram': '语义分数直方图',
|
|
|
|
| 192 |
'signal prob': 'signal概率',
|
| 193 |
'signal ratio': '信号比',
|
| 194 |
'pw score': 'pw 分数',
|
client/src/ts/start.ts
CHANGED
|
@@ -262,7 +262,21 @@ window.onload = () => {
|
|
| 262 |
currentParams['minimap'] = enableMinimap ? '1' : '0';
|
| 263 |
URLHandler.updateUrl(currentParams, false);
|
| 264 |
},
|
| 265 |
-
onSemanticAnalysisToggle: () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
visualizationUpdater.syncSemanticUiFromConfig();
|
| 267 |
},
|
| 268 |
},
|
|
|
|
| 262 |
currentParams['minimap'] = enableMinimap ? '1' : '0';
|
| 263 |
URLHandler.updateUrl(currentParams, false);
|
| 264 |
},
|
| 265 |
+
onSemanticAnalysisToggle: (enabled: boolean) => {
|
| 266 |
+
// 打开/关闭时都清除 URL 参数、输入框、选项,避免残留旧数据
|
| 267 |
+
const currentParams = URLHandler.parameters;
|
| 268 |
+
delete currentParams['semantic_query'];
|
| 269 |
+
delete currentParams['semantic_submode'];
|
| 270 |
+
delete currentParams['semantic_color_source'];
|
| 271 |
+
URLHandler.updateUrl(currentParams, false);
|
| 272 |
+
const queryEl = document.getElementById('semantic_search_input') as HTMLInputElement | null;
|
| 273 |
+
if (queryEl) queryEl.value = '';
|
| 274 |
+
const submodeEl = document.getElementById('semantic_submode_select') as HTMLSelectElement | null;
|
| 275 |
+
if (submodeEl) submodeEl.value = 'count';
|
| 276 |
+
const colorEl = document.getElementById('semantic_color_source_select') as HTMLSelectElement | null;
|
| 277 |
+
if (colorEl) colorEl.value = 'raw_score_normed';
|
| 278 |
+
appStateManager.setLastSearchedQuery(null);
|
| 279 |
+
if (enabled) visualizationUpdater.clearSemanticState();
|
| 280 |
visualizationUpdater.syncSemanticUiFromConfig();
|
| 281 |
},
|
| 282 |
},
|
client/src/ts/utils/settingsMenuManager.ts
CHANGED
|
@@ -96,6 +96,10 @@ export class SettingsMenuManager {
|
|
| 96 |
this.semanticAnalysisToggle.on('change', () => {
|
| 97 |
const enabled = (this.semanticAnalysisToggle.node() as HTMLInputElement)?.checked || false;
|
| 98 |
setSemanticAnalysisEnabled(enabled);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
if (this.callbacks.onSemanticAnalysisToggle) {
|
| 100 |
this.callbacks.onSemanticAnalysisToggle(enabled);
|
| 101 |
}
|
|
|
|
| 96 |
this.semanticAnalysisToggle.on('change', () => {
|
| 97 |
const enabled = (this.semanticAnalysisToggle.node() as HTMLInputElement)?.checked || false;
|
| 98 |
setSemanticAnalysisEnabled(enabled);
|
| 99 |
+
// 自动同步 Disable info density(用户仍可随时手动切换)
|
| 100 |
+
setInfoDensityRenderDisabled(enabled);
|
| 101 |
+
this.setDisableInfoDensity(enabled);
|
| 102 |
+
window.dispatchEvent(new CustomEvent('info-density-render-change'));
|
| 103 |
if (this.callbacks.onSemanticAnalysisToggle) {
|
| 104 |
this.callbacks.onSemanticAnalysisToggle(enabled);
|
| 105 |
}
|
client/src/ts/utils/signalThresholdDetector.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { fitLogNormalTruncatedMLE, logNormalExpectedCountInInterval, normCdf, LN
|
|
| 22 |
import { computeFitQuality } from './fitQuality';
|
| 23 |
|
| 24 |
/** 置信度阈值,达到此值即判定「确定找到」信号边界;默认 0.9999 */
|
| 25 |
-
const CONFIDENCE_THRESHOLD = 0.
|
| 26 |
/** excess 最小阈值,排除无意义随机波动;需 excess > 此值才计为命中 */
|
| 27 |
const EXCESS_MIN = 0.1;
|
| 28 |
const MIN_OBSERVED = 1; // 每个 bin 至少 N 个观测
|
|
|
|
| 22 |
import { computeFitQuality } from './fitQuality';
|
| 23 |
|
| 24 |
/** 置信度阈值,达到此值即判定「确定找到」信号边界;默认 0.9999 */
|
| 25 |
+
const CONFIDENCE_THRESHOLD = 0.99999;
|
| 26 |
/** excess 最小阈值,排除无意义随机波动;需 excess > 此值才计为命中 */
|
| 27 |
const EXCESS_MIN = 0.1;
|
| 28 |
const MIN_OBSERVED = 1; // 每个 bin 至少 N 个观测
|
client/src/ts/utils/topkChartUtils.ts
CHANGED
|
@@ -7,6 +7,8 @@ import * as d3 from 'd3';
|
|
| 7 |
import { processCandidateText } from './tokenDisplayUtils';
|
| 8 |
|
| 9 |
const DISPLAY_TOPK = 10;
|
|
|
|
|
|
|
| 10 |
/** Tooltip 默认条形宽度 */
|
| 11 |
const MAX_BAR_WIDTH = 60;
|
| 12 |
/** Semantic debug 专用:更大条形与列宽,tooltip 不受影响 */
|
|
@@ -23,6 +25,8 @@ export interface TopkChartOptions {
|
|
| 23 |
/** 条形列单元格宽度 px */
|
| 24 |
barCellWidth?: number;
|
| 25 |
numFormat?: (n: number) => string;
|
|
|
|
|
|
|
| 26 |
}
|
| 27 |
|
| 28 |
function getThemeColors(): { normalColor: string; selectedColor: string } {
|
|
@@ -44,13 +48,17 @@ export function renderTopkChartHtml(
|
|
| 44 |
const norm = options?.normalColor ?? normalColor;
|
| 45 |
const sel = options?.selectedColor ?? selectedColor;
|
| 46 |
const maxBar = options?.maxBarWidth ?? MAX_BAR_WIDTH;
|
| 47 |
-
const numF = options?.numFormat ?? d3.format('.
|
| 48 |
|
| 49 |
const maxProb = data[0]?.prob ?? 1;
|
| 50 |
const scale = d3.scaleLinear().domain([0, maxProb]).range([0, maxBar]);
|
| 51 |
const barCellW = options?.barCellWidth ?? 110;
|
|
|
|
| 52 |
|
| 53 |
-
const rows = data.slice(0,
|
|
|
|
|
|
|
|
|
|
| 54 |
const color = options?.selectedToken !== undefined && d.token === options.selectedToken ? sel : norm;
|
| 55 |
const bar = `<div style="display: table-cell; width:${barCellW}px;padding-left:5px;">` +
|
| 56 |
`<div style="display:inline-block;width: ${scale(d.prob)}px;background-color:${color};height: 10px;"></div>` +
|
|
|
|
| 7 |
import { processCandidateText } from './tokenDisplayUtils';
|
| 8 |
|
| 9 |
const DISPLAY_TOPK = 10;
|
| 10 |
+
/** 插入数据中表示省略行的占位符,左侧列空、token 列显示 ⋮ */
|
| 11 |
+
export const TOPK_SEP = '\0__TOPK_SEP__\0';
|
| 12 |
/** Tooltip 默认条形宽度 */
|
| 13 |
const MAX_BAR_WIDTH = 60;
|
| 14 |
/** Semantic debug 专用:更大条形与列宽,tooltip 不受影响 */
|
|
|
|
| 25 |
/** 条形列单元格宽度 px */
|
| 26 |
barCellWidth?: number;
|
| 27 |
numFormat?: (n: number) => string;
|
| 28 |
+
/** 最大显示行数,默认 DISPLAY_TOPK */
|
| 29 |
+
maxRows?: number;
|
| 30 |
}
|
| 31 |
|
| 32 |
function getThemeColors(): { normalColor: string; selectedColor: string } {
|
|
|
|
| 48 |
const norm = options?.normalColor ?? normalColor;
|
| 49 |
const sel = options?.selectedColor ?? selectedColor;
|
| 50 |
const maxBar = options?.maxBarWidth ?? MAX_BAR_WIDTH;
|
| 51 |
+
const numF = options?.numFormat ?? ((v: number) => d3.format('.3g')(v * 100) + '%');
|
| 52 |
|
| 53 |
const maxProb = data[0]?.prob ?? 1;
|
| 54 |
const scale = d3.scaleLinear().domain([0, maxProb]).range([0, maxBar]);
|
| 55 |
const barCellW = options?.barCellWidth ?? 110;
|
| 56 |
+
const maxRows = options?.maxRows ?? DISPLAY_TOPK;
|
| 57 |
|
| 58 |
+
const rows = data.slice(0, maxRows).map((d) => {
|
| 59 |
+
if (d.token === TOPK_SEP) {
|
| 60 |
+
return `<div class="row" style="display: block; text-align: left; padding-left: 30px; color: ${norm}; font-weight: bold;">⋮</div>`;
|
| 61 |
+
}
|
| 62 |
const color = options?.selectedToken !== undefined && d.token === options.selectedToken ? sel : norm;
|
| 63 |
const bar = `<div style="display: table-cell; width:${barCellW}px;padding-left:5px;">` +
|
| 64 |
`<div style="display:inline-block;width: ${scale(d.prob)}px;background-color:${color};height: 10px;"></div>` +
|
client/src/ts/utils/visualizationConfigs.ts
CHANGED
|
@@ -77,7 +77,7 @@ export const getSurprisalProgressConfig = (): ScatterPlotBaseConfig => ({
|
|
| 77 |
* 获取 Raw score normed 直方图配置(归一化 0-1)
|
| 78 |
*/
|
| 79 |
export const getRawScoreNormedHistogramConfig = (): HistogramBaseConfig => ({
|
| 80 |
-
label: tr("semantic
|
| 81 |
no_bins: 20,
|
| 82 |
xAxisTickSkip: 1,
|
| 83 |
xAxisTickRound: true,
|
|
|
|
| 77 |
* 获取 Raw score normed 直方图配置(归一化 0-1)
|
| 78 |
*/
|
| 79 |
export const getRawScoreNormedHistogramConfig = (): HistogramBaseConfig => ({
|
| 80 |
+
label: tr("semantic score histogram"),
|
| 81 |
no_bins: 20,
|
| 82 |
xAxisTickSkip: 1,
|
| 83 |
xAxisTickRound: true,
|
client/src/ts/utils/visualizationUpdater.ts
CHANGED
|
@@ -438,6 +438,16 @@ export class VisualizationUpdater {
|
|
| 438 |
this.updateSemanticDebugInfo();
|
| 439 |
}
|
| 440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
/**
|
| 442 |
* 根据语义分析配置同步 UI 状态(查询输入框、文本渲染模式等)
|
| 443 |
* 界面完全由配置决定,不因数据有无而改变
|
|
@@ -447,6 +457,19 @@ export class VisualizationUpdater {
|
|
| 447 |
const el = document.getElementById('semantic_analysis_section');
|
| 448 |
if (el) el.style.display = enabled ? '' : 'none';
|
| 449 |
this.deps.lmf.updateOptions({ semanticAnalysisMode: enabled }, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
// 语义分析配置影响 Upload/Save 的 dataReadyForSave 条件,需始终更新按钮状态
|
| 451 |
this.deps.appStateManager.updateButtonStates();
|
| 452 |
}
|
|
@@ -661,6 +684,7 @@ export class VisualizationUpdater {
|
|
| 661 |
}
|
| 662 |
if (!abbrev && !top10?.length) {
|
| 663 |
el.style.display = 'none';
|
|
|
|
| 664 |
return;
|
| 665 |
}
|
| 666 |
el.style.display = 'block';
|
|
|
|
| 438 |
this.updateSemanticDebugInfo();
|
| 439 |
}
|
| 440 |
|
| 441 |
+
/**
|
| 442 |
+
* 清除语义分析相关数据(直方图、debug、semanticData),用于打开模式时初始化
|
| 443 |
+
*/
|
| 444 |
+
public clearSemanticState(): void {
|
| 445 |
+
this.currentState.semanticData = null;
|
| 446 |
+
const rawScoreNormedItem = document.getElementById('raw_score_normed_histogram_item');
|
| 447 |
+
if (rawScoreNormedItem) rawScoreNormedItem.style.display = 'none';
|
| 448 |
+
this.updateSemanticDebugInfo();
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
/**
|
| 452 |
* 根据语义分析配置同步 UI 状态(查询输入框、文本渲染模式等)
|
| 453 |
* 界面完全由配置决定,不因数据有无而改变
|
|
|
|
| 457 |
const el = document.getElementById('semantic_analysis_section');
|
| 458 |
if (el) el.style.display = enabled ? '' : 'none';
|
| 459 |
this.deps.lmf.updateOptions({ semanticAnalysisMode: enabled }, false);
|
| 460 |
+
if (!enabled) {
|
| 461 |
+
// 关闭时清除语义数据、直方图、debug 信息(不重渲染,避免重复渲染信息密度)
|
| 462 |
+
this.currentState.semanticData = null;
|
| 463 |
+
const rawScoreNormedItem = document.getElementById('raw_score_normed_histogram_item');
|
| 464 |
+
if (rawScoreNormedItem) rawScoreNormedItem.style.display = 'none';
|
| 465 |
+
this.updateSemanticDebugInfo();
|
| 466 |
+
const displayResult = this.computeDisplayResult();
|
| 467 |
+
this.deps.highlightController.updateCurrentData(displayResult ? { result: displayResult } : null);
|
| 468 |
+
if (!displayResult) {
|
| 469 |
+
d3.select('#all_result').style('opacity', 0);
|
| 470 |
+
this.deps.appStateManager.updateState({ hasValidData: false });
|
| 471 |
+
}
|
| 472 |
+
}
|
| 473 |
// 语义分析配置影响 Upload/Save 的 dataReadyForSave 条件,需始终更新按钮状态
|
| 474 |
this.deps.appStateManager.updateButtonStates();
|
| 475 |
}
|
|
|
|
| 684 |
}
|
| 685 |
if (!abbrev && !top10?.length) {
|
| 686 |
el.style.display = 'none';
|
| 687 |
+
el.innerHTML = '';
|
| 688 |
return;
|
| 689 |
}
|
| 690 |
el.style.display = 'block';
|
client/src/ts/vis/ToolTip.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as d3 from "d3";
|
|
| 6 |
import { tr } from "../lang/i18n-lite";
|
| 7 |
import { getTokenRenderStyle } from "../utils/tokenRenderStyle";
|
| 8 |
import { escapeHtml, visualizeSpecialChars } from "../utils/tokenDisplayUtils";
|
| 9 |
-
import { renderTopkChartHtml } from "../utils/topkChartUtils";
|
| 10 |
|
| 11 |
const SEPARATOR = '─────────────';
|
| 12 |
|
|
@@ -280,9 +280,12 @@ export class ToolTip {
|
|
| 280 |
const tokenData = ri.tokenData as FrontendToken;
|
| 281 |
const s = ri.semantic;
|
| 282 |
const hasSemantic = s && (s.pwScore !== undefined || s.signalProb !== undefined || s.rawScoreNormed !== undefined || s.rawScore !== undefined);
|
| 283 |
-
const hasRealTopk = tokenData?.real_topk != null && Array.isArray(tokenData.real_topk);
|
| 284 |
const predTopk = tokenData?.pred_topk ?? [];
|
| 285 |
const hasPredictions = predTopk.length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
|
| 287 |
// 1. 构建语义区块(pw score = raw_score_normed × P_pw,P_pw: x≤threshold 为 0,x>threshold 为 1)
|
| 288 |
const semanticRows: string[] = [];
|
|
@@ -300,7 +303,6 @@ export class ToolTip {
|
|
| 300 |
const surprisal = calculateSurprisal(prob);
|
| 301 |
const isClassic = getTokenRenderStyle() === 'classic';
|
| 302 |
infoRows.push(renderField({ label: tr('information:'), value: `${this.significantF(surprisal)} bits` }, detailColor, valueColor));
|
| 303 |
-
infoRows.push(renderField({ label: tr('prob:'), value: this.significantF(prob), valueColor: false }, detailColor, valueColor));
|
| 304 |
if (!isClassic) {
|
| 305 |
const informationDensity = calculateSurprisalDensity(tokenData);
|
| 306 |
const utf8Size = new TextEncoder().encode(tokenData.raw).length;
|
|
@@ -332,10 +334,15 @@ export class ToolTip {
|
|
| 332 |
.style('display', 'block')
|
| 333 |
.html(() => `<div style="color:${detailColor};padding-left:5px;">${tr('Top-k data not available.')}</div>`);
|
| 334 |
} else {
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
this.predictions.html(renderTopkChartHtml(topkData, {
|
| 337 |
selectedToken: tokenData.raw,
|
| 338 |
-
|
| 339 |
}));
|
| 340 |
}
|
| 341 |
}
|
|
|
|
| 6 |
import { tr } from "../lang/i18n-lite";
|
| 7 |
import { getTokenRenderStyle } from "../utils/tokenRenderStyle";
|
| 8 |
import { escapeHtml, visualizeSpecialChars } from "../utils/tokenDisplayUtils";
|
| 9 |
+
import { renderTopkChartHtml, TOPK_SEP } from "../utils/topkChartUtils";
|
| 10 |
|
| 11 |
const SEPARATOR = '─────────────';
|
| 12 |
|
|
|
|
| 280 |
const tokenData = ri.tokenData as FrontendToken;
|
| 281 |
const s = ri.semantic;
|
| 282 |
const hasSemantic = s && (s.pwScore !== undefined || s.signalProb !== undefined || s.rawScoreNormed !== undefined || s.rawScore !== undefined);
|
|
|
|
| 283 |
const predTopk = tokenData?.pred_topk ?? [];
|
| 284 |
const hasPredictions = predTopk.length > 0;
|
| 285 |
+
// 占位符 real_topk: [0, 1](prob=1)+ 空 pred_topk 表示仅语义分析,无真实信息密度数据
|
| 286 |
+
const isPlaceholderTopk = tokenData?.real_topk != null && Array.isArray(tokenData.real_topk)
|
| 287 |
+
&& tokenData.real_topk[1] === 1 && predTopk.length === 0;
|
| 288 |
+
const hasRealTopk = tokenData?.real_topk != null && Array.isArray(tokenData.real_topk) && !isPlaceholderTopk;
|
| 289 |
|
| 290 |
// 1. 构建语义区块(pw score = raw_score_normed × P_pw,P_pw: x≤threshold 为 0,x>threshold 为 1)
|
| 291 |
const semanticRows: string[] = [];
|
|
|
|
| 303 |
const surprisal = calculateSurprisal(prob);
|
| 304 |
const isClassic = getTokenRenderStyle() === 'classic';
|
| 305 |
infoRows.push(renderField({ label: tr('information:'), value: `${this.significantF(surprisal)} bits` }, detailColor, valueColor));
|
|
|
|
| 306 |
if (!isClassic) {
|
| 307 |
const informationDensity = calculateSurprisalDensity(tokenData);
|
| 308 |
const utf8Size = new TextEncoder().encode(tokenData.raw).length;
|
|
|
|
| 334 |
.style('display', 'block')
|
| 335 |
.html(() => `<div style="color:${detailColor};padding-left:5px;">${tr('Top-k data not available.')}</div>`);
|
| 336 |
} else {
|
| 337 |
+
let topkData = predTopk.slice(0, 10).map(([token, prob]) => ({ token, prob }));
|
| 338 |
+
const isInTopk = topkData.some(d => d.token === tokenData.raw);
|
| 339 |
+
if (!isInTopk && hasRealTopk) {
|
| 340 |
+
const currentProb = tokenData.real_topk![1];
|
| 341 |
+
topkData = [...topkData, { token: TOPK_SEP, prob: 0 }, { token: tokenData.raw, prob: currentProb }];
|
| 342 |
+
}
|
| 343 |
this.predictions.html(renderTopkChartHtml(topkData, {
|
| 344 |
selectedToken: tokenData.raw,
|
| 345 |
+
maxRows: topkData.length,
|
| 346 |
}));
|
| 347 |
}
|
| 348 |
}
|
model_paths.py
CHANGED
|
@@ -11,8 +11,9 @@ DEFAULT_SEMANTIC_MODEL = "qwen3-0.6b-instruct"
|
|
| 11 |
SEMANTIC_MODEL_PATHS = {
|
| 12 |
"qwen3-0.6b-instruct": "Qwen/Qwen3-0.6B",
|
| 13 |
"qwen3-1.7b-instruct": "Qwen/Qwen3-1.7B",
|
| 14 |
-
# "qwen3-4b-instruct": "Qwen/Qwen3-4B",
|
| 15 |
"qwen3-4b-instruct": "Qwen/Qwen3-4B-Instruct-2507",
|
|
|
|
|
|
|
| 16 |
"qwen3.5-0.8b-instruct": "Qwen/Qwen3.5-0.8B",
|
| 17 |
"qwen3.5-2b-instruct": "Qwen/Qwen3.5-2B",
|
| 18 |
"qwen3.5-4b-instruct": "Qwen/Qwen3.5-4B"
|
|
@@ -22,16 +23,16 @@ SEMANTIC_MODEL_PATHS = {
|
|
| 22 |
MODEL_PATHS = {
|
| 23 |
# 标准模型(FP16/BF16)
|
| 24 |
'qwen2.5-0.5b': 'Qwen/Qwen2.5-0.5B',
|
|
|
|
| 25 |
'qwen3.0-0.6b': 'Qwen/Qwen3-0.6B-Base',
|
| 26 |
'qwen3.0-1.7b': 'Qwen/Qwen3-1.7B-Base',
|
| 27 |
'qwen3.0-4b': 'Qwen/Qwen3-4B-Base',
|
| 28 |
'qwen3.0-8b': 'Qwen/Qwen3-8B-Base',
|
| 29 |
'qwen3.0-14b': 'Qwen/Qwen3-14B-Base',
|
| 30 |
-
|
| 31 |
'qwen3.5-0.8b': 'Qwen/Qwen3.5-0.8B-Base',
|
| 32 |
-
'
|
| 33 |
-
'
|
| 34 |
-
|
| 35 |
# AWQ 量化模型(W4A16,显存占用约为标准模型的 1/4)
|
| 36 |
# 自动检测,仅支持 Docker + CUDA 环境
|
| 37 |
# Qwen3-14B-AWQ评估质量差,因为基于instruct版本而不是base版本
|
|
|
|
| 11 |
SEMANTIC_MODEL_PATHS = {
|
| 12 |
"qwen3-0.6b-instruct": "Qwen/Qwen3-0.6B",
|
| 13 |
"qwen3-1.7b-instruct": "Qwen/Qwen3-1.7B",
|
|
|
|
| 14 |
"qwen3-4b-instruct": "Qwen/Qwen3-4B-Instruct-2507",
|
| 15 |
+
"qwen3-8b-instruct": "Qwen/Qwen3-8B",
|
| 16 |
+
# qwen3.5
|
| 17 |
"qwen3.5-0.8b-instruct": "Qwen/Qwen3.5-0.8B",
|
| 18 |
"qwen3.5-2b-instruct": "Qwen/Qwen3.5-2B",
|
| 19 |
"qwen3.5-4b-instruct": "Qwen/Qwen3.5-4B"
|
|
|
|
| 23 |
MODEL_PATHS = {
|
| 24 |
# 标准模型(FP16/BF16)
|
| 25 |
'qwen2.5-0.5b': 'Qwen/Qwen2.5-0.5B',
|
| 26 |
+
# qwen3.0
|
| 27 |
'qwen3.0-0.6b': 'Qwen/Qwen3-0.6B-Base',
|
| 28 |
'qwen3.0-1.7b': 'Qwen/Qwen3-1.7B-Base',
|
| 29 |
'qwen3.0-4b': 'Qwen/Qwen3-4B-Base',
|
| 30 |
'qwen3.0-8b': 'Qwen/Qwen3-8B-Base',
|
| 31 |
'qwen3.0-14b': 'Qwen/Qwen3-14B-Base',
|
| 32 |
+
# qwen3.5
|
| 33 |
'qwen3.5-0.8b': 'Qwen/Qwen3.5-0.8B-Base',
|
| 34 |
+
'qwen3.5-2b': 'Qwen/Qwen3.5-2B-Base',
|
| 35 |
+
'qwen3.5-4b': 'Qwen/Qwen3.5-4B-Base',
|
|
|
|
| 36 |
# AWQ 量化模型(W4A16,显存占用约为标准模型的 1/4)
|
| 37 |
# 自动检测,仅支持 Docker + CUDA 环境
|
| 38 |
# Qwen3-14B-AWQ评估质量差,因为基于instruct版本而不是base版本
|
run.py
CHANGED
|
@@ -44,7 +44,7 @@ def _parse_args():
|
|
| 44 |
"--logits_gradient_submode",
|
| 45 |
default="fill_blank",
|
| 46 |
choices=["count", "match_score", "fill_blank"],
|
| 47 |
-
help="logits_gradient 子策略:count=数量;match_score=相关度打分;fill_blank=填空式",
|
| 48 |
)
|
| 49 |
parser.add_argument(
|
| 50 |
"--gradient_checkpointing",
|
|
|
|
| 44 |
"--logits_gradient_submode",
|
| 45 |
default="fill_blank",
|
| 46 |
choices=["count", "match_score", "fill_blank"],
|
| 47 |
+
help="logits_gradient 子策略:count=数量;match_score=相关度打分(已废弃);fill_blank=填空式",
|
| 48 |
)
|
| 49 |
parser.add_argument(
|
| 50 |
"--gradient_checkpointing",
|
scripts/cases/eval_cases_long.json
CHANGED
|
@@ -1,11 +1,112 @@
|
|
| 1 |
[
|
| 2 |
-
{
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
{
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
]
|
|
|
|
| 1 |
[
|
| 2 |
+
{
|
| 3 |
+
"name": "人工智能",
|
| 4 |
+
"query": "人工智能",
|
| 5 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 6 |
+
},
|
| 7 |
+
{
|
| 8 |
+
"name": "股票",
|
| 9 |
+
"query": "股票",
|
| 10 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"name": "墨西哥",
|
| 14 |
+
"query": "墨西哥",
|
| 15 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"name": "美联储",
|
| 19 |
+
"query": "美联储",
|
| 20 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,���新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"name": "航天",
|
| 24 |
+
"query": "航天",
|
| 25 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
"name": "玩具模型_无关",
|
| 29 |
+
"query": "玩具模型",
|
| 30 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"name": "爱情电影_无关",
|
| 34 |
+
"query": "爱情电影",
|
| 35 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"name": "经济以外",
|
| 39 |
+
"query": "与经济无关的内容",
|
| 40 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要���应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"name": "AI以外",
|
| 44 |
+
"query": "与AI无关的内容",
|
| 45 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"name": "政策",
|
| 49 |
+
"query": "政策",
|
| 50 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"name": "股票指数",
|
| 54 |
+
"query": "股票指数",
|
| 55 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"name": "经济数据",
|
| 59 |
+
"query": "经济数据",
|
| 60 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"name": "坏消息",
|
| 64 |
+
"query": "坏消息",
|
| 65 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"name": "利空消息",
|
| 69 |
+
"query": "利空消息",
|
| 70 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"name": "公司利空",
|
| 74 |
+
"query": "公司的利空消息",
|
| 75 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"name": "比预期差",
|
| 79 |
+
"query": "比预期差",
|
| 80 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"name": "公司名",
|
| 84 |
+
"query": "公司名",
|
| 85 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 86 |
+
},
|
| 87 |
+
{
|
| 88 |
+
"name": "国家名",
|
| 89 |
+
"query": "国家名",
|
| 90 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"name": "明示反义词",
|
| 94 |
+
"query": "明示的反义词",
|
| 95 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征���号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"name": "苹果水果_无关",
|
| 99 |
+
"query": "苹果(水果)",
|
| 100 |
+
"text": "【华尔街见闻早餐 | 2026年2月12日】标普500指数基本持平,纳指和道指小幅下跌;原油期货上涨;黄金、白银走高;镍价升至近两周来最高,主要供应国印尼暗示今年将大幅削减产量。\n美国1月非农新增就业13万人,创去年4月以来最大增幅,失业率降至4.3%,年度下修86.2万。华尔街预计首次降息延至7月,“新美联储通讯社”预计降息暂停期更久。\n油价日内涨超2%,报道:特朗普私下考虑退出《美墨加协定》。\n特朗普称同伊朗达成协议将是“首选”,伊朗最高领袖顾问:美国趋向理性。\n李强:全面推进人工智能科技创新、产业发展和赋能应用,培育壮大新质生产力。\n国务院国资委推动中央企业积极扩大算力有效投资。\n中国1月CPI同比涨幅回落至0.2%,PPI同比降幅收窄至1.4%。\n载人登月又一里程碑,长征十号与梦舟飞船首次飞行试验任务成功。\nDeepSeek正灰度测试新一代模型。\n智谱发布新一代旗舰模型GLM-5,重点提升编程与智能体能力。\n苹果新Siri发布或又推迟,据称测试暴露问题,部分功能或延至9月上线。 (来自华尔街见闻)"
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"name": "北京美食",
|
| 104 |
+
"query": "北京的美食",
|
| 105 |
+
"text": "北京烤鸭是北京最著名的特色美食,皮酥肉嫩,配上薄饼和甜面酱。\n故宫是明清两代的皇家宫殿,也是世界上现存规模最大的木质结构古建筑群。\n炸酱面是北京的传统面食,用黄酱配上黄瓜丝和豆芽菜。\n长城是中国古代的军事防御工程,绵延数千公里。\n老北京涮羊肉以铜锅为特色,羊肉鲜嫩,蘸料丰富。\n天坛是明清两代皇帝祭天的场所,建筑精美。\n豆汁儿是北京独特的传统小吃,口味特别,配上焦圈最地道。\n颐和园是清朝的皇家园林,以昆明湖和万寿山为主体。\n南京鸭血粉丝汤是南京最著名的特色美食。\n但是南京的盐水鸭就不好吃了。不喜欢它的颜色和味道。\n苏州的美食臊子面也挺好吃的。"
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"name": "北京景点",
|
| 109 |
+
"query": "北京的景点",
|
| 110 |
+
"text": "北京烤鸭是北京最著名的特色美食,皮酥肉嫩,配上薄饼和甜面酱。\n故宫是明清两代的皇家宫殿,也是世界上现存规模最大的木质结构古建筑群。\n炸酱面是北京的传统面食,用黄酱配上黄瓜丝和豆芽菜。\n长城是中国古代的军事防御工程,绵延数千公里。\n老北京涮羊肉以铜锅为特色,羊肉鲜嫩,蘸料丰富。\n天坛是明清两代皇帝祭天的场所,建筑精美。\n豆汁儿是北京独特的传统小吃,口味特别,配上焦圈最地道。\n颐和园是清朝的皇家园林,以昆明湖和万寿山为主体。\n南京鸭血粉丝汤是南京最著名的特色美食。\n但是南京的盐水鸭就不好吃了。不喜欢它的颜色和味道。\n苏州的美食臊子面也挺好吃的。"
|
| 111 |
+
}
|
| 112 |
]
|
scripts/{eval_semantic_submodes.py → eval_semantic.py}
RENAMED
|
@@ -1,29 +1,31 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
-
|
| 4 |
|
| 5 |
通过 HTTP 调用 /api/analyze-semantic 接口进行评估。
|
| 6 |
|
|
|
|
|
|
|
| 7 |
评估维度:
|
| 8 |
1. 生成的 top10 (token和概率) 的合理性
|
| 9 |
-
2. token_attention score 的合理性
|
| 10 |
3. 完全无关查询时的结果合理性
|
| 11 |
|
| 12 |
用法(从项目根目录运行):
|
| 13 |
-
python scripts/
|
| 14 |
-
python scripts/
|
| 15 |
-
python scripts/
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
不指定 --submode 时依次评估五个子模式;-c 可指定多个 JSON 用例文件。
|
| 19 |
"""
|
| 20 |
|
| 21 |
import argparse
|
| 22 |
import json
|
| 23 |
import os
|
| 24 |
import sys
|
|
|
|
| 25 |
from pathlib import Path
|
| 26 |
-
from typing import Optional
|
| 27 |
|
| 28 |
# Hugging Face Token(用于Private Space,可通过环境变量HF_TOKEN设置)
|
| 29 |
HF_TOKEN_ENV = "HF_TOKEN"
|
|
@@ -49,12 +51,12 @@ TEST_CASES = [
|
|
| 49 |
DEFAULT_API_BASE = "http://localhost:5001"
|
| 50 |
|
| 51 |
|
| 52 |
-
def analyze_semantic_http(api_base: str, query: str, text: str, submode:
|
| 53 |
"""通过 HTTP 调用 analyze-semantic 接口"""
|
| 54 |
url = f"{api_base.rstrip('/')}/api/analyze-semantic"
|
| 55 |
-
payload: dict = {"query": query, "text": text, "
|
| 56 |
-
if
|
| 57 |
-
payload["
|
| 58 |
headers = {"Content-Type": "application/json"}
|
| 59 |
if token:
|
| 60 |
headers["Authorization"] = f"Bearer {token}"
|
|
@@ -67,25 +69,68 @@ def analyze_semantic_http(api_base: str, query: str, text: str, submode: str, to
|
|
| 67 |
return data
|
| 68 |
|
| 69 |
|
| 70 |
-
def
|
|
|
|
|
|
|
|
|
|
| 71 |
results = []
|
| 72 |
-
for
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
print(f"query: {query}")
|
| 76 |
-
print(f"text: {text[:50]}...")
|
| 77 |
-
print("=" * 60)
|
| 78 |
try:
|
| 79 |
-
|
| 80 |
-
except
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
di = res.get("debug_info", {})
|
| 91 |
topk_tokens = di.get("topk_tokens", [])
|
|
@@ -126,34 +171,30 @@ def run_eval(api_base: str, submode: str, test_cases: list, token: Optional[str]
|
|
| 126 |
},
|
| 127 |
}
|
| 128 |
results.append(record)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
-
|
| 131 |
-
for t, p in zip(topk_tokens, topk_probs):
|
| 132 |
-
print(f" {repr(t):20} {p*100:.2f}%")
|
| 133 |
-
print(f"\n📌 token_attention 中 score 最高的 10 个 token (score / 0-max归一化):")
|
| 134 |
-
for item in top_scored:
|
| 135 |
-
print(f" {repr(item['raw']):20} score={item['score']} norm={item['score_norm']} offset={item['offset']}")
|
| 136 |
-
print(f"\n📌 score 统计: min={record['score_stats']['min']}, max={record['score_stats']['max']}, mean={record['score_stats']['mean']}, mean_norm={record['score_stats']['mean_norm']}")
|
| 137 |
-
if record.get("full_match_degree") is not None:
|
| 138 |
-
print(f"📌 full_match_degree: {record['full_match_degree']}")
|
| 139 |
-
|
| 140 |
-
return results
|
| 141 |
|
| 142 |
|
| 143 |
def main():
|
| 144 |
-
parser = argparse.ArgumentParser(description="评估 semantic analyzer
|
| 145 |
parser.add_argument(
|
| 146 |
"--submode",
|
| 147 |
-
choices=["
|
| 148 |
nargs="+",
|
| 149 |
default=None,
|
| 150 |
-
help="
|
| 151 |
)
|
| 152 |
parser.add_argument(
|
| 153 |
"--output", "-o",
|
| 154 |
type=Path,
|
| 155 |
default=None,
|
| 156 |
-
help="结果输出
|
| 157 |
)
|
| 158 |
parser.add_argument(
|
| 159 |
"--url",
|
|
@@ -166,12 +207,6 @@ def main():
|
|
| 166 |
default=None,
|
| 167 |
help=f"Hugging Face Token(用于Private Space,也可通过环境变量{HF_TOKEN_ENV}设置)",
|
| 168 |
)
|
| 169 |
-
parser.add_argument(
|
| 170 |
-
"--timeout",
|
| 171 |
-
type=int,
|
| 172 |
-
default=300,
|
| 173 |
-
help="请求超时秒数",
|
| 174 |
-
)
|
| 175 |
parser.add_argument(
|
| 176 |
"--cases", "-c",
|
| 177 |
type=Path,
|
|
@@ -180,11 +215,16 @@ def main():
|
|
| 180 |
help="自定义测试用例 JSON 文件,可指定多个,格式 [{name, query, text}, ...]",
|
| 181 |
)
|
| 182 |
parser.add_argument(
|
| 183 |
-
"--
|
| 184 |
-
|
| 185 |
-
default=
|
| 186 |
-
|
| 187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
)
|
| 189 |
args = parser.parse_args()
|
| 190 |
|
|
@@ -195,20 +235,29 @@ def main():
|
|
| 195 |
test_cases = []
|
| 196 |
for path in args.cases:
|
| 197 |
raw = json.loads(path.read_text(encoding="utf-8"))
|
| 198 |
-
|
|
|
|
| 199 |
print(f"已加载 {len(test_cases)} 个用例,来自 {len(args.cases)} 个文件")
|
| 200 |
else:
|
| 201 |
test_cases = TEST_CASES
|
| 202 |
|
| 203 |
-
submodes = args.submode if args.submode else ["
|
| 204 |
-
|
| 205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
for sm in submodes:
|
| 207 |
-
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
if args.output:
|
| 210 |
-
args.output
|
| 211 |
-
print(f"\n✅ 结果已写入 {args.output}")
|
| 212 |
|
| 213 |
|
| 214 |
if __name__ == "__main__":
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
+
Semantic analyzer 效果评估脚本
|
| 4 |
|
| 5 |
通过 HTTP 调用 /api/analyze-semantic 接口进行评估。
|
| 6 |
|
| 7 |
+
支持 submode:count / match_score(已废弃) / fill_blank
|
| 8 |
+
|
| 9 |
评估维度:
|
| 10 |
1. 生成的 top10 (token和概率) 的合理性
|
| 11 |
+
2. token_attention score 的合理性
|
| 12 |
3. 完全无关查询时的结果合理性
|
| 13 |
|
| 14 |
用法(从项目根目录运行):
|
| 15 |
+
python scripts/eval_semantic.py -c scripts/cases/eval_cases_short.json -o eval_result.jsonl
|
| 16 |
+
python scripts/eval_semantic.py --submode count fill_blank -o eval_result.jsonl
|
| 17 |
+
python scripts/eval_semantic.py --url http://localhost:5001
|
| 18 |
+
|
| 19 |
+
输出为 JSONL 格式,每完成一例追加一行;中断后可再次运行,从中断处续跑。
|
|
|
|
| 20 |
"""
|
| 21 |
|
| 22 |
import argparse
|
| 23 |
import json
|
| 24 |
import os
|
| 25 |
import sys
|
| 26 |
+
import time
|
| 27 |
from pathlib import Path
|
| 28 |
+
from typing import Optional, Tuple
|
| 29 |
|
| 30 |
# Hugging Face Token(用于Private Space,可通过环境变量HF_TOKEN设置)
|
| 31 |
HF_TOKEN_ENV = "HF_TOKEN"
|
|
|
|
| 51 |
DEFAULT_API_BASE = "http://localhost:5001"
|
| 52 |
|
| 53 |
|
| 54 |
+
def analyze_semantic_http(api_base: str, query: str, text: str, submode: Optional[str] = None, token: Optional[str] = None, timeout: int = 300) -> dict:
|
| 55 |
"""通过 HTTP 调用 analyze-semantic 接口"""
|
| 56 |
url = f"{api_base.rstrip('/')}/api/analyze-semantic"
|
| 57 |
+
payload: dict = {"query": query, "text": text, "debug_info": True}
|
| 58 |
+
if submode is not None:
|
| 59 |
+
payload["submode"] = submode
|
| 60 |
headers = {"Content-Type": "application/json"}
|
| 61 |
if token:
|
| 62 |
headers["Authorization"] = f"Bearer {token}"
|
|
|
|
| 69 |
return data
|
| 70 |
|
| 71 |
|
| 72 |
+
def _load_jsonl(path: Path) -> list:
|
| 73 |
+
"""加载 JSONL 文件,用于断点续跑"""
|
| 74 |
+
if not path.exists():
|
| 75 |
+
return []
|
| 76 |
results = []
|
| 77 |
+
for line in path.read_text(encoding="utf-8").strip().split("\n"):
|
| 78 |
+
if not line:
|
| 79 |
+
continue
|
|
|
|
|
|
|
|
|
|
| 80 |
try:
|
| 81 |
+
results.append(json.loads(line))
|
| 82 |
+
except json.JSONDecodeError:
|
| 83 |
+
pass
|
| 84 |
+
return results
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _append_record(path: Path, record: dict) -> None:
|
| 88 |
+
"""追加单条记录到 JSONL 文件"""
|
| 89 |
+
with path.open("a", encoding="utf-8") as f:
|
| 90 |
+
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def run_eval(
|
| 94 |
+
api_base: str,
|
| 95 |
+
submode: str,
|
| 96 |
+
test_cases: list,
|
| 97 |
+
token: Optional[str] = None,
|
| 98 |
+
output_path: Optional[Path] = None,
|
| 99 |
+
all_results: Optional[list] = None,
|
| 100 |
+
completed: Optional[set] = None,
|
| 101 |
+
max_retries: int = 3,
|
| 102 |
+
timeout: int = 300,
|
| 103 |
+
) -> Tuple[list, bool]:
|
| 104 |
+
"""返回 (results, aborted),重试后仍失败时 aborted 为 True"""
|
| 105 |
+
completed = completed or set()
|
| 106 |
+
results = []
|
| 107 |
+
for j, (name, query, text) in enumerate(test_cases):
|
| 108 |
+
prog = f"[{j+1}/{len(test_cases)}]"
|
| 109 |
+
if (submode, name) in completed:
|
| 110 |
+
print(f"{prog} ⏭ 跳过: {submode} | {name}", flush=True)
|
| 111 |
continue
|
| 112 |
+
print(f"{prog} 执行: {submode} | {name}", flush=True)
|
| 113 |
+
res = None
|
| 114 |
+
last_error = None
|
| 115 |
+
for attempt in range(max_retries + 1):
|
| 116 |
+
try:
|
| 117 |
+
res = analyze_semantic_http(api_base, query, text, submode, token=token, timeout=timeout)
|
| 118 |
+
break
|
| 119 |
+
except Exception as e:
|
| 120 |
+
last_error = e
|
| 121 |
+
if attempt < max_retries:
|
| 122 |
+
wait = 3 * (attempt + 1)
|
| 123 |
+
print(f"{prog} 重试 {attempt + 1}/{max_retries},{wait}s 后... - {e}", flush=True)
|
| 124 |
+
time.sleep(wait)
|
| 125 |
+
if res is None:
|
| 126 |
+
print(f"{prog} ✗ 失败(已重试 {max_retries} 次): {submode} | {name} - {last_error}", flush=True)
|
| 127 |
+
record = {"submode": submode, "case": name, "query": query, "error": str(last_error)}
|
| 128 |
+
results.append(record)
|
| 129 |
+
if all_results is not None:
|
| 130 |
+
all_results.append(record)
|
| 131 |
+
completed.add((submode, name))
|
| 132 |
+
print(f"\n⚠ 重试后仍失败,中断后续用例", flush=True)
|
| 133 |
+
return results, True
|
| 134 |
|
| 135 |
di = res.get("debug_info", {})
|
| 136 |
topk_tokens = di.get("topk_tokens", [])
|
|
|
|
| 171 |
},
|
| 172 |
}
|
| 173 |
results.append(record)
|
| 174 |
+
if all_results is not None:
|
| 175 |
+
all_results.append(record)
|
| 176 |
+
completed.add((submode, name))
|
| 177 |
+
if output_path:
|
| 178 |
+
_append_record(output_path, record)
|
| 179 |
+
print(f"{prog} ✓ 完成: {submode} | {name}", flush=True)
|
| 180 |
|
| 181 |
+
return results, False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
|
| 184 |
def main():
|
| 185 |
+
parser = argparse.ArgumentParser(description="评估 semantic analyzer 效果(HTTP)")
|
| 186 |
parser.add_argument(
|
| 187 |
"--submode",
|
| 188 |
+
choices=["count", "match_score", "fill_blank"],
|
| 189 |
nargs="+",
|
| 190 |
default=None,
|
| 191 |
+
help="instruct 模型子模式(可多个),不指定则依次评估 count/fill_blank;match_score 已废弃",
|
| 192 |
)
|
| 193 |
parser.add_argument(
|
| 194 |
"--output", "-o",
|
| 195 |
type=Path,
|
| 196 |
default=None,
|
| 197 |
+
help="结果输出 JSONL 路径(支持断点续跑)",
|
| 198 |
)
|
| 199 |
parser.add_argument(
|
| 200 |
"--url",
|
|
|
|
| 207 |
default=None,
|
| 208 |
help=f"Hugging Face Token(用于Private Space,也可通过环境变量{HF_TOKEN_ENV}设置)",
|
| 209 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
parser.add_argument(
|
| 211 |
"--cases", "-c",
|
| 212 |
type=Path,
|
|
|
|
| 215 |
help="自定义测试用例 JSON 文件,可指定多个,格式 [{name, query, text}, ...]",
|
| 216 |
)
|
| 217 |
parser.add_argument(
|
| 218 |
+
"--retries",
|
| 219 |
+
type=int,
|
| 220 |
+
default=3,
|
| 221 |
+
help="失败时自动重试次数,默认 3",
|
| 222 |
+
)
|
| 223 |
+
parser.add_argument(
|
| 224 |
+
"--timeout",
|
| 225 |
+
type=int,
|
| 226 |
+
default=300,
|
| 227 |
+
help="单次请求超时秒数,默认 300",
|
| 228 |
)
|
| 229 |
args = parser.parse_args()
|
| 230 |
|
|
|
|
| 235 |
test_cases = []
|
| 236 |
for path in args.cases:
|
| 237 |
raw = json.loads(path.read_text(encoding="utf-8"))
|
| 238 |
+
# strip() 与浏览器语义分析时的 trim() 保持一致,避免 token 数差异
|
| 239 |
+
test_cases.extend([(c["name"], c["query"], (c["text"] or "").strip()) for c in raw])
|
| 240 |
print(f"已加载 {len(test_cases)} 个用例,来自 {len(args.cases)} 个文件")
|
| 241 |
else:
|
| 242 |
test_cases = TEST_CASES
|
| 243 |
|
| 244 |
+
submodes = args.submode if args.submode else ["count", "match_score", "fill_blank"]
|
| 245 |
+
all_results: list = []
|
| 246 |
+
completed: set = set()
|
| 247 |
+
if args.output and args.output.exists():
|
| 248 |
+
all_results = _load_jsonl(args.output)
|
| 249 |
+
completed = {(r["submode"], r["case"]) for r in all_results}
|
| 250 |
+
print(f"已加载 {len(all_results)} 条历史结果,从中断处续跑")
|
| 251 |
for sm in submodes:
|
| 252 |
+
_, aborted = run_eval(
|
| 253 |
+
api_base, sm, test_cases, token=hf_token,
|
| 254 |
+
output_path=args.output, all_results=all_results,
|
| 255 |
+
completed=completed, max_retries=args.retries, timeout=args.timeout,
|
| 256 |
+
)
|
| 257 |
+
if aborted:
|
| 258 |
+
break
|
| 259 |
if args.output:
|
| 260 |
+
print(f"\n✅ 结果已写入 {args.output}(共 {len(all_results)} 条)")
|
|
|
|
| 261 |
|
| 262 |
|
| 263 |
if __name__ == "__main__":
|
server.yaml
CHANGED
|
@@ -472,7 +472,7 @@ paths:
|
|
| 472 |
submode:
|
| 473 |
type: string
|
| 474 |
enum: [count, match_score, fill_blank]
|
| 475 |
-
description: 可选子模式,不传则用服务端默认
|
| 476 |
required:
|
| 477 |
- query
|
| 478 |
- text
|
|
|
|
| 472 |
submode:
|
| 473 |
type: string
|
| 474 |
enum: [count, match_score, fill_blank]
|
| 475 |
+
description: 可选子模式,不传则用服务端默认。match_score 已废弃
|
| 476 |
required:
|
| 477 |
- query
|
| 478 |
- text
|