kiku-agent 1.0.11 → 1.2.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.
- package/README.md +300 -495
- package/package.json +12 -11
- package/public/fonts/GoogleSans-Bold.ttf +0 -0
- package/public/fonts/GoogleSans-Bold.woff2 +0 -0
- package/public/fonts/GoogleSans-BoldItalic.ttf +0 -0
- package/public/fonts/GoogleSans-Italic.ttf +0 -0
- package/public/fonts/GoogleSans-Latin-400.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-500.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-600.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-700.woff2 +0 -0
- package/public/fonts/GoogleSans-Medium.ttf +0 -0
- package/public/fonts/GoogleSans-Medium.woff2 +0 -0
- package/public/fonts/GoogleSans-Regular.ttf +0 -0
- package/public/fonts/GoogleSans-Regular.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-400.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-500.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-600.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-700.woff2 +0 -0
- package/public/fonts/NotoSansThai-Bold.woff2 +0 -0
- package/public/fonts/NotoSansThai-Regular.woff2 +0 -0
- package/public/fonts/google-sans-official.css +900 -0
- package/public/images/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
- package/public/images/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
- package/public/images/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
- package/public/images/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
- package/public/images/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
- package/public/images/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
- package/public/images/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
- package/public/images/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-399915.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-424041.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-588166.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-672198.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-700522.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-839722.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026825-460586.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/263202694-689407.jpg +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-399915.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-424041.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-588166.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-672198.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-700522.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-839722.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026825-460586.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/263202694-689407.mp4 +0 -0
- package/public/images/general/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
- package/public/images/general/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
- package/public/images/general/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
- package/public/images/general/jujingyi_portrait.jpg +0 -0
- package/public/images/jujingyi_portrait.jpg +0 -0
- package/public/images/series/a-millenniums-wait/poster.jpg +0 -0
- package/public/images/series/beauty-of-resilience/poster.jpg +0 -0
- package/public/images/series/detective-samoyeds/poster.jpg +0 -0
- package/public/images/series/everyday/poster.jpg +0 -0
- package/public/images/series/fu-rong-ancient-painting/poster.jpg +0 -0
- package/public/images/series/in-a-class-of-her-own/poster.jpg +0 -0
- package/public/images/series/in-blossom/poster.jpg +0 -0
- package/public/images/series/legend-of-yunxi/poster.jpg +0 -0
- package/public/images/series/lets-fight/poster-2.jpg +0 -0
- package/public/images/series/lets-fight/poster.jpg +0 -0
- package/public/images/series/love-emergency/poster.jpg +0 -0
- package/public/images/series/love-under-the-full-moon/poster.jpg +0 -0
- package/public/images/series/money-bastion/poster.jpg +0 -0
- package/public/images/series/mr-swimmer/poster.jpg +0 -0
- package/public/images/series/novoland-castle-in-the-sky/poster.jpg +0 -0
- package/public/images/series/please-give-me-a-pair-of-wings/poster.jpg +0 -0
- package/public/images/series/rebirth-for-you/poster.jpg +0 -0
- package/public/images/series/sword-and-fairy-4/poster.jpg +0 -0
- package/public/images/series/the-blooms-at-ruyi-pavilion/poster.jpg +0 -0
- package/public/images/series/the-eternal-fragrance/poster-2.jpg +0 -0
- package/public/images/series/the-eternal-fragrance/poster.jpg +0 -0
- package/public/images/series/the-fallen-flowers/poster.jpg +0 -0
- package/public/images/series/the-legend-of-white-snake/poster.jpg +0 -0
- package/public/images/series/veil-of-shadows/poster.jpg +0 -0
- package/public/images/series/wanhua-sin/poster-2.jpg +0 -0
- package/public/images/series/wanhua-sin/poster.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj10yogj62u03s0npe02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj2pdvyj61li25jkjl02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj71177j62672w9e8302.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj7twpfj62192197rw02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj8jfu8j62c03401kx02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj9dhdsj622k2l77wh02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkja31s4j62bw3407r802.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjcf331j61402ey1kx02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjdx8gqj62602pihdt02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjfvxnzj62432n57wh02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixniv2yj627y4fxb2b02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixsvbdaj625k2vfu1102.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixxxdaoj62c03501l202.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixzeef9j62c0340npd02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy0vs6tj626t4rbhdu02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy21wwuj62bt34hx6q02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly4wr85j62z629y7wh02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly67oilj623u2t5npd02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly9qtw8j62c0340u0x02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyem4m4j63402c01kz02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlygkoq4j627s2yeavd02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyjh0daj62bw340nm102.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlylejlhj62bw3401kx02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiaa8b7fj627j2y67wh02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiagw3ezj61s02ebu0x02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiaivng3j61rv2ebx6p02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiajuixvj62bw340qv502.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiapyartj62bw340hdt02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaibkwu4ij61vy2iq1kx02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaic1fvkpj61u92eo1ky02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaic3cruhj62822ywb2902.jpg +0 -0
- package/public/manifest.json +25 -0
- package/public/mcp-logo-128.png +0 -0
- package/public/mcp-logo-64.png +0 -0
- package/public/mcp-logo.svg +16 -0
- package/server/agent/agentCore.js +542 -74
- package/server/agent/appPaths.js +1 -0
- package/server/agent/audioPlayer.js +4 -2
- package/server/agent/citations.js +294 -0
- package/server/agent/clarificationAnalytics.js +477 -0
- package/server/agent/clarifyingQuestions.js +1089 -0
- package/server/agent/cli.js +549 -125
- package/server/agent/commandFeatures.js +1 -0
- package/server/agent/commandRegistry.js +946 -879
- package/server/agent/commands.js +546 -272
- package/server/agent/config.js +581 -412
- package/server/agent/contextCompactor.js +2 -2
- package/server/agent/debate.js +235 -0
- package/server/agent/eventBus.js +3 -0
- package/server/agent/feedbackLearning.js +217 -0
- package/server/agent/llmClient.js +512 -394
- package/server/agent/mcpClient.js +182 -32
- package/server/agent/memory.js +283 -224
- package/server/agent/modelManager.js +79 -39
- package/server/agent/profiles.js +178 -0
- package/server/agent/publicTools.js +22 -0
- package/server/agent/queryDetails.js +68 -8
- package/server/agent/quizData.js +398 -384
- package/server/agent/research.js +121 -79
- package/server/agent/sanitizer.js +104 -13
- package/server/agent/session.js +319 -11
- package/server/agent/subagent.js +8 -3
- package/server/agent/suggestionTui.js +294 -0
- package/server/agent/suggestions.js +69 -3
- package/server/agent/telemetry.js +119 -0
- package/server/agent/terminalImage.js +146 -27
- package/server/agent/theme.js +17 -0
- package/server/agent/toolRouter.js +16 -1
- package/server/agent/tools.js +424 -225
- package/server/agent/ui.js +486 -64
- package/server/agent/version.js +2 -4
- package/server/agent/webSearch.js +111 -11
- package/server/agent/youtubeAuth.js +24 -6
- package/server/apiRouter.js +4222 -0
- package/server/mcp/data/discography.js +25 -16
- package/server/mcp/data/gallery.js +308 -20
- package/server/mcp/data/profile.js +74 -44
- package/server/mcp/data/timeline.js +1 -1
- package/server/mcp/data/works.js +51 -17
- package/server/mcp/dataAdapter.js +5 -3
- package/server/mcp/handler.js +5 -11
- package/server/mcp/prompts.js +86 -0
- package/server/mcp/security.js +18 -15
- package/server/mcp/server.js +7 -2
- package/server/mcp/tools.js +66 -2
- package/server/web-chat/core/browserLauncher.js +18 -0
- package/server/web-chat/core/connection.js +194 -0
- package/server/web-chat/core/conversationContext.js +82 -0
- package/server/web-chat/core/directCatalogEngine.js +196 -0
- package/server/web-chat/core/dramaKnowledge.js +142 -0
- package/server/web-chat/core/errorHandler.js +51 -0
- package/server/web-chat/core/followUpEngine.js +182 -0
- package/server/web-chat/core/futureEventGuard.js +80 -0
- package/server/web-chat/core/memorySnapshot.js +12 -0
- package/server/web-chat/core/security.js +146 -0
- package/server/web-chat/core/toolInspector.js +52 -0
- package/server/web-chat/router.js +120 -0
- package/server/web-chat/routes/chatRoute.js +397 -0
- package/server/web-chat/routes/clarifyConfigRoute.js +98 -0
- package/server/web-chat/routes/clarifyRoute.js +83 -0
- package/server/web-chat/routes/compareRoute.js +79 -0
- package/server/web-chat/routes/exportRoute.js +59 -0
- package/server/web-chat/routes/feedbackRoute.js +57 -0
- package/server/web-chat/routes/newsRoute.js +33 -0
- package/server/web-chat/routes/quizRoute.js +11 -0
- package/server/web-chat/routes/staticRoute.js +103 -0
- package/server/web-chat/routes/statusRoute.js +52 -0
- package/server/web-chat/routes/suggestRoute.js +77 -0
- package/server/web-chat/routes/timelineRoute.js +34 -0
- package/server/web-chat/routes/verifyRoute.js +57 -0
- package/server/web-chat/server.js +116 -0
- package/server/web-chat/ui/htmlBuilder.js +91 -0
- package/server/web-chat/ui/previews/agentBubble.preview.html +134 -0
- package/server/web-chat/ui/scripts/agentBubbleSystem.js +320 -0
- package/server/web-chat/ui/scripts/client.js +8397 -0
- package/server/web-chat/ui/scripts/kikuDb.js +177 -0
- package/server/web-chat/ui/scripts/suggestionsData.js +118 -0
- package/server/web-chat/ui/scripts/webchatI18n.js +291 -0
- package/server/web-chat/ui/styles/main.css +8568 -0
- package/server/web-chat/ui/templates/body.html +796 -0
- package/server/webChatHtml.js +1 -0
- package/server/webChatServer.js +6 -0
- package/src/data/discographyData.js +892 -866
- package/src/data/galleryData.js +1805 -561
- package/src/data/profileData.js +79 -2
- package/src/data/socialUpdatesData.js +106 -47
- package/src/data/timelineData.js +1 -1
- package/src/data/worksData.js +25 -4
- package/tokens.css +97 -0
- package/server/agent/updateManager.js +0 -456
|
@@ -1,32 +1,61 @@
|
|
|
1
|
-
import { config } from './config.js';
|
|
2
|
-
import { toolsSchema, executeTool, prepareToolArguments } from './tools.js';
|
|
3
|
-
import { LLMClient, LMStudioClient } from './llmClient.js';
|
|
4
|
-
import { AgentSession } from './session.js';
|
|
5
|
-
import { AgentMemory } from './memory.js';
|
|
6
1
|
import { AgentStateMachine, AgentStates } from './agentState.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
2
|
+
import { config } from './config.js';
|
|
3
|
+
import { contextCompactor } from './contextCompactor.js';
|
|
9
4
|
import { createAbortError, isAbortError } from './errors.js';
|
|
10
|
-
import {
|
|
5
|
+
import { Events, eventBus } from './eventBus.js';
|
|
6
|
+
import { AgentJobManager } from './jobManager.js';
|
|
7
|
+
import { LLMClient } from './llmClient.js';
|
|
8
|
+
import { McpClientManager } from './mcpClient.js';
|
|
9
|
+
import { AgentMemory } from './memory.js';
|
|
11
10
|
import { ResearchOrchestrator } from './research.js';
|
|
11
|
+
import { sanitizeQuery, sanitizeResponse } from './sanitizer.js';
|
|
12
|
+
import { AgentSession } from './session.js';
|
|
13
|
+
import { ResearchSubagent } from './subagent.js';
|
|
12
14
|
import { SuggestionEngine } from './suggestions.js';
|
|
13
|
-
import { McpClientManager } from './mcpClient.js';
|
|
14
15
|
import { DynamicToolRouter } from './toolRouter.js';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
16
|
+
import { executeTool, prepareToolArguments, toolsSchema, isBuiltinTool } from './tools.js';
|
|
17
|
+
import { detectAmbiguities, isQueryTooVague, emitClarifyingQuestions, calculateConfidence, generateClarifyingQuestions, buildClarificationContext } from './clarifyingQuestions.js';
|
|
18
|
+
import { ClarificationAnalytics, ClarificationConfig } from './clarificationAnalytics.js';
|
|
19
|
+
import { calculateTokenCost } from './telemetry.js';
|
|
20
|
+
import { extractCitations, resolveCitationReference } from './citations.js';
|
|
17
21
|
|
|
18
22
|
export function detectDominantLanguage(text) {
|
|
19
23
|
if (!text || typeof text !== 'string') return 'th';
|
|
20
24
|
|
|
21
|
-
const
|
|
25
|
+
const clean = text
|
|
26
|
+
.replace(/\[SYSTEM\s+[A-Z]+:[^\]]*\]/gi, '')
|
|
27
|
+
.replace(/\[SYSTEM:[^\]]*\]/gi, '')
|
|
28
|
+
.replace(/\[SYSTEM\s+INSTRUCTION:[^\]]*\]/gi, '')
|
|
29
|
+
.replace(/\[SYSTEM\s+DIRECTIVE:[^\]]*\]/gi, '')
|
|
30
|
+
.replace(/^User Question:\s*/gim, '')
|
|
31
|
+
.trim();
|
|
32
|
+
|
|
33
|
+
const targetText = clean || text;
|
|
34
|
+
|
|
35
|
+
const thaiMatches = targetText.match(/[\u0E00-\u0E7F]/g) || [];
|
|
22
36
|
const thaiCount = thaiMatches.length;
|
|
23
37
|
|
|
24
|
-
const chineseMatches =
|
|
38
|
+
const chineseMatches = targetText.match(/[\u4E00-\u9FA5]/g) || [];
|
|
25
39
|
const chineseCount = chineseMatches.length;
|
|
26
40
|
|
|
27
|
-
const
|
|
41
|
+
const kanaMatches = targetText.match(/[\u3040-\u30FF]/g) || [];
|
|
42
|
+
const kanaCount = kanaMatches.length;
|
|
43
|
+
|
|
44
|
+
const hangulMatches = targetText.match(/[\uAC00-\uD7AF]/g) || [];
|
|
45
|
+
const hangulCount = hangulMatches.length;
|
|
46
|
+
|
|
47
|
+
const englishMatches = targetText.match(/[a-zA-Z]+/g) || [];
|
|
28
48
|
const englishCount = englishMatches.join('').length;
|
|
29
49
|
|
|
50
|
+
|
|
51
|
+
if (kanaCount > 0) {
|
|
52
|
+
return 'ja';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (hangulCount > 0 && hangulCount >= thaiCount && hangulCount >= chineseCount && hangulCount >= englishCount) {
|
|
56
|
+
return 'ko';
|
|
57
|
+
}
|
|
58
|
+
|
|
30
59
|
if (thaiCount >= chineseCount && thaiCount >= englishCount && thaiCount > 0) {
|
|
31
60
|
return 'th';
|
|
32
61
|
} else if (chineseCount > thaiCount && chineseCount >= englishCount && chineseCount > 0) {
|
|
@@ -49,8 +78,11 @@ function stableToolSignature(toolName, args) {
|
|
|
49
78
|
return `${toolName}:${JSON.stringify(sortValue(args || {}))}`;
|
|
50
79
|
}
|
|
51
80
|
|
|
52
|
-
export class KikuAgent {
|
|
53
|
-
constructor(options = {}) {
|
|
81
|
+
export class KikuAgent {
|
|
82
|
+
constructor(options = {}) {
|
|
83
|
+
this.allowedToolNames = options.allowedToolNames
|
|
84
|
+
? new Set(Array.from(options.allowedToolNames, String))
|
|
85
|
+
: null;
|
|
54
86
|
this.client = options.client || new LLMClient({
|
|
55
87
|
provider: options.provider || config.activeProvider,
|
|
56
88
|
baseUrl: options.baseUrl,
|
|
@@ -59,20 +91,29 @@ export class KikuAgent {
|
|
|
59
91
|
});
|
|
60
92
|
this.maxIterations = options.maxIterations || config.agent.maxIterations || 5;
|
|
61
93
|
this.slidingWindowSize = options.slidingWindowSize || config.agent.slidingWindowSize || 8;
|
|
62
|
-
this.session = new AgentSession({
|
|
63
|
-
|
|
94
|
+
this.session = new AgentSession({
|
|
95
|
+
thinkingMode: options.thinkingMode || 'hide',
|
|
96
|
+
sessionId: options.sessionId,
|
|
97
|
+
storageKey: options.sessionStorageKey,
|
|
98
|
+
storageFile: options.sessionStorageFile,
|
|
99
|
+
persistence: options.sessionPersistence !== false,
|
|
100
|
+
});
|
|
101
|
+
this.memory = options.memory || new AgentMemory();
|
|
64
102
|
this.mcpManager = options.mcpManager || new McpClientManager();
|
|
65
103
|
this.router = new DynamicToolRouter(this.getAllToolsSchema());
|
|
66
104
|
this.research = new ResearchOrchestrator({ client: this.client });
|
|
67
105
|
this.suggestions = new SuggestionEngine({ client: this.client, memory: this.memory });
|
|
68
106
|
this.jobs = new AgentJobManager();
|
|
107
|
+
this.clarifyConfig = options.clarifyConfig || new ClarificationConfig();
|
|
108
|
+
this.clarifyAnalytics = options.clarifyAnalytics || new ClarificationAnalytics();
|
|
69
109
|
this.systemPrompt = this.buildSystemPrompt();
|
|
70
110
|
this.initHistory();
|
|
71
111
|
this.restoredMessageCount = 0;
|
|
112
|
+
this.lastCitations = [];
|
|
72
113
|
|
|
73
114
|
this.stateMachine = new AgentStateMachine();
|
|
74
115
|
|
|
75
|
-
this.stateMachine.on('transition', ({
|
|
116
|
+
this.stateMachine.on('transition', ({ to }) => {
|
|
76
117
|
eventBus.emit(Events.AGENT_STATE_CHANGE, {
|
|
77
118
|
state: to,
|
|
78
119
|
label: this.stateMachine.label,
|
|
@@ -85,7 +126,7 @@ export class KikuAgent {
|
|
|
85
126
|
}
|
|
86
127
|
|
|
87
128
|
async _init() {
|
|
88
|
-
await this.memory.load();
|
|
129
|
+
if (!this.memory._loaded) await this.memory.load();
|
|
89
130
|
await this.loadSession();
|
|
90
131
|
if (this.mcpManager && typeof this.mcpManager.loadPersistedServers === 'function') {
|
|
91
132
|
try {
|
|
@@ -171,6 +212,141 @@ export class KikuAgent {
|
|
|
171
212
|
return this.jobs.clear();
|
|
172
213
|
}
|
|
173
214
|
|
|
215
|
+
async _handleClarifyingQuestions(query, lang, queryId, callbacks) {
|
|
216
|
+
if (!callbacks.onClarifyingQuestions || !callbacks.onClarifyingAnswer) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const clarifyConfig = this.clarifyConfig.getConfig();
|
|
221
|
+
if (!clarifyConfig.enabled) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
const hasPriorClarification = (this._clarificationAskedCount && this._clarificationAskedCount >= 1) ||
|
|
227
|
+
(this.conversationHistory && this.conversationHistory.some(m => m.content && (m.content.includes('[SYSTEM CLARIFICATION]') || m.content.includes('CLARIFICATION:'))));
|
|
228
|
+
if (hasPriorClarification) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (isQueryTooVague(query)) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (query.includes('[SYSTEM CLARIFICATION]') || query.includes('CLARIFICATION:')) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const ambiguities = detectAmbiguities(query, lang);
|
|
241
|
+
const confidence = calculateConfidence(query, ambiguities);
|
|
242
|
+
const threshold = clarifyConfig.confidenceThreshold;
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
const CROSS_CATEGORY_PRIORITIES = ['media_type', 'career_phase', 'specific_work', 'timeframe', 'comparison'];
|
|
246
|
+
const highImpactAmbiguities = ambiguities.filter(a => CROSS_CATEGORY_PRIORITIES.includes(a.category));
|
|
247
|
+
|
|
248
|
+
const shouldAsk = (confidence.score < threshold || confidence.shouldAsk) && highImpactAmbiguities.length > 0;
|
|
249
|
+
|
|
250
|
+
eventBus.emit(Events.AGENT_CLARIFYING_QUESTIONS, {
|
|
251
|
+
queryId,
|
|
252
|
+
confidence,
|
|
253
|
+
threshold,
|
|
254
|
+
shouldAsk,
|
|
255
|
+
timestamp: Date.now(),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (!shouldAsk || highImpactAmbiguities.length === 0) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
let questionsToAsk = null;
|
|
265
|
+
try {
|
|
266
|
+
questionsToAsk = await generateClarifyingQuestions(query, lang, {
|
|
267
|
+
conversationHistory: this.conversationHistory,
|
|
268
|
+
client: this.client,
|
|
269
|
+
maxQuestions: 4,
|
|
270
|
+
detectedAmbiguities: highImpactAmbiguities,
|
|
271
|
+
});
|
|
272
|
+
} catch {
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!questionsToAsk || questionsToAsk.length === 0) {
|
|
277
|
+
questionsToAsk = highImpactAmbiguities.slice(0, 4).map(q => ({
|
|
278
|
+
...q,
|
|
279
|
+
options: (q.options || []).slice(0, 4)
|
|
280
|
+
}));
|
|
281
|
+
} else {
|
|
282
|
+
questionsToAsk = questionsToAsk.slice(0, 5).map(q => ({
|
|
283
|
+
...q,
|
|
284
|
+
options: (q.options || []).slice(0, 4)
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this._clarificationAskedCount = (this._clarificationAskedCount || 0) + 1;
|
|
289
|
+
|
|
290
|
+
const startTime = Date.now();
|
|
291
|
+
this.clarifyAnalytics.recordClarificationStart(queryId, questionsToAsk, confidence.score);
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
eventBus.emit(Events.AGENT_CLARIFYING_QUESTIONS, {
|
|
295
|
+
queryId,
|
|
296
|
+
questions: questionsToAsk,
|
|
297
|
+
confidence,
|
|
298
|
+
threshold,
|
|
299
|
+
timestamp: Date.now(),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
callbacks.onClarifyingQuestions(questionsToAsk, queryId);
|
|
303
|
+
|
|
304
|
+
const answers = await Promise.race([
|
|
305
|
+
callbacks.onClarifyingAnswer(queryId),
|
|
306
|
+
new Promise((_, reject) =>
|
|
307
|
+
setTimeout(() => reject(new Error('CLARIFY_TIMEOUT')), clarifyConfig.timeoutMs)
|
|
308
|
+
),
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
const responseTimeMs = Date.now() - startTime;
|
|
312
|
+
const { parseClarifyingAnswers } = await import('./clarifyingQuestions.js');
|
|
313
|
+
const result = parseClarifyingAnswers(answers, questionsToAsk);
|
|
314
|
+
|
|
315
|
+
if (result.summary.includes('skip')) {
|
|
316
|
+
this.clarifyAnalytics.recordClarificationSkipped(queryId);
|
|
317
|
+
} else {
|
|
318
|
+
this.clarifyAnalytics.recordClarificationAnswer(queryId, result.answers, 1, responseTimeMs);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
eventBus.emit(Events.AGENT_CLARIFYING_ANSWER, {
|
|
322
|
+
queryId,
|
|
323
|
+
answers: result.answers,
|
|
324
|
+
summary: result.summary,
|
|
325
|
+
confidence,
|
|
326
|
+
threshold,
|
|
327
|
+
responseTimeMs,
|
|
328
|
+
timestamp: Date.now(),
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
return result;
|
|
332
|
+
} catch {
|
|
333
|
+
this.clarifyAnalytics.recordClarificationTimeout(queryId);
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
getClarificationAnalytics(options = {}) {
|
|
339
|
+
return this.clarifyAnalytics.getAnalytics(options);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
getClarificationConfig() {
|
|
343
|
+
return this.clarifyConfig.getConfig();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
updateClarificationConfig(updates) {
|
|
347
|
+
return this.clarifyConfig.updateConfig(updates);
|
|
348
|
+
}
|
|
349
|
+
|
|
174
350
|
initHistory() {
|
|
175
351
|
this.conversationHistory = [
|
|
176
352
|
{
|
|
@@ -280,19 +456,74 @@ export class KikuAgent {
|
|
|
280
456
|
eventBus.emit(Events.SESSION_SAVED, { sessionId: this.session.id });
|
|
281
457
|
}
|
|
282
458
|
|
|
459
|
+
async hydrateConversationHistory(messages = [], options = {}) {
|
|
460
|
+
await this._ready;
|
|
461
|
+
const alreadyHasConversation = this.conversationHistory?.some(message =>
|
|
462
|
+
message.role === 'user' || message.role === 'assistant'
|
|
463
|
+
);
|
|
464
|
+
if (alreadyHasConversation && options.force !== true) {
|
|
465
|
+
return { hydrated: false, reason: 'session_already_loaded', messageCount: this.session.messages.length };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const maxMessages = Math.max(1, Math.min(Number(options.maxMessages) || 24, 50));
|
|
469
|
+
const normalized = (Array.isArray(messages) ? messages : [])
|
|
470
|
+
.slice(-maxMessages)
|
|
471
|
+
.map(message => {
|
|
472
|
+
const role = message?.role === 'user' ? 'user' :
|
|
473
|
+
(message?.role === 'assistant' || message?.role === 'bot' ? 'assistant' : null);
|
|
474
|
+
const content = String(message?.content ?? message?.text ?? '').trim().slice(0, 6000);
|
|
475
|
+
return role && content ? { role, content } : null;
|
|
476
|
+
})
|
|
477
|
+
.filter(Boolean);
|
|
478
|
+
|
|
479
|
+
if (normalized.length === 0) {
|
|
480
|
+
return { hydrated: false, reason: 'empty_history', messageCount: 0 };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const createdAt = new Date();
|
|
484
|
+
const sessionMessages = normalized.map((message, index) => ({
|
|
485
|
+
id: `msg_hydrated_${createdAt.getTime()}_${index}`,
|
|
486
|
+
role: message.role,
|
|
487
|
+
queryNumber: Math.floor(index / 2) + 1,
|
|
488
|
+
createdAt,
|
|
489
|
+
parts: [{ type: 'text', content: message.content }]
|
|
490
|
+
}));
|
|
491
|
+
const userMessageCount = normalized.filter(message => message.role === 'user').length;
|
|
492
|
+
|
|
493
|
+
this.session.restoreFromSnapshot({
|
|
494
|
+
id: options.sessionId || this.session.id,
|
|
495
|
+
createdAt,
|
|
496
|
+
thinkingMode: this.session.thinkingMode,
|
|
497
|
+
messages: sessionMessages,
|
|
498
|
+
stats: {
|
|
499
|
+
totalQueries: userMessageCount,
|
|
500
|
+
totalToolCalls: 0,
|
|
501
|
+
totalDurationMs: 0,
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
this.initHistory();
|
|
505
|
+
normalized.forEach(message => this.conversationHistory.push(message));
|
|
506
|
+
this.restoredMessageCount = normalized.length;
|
|
507
|
+
await this.saveSession();
|
|
508
|
+
|
|
509
|
+
return { hydrated: true, messageCount: normalized.length };
|
|
510
|
+
}
|
|
511
|
+
|
|
283
512
|
applySlidingWindow() {
|
|
284
513
|
if (this.conversationHistory.length > this.slidingWindowSize + 1) {
|
|
285
514
|
this.conversationHistory = contextCompactor.compact(this.conversationHistory, {
|
|
286
|
-
maxTokens:
|
|
515
|
+
maxTokens: 24000,
|
|
287
516
|
keepRecentMessages: this.slidingWindowSize,
|
|
288
517
|
});
|
|
289
518
|
}
|
|
290
519
|
}
|
|
291
520
|
|
|
292
|
-
getAllToolsSchema() {
|
|
293
|
-
const mcpTools = this.mcpManager ? this.mcpManager.getToolsSchema() : [];
|
|
294
|
-
|
|
295
|
-
|
|
521
|
+
getAllToolsSchema() {
|
|
522
|
+
const mcpTools = this.mcpManager ? this.mcpManager.getToolsSchema() : [];
|
|
523
|
+
const allTools = [...toolsSchema, ...mcpTools];
|
|
524
|
+
if (!this.allowedToolNames) return allTools;
|
|
525
|
+
return allTools.filter(tool => this.allowedToolNames.has(tool.function?.name));
|
|
526
|
+
}
|
|
296
527
|
|
|
297
528
|
async connectMcp(name, serverConfig) {
|
|
298
529
|
const result = await this.mcpManager.connect(name, serverConfig);
|
|
@@ -357,6 +588,7 @@ export class KikuAgent {
|
|
|
357
588
|
|
|
358
589
|
buildSystemPrompt() {
|
|
359
590
|
const allTools = this.getAllToolsSchema();
|
|
591
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
360
592
|
const toolsSummary = allTools.map(t => {
|
|
361
593
|
const f = t.function;
|
|
362
594
|
const paramKeys = Object.keys(f.parameters?.properties || {}).join(', ');
|
|
@@ -390,25 +622,49 @@ ${toolsSummary}
|
|
|
390
622
|
|
|
391
623
|
3. FACT ACCURACY & NO DUPLICATION:
|
|
392
624
|
- Always use tools to verify facts before answering. Do NOT hallucinate.
|
|
393
|
-
- Current
|
|
625
|
+
- Current date is ${today}. Prioritize current and recent projects when relevant.
|
|
394
626
|
- Never repeat the same statement. Sort newest first.
|
|
395
627
|
|
|
396
|
-
|
|
397
|
-
-
|
|
398
|
-
-
|
|
399
|
-
-
|
|
400
|
-
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
-
|
|
404
|
-
-
|
|
405
|
-
-
|
|
628
|
+
3A. CONVERSATION TOPIC ANCHOR:
|
|
629
|
+
- Resolve short or ambiguous follow-ups (for example "ทำไมน่าจับตามอง?", "งานนี้", "why is it notable?") against the most recent user topic and assistant answer.
|
|
630
|
+
- The current follow-up inherits that topic. Do not default the subject to Ju Jingyi merely because the persona or knowledge base focuses on her.
|
|
631
|
+
- A persona changes voice and tone only; it never replaces the user's subject.
|
|
632
|
+
- If the reference remains genuinely ambiguous after reading recent history, ask one focused clarification question instead of guessing.
|
|
633
|
+
|
|
634
|
+
3B. FUTURE-EVENT CLAIM SAFETY:
|
|
635
|
+
- Treat any event after today's date as scheduled or reported, not completed fact.
|
|
636
|
+
- Never assert an exact future date, invitation, guest list, or attendance without checking a current official source with a live web tool.
|
|
637
|
+
- Never carry a date, guest list, or attendance status from one annual edition into another year.
|
|
638
|
+
- When official confirmation is unavailable, say clearly that the detail is unconfirmed and subject to change.
|
|
639
|
+
|
|
640
|
+
4. RESPONSE STYLE:
|
|
641
|
+
- NEVER start with template greetings — dive DIRECTLY into the answer.
|
|
642
|
+
- Format with Markdown headers (###), bold (**text**), bullet points (•).
|
|
643
|
+
- Emoji are optional: use at most one contextually relevant emoji per response and never repeat them across headings or bullets.
|
|
644
|
+
- Match answer depth to what the user asked.
|
|
645
|
+
- Separate catalog facts from interpretation. Clearly frame acting observations as analysis unless a cited review supports them.
|
|
646
|
+
- Do not infer that newer means deeper, more realistic, or better acted. Compare concrete role demands, narrative agency, genre, and verified scenes instead.
|
|
647
|
+
- Omit catalog ratings from analytical conclusions unless the tool output names the rating source and scale.
|
|
648
|
+
- Avoid unsupported superlatives or career verdicts (for example "unrivalled", "top-tier", or "completely transcended idol status").
|
|
649
|
+
- In Blossom (花间令): Ju Jingyi portrays Yang Caiwei's identity in Shangguan Zhi's appearance after the face-swap premise; do not describe this as two unrelated roles throughout the series.
|
|
650
|
+
- The Legend of White Snake (新白娘子传奇): Bai Suzhen is the white-snake spirit in human form and Xu Xian is the human man; never reverse this relationship.
|
|
651
|
+
- On an explicit continuation, answer the new angle without restating the previous response.
|
|
652
|
+
|
|
653
|
+
5. SMART TOOL SELECTION (MANDATORY LOCAL DATABASE FIRST):
|
|
654
|
+
- ALWAYS call LOCAL built-in database tools FIRST. They query the rich, local catalog database directly in-process and are the authoritative source of truth.
|
|
655
|
+
- Videos/stages/audio ➔ search_discography, get_music_videos, get_media_links, play_music
|
|
656
|
+
- Songs/OSTs ➔ search_discography
|
|
657
|
+
- Dramas/roles ➔ search_works, get_work_detail, get_character_roles
|
|
658
|
+
- Profile/fandom ➔ get_profile_and_fandom
|
|
659
|
+
- Brands ➔ get_brand_endorsements
|
|
660
|
+
- Awards ➔ get_awards
|
|
661
|
+
- Career timeline ➔ get_career_timeline
|
|
662
|
+
- Local files ➔ read_local_file, list_directory, get_system_stats
|
|
663
|
+
- MCP tools (mcp_*) ➔ Secondary only; only invoke when user explicitly mentions MCP (@mcp) or when specialized external services are required.
|
|
406
664
|
- News/external facts ➔ web_search or web_extract
|
|
407
665
|
- Deep multi-source research ➔ web_extract (search + auto-fetch)
|
|
408
666
|
- Read webpage content ➔ fetch_webpage
|
|
409
667
|
- Fetch API/JSON ➔ fetch_json
|
|
410
|
-
- Local files ➔ read_local_file
|
|
411
|
-
- Directory listing ➔ list_directory
|
|
412
668
|
- Screenshot ➔ screenshot_webpage
|
|
413
669
|
|
|
414
670
|
6. AGGRESSIVE WEB SEARCH FALLBACK (CRITICAL):
|
|
@@ -429,7 +685,33 @@ ${toolsSummary}
|
|
|
429
685
|
• Isolated In-Depth Subtask ➔ Call run_subagent with a specific task parameter to delegate an isolated iterative investigation with evidence and tool call tracing.
|
|
430
686
|
• Complex Multi-Faceted Research ➔ Call run_deep_research with question when the user asks an open-ended, complex query that requires parallel multi-worker investigation across catalog, live web sources, and independent verifier with cited sources.
|
|
431
687
|
• Rumors, Cast Leaks & Fact Verification ➔ Call verify_claim with claim when the user asks whether a rumor, statement, or unconfirmed news is true (returns VERIFIED, UNVERIFIED, CONFLICTING, or FALSE).
|
|
432
|
-
|
|
688
|
+
• Teaching / Updating Response Preferences ➔ Call manage_skill with action 'learn' (with instruction), 'list', 'remove' (with skillId), 'clear', 'enable', or 'disable' when the user instructs how they want Kiku to answer in future conversations.
|
|
689
|
+
|
|
690
|
+
9. ANTI-HALLUCINATION GUARDRAILS (CRITICAL — MANDATORY COMPLIANCE):
|
|
691
|
+
|
|
692
|
+
0. INDEPENDENT VERIFICATION FIRST:
|
|
693
|
+
- When asked about specific named entities (e.g., upcoming dramas, casts, titles, products, brand deals, real-world releases), do NOT generate an explanation based solely on keyword inference or assumed context.
|
|
694
|
+
- You MUST cross-reference and verify the exact entity existence using your local catalog tools (search_works, search_discography, get_brand_endorsements, etc.) BEFORE writing the response.
|
|
695
|
+
- If the local catalog has no match, you MUST call web_search or web_extract to verify externally before responding.
|
|
696
|
+
|
|
697
|
+
1. ZERO-ASSUMPTION ON UNVERIFIED ENTITIES:
|
|
698
|
+
- If the subject/entity cannot be verified with official records (not found in catalog tools AND not found via web_search), explicitly state that no verified record exists.
|
|
699
|
+
- Strictly avoid fabricating plausible generic narratives (e.g., generic tropes, placeholder plotlines, speculative casting, invented brand partnerships).
|
|
700
|
+
- NEVER invent drama titles, song names, character roles, brand endorsements, or career milestones that are not backed by tool results.
|
|
701
|
+
|
|
702
|
+
2. FALLBACK & UNCERTAINTY PROTOCOL:
|
|
703
|
+
- If the entity appears to be misspelled, fan fiction, or an unverified rumor, directly flag the discrepancy (including linguistic errors like incorrect Pinyin or romanization) rather than adopting the user's premise as true.
|
|
704
|
+
- Always prioritize stating "No official information found in the database" over generating a complete but ungrounded description.
|
|
705
|
+
- If tool results are ambiguous or partial, present only what is confirmed and clearly label uncertain parts as "unverified" or "not confirmed."
|
|
706
|
+
|
|
707
|
+
3. SOURCE ATTRIBUTION:
|
|
708
|
+
- When stating facts, prefer answers that trace back to tool results. If a claim comes from local catalog, you may present it as confirmed data. If from web search, note it as external/recent information.
|
|
709
|
+
- NEVER present fabricated information as if it were verified catalog data.
|
|
710
|
+
|
|
711
|
+
4. SELF-CHECK BEFORE FINAL ANSWER:
|
|
712
|
+
- Before synthesizing your final response, mentally verify: "Does every factual claim in my answer have a corresponding tool result backing it up?"
|
|
713
|
+
- If any claim lacks tool evidence, either (a) call the appropriate tool to verify it, or (b) remove it from your answer.
|
|
714
|
+
- This applies to ALL entity types: dramas, songs, MVs, brands, awards, dates, character names, and career milestones.`;
|
|
433
715
|
}
|
|
434
716
|
|
|
435
717
|
parseTextToolCalls(content) {
|
|
@@ -438,8 +720,7 @@ ${toolsSummary}
|
|
|
438
720
|
const allTools = this.getAllToolsSchema();
|
|
439
721
|
|
|
440
722
|
const jsonBlockRegex = /```(?:json)?\s*(\{[\s\S]*?\})\s*```/g;
|
|
441
|
-
|
|
442
|
-
while ((match = jsonBlockRegex.exec(content)) !== null) {
|
|
723
|
+
for (const match of content.matchAll(jsonBlockRegex)) {
|
|
443
724
|
try {
|
|
444
725
|
const parsed = JSON.parse(match[1]);
|
|
445
726
|
const toolName = parsed.action || parsed.tool || parsed.name || parsed.function;
|
|
@@ -458,9 +739,9 @@ ${toolsSummary}
|
|
|
458
739
|
}
|
|
459
740
|
|
|
460
741
|
const actionRegex = /Action:\s*([a-zA-Z0-9_]+)\s*\nAction Input:\s*(\{[\s\S]*?\}|[^\n]+)/g;
|
|
461
|
-
|
|
742
|
+
for (const match of content.matchAll(actionRegex)) {
|
|
462
743
|
const toolName = match[1].trim();
|
|
463
|
-
|
|
744
|
+
const rawInput = match[2].trim();
|
|
464
745
|
if (allTools.some(t => t.function.name === toolName)) {
|
|
465
746
|
let args = {};
|
|
466
747
|
try {
|
|
@@ -506,6 +787,7 @@ ${toolsSummary}
|
|
|
506
787
|
onReasoningEnd = () => { },
|
|
507
788
|
onToolStart = () => { },
|
|
508
789
|
onToolResult = () => { },
|
|
790
|
+
onSubagentProgress = () => { },
|
|
509
791
|
onTokenStart = () => { },
|
|
510
792
|
onTokenDelta = () => { },
|
|
511
793
|
onFinalAnswer = () => { },
|
|
@@ -527,8 +809,16 @@ ${toolsSummary}
|
|
|
527
809
|
];
|
|
528
810
|
}
|
|
529
811
|
|
|
812
|
+
let promptForLLMWithGrounding = promptForLLM;
|
|
813
|
+
if (this.lastCitations && this.lastCitations.length > 0) {
|
|
814
|
+
const citedRef = resolveCitationReference(promptForLLM, this.lastCitations);
|
|
815
|
+
if (citedRef) {
|
|
816
|
+
promptForLLMWithGrounding = `${promptForLLM}\n\n[CONTEXT GROUNDING: User references Citation [${citedRef.id}] "${citedRef.title}" (${citedRef.category || citedRef.type || ''}${citedRef.year ? `, ${citedRef.year}` : ''})]`;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
530
820
|
this.session.createUserMessage(promptForLLM);
|
|
531
|
-
const userMessage = { role: 'user', content:
|
|
821
|
+
const userMessage = { role: 'user', content: promptForLLMWithGrounding };
|
|
532
822
|
this.conversationHistory.push(userMessage);
|
|
533
823
|
|
|
534
824
|
const memoryContext = this.memory.buildMemoryContext(promptForLLM);
|
|
@@ -542,6 +832,10 @@ ${toolsSummary}
|
|
|
542
832
|
langInstruction = '[SYSTEM NOTE: User language is Thai. Reply in Thai only.]\n';
|
|
543
833
|
} else if (dominantLang === 'zh') {
|
|
544
834
|
langInstruction = '[SYSTEM NOTE: User language is Chinese. Reply in Chinese only.]\n';
|
|
835
|
+
} else if (dominantLang === 'ja') {
|
|
836
|
+
langInstruction = '[SYSTEM NOTE: User language is Japanese. Reply in Japanese only.]\n';
|
|
837
|
+
} else if (dominantLang === 'ko') {
|
|
838
|
+
langInstruction = '[SYSTEM NOTE: User language is Korean. Reply in Korean only.]\n';
|
|
545
839
|
} else {
|
|
546
840
|
langInstruction = '[SYSTEM NOTE: User language is English. Reply in English only.]\n';
|
|
547
841
|
}
|
|
@@ -552,6 +846,19 @@ ${toolsSummary}
|
|
|
552
846
|
}
|
|
553
847
|
this.conversationHistory.push({ role: 'system', content: langInstruction });
|
|
554
848
|
|
|
849
|
+
const clarifyingResult = await this._handleClarifyingQuestions(
|
|
850
|
+
promptForLLM,
|
|
851
|
+
dominantLang,
|
|
852
|
+
queryId,
|
|
853
|
+
callbacks
|
|
854
|
+
);
|
|
855
|
+
if (clarifyingResult) {
|
|
856
|
+
this.conversationHistory.push({
|
|
857
|
+
role: 'user',
|
|
858
|
+
content: `[SYSTEM CLARIFICATION] ${clarifyingResult.summary}\nOriginal query: ${promptForLLM}`
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
|
|
555
862
|
this.applySlidingWindow();
|
|
556
863
|
|
|
557
864
|
const currentAssistantSessionMsg = this.session.createAssistantMessage({
|
|
@@ -559,8 +866,13 @@ ${toolsSummary}
|
|
|
559
866
|
provider: this.client?.providerName || this.client?.provider || null,
|
|
560
867
|
});
|
|
561
868
|
|
|
869
|
+
let queryPromptTokens = 0;
|
|
870
|
+
let queryCompletionTokens = 0;
|
|
871
|
+
let queryReasoningTokens = 0;
|
|
872
|
+
|
|
562
873
|
let currentIteration = 0;
|
|
563
874
|
const toolResultCache = new Map();
|
|
875
|
+
const toolResultTracker = { notFound: 0, error: 0, success: 0, total: 0, toolNames: [] };
|
|
564
876
|
const activeTools = this.router.routeToolsForQuery(promptForLLM, {
|
|
565
877
|
showAll: callbacks.showAllTools || false,
|
|
566
878
|
includeMcp: true,
|
|
@@ -584,6 +896,7 @@ ${toolsSummary}
|
|
|
584
896
|
messages: this.conversationHistory,
|
|
585
897
|
tools: activeTools,
|
|
586
898
|
temperature: 0.3,
|
|
899
|
+
maxTokens: 8192,
|
|
587
900
|
stream: true,
|
|
588
901
|
signal,
|
|
589
902
|
onThought: (thoughtChunk) => {
|
|
@@ -640,6 +953,12 @@ ${toolsSummary}
|
|
|
640
953
|
throw new Error(`No response received from ${this.client?.providerName || 'AI provider'}.`);
|
|
641
954
|
}
|
|
642
955
|
|
|
956
|
+
if (assistantMessage.usage) {
|
|
957
|
+
queryPromptTokens += Number(assistantMessage.usage.prompt_tokens || 0);
|
|
958
|
+
queryCompletionTokens += Number(assistantMessage.usage.completion_tokens || 0);
|
|
959
|
+
queryReasoningTokens += Number(assistantMessage.usage.reasoning_tokens || 0);
|
|
960
|
+
}
|
|
961
|
+
|
|
643
962
|
let toolCalls = assistantMessage.tool_calls || [];
|
|
644
963
|
if (toolCalls.length === 0 && assistantMessage.content) {
|
|
645
964
|
toolCalls = this.parseTextToolCalls(assistantMessage.content);
|
|
@@ -660,11 +979,13 @@ ${toolsSummary}
|
|
|
660
979
|
const totalDurationMs = Date.now() - queryStartTime;
|
|
661
980
|
this.session.stats.totalDurationMs += totalDurationMs;
|
|
662
981
|
|
|
663
|
-
const finalAnswer = assistantMessage.content || (
|
|
982
|
+
const finalAnswer = sanitizeResponse(assistantMessage.content || (
|
|
664
983
|
dominantLang === 'zh' ? "抱歉,无法处理您的回答。" :
|
|
665
|
-
dominantLang === '
|
|
666
|
-
"
|
|
667
|
-
|
|
984
|
+
dominantLang === 'ja' ? "申し訳ありません、回答を処理できませんでした。" :
|
|
985
|
+
dominantLang === 'ko' ? "죄송합니다. 답변을 처리할 수 없습니다." :
|
|
986
|
+
dominantLang === 'en' ? "Sorry, unable to process a response." :
|
|
987
|
+
"ขออภัยค่ะ ไม่สามารถประมวลผลคำตอบได้"
|
|
988
|
+
));
|
|
668
989
|
onFinalAnswer(finalAnswer, { durationMs: totalDurationMs, language: dominantLang });
|
|
669
990
|
this._extractFactsFromFinalAnswer(finalAnswer);
|
|
670
991
|
|
|
@@ -672,15 +993,30 @@ ${toolsSummary}
|
|
|
672
993
|
eventBus.emit(Events.AGENT_FINAL_ANSWER, { queryId, answer: finalAnswer, durationMs: totalDurationMs });
|
|
673
994
|
this.stateMachine.reset();
|
|
674
995
|
|
|
996
|
+
await this.saveSession().catch(err => console.error('Session save failed:', err));
|
|
675
997
|
return finalAnswer;
|
|
676
998
|
}
|
|
677
999
|
|
|
678
1000
|
const executionPromises = toolCalls.map(async (tc, toolCallIndex) => {
|
|
679
1001
|
if (signal?.aborted) throw createAbortError();
|
|
680
1002
|
|
|
681
|
-
const toolName = tc?.function?.name || '';
|
|
682
|
-
const toolCallId = tc?.id || `call_${queryId}_${currentIteration}_${toolCallIndex}`;
|
|
683
|
-
|
|
1003
|
+
const toolName = tc?.function?.name || '';
|
|
1004
|
+
const toolCallId = tc?.id || `call_${queryId}_${currentIteration}_${toolCallIndex}`;
|
|
1005
|
+
if (this.allowedToolNames && !this.allowedToolNames.has(toolName)) {
|
|
1006
|
+
const rejectedResult = { status: 'error', message: `Tool "${toolName}" is not permitted in this execution context.` };
|
|
1007
|
+
onToolStart(toolName, {});
|
|
1008
|
+
onToolResult(toolName, rejectedResult, 0);
|
|
1009
|
+
return {
|
|
1010
|
+
toolMessage: {
|
|
1011
|
+
role: 'tool',
|
|
1012
|
+
tool_call_id: toolCallId,
|
|
1013
|
+
name: toolName,
|
|
1014
|
+
content: JSON.stringify(rejectedResult),
|
|
1015
|
+
},
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
const isBuiltin = isBuiltinTool(toolName);
|
|
1019
|
+
const isMcp = !isBuiltin && (toolName.startsWith('mcp_') || Boolean(this.mcpManager?.isMcpTool(toolName)));
|
|
684
1020
|
|
|
685
1021
|
let prepared;
|
|
686
1022
|
if (isMcp) {
|
|
@@ -735,10 +1071,12 @@ ${toolsSummary}
|
|
|
735
1071
|
let result;
|
|
736
1072
|
if (cachedResult) {
|
|
737
1073
|
result = { ...cachedResult, cache: { hit: true, reason: 'duplicate_call_in_query' } };
|
|
1074
|
+
} else if (isBuiltin) {
|
|
1075
|
+
result = await executeTool(toolName, finalArgs, { agent: this, memory: this.memory, client: this.client, signal, onSubagentProgress });
|
|
738
1076
|
} else if (isMcp) {
|
|
739
1077
|
result = await this.mcpManager.callTool(toolName, finalArgs);
|
|
740
1078
|
} else {
|
|
741
|
-
result = await executeTool(toolName, finalArgs, { agent: this, memory: this.memory, client: this.client, signal });
|
|
1079
|
+
result = await executeTool(toolName, finalArgs, { agent: this, memory: this.memory, client: this.client, signal, onSubagentProgress });
|
|
742
1080
|
}
|
|
743
1081
|
|
|
744
1082
|
if (!cachedResult) toolResultCache.set(signature, result);
|
|
@@ -750,7 +1088,12 @@ ${toolsSummary}
|
|
|
750
1088
|
|
|
751
1089
|
this._extractFactsFromToolResult(toolName, finalArgs, result);
|
|
752
1090
|
|
|
753
|
-
|
|
1091
|
+
toolResultTracker.total++;
|
|
1092
|
+
toolResultTracker.toolNames.push(toolName);
|
|
1093
|
+
if (result.status === 'not_found') toolResultTracker.notFound++;
|
|
1094
|
+
else if (result.status === 'error') toolResultTracker.error++;
|
|
1095
|
+
else toolResultTracker.success++;
|
|
1096
|
+
|
|
754
1097
|
const cappedResult = this._capToolResultForHistory(result);
|
|
755
1098
|
|
|
756
1099
|
return {
|
|
@@ -758,14 +1101,14 @@ ${toolsSummary}
|
|
|
758
1101
|
role: 'tool',
|
|
759
1102
|
tool_call_id: toolCallId,
|
|
760
1103
|
name: toolName,
|
|
761
|
-
|
|
1104
|
+
content: JSON.stringify({
|
|
762
1105
|
authoritative: true,
|
|
763
1106
|
result: cappedResult,
|
|
764
1107
|
instruction: result.status === 'not_found'
|
|
765
|
-
? 'No matching data was found. Do
|
|
1108
|
+
? 'CRITICAL: No matching data was found for this query. Do NOT invent, fabricate, or guess any records. You MUST either (a) state explicitly that no verified information exists, or (b) immediately call web_search to look up live external information. Under no circumstances should you generate plausible-sounding but unverified details.'
|
|
766
1109
|
: result.status === 'error'
|
|
767
|
-
? 'The tool failed. Do
|
|
768
|
-
: 'Use
|
|
1110
|
+
? 'CRITICAL: The tool failed to retrieve data. Do NOT claim that the requested facts were verified. You MUST acknowledge the error and either retry or inform the user that the information could not be retrieved.'
|
|
1111
|
+
: 'Use ONLY the specific facts present in this result when answering. Do NOT add, infer, or extrapolate information beyond what is explicitly provided. Do NOT fabricate additional details, dates, names, or relationships not present in this result. Do not echo raw JSON data — summarize in natural language while preserving factual accuracy.'
|
|
769
1112
|
})
|
|
770
1113
|
}
|
|
771
1114
|
};
|
|
@@ -779,13 +1122,20 @@ ${toolsSummary}
|
|
|
779
1122
|
}
|
|
780
1123
|
}
|
|
781
1124
|
|
|
1125
|
+
const selfCritiqueDirective = await this._performSelfCritique(
|
|
1126
|
+
toolResultTracker,
|
|
1127
|
+
dominantLang,
|
|
1128
|
+
promptForLLM
|
|
1129
|
+
);
|
|
1130
|
+
|
|
782
1131
|
const langPrompt = dominantLang === 'zh'
|
|
783
|
-
? "
|
|
1132
|
+
? "请根据上述工具查询结果,为粉丝提供详细、全面且深入的解答。条理清晰地阐述作品背景、角色特质、音乐成就与演艺里程碑,包含具体的 YouTube 真实链接(如 Direct Watch: https://www.youtube.com/watch?v=... 或 Search: https://www.youtube.com/results?search_query=...)及完整事实数据。\n\n【反幻觉守则 — 必须遵守】只陈述工具结果中明确包含的事实。禁止编造、推测或补充工具结果中不存在的剧名、歌曲名、角色名、品牌名、日期或任何细节。如果工具结果不足以完整回答,请明确说明哪些部分未经验证,而不是凭空补全。"
|
|
784
1133
|
: dominantLang === 'en'
|
|
785
|
-
? "Based on the tool results above,
|
|
786
|
-
: "สรุปคำตอบจากผลลัพธ์เครื่องมือข้างต้น
|
|
1134
|
+
? "Based on the tool results above, provide a comprehensive, richly detailed response for fans. Explain background storylines, character traits, musical highlights, and career milestones with well-organized Markdown headers, bullet points, and verified YouTube links (https://www.youtube.com/results?search_query=...).\n\n【Anti-Hallucination Mandate — MANDATORY】Only state facts explicitly present in the tool results above. Do NOT fabricate, infer, or supplement drama titles, song names, character roles, brand names, dates, or any details not found in the tool results. If the tool results are insufficient to fully answer, explicitly note which parts are unverified rather than inventing plausible-sounding details."
|
|
1135
|
+
: "สรุปคำตอบจากผลลัพธ์เครื่องมือข้างต้น โดยให้ข้อมูลแฟนคลับอย่างละเอียด ลึกซึ้ง ครบถ้วนและชัดเจน จัดโครงสร้างด้วยหัวข้อ Markdown, ตาราง และ bullet points ให้สวยงาม พร้อมแสดงลิงก์ YouTube จริง (Direct Watch Link และ https://www.youtube.com/results?search_query=...) พร้อมเกร็ดประวัติและผลงานอย่างเต็มที่\n\n【กฎป้องกันการแต่งข้อมูล — บังคับใช้】ระบุเฉพาะข้อเท็จจริงที่มีอยู่จริงในผลลัพธ์เครื่องมือเท่านั้น ห้ามสร้างชื่อละคร เพลง ตัวละคร แบรนด์ วันที่ หรือรายละเอียดใดๆ ที่ไม่มีอยู่ในผลลัพธ์เครื่องมือ หากข้อมูลไม่เพียงพอสำหรับคำตอบที่สมบูรณ์ ให้ระบุชัดเจนว่าส่วนใดยังไม่ได้รับการยืนยัน แทนการเติมข้อมูลที่ฟังดูน่าเชื่อแต่ไม่มีหลักฐาน";
|
|
787
1136
|
|
|
788
1137
|
this.conversationHistory.push({ role: 'user', content: langPrompt });
|
|
1138
|
+
this.conversationHistory.push({ role: 'system', content: selfCritiqueDirective });
|
|
789
1139
|
|
|
790
1140
|
if (this.stateMachine.state !== AgentStates.STREAMING_ANSWER) {
|
|
791
1141
|
this.stateMachine.transition(AgentStates.STREAMING_ANSWER);
|
|
@@ -796,6 +1146,7 @@ ${toolsSummary}
|
|
|
796
1146
|
const finalRes = await this.client.chat({
|
|
797
1147
|
messages: this.conversationHistory,
|
|
798
1148
|
temperature: 0.3,
|
|
1149
|
+
maxTokens: 8192,
|
|
799
1150
|
stream: true,
|
|
800
1151
|
signal,
|
|
801
1152
|
onThought: (thoughtChunk) => {
|
|
@@ -821,29 +1172,147 @@ ${toolsSummary}
|
|
|
821
1172
|
}
|
|
822
1173
|
});
|
|
823
1174
|
|
|
1175
|
+
if (finalRes?.usage) {
|
|
1176
|
+
queryPromptTokens += Number(finalRes.usage.prompt_tokens || 0);
|
|
1177
|
+
queryCompletionTokens += Number(finalRes.usage.completion_tokens || 0);
|
|
1178
|
+
queryReasoningTokens += Number(finalRes.usage.reasoning_tokens || 0);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const queryTokens = {
|
|
1182
|
+
prompt: queryPromptTokens,
|
|
1183
|
+
completion: queryCompletionTokens,
|
|
1184
|
+
reasoning: queryReasoningTokens,
|
|
1185
|
+
total: queryPromptTokens + queryCompletionTokens
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
const queryCostUsd = calculateTokenCost(
|
|
1189
|
+
this.client?.provider || 'lmstudio',
|
|
1190
|
+
this.client?.model || 'google/gemma-4-e4b',
|
|
1191
|
+
queryPromptTokens,
|
|
1192
|
+
queryCompletionTokens
|
|
1193
|
+
);
|
|
1194
|
+
|
|
1195
|
+
this.session.recordQueryTokens(queryTokens, queryCostUsd);
|
|
1196
|
+
this.lastTelemetry = { tokens: queryTokens, costUsd: queryCostUsd };
|
|
1197
|
+
|
|
824
1198
|
const totalDurationMs = Date.now() - queryStartTime;
|
|
825
1199
|
this.session.stats.totalDurationMs += totalDurationMs;
|
|
826
1200
|
|
|
827
|
-
const defaultDone =
|
|
828
|
-
|
|
829
|
-
|
|
1201
|
+
const defaultDone =
|
|
1202
|
+
dominantLang === 'zh' ? "查询完毕" :
|
|
1203
|
+
dominantLang === 'ja' ? "検索が完了しました" :
|
|
1204
|
+
dominantLang === 'ko' ? "검색이 완료되었습니다" :
|
|
1205
|
+
dominantLang === 'en' ? "Search complete." : "เสร็จสิ้นการค้นหาข้อมูล";
|
|
1206
|
+
const output = sanitizeResponse(finalRes?.content || defaultDone);
|
|
1207
|
+
const citations = extractCitations(output, recordedTools);
|
|
1208
|
+
this.lastCitations = citations;
|
|
1209
|
+
if (currentAssistantSessionMsg) {
|
|
1210
|
+
currentAssistantSessionMsg.citations = citations;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
onFinalAnswer(output, {
|
|
1214
|
+
durationMs: totalDurationMs,
|
|
1215
|
+
language: dominantLang,
|
|
1216
|
+
tokens: queryTokens,
|
|
1217
|
+
costUsd: queryCostUsd,
|
|
1218
|
+
citations
|
|
1219
|
+
});
|
|
830
1220
|
|
|
831
1221
|
this._extractFactsFromFinalAnswer(output);
|
|
832
1222
|
|
|
833
1223
|
this.stateMachine.transition(AgentStates.COMPLETE);
|
|
834
|
-
eventBus.emit(Events.AGENT_FINAL_ANSWER, {
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1224
|
+
eventBus.emit(Events.AGENT_FINAL_ANSWER, {
|
|
1225
|
+
queryId,
|
|
1226
|
+
answer: output,
|
|
1227
|
+
durationMs: totalDurationMs,
|
|
1228
|
+
tokens: queryTokens,
|
|
1229
|
+
costUsd: queryCostUsd,
|
|
1230
|
+
citations
|
|
1231
|
+
});
|
|
1232
|
+
eventBus.emit(Events.AGENT_QUERY_END, {
|
|
1233
|
+
queryId,
|
|
1234
|
+
durationMs: totalDurationMs,
|
|
1235
|
+
tokens: queryTokens,
|
|
1236
|
+
costUsd: queryCostUsd,
|
|
1237
|
+
citations
|
|
1238
|
+
});
|
|
1239
|
+
await this.saveSession().catch(err => console.error('Session save failed:', err));
|
|
839
1240
|
|
|
840
1241
|
return output;
|
|
841
1242
|
}
|
|
842
1243
|
|
|
1244
|
+
async run(userPrompt, callbacks = {}) {
|
|
1245
|
+
return this.ask(userPrompt, callbacks);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
_computeConfidenceScore(toolResultTracker) {
|
|
1249
|
+
if (toolResultTracker.total === 0) return 0;
|
|
1250
|
+
|
|
1251
|
+
const successRatio = toolResultTracker.success / toolResultTracker.total;
|
|
1252
|
+
const errorPenalty = toolResultTracker.error * 0.5;
|
|
1253
|
+
const notFoundPenalty = toolResultTracker.notFound * 0.35;
|
|
1254
|
+
const base = Math.max(0, successRatio - errorPenalty - notFoundPenalty);
|
|
1255
|
+
|
|
1256
|
+
const hasVerifiedSource = toolResultTracker.success > 0;
|
|
1257
|
+
const allFailed = toolResultTracker.success === 0 && (toolResultTracker.notFound > 0 || toolResultTracker.error > 0);
|
|
1258
|
+
|
|
1259
|
+
if (allFailed) return Math.min(base, 0.2);
|
|
1260
|
+
if (!hasVerifiedSource) return Math.min(base, 0.4);
|
|
1261
|
+
|
|
1262
|
+
return Math.max(0.1, Math.min(1, base));
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
async _performSelfCritique(toolResultTracker, dominantLang, originalQuery) {
|
|
1266
|
+
const confidence = this._computeConfidenceScore(toolResultTracker);
|
|
1267
|
+
|
|
1268
|
+
const th = dominantLang === 'th';
|
|
1269
|
+
const zh = dominantLang === 'zh';
|
|
1270
|
+
|
|
1271
|
+
if (confidence <= 0.35) {
|
|
1272
|
+
const prefix = th
|
|
1273
|
+
? '【คำสั่งระบบเชิงวิพากษ์ — ลดระดับความมั่นใจต่ำ】'
|
|
1274
|
+
: zh
|
|
1275
|
+
? '【系统自我审查 — 证据不足】'
|
|
1276
|
+
: '【SYSTEM SELF-CRITIQUE — EVIDENCE INSUFFICIENT】';
|
|
1277
|
+
const body = th
|
|
1278
|
+
? 'เครื่องมือที่เรียกใช้ส่วนใหญ่ (หรือทั้งหมด) ไม่พบข้อมูลที่ยืนยันได้ หรือเกิดข้อผิดพลาด ความมั่นใจในความถูกต้องของข้อมูลต่ำในระดับที่จะตอบได้อย่างสมบูรณ์ ดังนั้นคุณต้องปฏิบัติตามนี้: (1) ห้ามแต่งเติมหรือคาดเดารายละเอียดใดๆ (ชื่อผลงาน, นักแสดง, วันที่, แบรนด์) (2) ระบุอย่างตรงไปตรงมาว่าไม่พบข้อมูลที่ยืนยันได้สำหรับการสอบถามนี้ และ (3) แนะนำวิธีอื่นให้ผู้ใช้ค้นหาข้อมูล เช่น ถามใหม่ด้วยคำค้นหาอื่น หรือรอการประกาศอย่างเป็นทางการ การตอบแบบ "ไม่มีข้อมูลอย่างเป็นทางการ" ดีกว่าการสร้างคำตอบที่สมบูรณ์แต่ไม่มีหลักฐานเสมอ'
|
|
1279
|
+
: zh
|
|
1280
|
+
? '您请求的信息在已验证的数据库中未能找到充分的证据支持。您必须遵循以下规则:(1) 严禁编造、推测或虚构任何清晰信息(作品名称、演员、日期、品牌);(2) 如实说明当前没有找到可核实的记录;(3) 建议用户换一种问法或等待官方公告。始终优先说"未找到官方信息",而不是生成完整但缺乏依据的描述。'
|
|
1281
|
+
: 'The verification tools returned insufficient or no confirmed evidence for this query. You MUST follow these rules: (1) Do NOT fabricate, infer, or invent any concrete details (titles, casts, dates, brands); (2) Honestly state that no verified record could be found; (3) Suggest alternative ways for the user to find the information. Always prefer stating "No official information found" over generating a complete but ungrounded description.';
|
|
1282
|
+
return `${prefix}\n${body}\nOriginal query: ${originalQuery}`;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
if (confidence >= 0.7) {
|
|
1286
|
+
const prefix = th
|
|
1287
|
+
? '【คำสั่งระบบเชิงวิพากษ์ — แหล่งข้อมูลแข็งแกร่ง】'
|
|
1288
|
+
: zh
|
|
1289
|
+
? '【系统自我审查 — 证据充分】'
|
|
1290
|
+
: '【SYSTEM SELF-CRITIQUE — EVIDENCE STRONG】';
|
|
1291
|
+
const body = th
|
|
1292
|
+
? 'เครื่องมือยืนยันข้อมูลได้สำเร็จเพียงพอสำหรับการตั้งคำถามนี้ คุณสามารถตอบได้อย่างมั่นใจโดยอ้างอิงเฉพาะข้อเท็จจริงที่ปรากฏในผลลัพธ์เครื่องมือเท่านั้น ยังคงห้ามเติมรายละเอียดที่ไม่มีในผลลัพธ์เครื่องมือ'
|
|
1293
|
+
: zh
|
|
1294
|
+
? '工具已成功验证足够的数据。您可以自信地回答,但仅基于工具结果中呈现的事实。仍然禁止添加工具结果中不存在的细节。'
|
|
1295
|
+
: 'Tool results successfully verified sufficient data for this query. You may answer with confidence, but ONLY based on what is present in the tool results. Do NOT add details not present in the tool output.';
|
|
1296
|
+
return `${prefix}\n${body}`;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
const prefix = th
|
|
1300
|
+
? '【คำสั่งระบบเชิงวิพากษ์ — ความมั่นใจปานกลาง】'
|
|
1301
|
+
: zh
|
|
1302
|
+
? '【系统自我审查 — 需谨慎】'
|
|
1303
|
+
: '【SYSTEM SELF-CRITIQUE — PROCEED WITH CAUTION】';
|
|
1304
|
+
const body = th
|
|
1305
|
+
? 'เครื่องมือให้ข้อมูลบางส่วนแต่ยังไม่ครบถ้วนสมบูรณ์ ตรวจสอบคำตอบของคุณก่อนส่งให้ผู้ใช้: (1) ระบุเฉพาะข้อเท็จจริงที่ยืนยันโดยผลลัพธ์เครื่องมือเท่านั้น (2) ส่วนที่ยังไม่ยืนยัน ให้ระบุชัดเจนว่า "ยังไม่ได้รับการยืนยัน" หรือ "รายงานไม่ชัดเจน" (3) ห้ามเติมโครงเรื่อง การคัดเลือกนักแสดง หรือวันที่ที่ไม่มีแหล่งที่มา'
|
|
1306
|
+
: zh
|
|
1307
|
+
? '工具提供部分但非完整的信息。在回答前自查:(1) 只陈述工具结果已确认的事实;(2) 明确标记仍未确认的部分为"未验证"或"未官方确认";(3) 不要补充没有来源支持的剧情、选角或日期。'
|
|
1308
|
+
: 'Tool results provide partial but incomplete information. Before answering, self-check: (1) Only state facts confirmed by tool results. (2) Explicitly label unconfirmed aspects as "unverified" or "not officially confirmed." (3) Do not supplement plotlines, casting, or dates without supporting source.';
|
|
1309
|
+
return `${prefix}\n${body}`;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
843
1312
|
_capToolResultForHistory(result) {
|
|
844
1313
|
if (!result || typeof result !== 'object') return result;
|
|
845
1314
|
|
|
846
|
-
const MAX_RESULT_BYTES =
|
|
1315
|
+
const MAX_RESULT_BYTES = 16384;
|
|
847
1316
|
const serialized = JSON.stringify(result);
|
|
848
1317
|
if (serialized.length <= MAX_RESULT_BYTES) return result;
|
|
849
1318
|
|
|
@@ -866,7 +1335,7 @@ ${toolsSummary}
|
|
|
866
1335
|
}
|
|
867
1336
|
|
|
868
1337
|
if (typeof obj === 'string' && obj.length > 500) {
|
|
869
|
-
return obj.slice(0, 500)
|
|
1338
|
+
return `${obj.slice(0, 500)}…[truncated]`;
|
|
870
1339
|
}
|
|
871
1340
|
return obj;
|
|
872
1341
|
};
|
|
@@ -876,7 +1345,7 @@ ${toolsSummary}
|
|
|
876
1345
|
return capped;
|
|
877
1346
|
}
|
|
878
1347
|
|
|
879
|
-
_extractFactsFromToolResult(toolName,
|
|
1348
|
+
_extractFactsFromToolResult(toolName, _toolArgs, result) {
|
|
880
1349
|
if (!result || result.status === 'error' || result.status === 'not_found') return;
|
|
881
1350
|
|
|
882
1351
|
const facts = [];
|
|
@@ -898,7 +1367,7 @@ ${toolsSummary}
|
|
|
898
1367
|
|
|
899
1368
|
case 'get_work_detail': {
|
|
900
1369
|
const w = result.work;
|
|
901
|
-
if (w
|
|
1370
|
+
if (w?.titleCn) {
|
|
902
1371
|
facts.push({
|
|
903
1372
|
content: `${w.titleCn} (${w.titleEn || w.titleTh || ''}) — ${w.role || 'Actor'} | ${w.platform || ''} | ${w.year || ''}`,
|
|
904
1373
|
category: 'drama',
|
|
@@ -1029,9 +1498,8 @@ ${toolsSummary}
|
|
|
1029
1498
|
if (!answer || answer.length < 20) return;
|
|
1030
1499
|
|
|
1031
1500
|
const ytRegex = /https?:\/\/www\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/g;
|
|
1032
|
-
let match;
|
|
1033
1501
|
const ytIds = new Set();
|
|
1034
|
-
|
|
1502
|
+
for (const match of answer.matchAll(ytRegex)) {
|
|
1035
1503
|
ytIds.add(match[1]);
|
|
1036
1504
|
}
|
|
1037
1505
|
if (ytIds.size > 0) {
|