@shun-js/aibaiban-server 1.1.3 → 1.1.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.
- package/assets/manifest.json +1 -1
- package/assets/sitemap.xml +1 -1
- package/package.json +3 -3
- package/server/service/LLMService.js +125 -70
- package/views/index.html +34 -38
- package/server/util/llm-agent.js +0 -63
- package/server/util/llm-intent.js +0 -79
- package/server/util/llm-toolcall.js +0 -314
- package/server/util/prompt-intent.md +0 -277
- package/server/util/prompt-toolcall.md +0 -273
package/assets/manifest.json
CHANGED
package/assets/sitemap.xml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shun-js/aibaiban-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "aibaiban.com server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai aibaiban"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"qiao-z-nuser": "^6.0.0",
|
|
38
38
|
"qiao-z-service": "^6.0.0",
|
|
39
39
|
"qiao-z-sms": "^6.0.0",
|
|
40
|
-
"viho-llm": "^1.0.
|
|
40
|
+
"viho-llm": "^1.0.6",
|
|
41
41
|
"zod": "^4.3.6",
|
|
42
42
|
"zod-to-json-schema": "^3.25.1"
|
|
43
43
|
},
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"access": "public",
|
|
46
46
|
"registry": "https://registry.npmjs.org/"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "94bf576ed7112fe207b8ed4c4db530db6b8ecc3d"
|
|
49
49
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
// llm
|
|
2
|
-
const {
|
|
1
|
+
// llm
|
|
2
|
+
const { OpenAIAPI, runAgents } = require('viho-llm');
|
|
3
3
|
const prompts = require('../util/prompt-agent.js');
|
|
4
4
|
|
|
5
5
|
// util
|
|
6
6
|
const { chatFeishuMsg, errorFeishuMsg } = require('../util/feishu.js');
|
|
7
7
|
|
|
8
|
+
// LLM 配置
|
|
9
|
+
const llmConfig = global.QZ_CONFIG.llm;
|
|
10
|
+
const finalLLMConfig = llmConfig[llmConfig.default];
|
|
11
|
+
const llm = OpenAIAPI(finalLLMConfig);
|
|
12
|
+
const modelName = finalLLMConfig.modelName;
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* drawAgent - 流式 Agent 接口
|
|
10
16
|
* 流程:router -> classify -> elaborate -> generate
|
|
@@ -31,76 +37,125 @@ exports.drawAgent = async (req, res) => {
|
|
|
31
37
|
const startTime = Date.now();
|
|
32
38
|
let stepStart = startTime;
|
|
33
39
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// intent
|
|
39
|
-
const intentResult = await callLLMForJSON(prompts.ROUTER_PROMPT.replace('{input}', input), res, 'router');
|
|
40
|
-
const intent = intentResult.intent;
|
|
41
|
-
const routerTime = Date.now() - stepStart;
|
|
42
|
-
req.logger.info(methodName, 'intent', intent, `${routerTime}ms`);
|
|
43
|
-
chatFeishuMsg(req, `intent-${intent}`);
|
|
44
|
-
res.streaming(`data: ${JSON.stringify({ step: 'router', intent, duration: routerTime })}\n\n`);
|
|
45
|
-
|
|
46
|
-
// 非白板请求
|
|
47
|
-
if (intent === 'irrelevant') {
|
|
48
|
-
res.streaming(
|
|
49
|
-
`data: ${JSON.stringify({ step: 'router', result: 'irrelevant', response: prompts.FIXED_REPLY })}\n\n`,
|
|
50
|
-
);
|
|
51
|
-
res.streamingEnd();
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// 2. classify - 分类图表类型
|
|
56
|
-
stepStart = Date.now();
|
|
57
|
-
res.streaming(`data: ${JSON.stringify({ step: 'classify', status: 'start' })}\n\n`);
|
|
58
|
-
req.logger.info(methodName, 'step: classify');
|
|
59
|
-
const classifyResult = await callLLMForJSON(prompts.CLASSIFY_PROMPT.replace('{input}', input), res, 'classify');
|
|
60
|
-
const diagramType = classifyResult.diagramType;
|
|
61
|
-
const classifyTime = Date.now() - stepStart;
|
|
62
|
-
req.logger.info(methodName, 'diagramType', diagramType, `${classifyTime}ms`);
|
|
63
|
-
chatFeishuMsg(req, `diagramType-${diagramType}`);
|
|
64
|
-
res.streaming(`data: ${JSON.stringify({ step: 'classify', diagramType, duration: classifyTime })}\n\n`);
|
|
65
|
-
|
|
66
|
-
// 3. elaborate - 细化内容
|
|
67
|
-
stepStart = Date.now();
|
|
68
|
-
res.streaming(`data: ${JSON.stringify({ step: 'elaborate', status: 'start' })}\n\n`);
|
|
69
|
-
req.logger.info(methodName, 'step: elaborate');
|
|
70
|
-
const elaboration = await callLLM(
|
|
71
|
-
prompts.ELABORATE_PROMPT.replace('{input}', input).replace('{diagramType}', diagramType),
|
|
72
|
-
res,
|
|
73
|
-
'elaborate',
|
|
74
|
-
);
|
|
75
|
-
const elaborateTime = Date.now() - stepStart;
|
|
76
|
-
req.logger.info(methodName, 'elaboration', elaboration.slice(0, 100) + '...', `${elaborateTime}ms`);
|
|
77
|
-
chatFeishuMsg(req, `elaboration-${elaboration}`);
|
|
78
|
-
res.streaming(`data: ${JSON.stringify({ step: 'elaborate', done: true, duration: elaborateTime })}\n\n`);
|
|
40
|
+
// agent 管线共享状态
|
|
41
|
+
let diagramType = '';
|
|
42
|
+
let elaboration = '';
|
|
79
43
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
44
|
+
await runAgents([
|
|
45
|
+
// 1. router - 意图分类
|
|
46
|
+
{
|
|
47
|
+
agentStartCallback: () => {
|
|
48
|
+
stepStart = Date.now();
|
|
49
|
+
res.streaming(`data: ${JSON.stringify({ step: 'router', status: 'start' })}\n\n`);
|
|
50
|
+
req.logger.info(methodName, 'step: router');
|
|
51
|
+
},
|
|
52
|
+
agentRequestOptions: {
|
|
53
|
+
llm,
|
|
54
|
+
modelName,
|
|
55
|
+
messages: [{ role: 'user', content: prompts.ROUTER_PROMPT.replace('{input}', input) }],
|
|
56
|
+
isJson: true,
|
|
57
|
+
},
|
|
58
|
+
agentEndCallback: (result) => {
|
|
59
|
+
const intent = result.intent;
|
|
60
|
+
const duration = Date.now() - stepStart;
|
|
61
|
+
req.logger.info(methodName, 'intent', intent, `${duration}ms`);
|
|
62
|
+
chatFeishuMsg(req, `intent-${intent}`);
|
|
63
|
+
res.streaming(`data: ${JSON.stringify({ step: 'router', intent, duration })}\n\n`);
|
|
64
|
+
return intent === 'irrelevant';
|
|
65
|
+
},
|
|
66
|
+
agentBreakCallback: () => {
|
|
67
|
+
res.streaming(
|
|
68
|
+
`data: ${JSON.stringify({ step: 'router', result: 'irrelevant', response: prompts.FIXED_REPLY })}\n\n`,
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
// 2. classify - 分类图表类型
|
|
73
|
+
{
|
|
74
|
+
agentStartCallback: () => {
|
|
75
|
+
stepStart = Date.now();
|
|
76
|
+
res.streaming(`data: ${JSON.stringify({ step: 'classify', status: 'start' })}\n\n`);
|
|
77
|
+
req.logger.info(methodName, 'step: classify');
|
|
78
|
+
},
|
|
79
|
+
agentRequestOptions: {
|
|
80
|
+
llm,
|
|
81
|
+
modelName,
|
|
82
|
+
messages: [{ role: 'user', content: prompts.CLASSIFY_PROMPT.replace('{input}', input) }],
|
|
83
|
+
isJson: true,
|
|
84
|
+
},
|
|
85
|
+
agentEndCallback: (result) => {
|
|
86
|
+
diagramType = result.diagramType;
|
|
87
|
+
const duration = Date.now() - stepStart;
|
|
88
|
+
req.logger.info(methodName, 'diagramType', diagramType, `${duration}ms`);
|
|
89
|
+
chatFeishuMsg(req, `diagramType-${diagramType}`);
|
|
90
|
+
res.streaming(`data: ${JSON.stringify({ step: 'classify', diagramType, duration })}\n\n`);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
// 3. elaborate - 细化内容
|
|
94
|
+
{
|
|
95
|
+
agentStartCallback: () => {
|
|
96
|
+
stepStart = Date.now();
|
|
97
|
+
res.streaming(`data: ${JSON.stringify({ step: 'elaborate', status: 'start' })}\n\n`);
|
|
98
|
+
req.logger.info(methodName, 'step: elaborate');
|
|
99
|
+
},
|
|
100
|
+
agentRequestOptions: {
|
|
101
|
+
llm,
|
|
102
|
+
modelName,
|
|
103
|
+
get messages() {
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
role: 'user',
|
|
107
|
+
content: prompts.ELABORATE_PROMPT.replace('{input}', input).replace('{diagramType}', diagramType),
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
agentEndCallback: (result) => {
|
|
113
|
+
elaboration = result;
|
|
114
|
+
const duration = Date.now() - stepStart;
|
|
115
|
+
req.logger.info(methodName, 'elaboration', elaboration.slice(0, 100) + '...', `${duration}ms`);
|
|
116
|
+
chatFeishuMsg(req, `elaboration-${elaboration}`);
|
|
117
|
+
res.streaming(`data: ${JSON.stringify({ step: 'elaborate', done: true, duration })}\n\n`);
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
// 4. generate - 生成 Mermaid
|
|
121
|
+
{
|
|
122
|
+
agentStartCallback: () => {
|
|
123
|
+
stepStart = Date.now();
|
|
124
|
+
res.streaming(`data: ${JSON.stringify({ step: 'generate', status: 'start' })}\n\n`);
|
|
125
|
+
req.logger.info(methodName, 'step: generate');
|
|
126
|
+
},
|
|
127
|
+
agentRequestOptions: {
|
|
128
|
+
llm,
|
|
129
|
+
modelName,
|
|
130
|
+
get messages() {
|
|
131
|
+
return [
|
|
132
|
+
{
|
|
133
|
+
role: 'user',
|
|
134
|
+
content: prompts.GENERATE_PROMPT.replace('{diagramType}', diagramType).replace(
|
|
135
|
+
'{elaboration}',
|
|
136
|
+
elaboration,
|
|
137
|
+
),
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
agentEndCallback: (result) => {
|
|
143
|
+
const mermaidCode = result;
|
|
144
|
+
const duration = Date.now() - stepStart;
|
|
145
|
+
const totalDuration = Date.now() - startTime;
|
|
146
|
+
req.logger.info(
|
|
147
|
+
methodName,
|
|
148
|
+
'mermaidCode',
|
|
149
|
+
mermaidCode.slice(0, 100) + '...',
|
|
150
|
+
`${duration}ms`,
|
|
151
|
+
`total: ${totalDuration}ms`,
|
|
152
|
+
);
|
|
153
|
+
chatFeishuMsg(req, `mermaidCode-${mermaidCode}`);
|
|
154
|
+
res.streaming(`data: ${JSON.stringify({ step: 'generate', mermaidCode, duration, totalDuration })}\n\n`);
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
]);
|
|
99
158
|
|
|
100
|
-
// 返回最终结果
|
|
101
|
-
res.streaming(
|
|
102
|
-
`data: ${JSON.stringify({ step: 'generate', mermaidCode, duration: generateTime, totalDuration: totalTime })}\n\n`,
|
|
103
|
-
);
|
|
104
159
|
res.streamingEnd();
|
|
105
160
|
} catch (error) {
|
|
106
161
|
req.logger.error(methodName, 'error', error);
|
package/views/index.html
CHANGED
|
@@ -5,30 +5,31 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
|
|
7
7
|
<!-- Primary Meta Tags -->
|
|
8
|
-
<title>AI白板 - 在线协作白板工具 | AI
|
|
9
|
-
<meta name="title" content="AI白板 - 在线协作白板工具 | AI
|
|
8
|
+
<title>AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图</title>
|
|
9
|
+
<meta name="title" content="AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图" />
|
|
10
10
|
<meta
|
|
11
11
|
name="description"
|
|
12
|
-
content="AI白板是专业的在线协作白板软件,AI
|
|
12
|
+
content="AI白板是专业的在线协作白板软件,AI驱动自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公、团队协同。Miro、FigJam、boardmix优质替代方案,一句话完成专业图表绘制。"
|
|
13
13
|
/>
|
|
14
14
|
<meta
|
|
15
15
|
name="keywords"
|
|
16
|
-
content="AI白板,在线白板,电子白板,协作白板,白板软件,AI生成流程图,AI
|
|
16
|
+
content="AI白板,在线白板,电子白板,协作白板,白板软件,AI生成流程图,AI时序图,AI类图,AI ER图,在线协同,远程办公,团队协作,头脑风暴工具,设计协作,可视化工具,Miro替代,FigJam替代,boardmix替代,Excalidraw,ProcessOn,GitMind"
|
|
17
17
|
/>
|
|
18
18
|
<meta name="author" content="Vincent" />
|
|
19
19
|
<meta name="robots" content="index, follow" />
|
|
20
|
+
<link rel="canonical" href="https://aibaiban.com/" />
|
|
20
21
|
|
|
21
22
|
<!-- Theme Color -->
|
|
22
|
-
<meta name="theme-color" content="#
|
|
23
|
+
<meta name="theme-color" content="#ffffff" />
|
|
23
24
|
<meta name="color-scheme" content="light dark" />
|
|
24
25
|
|
|
25
26
|
<!-- Open Graph / Facebook -->
|
|
26
27
|
<meta property="og:type" content="website" />
|
|
27
28
|
<meta property="og:url" content="https://aibaiban.com/" />
|
|
28
|
-
<meta property="og:title" content="AI白板 - 在线协作白板工具 | AI
|
|
29
|
+
<meta property="og:title" content="AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图" />
|
|
29
30
|
<meta
|
|
30
31
|
property="og:description"
|
|
31
|
-
content="专业的在线协作白板软件,AI
|
|
32
|
+
content="专业的在线协作白板软件,AI自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公。Miro/FigJam优质替代方案。"
|
|
32
33
|
/>
|
|
33
34
|
<meta property="og:image" content="https://static-small.vincentqiao.com/aibaiban/static/og-image.png" />
|
|
34
35
|
<meta property="og:site_name" content="AI白板" />
|
|
@@ -37,15 +38,15 @@
|
|
|
37
38
|
<!-- Twitter Card -->
|
|
38
39
|
<meta name="twitter:card" content="summary_large_image" />
|
|
39
40
|
<meta name="twitter:url" content="https://aibaiban.com/" />
|
|
40
|
-
<meta name="twitter:title" content="AI白板 - 在线协作白板工具 | AI
|
|
41
|
+
<meta name="twitter:title" content="AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图" />
|
|
41
42
|
<meta
|
|
42
43
|
name="twitter:description"
|
|
43
|
-
content="专业的在线协作白板软件,AI
|
|
44
|
+
content="专业的在线协作白板软件,AI自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公。"
|
|
44
45
|
/>
|
|
45
46
|
<meta name="twitter:image" content="https://static-small.vincentqiao.com/aibaiban/static/og-image.png" />
|
|
46
47
|
|
|
47
48
|
<!-- Apple Mobile Web App -->
|
|
48
|
-
<meta name="
|
|
49
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
49
50
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
50
51
|
<meta name="apple-mobile-web-app-title" content="AI白板" />
|
|
51
52
|
|
|
@@ -59,35 +60,30 @@
|
|
|
59
60
|
|
|
60
61
|
<!-- Structured Data (JSON-LD) for SEO -->
|
|
61
62
|
<script type="application/ld+json">
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"offers": {
|
|
69
|
-
"@type": "Offer",
|
|
70
|
-
"price": "0",
|
|
71
|
-
"priceCurrency": "CNY"
|
|
63
|
+
[
|
|
64
|
+
{
|
|
65
|
+
"@context": "https://schema.org",
|
|
66
|
+
"@type": "WebSite",
|
|
67
|
+
"name": "AI白板",
|
|
68
|
+
"url": "https://aibaiban.com"
|
|
72
69
|
},
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
70
|
+
{
|
|
71
|
+
"@context": "https://schema.org",
|
|
72
|
+
"@type": "SoftwareApplication",
|
|
73
|
+
"name": "AI白板",
|
|
74
|
+
"applicationCategory": "DesignApplication",
|
|
75
|
+
"operatingSystem": "Web",
|
|
76
|
+
"offers": {
|
|
77
|
+
"@type": "Offer",
|
|
78
|
+
"price": "0",
|
|
79
|
+
"priceCurrency": "CNY"
|
|
80
|
+
},
|
|
81
|
+
"description": "专业的在线协作白板软件,AI驱动自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公、团队协同。",
|
|
82
|
+
"url": "https://aibaiban.com",
|
|
83
|
+
"image": "https://static-small.vincentqiao.com/aibaiban/static/og-image.png",
|
|
84
|
+
"featureList": ["AI生成流程图", "AI生成时序图", "AI生成类图", "AI生成ER图", "在线协作白板", "远程办公支持"]
|
|
85
|
+
}
|
|
86
|
+
]
|
|
91
87
|
</script>
|
|
92
88
|
<!-- Microsoft Clarity (production only) -->
|
|
93
89
|
<script type="text/javascript">
|
package/server/util/llm-agent.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// llm
|
|
2
|
-
const { OpenAIAPI } = require('viho-llm');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* LLM 配置 - 使用 moonshot 平台的 kimi-k2.5
|
|
6
|
-
*/
|
|
7
|
-
const llmConfig = global.QZ_CONFIG.llm;
|
|
8
|
-
const finalLLMConfig = llmConfig[llmConfig.default];
|
|
9
|
-
const llm = OpenAIAPI(finalLLMConfig);
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 从文本中提取 JSON
|
|
13
|
-
*/
|
|
14
|
-
function extractJSON(text) {
|
|
15
|
-
const cleaned = text.replace(/\{\{/g, '{').replace(/\}\}/g, '}');
|
|
16
|
-
try {
|
|
17
|
-
return JSON.parse(cleaned);
|
|
18
|
-
} catch (e) {
|
|
19
|
-
// try next
|
|
20
|
-
}
|
|
21
|
-
const codeBlock = cleaned.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
22
|
-
if (codeBlock) {
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(codeBlock[1].trim());
|
|
25
|
-
} catch (e) {
|
|
26
|
-
// try next
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
const match = cleaned.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
|
|
30
|
-
if (match) {
|
|
31
|
-
try {
|
|
32
|
-
return JSON.parse(match[1]);
|
|
33
|
-
} catch (e) {
|
|
34
|
-
// try next
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
throw new Error(`Cannot extract JSON from: ${text.slice(0, 200)}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 调用 LLM 并解析 JSON
|
|
42
|
-
*/
|
|
43
|
-
exports.callLLMForJSON = async (prompt) => {
|
|
44
|
-
const response = await llm.chat({
|
|
45
|
-
model: finalLLMConfig.modelName,
|
|
46
|
-
messages: [{ role: 'user', content: prompt }],
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const fullContent = response.content || '';
|
|
50
|
-
return extractJSON(fullContent);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 调用 LLM(普通文本)
|
|
55
|
-
*/
|
|
56
|
-
exports.callLLM = async (prompt) => {
|
|
57
|
-
const response = await llm.chat({
|
|
58
|
-
model: finalLLMConfig.modelName,
|
|
59
|
-
messages: [{ role: 'user', content: prompt }],
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return response.content || '';
|
|
63
|
-
};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// path
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { readFile } = require('qiao-file');
|
|
4
|
-
|
|
5
|
-
// z
|
|
6
|
-
const { z } = require('zod');
|
|
7
|
-
const { zodToJsonSchema } = require('zod-to-json-schema');
|
|
8
|
-
|
|
9
|
-
// llm
|
|
10
|
-
const { GeminiVertex } = require('viho-llm');
|
|
11
|
-
const gemini = GeminiVertex({
|
|
12
|
-
projectId: global.QZ_CONFIG.gemini.projectId,
|
|
13
|
-
location: global.QZ_CONFIG.gemini.location,
|
|
14
|
-
modelName: global.QZ_CONFIG.gemini.modelName,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// const
|
|
18
|
-
const chatConfig = {
|
|
19
|
-
responseMimeType: 'application/json',
|
|
20
|
-
temperature: 0.2, // 保持较低温度,确保输出稳定
|
|
21
|
-
topP: 0.9,
|
|
22
|
-
topK: 40,
|
|
23
|
-
maxOutputTokens: 8192, // 足够大以支持复杂图表
|
|
24
|
-
thinkingConfig: {
|
|
25
|
-
thinkingBudget: 0,
|
|
26
|
-
includeThoughts: false,
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
const safetySettings = [
|
|
30
|
-
{
|
|
31
|
-
category: 'HARM_CATEGORY_HATE_SPEECH',
|
|
32
|
-
threshold: 'BLOCK_ONLY_HIGH',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
|
|
36
|
-
threshold: 'BLOCK_ONLY_HIGH',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
40
|
-
threshold: 'BLOCK_ONLY_HIGH',
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
category: 'HARM_CATEGORY_HARASSMENT',
|
|
44
|
-
threshold: 'BLOCK_ONLY_HIGH',
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* llmParseIntent
|
|
50
|
-
* @param {*} userPrompts
|
|
51
|
-
*/
|
|
52
|
-
let intentSystemPrompt = null;
|
|
53
|
-
exports.llmParseIntent = async (userPrompts) => {
|
|
54
|
-
// intent system prompt - 支持版本切换
|
|
55
|
-
if (!intentSystemPrompt) {
|
|
56
|
-
const promptFile = './prompt-intent.md';
|
|
57
|
-
intentSystemPrompt = await readFile(path.resolve(__dirname, promptFile));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// chat config - 新增 confidence 字段支持
|
|
61
|
-
chatConfig.responseJsonSchema = zodToJsonSchema(
|
|
62
|
-
z.object({
|
|
63
|
-
intent: z.enum(['DRAW', 'REJECT']).describe('用户意图:DRAW(绘图)或 REJECT(非绘图)'),
|
|
64
|
-
reason: z.string().describe('判断理由(一句话说明)'),
|
|
65
|
-
confidence: z.number().min(0).max(1).optional().describe('置信度评分(0.0-1.0),可选字段'),
|
|
66
|
-
}),
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
// chat options
|
|
70
|
-
const chatOptions = {
|
|
71
|
-
contents: userPrompts,
|
|
72
|
-
systemInstruction: intentSystemPrompt,
|
|
73
|
-
config: chatConfig,
|
|
74
|
-
safetySettings: safetySettings,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// go
|
|
78
|
-
return await gemini.chat(chatOptions);
|
|
79
|
-
};
|