@things-factory/ai-client-base 10.1.3 → 10.1.6

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.
Files changed (39) hide show
  1. package/dist-server/service/assistant/abort-notice.d.ts +7 -0
  2. package/dist-server/service/assistant/abort-notice.js +29 -0
  3. package/dist-server/service/assistant/abort-notice.js.map +1 -0
  4. package/dist-server/service/assistant/assistant-chat.d.ts +41 -0
  5. package/dist-server/service/assistant/assistant-chat.js +63 -0
  6. package/dist-server/service/assistant/assistant-chat.js.map +1 -0
  7. package/dist-server/service/assistant/index.d.ts +4 -0
  8. package/dist-server/service/assistant/index.js +13 -0
  9. package/dist-server/service/assistant/index.js.map +1 -0
  10. package/dist-server/service/assistant/registry-gate.d.ts +31 -0
  11. package/dist-server/service/assistant/registry-gate.js +61 -0
  12. package/dist-server/service/assistant/registry-gate.js.map +1 -0
  13. package/dist-server/service/assistant/summarize.d.ts +7 -0
  14. package/dist-server/service/assistant/summarize.js +67 -0
  15. package/dist-server/service/assistant/summarize.js.map +1 -0
  16. package/dist-server/service/index.d.ts +1 -0
  17. package/dist-server/service/index.js +1 -0
  18. package/dist-server/service/index.js.map +1 -1
  19. package/dist-server/service/tool-registry/registry.js +19 -0
  20. package/dist-server/service/tool-registry/registry.js.map +1 -1
  21. package/dist-server/service/tool-registry/types.d.ts +11 -1
  22. package/dist-server/service/tool-registry/types.js.map +1 -1
  23. package/dist-server/service/utils/grounding.d.ts +25 -2
  24. package/dist-server/service/utils/grounding.js +44 -8
  25. package/dist-server/service/utils/grounding.js.map +1 -1
  26. package/dist-server/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +2 -2
  28. package/server/service/assistant/abort-notice.ts +44 -0
  29. package/server/service/assistant/assistant-chat.test.ts +218 -0
  30. package/server/service/assistant/assistant-chat.ts +99 -0
  31. package/server/service/assistant/index.ts +9 -0
  32. package/server/service/assistant/registry-gate.ts +76 -0
  33. package/server/service/assistant/summarize.ts +61 -0
  34. package/server/service/index.ts +1 -0
  35. package/server/service/tool-registry/registry.test.ts +59 -4
  36. package/server/service/tool-registry/registry.ts +22 -0
  37. package/server/service/tool-registry/types.ts +11 -1
  38. package/server/service/utils/grounding.test.ts +49 -0
  39. package/server/service/utils/grounding.ts +74 -8
@@ -18,21 +18,43 @@
18
18
  * 한국어·하이픈 없는 일반어는 애초에 매칭하지 않는다.
19
19
  */
20
20
 
21
- /** id 관례 — 소문자로 시작, 하이픈으로 이어진 소문자·숫자 토큰. `fk-1` `weld-station` `a-dc` 등. */
22
- const ID_TOKEN = /[a-z][a-z0-9]*(?:-[a-z0-9]+)+/g
21
+ /**
22
+ * id 관례 — 영문자로 시작, 하이픈으로 이어진 영숫자 토큰. `fk-1` · `weld-station` · `WO-ECU-2609B`.
23
+ *
24
+ * ── 소문자만 보다가 두 제품을 통째로 놓치고 있었다 (2026-09-10) ──────────────
25
+ * 이 패턴이 `[a-z]…` 였다. 그래서 **대문자 식별자를 쓰는 제품에는 이 가드가 아예 닿지 않았다.**
26
+ * 실측:
27
+ *
28
+ * 'WO-ECU-SET-2609B' → [] plant 의 실제 오더 이름(seed/sets/svm-gunsan.ts)
29
+ * 'TAG-FUNC-AGING' → [] 트윈의 태그
30
+ * 'dock-3' → ['dock-3']
31
+ *
32
+ * 즉 「지어낸 오더 번호를 잡는다」는 보장이 plant 에서는 성립하지 않았고, 경고가 0 건인 것이
33
+ * 「깨끗하다」로 읽혔다. 없는 것과 안 보는 것이 같은 그림이었다.
34
+ *
35
+ * 넓혀도 소음이 늘지 않는 이유는 `filterEntityLike` 가 뒤에 있기 때문이다 — `Read-Only` 는 숫자도
36
+ * 없고 근거에 `Read-` 계열도 없어 그대로 걸러진다.
37
+ */
38
+ const ID_TOKEN = /[A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)+/g
23
39
 
24
- /** 문자열에서 id 후보 토큰을 뽑는다(중복 제거). */
40
+ /** 문자열에서 id 후보 토큰을 뽑는다(중복 제거). **쓰인 그대로** 담는다 — 사람에게 보일 값이다. */
25
41
  export function extractIdTokens(s: string): Set<string> {
26
42
  const out = new Set<string>()
27
43
  for (const m of (s ?? '').matchAll(ID_TOKEN)) out.add(m[0])
28
44
  return out
29
45
  }
30
46
 
