@customize-agent/knowledge 4.3.3 → 4.3.5

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.
@@ -109,9 +109,14 @@ function collectTenderFormatBlock(lines, start) {
109
109
  /** 招标文件:泛化引用行("详见投标人须知前附表"类短行,无实质项目信息;后缀短语可选) */
110
110
  const TENDER_GENERIC_LINE_RE = /^(详见|具体见|以|按).{0,30}?(前附表|招标文件|招标公告|补疑|澄清|清单|图纸|合同文件)((的)?(要求|规定|为准|执行))?[。;;]?$|^投标人应(仔细|认真)(阅读|核对)(招标文件|本项目)(的全部内容)?[。;;]?$|^未(按|依照|按照)(上述|本|招标文件)要求/u;
111
111
  /** 招标文件:电子投标程序句(行级)。投标人须知条目式排版无可靠段落边界,按句删除
112
- * 程序性操作(加密/解密/上传/撤回/签号/病毒防范/获取/发布/提出/发出/发送/下载/拒收/提交/开标/导入/制作/中断/在线);
112
+ * 程序性操作(加密/解密/上传/撤回/签号/病毒防范/获取/发布/提出/发出/发送/下载/拒收/提交/开标/导入/制作/中断/在线/查询/登录/进行/公示);
113
113
  * 双重信号(程序动词 + 程序对象)防止误删技术条款。仅招标文件生效。 */
114
- const TENDER_EPROCEDURE_LINE_RE = /(?:电子交易系统|数字证书|加密|解密|撤回操作|上传投标文件|签号|病毒防范).{0,45}(?:上传|递交|撤回|解密|完成|显示|关闭|发放|发送|防范|获取|发布|提出|发出|下载|拒收|查看|接收|提交|开标|导入|拒绝|制作|中断|在线)|(?:投标截止时间).{0,35}(?:上传|递交|撤回|自动关闭)/u;
114
+ const TENDER_EPROCEDURE_PREFIX_RE = /(?:电子交易系统|数字证书|加密|解密|撤回操作|上传投标文件|签号|病毒防范)/u;
115
+ const TENDER_EPROCEDURE_LINE_RE = /(?:电子交易系统|数字证书|加密|解密|撤回操作|上传投标文件|签号|病毒防范).{0,45}(?:上传|递交|撤回|解密|完成|显示|关闭|发放|发送|防范|获取|发布|提出|发出|下载|拒收|查看|接收|提交|开标|导入|拒绝|制作|中断|在线|查询|登录|进行|公示)|(?:投标截止时间).{0,35}(?:上传|递交|撤回|自动关闭)/u;
116
+ /** 具体时间/地点证据:跨行拼接删除的防护——拼接后含实质时间地点信息的不删 */
117
+ const TENDER_TIME_PLACE_RE = /\d{4}年\d{1,2}月\d{1,2}日|\d{1,2}时\d{1,2}分|地点[::为是]/u;
118
+ /** 折行接续行判定:行首非编号/序号/条文标题(PDF 折行接续的行首是句中词) */
119
+ const TENDER_CONTINUATION_HEAD_RE = /^(?:###\s*)?(?:第[一二三四五六七八九十百零两\d]+[条章节]|[((]?[一二三四五六七八九十\d]+[))、.、.]|\d{1,3}[.、.]|[【[])/u;
115
120
  /** 补疑文件:零信息回复行("答:按招标文件执行"类套话;含数字/单位的实质回复不删) */
116
121
  const CLARIFICATION_BOILERPLATE_RE = /^(?:(?:答|答复|回复)[::]\s*)?(?:按|以)(招标文件|招标公告|合同文件|清单|图纸|补疑|答疑)?(约定|规定|要求|执行|为准)[。;;]?$|^(?:(?:答|答复|回复)[::]\s*)?不(作|做|予)(调整|修改|变更)[。;;]?$|^(?:(?:答|答复|回复)[::]\s*)?维持(不变|原状)[。;;]?$|^(?:(?:答|答复|回复)[::]\s*)?(本项|此项)(无|没有)(调整|修改|变化)[。;;]?$/u;
117
122
  /** 图纸:无汉字纯坐标数字行(≥2 组相邻 "数字,数字" 坐标对即 ≥3 个连续坐标值,设计坐标不写入施组正文) */
@@ -424,6 +429,24 @@ export function cleanExtractedText(input) {
424
429
  index += 1;
425
430
  continue;
426
431
  }
432
+ // 招标文件:电子投标程序句(跨行拼接;PDF 提取折行把程序动词折到下一行,行间夹空行)
433
+ if (TENDER_EPROCEDURE_PREFIX_RE.test(trimmed)
434
+ && !/[。;;::!!??]$/u.test(trimmed)) {
435
+ let nextIdx = index + 1;
436
+ while (nextIdx < lines.length && !lines[nextIdx].trim())
437
+ nextIdx += 1;
438
+ if (nextIdx < lines.length && nextIdx - index <= 3) {
439
+ const next = lines[nextIdx].trim();
440
+ const continuation = next.length > 0 && next.length < 80 && !TENDER_CONTINUATION_HEAD_RE.test(next);
441
+ const joined = `${trimmed}${next}`;
442
+ if (continuation && TENDER_EPROCEDURE_LINE_RE.test(joined) && !TENDER_TIME_PLACE_RE.test(joined)) {
443
+ drop(line, s => { s.tenderEprocedureLines += 1; });
444
+ drop(lines[nextIdx], s => { s.tenderEprocedureLines += 1; });
445
+ index = nextIdx + 1;
446
+ continue;
447
+ }
448
+ }
449
+ }
427
450
  }
428
451
  // 6. 补疑文件:零信息回复行(行级,长度 ≤25 且无数字/单位)
429
452
  if (docKind === 'clarification' && trimmed.length <= 25 && CLARIFICATION_BOILERPLATE_RE.test(trimmed) && !/\d/u.test(trimmed)) {
@@ -856,6 +856,10 @@ export class KnowledgeBaseManager {
856
856
  rerankBoost += 160;
857
857
  if (normalizedPhrase.length >= 4 && normalizedContent.includes(normalizedPhrase))
858
858
  rerankBoost += 220;
859
+ // 时间类查询下,含具体日期/时刻的 chunk 是精确答案(如「获取时间:2026年8月13日」),
860
+ // 应优先于仅含规则条款的程序句(「距投标截止时间不足15日」类无具体时间)
861
+ if (/(时间|日期|工期|截止|开标|获取|递交)/u.test(query) && /20\d{2}年\d{1,2}月\d{1,2}日|\d{1,2}时\d{1,2}分/u.test(item.content))
862
+ rerankBoost += 60;
859
863
  const titleText = `${item.titlePath ?? ''}\n${item.sectionTitle ?? ''}`.toLowerCase();
860
864
  const normalizedTitle = this.normalizeSearchText(titleText);
861
865
  let matchedTermCount = 0;
@@ -873,7 +877,7 @@ export class KnowledgeBaseManager {
873
877
  rerankBoost += 22;
874
878
  }
875
879
  if (matchedTermCount >= 2)
876
- rerankBoost += matchedTermCount * 18;
880
+ rerankBoost += matchedTermCount * 30;
877
881
  for (const label of factLabels) {
878
882
  const normalizedLabel = this.normalizeSearchText(label);
879
883
  if (normalizedContent.includes(normalizedLabel))
@@ -881,8 +885,11 @@ export class KnowledgeBaseManager {
881
885
  if (normalizedTitle.includes(normalizedLabel))
882
886
  rerankBoost += 120;
883
887
  }
888
+ // 表格块惩罚仅针对「无任何查询词命中」的表格(清单特征块命中查询词时应保留竞争力);
889
+ // 表格行(清单特征「混凝土强度等级:C35」)在向量空间天然弱势,RRF 名次扁平下
890
+ // 若再吃 -80 惩罚,会被普通段落(绿建专篇)挤到后面,丢失精确词命中优势
884
891
  if (item.chunkKind === 'table')
885
- rerankBoost += wantsTable ? 25 : -80;
892
+ rerankBoost += wantsTable ? 25 : (matchedTermCount > 0 ? -10 : -80);
886
893
  if (item.chunkKind === 'metadata')
887
894
  rerankBoost += wantsDrawing ? 60 : -100;
888
895
  if (item.chunkKind === 'data')
@@ -893,7 +900,9 @@ export class KnowledgeBaseManager {
893
900
  rerankBoost -= wantsDrawing ? 60 : 160;
894
901
  }
895
902
  if (/工作表:|COL\d+|专业工程暂估价计价表|分部分项工程量清单计价表|材料(工程设备)暂估单价一览表/u.test(item.content)) {
896
- rerankBoost -= wantsTable ? 35 : 140;
903
+ // 计价表行无查询词命中是纯标题噪音(-140);命中查询词的清单特征行
904
+ // (「混凝土强度等级:C35」)是用户直接目标,仅轻惩罚维持正文优先
905
+ rerankBoost -= wantsTable ? 35 : (matchedTermCount > 0 ? 20 : 140);
897
906
  }
898
907
  if (/第\s*\d+\s*页\s*共\s*\d+\s*页|PDF\s*第\s*\d+\s*页/iu.test(item.content) && factLabels.length > 0)
899
908
  rerankBoost -= 12;
@@ -921,8 +930,12 @@ export class KnowledgeBaseManager {
921
930
  }
922
931
  queryTerms(query) {
923
932
  const base = query.toLowerCase().split(/[\s,,。;;::、]+/u).filter(Boolean);
933
+ // 字母数字与汉字连续串按边界拆词(与 store.expandSearchTerms 的边界拆分一致):
934
+ // 「C35混凝土」须拆出「c35」「混凝土」,重排层才能识别清单特征块的双词命中,
935
+ // 否则 terms 只有整串连写短语,对「强度等级:C35」类特征块零命中、错吃 table 惩罚
936
+ const splitParts = base.flatMap(term => term.split(/([\p{Script=Han}]+)/u).filter(part => part.length > 0));
924
937
  const labels = this.queryFactLabels(query).map(label => label.toLowerCase());
925
- return [...new Set([...base, ...labels].filter(term => term.length > 0))];
938
+ return [...new Set([...base, ...splitParts, ...labels].filter(term => term.length > 0))];
926
939
  }
927
940
  queryFactLabels(query) {
928
941
  const labels = [];
@@ -944,6 +957,12 @@ export class KnowledgeBaseManager {
944
957
  labels.push('招标人', '项目业主', '建设单位');
945
958
  if (/临水|临电|临时水电|水电接引|接驳点|挂表计量|施工水电/u.test(query))
946
959
  labels.push('临水临电', '临时水电接引', '施工水电接引费', '接驳点挂表计量', '挂表计量');
960
+ if (/获取时间|获取方式/u.test(query))
961
+ labels.push('获取时间', '招标文件获取时间');
962
+ if (/投标截止|截止时间/u.test(query))
963
+ labels.push('投标截止时间', '投标文件递交的截止时间');
964
+ if (/开标时间|开标地点|开标/u.test(query))
965
+ labels.push('开标时间和地点', '开标时间', '开标地点');
947
966
  if (/场地限制|材料堆场|办公区|生活区|加工区/u.test(query))
948
967
  labels.push('场地限制', '不具备材料堆场', '搭设加工区', '搭设办公区', '搭设生活区');
949
968
  if (/拆除|修补|破损处|改造维修/u.test(query))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@customize-agent/knowledge",
3
- "version": "4.3.3",
3
+ "version": "4.3.5",
4
4
  "description": "Local knowledge base infrastructure for customize-agent",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",