Fix garbled text checks and L0 LLM handling
This commit is contained in:
@@ -26,7 +26,13 @@ export function isBatchCancelled() {
|
||||
|
||||
const SYSTEM_PROMPT = `你是叙事锚点提取器。从一轮对话(用户发言+角色回复)中提取4-8个关键锚点。
|
||||
|
||||
只输出JSON:
|
||||
输入格式:
|
||||
<round>
|
||||
<user>...</user>
|
||||
<assistant>...</assistant>
|
||||
</round>
|
||||
|
||||
只输出严格JSON(不要解释,不要前后多余文字):
|
||||
{"atoms":[{"t":"类型","s":"主体","v":"值","f":"来源"}]}
|
||||
|
||||
类型(t):
|
||||
@@ -72,13 +78,13 @@ async function extractAtomsForRoundWithRetry(userMessage, aiMessage, aiFloor, op
|
||||
|
||||
if (userMessage?.mes?.trim()) {
|
||||
const userText = filterText(userMessage.mes);
|
||||
parts.push(`【用户:${userName}】\n${userText}`);
|
||||
parts.push(`<user name="${userName}">\n${userText}\n</user>`);
|
||||
}
|
||||
|
||||
const aiText = filterText(aiMessage.mes);
|
||||
parts.push(`【角色:${aiName}】\n${aiText}`);
|
||||
parts.push(`<assistant name="${aiName}">\n${aiText}\n</assistant>`);
|
||||
|
||||
const input = parts.join('\n\n---\n\n');
|
||||
const input = `<round>\n${parts.join('\n')}\n</round>`;
|
||||
|
||||
xbLog.info(MODULE_ID, `floor ${aiFloor} 发送输入 len=${input.length}`);
|
||||
|
||||
@@ -89,43 +95,46 @@ async function extractAtomsForRoundWithRetry(userMessage, aiMessage, aiFloor, op
|
||||
const response = await callLLM([
|
||||
{ role: 'system', content: SYSTEM_PROMPT },
|
||||
{ role: 'user', content: input },
|
||||
{ role: 'assistant', content: '收到,开始提取并仅输出 JSON。' },
|
||||
], {
|
||||
temperature: 0.2,
|
||||
max_tokens: 500,
|
||||
timeout,
|
||||
});
|
||||
|
||||
if (!response || !String(response).trim()) {
|
||||
const rawText = String(response || '');
|
||||
if (!rawText.trim()) {
|
||||
xbLog.warn(MODULE_ID, `floor ${aiFloor} 解析失败:响应为空`);
|
||||
if (attempt < RETRY_COUNT) {
|
||||
await sleep(RETRY_DELAY);
|
||||
continue;
|
||||
}
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
parsed = parseJson(response);
|
||||
parsed = parseJson(rawText);
|
||||
} catch (e) {
|
||||
xbLog.warn(MODULE_ID, `floor ${aiFloor} 解析失败:JSON 异常`);
|
||||
if (attempt < RETRY_COUNT) {
|
||||
await sleep(RETRY_DELAY);
|
||||
continue;
|
||||
}
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!parsed?.atoms || !Array.isArray(parsed.atoms)) {
|
||||
xbLog.warn(MODULE_ID, `floor ${aiFloor} atoms 缺失,raw="${rawText.slice(0, 300)}"`);
|
||||
xbLog.warn(MODULE_ID, `floor ${aiFloor} 解析失败:atoms 缺失`);
|
||||
if (attempt < RETRY_COUNT) {
|
||||
await sleep(RETRY_DELAY);
|
||||
continue;
|
||||
}
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsed.atoms
|
||||
const filtered = parsed.atoms
|
||||
.filter(a => a?.t && a?.v)
|
||||
.map((a, idx) => ({
|
||||
atomId: `atom-${aiFloor}-${idx}`,
|
||||
@@ -136,9 +145,13 @@ async function extractAtomsForRoundWithRetry(userMessage, aiMessage, aiFloor, op
|
||||
source: a.f === 'u' ? 'user' : 'ai',
|
||||
semantic: buildSemantic(a, userName, aiName),
|
||||
}));
|
||||
if (!filtered.length) {
|
||||
xbLog.warn(MODULE_ID, `floor ${aiFloor} atoms 为空,raw="${rawText.slice(0, 300)}"`);
|
||||
}
|
||||
return filtered;
|
||||
|
||||
} catch (e) {
|
||||
if (batchCancelled) return [];
|
||||
if (batchCancelled) return null;
|
||||
|
||||
if (attempt < RETRY_COUNT) {
|
||||
xbLog.warn(MODULE_ID, `floor ${aiFloor} 第${attempt + 1}次失败,重试...`, e?.message);
|
||||
@@ -146,11 +159,11 @@ async function extractAtomsForRoundWithRetry(userMessage, aiMessage, aiFloor, op
|
||||
continue;
|
||||
}
|
||||
xbLog.error(MODULE_ID, `floor ${aiFloor} 失败`, e);
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// vector/llm/llm-service.js
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
import { xbLog } from '../../../../core/debug-core.js';
|
||||
import { getVectorConfig } from '../../data/config.js';
|
||||
|
||||
const MODULE_ID = 'vector-llm-service';
|
||||
const SILICONFLOW_API_URL = 'https://api.siliconflow.cn';
|
||||
const DEFAULT_L0_MODEL = 'Qwen/Qwen3-8B';
|
||||
|
||||
// 唯一 ID 计数器
|
||||
let callCounter = 0;
|
||||
@@ -36,11 +38,17 @@ export async function callLLM(messages, options = {}) {
|
||||
} = options;
|
||||
|
||||
const mod = getStreamingModule();
|
||||
if (!mod) throw new Error('生成模块未加载');
|
||||
if (!mod) throw new Error('Streaming module not ready');
|
||||
|
||||
const cfg = getVectorConfig();
|
||||
const apiKey = cfg?.online?.key || '';
|
||||
if (!apiKey) {
|
||||
throw new Error('L0 requires siliconflow API key');
|
||||
}
|
||||
|
||||
const top64 = b64UrlEncode(JSON.stringify(messages));
|
||||
|
||||
// ★ 每次调用用唯一 ID,避免 session 冲突
|
||||
// 每次调用用唯一 ID,避免 session 冲突
|
||||
const uniqueId = generateUniqueId('l0');
|
||||
|
||||
const args = {
|
||||
@@ -50,6 +58,10 @@ export async function callLLM(messages, options = {}) {
|
||||
id: uniqueId,
|
||||
temperature: String(temperature),
|
||||
max_tokens: String(max_tokens),
|
||||
api: 'openai',
|
||||
apiurl: SILICONFLOW_API_URL,
|
||||
apipassword: apiKey,
|
||||
model: DEFAULT_L0_MODEL,
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user