31
- /** 답이 언급한 id − 근거 코퍼스의 id = 미확인(환각 후보). 순서는 답에 나온 순서. */
47
+ /**
48
+ * 답이 언급한 id − 근거 코퍼스의 id = 미확인(환각 후보). 순서는 답에 나온 순서.
49
+ *
50
+ * **대조는 대소문자를 무시한다.** 모델이 `WO-2609B` 를 `wo-2609b` 로 적는 것은 표기이지 다른
51
+ * 대상이 아니다. 엄격히 보면 그 한 글자 차이로 정상 답이 경고를 받고, 그런 경고가 몇 번 나오면
52
+ * 사람이 경고 자체를 안 본다. 대소문자만 다른 두 식별자를 쓰는 체계라면 그것이 먼저 고칠 문제다.
53
+ */
32
54
  export function checkGrounding(reply: string, groundedCorpus: string): string[] {
33
- const grounded = extractIdTokens(groundedCorpus)
55
+ const grounded = new Set([...extractIdTokens(groundedCorpus)].map(id => id.toLowerCase()))
34
56
  const mentioned = extractIdTokens(reply)
35
- return [...mentioned].filter(id => !grounded.has(id))
57
+ return [...mentioned].filter(id => !grounded.has(id.toLowerCase()))
36
58
  }
37
59
 
38
60
  /**
@@ -51,9 +73,11 @@ export function checkGrounding(reply: string, groundedCorpus: string): string[]
51
73
  * 잡으려면 일반 영어 합성어를 함께 잡게 되므로, 이 가드는 **정확도를 택하고 재현율을 버린다**.
52
74
  */
53
75
  export function filterEntityLike(tokens: string[], groundedCorpus: string): string[] {
76
+ /* 계열 대조도 대소문자를 무시한다 — `checkGrounding` 과 같은 규칙이어야 한다. 한쪽만 엄격하면
77
+ * 근거에 `WO-1` 이 있는데 답의 `wo-7` 이 계열 밖으로 새어 경고를 놓친다. */
54
78
  const families = new Set<string>()
55
- for (const id of extractIdTokens(groundedCorpus)) families.add(id.split('-')[0])
56
- return tokens.filter(t => /[0-9]/.test(t) || families.has(t.split('-')[0]))
79
+ for (const id of extractIdTokens(groundedCorpus)) families.add(id.split('-')[0].toLowerCase())
80
+ return tokens.filter(t => /[0-9]/.test(t) || families.has(t.split('-')[0].toLowerCase()))
57
81
  }
58
82
 
59
83
  /**
@@ -63,3 +87,45 @@ export function filterEntityLike(tokens: string[], groundedCorpus: string): stri
63
87
  export function groundingWarnings(reply: string, groundedCorpus: string): string[] {
64
88
  return filterEntityLike(checkGrounding(reply, groundedCorpus), groundedCorpus)
65
89
  }
90
+
91
+ /*
92
+ * ── 근거집합의 정의 (2026-09-10 에 board-ai 에서 옮겨 왔다) ────────────────────
93
+ *
94
+ * 이 모양이 `board-ai` 에 있었고, 그래서 접지 검사를 하려면 보드 편집기를 지나야 했다. 판정 규칙은
95
+ * 여기 있었는데 **무엇을 근거로 볼 것인가**가 저기 있었으니, 보드가 없는 제품은 규칙만 있고 쓸 길이
96
+ * 없었다. 자리는 있고 길이 없는 쪽이다.
97
+ *
98
+ * 옮기면서 칸 하나를 고쳤다: `boardContext` → `hostContext`. 근거는 보드일 수도 있고 공장 현황일
99
+ * 수도 있고 저작 중인 자산일 수도 있다 — 그것을 아는 것은 호스트뿐이다.
100
+ *
101
+ * **하나라도 빼면 정상 답이 경고를 받는다.** 모델이 시스템 프롬프트에서 읽은 타입 이름을 말하는 것은
102
+ * 근거 있는 발언이다. 그래서 이 목록은 「모델이 이 턴에 본 것 전부」이고, 줄이는 것이 아니라 채우는
103
+ * 것이 맞다.
104
+ */
105
+ export interface GroundingSources {
106
+ /** 시스템 프롬프트 — 생성 가능한 타입 목록·규칙 등. 여기 있는 이름을 말하는 것은 정상이다. */
107
+ systemPrompt?: string
108
+ /** 호스트가 대화 앞에 붙인 문맥 — 보드 요약·현장 상태·저작 중인 자산. */
109
+ hostContext?: string
110
+ /** 사용자·어시스턴트 이력 텍스트. 사용자가 말한 식별자를 되받는 것은 정상이다. */
111
+ messages?: string[]
112
+ /**
113
+ * `tool_result` 로 회신된 **원문**(시간순).
114
+ *
115
+ * ⚠ `toolUsages[].result` 를 넣지 말 것 — 그것은 화면 표시용으로 깊이·길이가 잘린 값이다. 잘려
116
+ * 나간 식별자를 「근거 없음」으로 오판한다. 넣을 값은 `AgenticLoopResult.groundedTexts` 다.
117
+ */
118
+ toolResults?: string[]
119
+ }
120
+
121
+ /** 근거집합을 한 덩어리로. 식별자 토큰만 대조하므로 구분자는 개행으로 충분하다. */
122
+ export function buildGroundedCorpus(sources: GroundingSources): string {
123
+ return [
124
+ sources.systemPrompt ?? '',
125
+ sources.hostContext ?? '',
126
+ ...(sources.messages ?? []),
127
+ ...(sources.toolResults ?? [])
128
+ ]
129
+ .filter(Boolean)
130
+ .join('\n')
131
+ }