@customize-agent/knowledge 4.0.44 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -48,6 +48,12 @@ export declare class TextChunker {
|
|
|
48
48
|
private extractMarkdownTableBlocks;
|
|
49
49
|
private extractMarkdownTableRowRange;
|
|
50
50
|
private splitMarkdownTable;
|
|
51
|
+
/** 按行边界拆分超预算表块:表头行与数据行均保持完整;单行超预算也保持完整,不做窗口硬切 */
|
|
52
|
+
private splitTableByRowBoundary;
|
|
53
|
+
/** 表头行数(表头行 + 分隔行),非标准表格返回 0 */
|
|
54
|
+
private tableHeaderLineCount;
|
|
55
|
+
/** 计算切分片段在原文本中的起始偏移(顺序扫描,避免共享表头前缀导致 indexOf 重复命中) */
|
|
56
|
+
private chunkPartStartOffsets;
|
|
51
57
|
private enforceCandidateLimit;
|
|
52
58
|
private buildTitlePaths;
|
|
53
59
|
private withHeader;
|
|
@@ -413,12 +413,58 @@ export class TextChunker {
|
|
|
413
413
|
}
|
|
414
414
|
if (current.length > 0)
|
|
415
415
|
chunks.push([...header, ...current].join('\n'));
|
|
416
|
-
|
|
416
|
+
// 行原子性:超出预算的表块按行边界拆分并保留表头,绝不切开数据行(旧 splitByWindow 会把行切成碎片)
|
|
417
|
+
return chunks.flatMap(chunk => this.estimateTokens(chunk) > maxTokens ? this.splitTableByRowBoundary(chunk, header.length, maxTokens) : [chunk]);
|
|
418
|
+
}
|
|
419
|
+
/** 按行边界拆分超预算表块:表头行与数据行均保持完整;单行超预算也保持完整,不做窗口硬切 */
|
|
420
|
+
splitTableByRowBoundary(chunk, headerLineCount, maxTokens) {
|
|
421
|
+
const lines = chunk.split(/\r?\n/u).filter(Boolean);
|
|
422
|
+
const header = lines.slice(0, Math.min(headerLineCount, lines.length - 1));
|
|
423
|
+
const rows = lines.slice(header.length);
|
|
424
|
+
if (rows.length <= 1)
|
|
425
|
+
return [chunk];
|
|
426
|
+
const mid = Math.ceil(rows.length / 2);
|
|
427
|
+
const left = [...header, ...rows.slice(0, mid)].join('\n');
|
|
428
|
+
const right = [...header, ...rows.slice(mid)].join('\n');
|
|
429
|
+
return [left, right].flatMap(part => this.estimateTokens(part) > maxTokens ? this.splitTableByRowBoundary(part, header.length, maxTokens) : [part]);
|
|
430
|
+
}
|
|
431
|
+
/** 表头行数(表头行 + 分隔行),非标准表格返回 0 */
|
|
432
|
+
tableHeaderLineCount(text) {
|
|
433
|
+
const lines = text.trim().split(/\r?\n/u);
|
|
434
|
+
const separatorIndex = lines.findIndex(line => /^\s*\|?\s*:?-{3,}:?\s*\|/u.test(line));
|
|
435
|
+
return separatorIndex > 0 ? separatorIndex + 1 : 0;
|
|
436
|
+
}
|
|
437
|
+
/** 计算切分片段在原文本中的起始偏移(顺序扫描,避免共享表头前缀导致 indexOf 重复命中) */
|
|
438
|
+
chunkPartStartOffsets(text, parts) {
|
|
439
|
+
const offsets = [];
|
|
440
|
+
let cursor = 0;
|
|
441
|
+
for (const part of parts) {
|
|
442
|
+
const index = text.indexOf(part, cursor);
|
|
443
|
+
offsets.push(index >= 0 ? index : cursor);
|
|
444
|
+
cursor = index >= 0 ? index + part.length : cursor;
|
|
445
|
+
}
|
|
446
|
+
return offsets;
|
|
417
447
|
}
|
|
418
448
|
enforceCandidateLimit(candidates, config) {
|
|
419
449
|
return candidates.flatMap(candidate => {
|
|
420
450
|
if (this.estimateTokens(candidate.text) <= config.maxChunkSize)
|
|
421
451
|
return [candidate];
|
|
452
|
+
// 表格候选:按行边界拆分并保留表头,避免 splitByWindow 把数据行切成碎片
|
|
453
|
+
if (candidate.kind === 'table') {
|
|
454
|
+
const headerLineCount = this.tableHeaderLineCount(candidate.text);
|
|
455
|
+
if (headerLineCount > 0) {
|
|
456
|
+
const parts = this.splitTableByRowBoundary(candidate.text, headerLineCount, config.maxChunkSize);
|
|
457
|
+
const offsets = this.chunkPartStartOffsets(candidate.text, parts);
|
|
458
|
+
return parts.map((text, index) => ({
|
|
459
|
+
...candidate,
|
|
460
|
+
text,
|
|
461
|
+
childIndex: candidate.childIndex + index,
|
|
462
|
+
startChar: candidate.startChar + (offsets[index] ?? 0),
|
|
463
|
+
endChar: candidate.startChar + (offsets[index] ?? 0) + text.length,
|
|
464
|
+
rowRange: candidate.rowRange ? `${candidate.rowRange}#${index + 1}` : undefined,
|
|
465
|
+
}));
|
|
466
|
+
}
|
|
467
|
+
}
|
|
422
468
|
return this.splitByWindow(candidate.text, config.maxChunkSize, config.overlap).map((text, index) => ({
|
|
423
469
|
...candidate,
|
|
424
470
|
text,
|