@blocklet/pages-kit-agents 0.5.15 → 0.5.17

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.
@@ -5,10 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getModel = getModel;
7
7
  const claude_chat_model_js_1 = require("@aigne/core/models/claude-chat-model.js");
8
+ const deepseek_chat_model_js_1 = require("@aigne/core/models/deepseek-chat-model.js");
8
9
  const gemini_chat_model_js_1 = require("@aigne/core/models/gemini-chat-model.js");
9
10
  const openai_chat_model_js_1 = require("@aigne/core/models/openai-chat-model.js");
11
+ const xai_chat_model_js_1 = require("@aigne/core/models/xai-chat-model.js");
10
12
  const node_assert_1 = __importDefault(require("node:assert"));
11
- const { OPENAI_API_KEY, GEMINI_API_KEY, CLAUDE_API_KEY } = process.env;
13
+ const { OPENAI_API_KEY, GEMINI_API_KEY, CLAUDE_API_KEY, XAI_API_KEY, DEEPSEEK_API_KEY } = process.env;
12
14
  function getModel(modelName = 'openai') {
13
15
  if (modelName?.includes('gpt')) {
14
16
  (0, node_assert_1.default)(OPENAI_API_KEY, 'Please set the OPENAI_API_KEY environment variable');
@@ -40,5 +42,21 @@ function getModel(modelName = 'openai') {
40
42
  });
41
43
  return model;
42
44
  }
45
+ if (modelName?.includes('grok')) {
46
+ (0, node_assert_1.default)(XAI_API_KEY, 'Please set the XAI_API_KEY environment variable');
47
+ const grok = new xai_chat_model_js_1.XAIChatModel({
48
+ apiKey: XAI_API_KEY,
49
+ model: modelName,
50
+ });
51
+ return grok;
52
+ }
53
+ if (modelName?.includes('deepseek')) {
54
+ (0, node_assert_1.default)(DEEPSEEK_API_KEY, 'Please set the DEEPSEEK_API_KEY environment variable');
55
+ const deepseek = new deepseek_chat_model_js_1.DeepSeekChatModel({
56
+ apiKey: DEEPSEEK_API_KEY,
57
+ model: modelName,
58
+ });
59
+ return deepseek;
60
+ }
43
61
  return undefined;
44
62
  }
@@ -37,6 +37,7 @@ const path_1 = __importDefault(require("path"));
37
37
  async function updateFileList() {
38
38
  const outputDir = path_1.default.join(process.cwd(), 'sample-output');
39
39
  const indexPath = path_1.default.join(outputDir, 'index.html');
40
+ const dataJsPath = path_1.default.join(outputDir, 'file-list-data.js');
40
41
  // 存储所有子文件夹及其HTML文件信息
41
42
  const fileList = [];
42
43
  try {
@@ -59,28 +60,71 @@ async function updateFileList() {
59
60
  .filter((file) => file.isFile() && file.name.endsWith('.html'))
60
61
  .map((file) => {
61
62
  const fileName = file.name.replace(/\.html$/, '');
63
+ const jsonFileName = `${fileName}.json`;
64
+ const jsonFilePath = path_1.default.join(folderPath, jsonFileName);
65
+ const htmlFilePath = `${folderName}/${file.name}`;
66
+ const jsonFileRelativePath = `${folderName}/${jsonFileName}`;
62
67
  return {
63
68
  name: fileName,
64
- path: `${folderName}/${file.name}`,
69
+ path: htmlFilePath,
70
+ jsonPath: (0, fs_1.existsSync)(jsonFilePath) ? jsonFileRelativePath : undefined,
65
71
  createdAt: new Date().toISOString(),
66
72
  };
67
73
  });
74
+ // 读取每个HTML文件对应的JSON文件(如果存在)
75
+ for (const htmlFile of htmlFiles) {
76
+ if (htmlFile.jsonPath) {
77
+ try {
78
+ // eslint-disable-next-line no-await-in-loop
79
+ const jsonContent = await fs.readFile(path_1.default.join(outputDir, htmlFile.jsonPath), 'utf8');
80
+ const jsonData = JSON.parse(jsonContent);
81
+ // 提取评分数据
82
+ if (jsonData && jsonData.result) {
83
+ htmlFile.scoreData = {
84
+ total_score: jsonData.result.total_score,
85
+ level: jsonData.result.level,
86
+ suggestions: jsonData.result.suggestions,
87
+ dimension_scores: jsonData.result.dimension_scores,
88
+ };
89
+ }
90
+ }
91
+ catch (jsonError) {
92
+ console.error(`Error reading JSON file ${htmlFile.jsonPath}:`, jsonError);
93
+ }
94
+ }
95
+ }
68
96
  // 将文件夹信息添加到列表
69
97
  fileList.push({
70
98
  folder: folderName,
71
99
  files: htmlFiles,
72
100
  });
73
101
  }
74
- // 更新index.html,内联JSON数据
102
+ // 创建独立的JS文件
103
+ const jsonString = JSON.stringify(fileList, null, 2);
104
+ const jsContent = `/* eslint-disable prettier/prettier */
105
+ /* eslint-disable @typescript-eslint/quotes */
106
+ // 自动生成的文件列表数据 - 请勿手动修改
107
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
108
+ const FILE_LIST_DATA = ${jsonString};
109
+ `;
110
+ await fs.writeFile(dataJsPath, jsContent, 'utf8');
111
+ // 确保index.html引用了这个JS文件
75
112
  if ((0, fs_1.existsSync)(indexPath)) {
76
113
  let indexHtml = await fs.readFile(indexPath, 'utf8');
77
- // 使用更简洁的替换方式
78
- const fileListPlaceholder = /const FILE_LIST_DATA = .*?;/s;
79
- if (fileListPlaceholder.test(indexHtml)) {
80
- // 替换占位符为实际数据
81
- const jsonString = JSON.stringify(fileList, null, 2);
82
- indexHtml = indexHtml.replace(fileListPlaceholder, `const FILE_LIST_DATA = ${jsonString};`);
83
- await fs.writeFile(indexPath, indexHtml, 'utf8');
114
+ // 检查index.html是否已经包含了对file-list-data.js的引用
115
+ const scriptIncludeRegex = /<script src=["']file-list-data\.js["']><\/script>/;
116
+ if (!scriptIncludeRegex.test(indexHtml)) {
117
+ // 如果没有引用,则替换原来的内联数据定义为引用外部JS文件
118
+ const fileListPlaceholder = /const FILE_LIST_DATA = .*?;/s;
119
+ if (fileListPlaceholder.test(indexHtml)) {
120
+ // 替换内联数据为引用外部JS文件
121
+ indexHtml = indexHtml.replace(fileListPlaceholder, `// 文件列表数据已移至外部JS文件
122
+ // 以下行将在第一次更新时被删除
123
+ const FILE_LIST_DATA = [];`);
124
+ // 在</head>前添加script标签引用外部JS文件
125
+ indexHtml = indexHtml.replace('</head>', ' <script src="file-list-data.js"></script>\n </head>');
126
+ await fs.writeFile(indexPath, indexHtml, 'utf8');
127
+ }
84
128
  }
85
129
  }
86
130
  }
@@ -26,3 +26,16 @@ export declare function saveHtmlContent({ htmlContent, outputFolder, outputFileN
26
26
  outputFolder: string;
27
27
  outputFileName: string;
28
28
  }): Promise<string>;
29
+ /**
30
+ * 保存JSON内容到指定目录
31
+ * @param jsonContent 要保存的JSON数据
32
+ * @param outputFolder 输出文件夹路径
33
+ * @param outputFileName 输出JSON文件名称,不包含扩展名
34
+ * @returns 生成的JSON文件的完整路径
35
+ * @throws 如果无法创建输出目录或写入文件则抛出异常
36
+ */
37
+ export declare function saveJsonContent({ jsonContent, outputFolder, outputFileName, }: {
38
+ jsonContent: Record<string, any> | string;
39
+ outputFolder: string;
40
+ outputFileName: string;
41
+ }): Promise<string>;
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.generateHtmlFromTemplate = generateHtmlFromTemplate;
7
7
  exports.saveHtmlContent = saveHtmlContent;
8
+ exports.saveJsonContent = saveJsonContent;
8
9
  const node_fs_1 = __importDefault(require("node:fs"));
9
10
  const promises_1 = require("node:fs/promises");
10
11
  const node_path_1 = __importDefault(require("node:path"));
@@ -72,3 +73,32 @@ async function saveHtmlContent({ htmlContent, outputFolder, outputFileName, }) {
72
73
  throw new Error(`Failed to save HTML content: ${err.message}`);
73
74
  }
74
75
  }
76
+ /**
77
+ * 保存JSON内容到指定目录
78
+ * @param jsonContent 要保存的JSON数据
79
+ * @param outputFolder 输出文件夹路径
80
+ * @param outputFileName 输出JSON文件名称,不包含扩展名
81
+ * @returns 生成的JSON文件的完整路径
82
+ * @throws 如果无法创建输出目录或写入文件则抛出异常
83
+ */
84
+ async function saveJsonContent({ jsonContent, outputFolder, outputFileName, }) {
85
+ try {
86
+ // 创建输出目录
87
+ const outputDir = node_path_1.default.resolve(process.cwd(), 'sample-output', outputFolder);
88
+ // 确保输出目录存在
89
+ if (!node_fs_1.default.existsSync(outputDir)) {
90
+ node_fs_1.default.mkdirSync(outputDir, { recursive: true });
91
+ }
92
+ // 构建输出文件的完整路径
93
+ const outputFilePath = node_path_1.default.join(outputDir, `${outputFileName}.json`);
94
+ // 如果传入的是对象,转换为JSON字符串
95
+ const content = typeof jsonContent === 'string' ? jsonContent : JSON.stringify(jsonContent, null, 2);
96
+ // 写入JSON内容
97
+ node_fs_1.default.writeFileSync(outputFilePath, content, 'utf-8');
98
+ return outputFilePath;
99
+ }
100
+ catch (error) {
101
+ const err = error;
102
+ throw new Error(`Failed to save JSON content: ${err.message}`);
103
+ }
104
+ }
@@ -0,0 +1,19 @@
1
+ import { AIAgent } from '@aigne/core';
2
+ export declare const contentReviewerAgent: AIAgent<{
3
+ question: string;
4
+ locale: string;
5
+ dataSource: string;
6
+ content: string;
7
+ }, {
8
+ result: {
9
+ total_score: number;
10
+ level: string;
11
+ suggestions: string[];
12
+ dimension_scores: {
13
+ name: string;
14
+ score: number;
15
+ maxScore: number;
16
+ comment: string;
17
+ }[];
18
+ };
19
+ }>;
@@ -0,0 +1,151 @@
1
+ import { AIAgent } from '@aigne/core';
2
+ import { z } from 'zod';
3
+ const prompt = `
4
+ 你是一个专业的网页文案评估专家,具备丰富的多行业文案分析经验。现在你将对一段网站文案进行全面评估,这些文案以结构化 JSON 格式提供,每一个字段对应网页中的一部分内容,主要字段包括:
5
+
6
+ - title: 页面主标题
7
+ - description: 页面描述
8
+ - sectionsData: 页面内容部分,由多个模块(如 heroSection、featuresSection、faqSection 等)组成,每个模块有自己的标题、描述、详细内容等字段
9
+
10
+ 目标是:帮助创作者发现文案中的逻辑、表达、结构或营销短板,从而优化出更具影响力和传播力的内容。
11
+
12
+ ** 不要轻易给出高分**,除非你真的觉得提供的文案十分优秀。
13
+
14
+ 你可以参考提供原始数据来了解产品信息,帮助你判断文案中的信息是否准确、完整。
15
+
16
+ ---
17
+
18
+ ### 打分区间说明(满分 100 分)
19
+
20
+ - **95-100 卓越级**:极具说服力与感染力,逻辑、节奏、表现力、SEO 全维度优秀,能直接用于投放或上线。
21
+ - **90-94 优秀级**:表达清晰有力,有一定记忆点,结构严谨,整体完成度高,稍作优化即可达到专业发布水平。
22
+ - **85-89 合格上乘**:表达准确、结构清楚,但较中规中矩,缺乏差异化或语言张力,建议提升亮点与传播力。
23
+ - **80-84 可用但平庸**:内容基本清晰,但语言或结构平淡,缺乏吸引力或说服力,有明显可优化之处。
24
+ - **70-79 结构与逻辑需优化**:存在信息遗漏、表达冗长或语言生硬等问题,可能影响理解或转化。
25
+ - **<70 不达标**:表达混乱、价值不清、逻辑缺失,建议重写或从受众视角重新构思。
26
+
27
+ ---
28
+
29
+ ### 评估维度(共 7 项)
30
+
31
+ ** 每个维度的分数不能超过最大分数 **
32
+
33
+ 1. **信息完整性(15分)**
34
+ - 是否明确传达核心价值或主张?
35
+ - 是否覆盖了“是什么、能做什么、为什么值得关注”?
36
+
37
+ 2. **结构与逻辑清晰度(15分)**
38
+ - sectionsData 中的数据表示页面中的 Section ,页面结构是否完整清晰?
39
+ - 结构是否符合读者的阅读预期(如金字塔结构、FAB 模式等)?
40
+
41
+ 3. **语言表达质量(15分)**
42
+ - 表达是否简练、自然、口语/书面语风格合适?
43
+ - 是否避免术语堆砌、空话套话?
44
+
45
+ 4. **吸引力与情绪感染力(15分)**
46
+ - 是否有记忆点或引发兴趣?
47
+ - 是否有利益点、情绪驱动或视觉画面感?
48
+
49
+ 5. **品牌调性与目标受众匹配度(15分)**
50
+ - 是否符合品牌语气?
51
+ - 是否贴合目标受众的心理与语境?
52
+
53
+ 6. **差异化与独特性(10分)**
54
+ - 是否展现出区别于竞品的亮点或立场?
55
+ - 是否传递出“为什么选我”?
56
+
57
+ 7. **SEO 友好度(15分)**
58
+ - 是否包含明确关键词?
59
+ - 是否具备良好的标题、副标题与语义层级?
60
+ - 是否利于搜索引擎抓取和用户点击?
61
+
62
+ ---
63
+
64
+ ### 输出格式要求:
65
+
66
+ 1. 每个维度请给出【分数(x/总分)】+ 简洁点评;
67
+ 2. 总结总体得分(满分 100);
68
+ 3. 提供 3-5 条具体、可操作的优化建议,不能泛泛而谈;
69
+ 4. 如内容已接近满分,也需说明“进一步打磨”的方向。
70
+
71
+ ### 📦 输出格式要求(结构化)
72
+
73
+ 请使用以下结构输出评分结果:
74
+ ** 下面的 json 只是示例,只参考输出格式,不能影响评估结果**
75
+ 只返回 json 不要包含其他描述信息。
76
+
77
+ \`\`\`json
78
+ {
79
+ "total_score": xx,
80
+ "level": "xx",
81
+ "dimension_scores": [
82
+ {
83
+ "name": "信息完整性",
84
+ "score": xx,
85
+ "maxScore": xx,
86
+ "comment": "xxxxxx"
87
+ },
88
+ {
89
+ "name": "结构与逻辑清晰度",
90
+ "score": xx,
91
+ "maxScore": xx,
92
+ "comment": "xx"
93
+ },
94
+ {
95
+ "name": "语言表达质量",
96
+ "score": xx,
97
+ "maxScore": xx,
98
+ "comment": "xx"
99
+ },
100
+ ...
101
+ ],
102
+ "suggestions": [
103
+ "xxxx",
104
+ "xxxxxxx",
105
+ "xxxxxx。",
106
+ "xxxxxxxxxxx。"
107
+ ]
108
+ }
109
+ \`\`\`
110
+ ---
111
+
112
+ ### 📌 适用场景举例:
113
+
114
+ - SEO 内容型页面(如博客、产品 landing page、产品介绍页)
115
+
116
+ ### 原始数据
117
+ {{dataSource}}
118
+
119
+
120
+ ### 输入的营销文案
121
+ {{ content }}
122
+
123
+ ### 其他要求
124
+ {{ question }}
125
+
126
+ ### 语言
127
+ 请使用{{ locale }}语言输出
128
+ `;
129
+ export const contentReviewerAgent = AIAgent.from({
130
+ name: 'Content Reviewer',
131
+ instructions: prompt,
132
+ inputSchema: z.object({
133
+ dataSource: z.string(),
134
+ content: z.string(),
135
+ question: z.string(),
136
+ locale: z.string(),
137
+ }),
138
+ outputSchema: z.object({
139
+ result: z.object({
140
+ total_score: z.number(),
141
+ level: z.string(),
142
+ dimension_scores: z.array(z.object({
143
+ name: z.string(),
144
+ score: z.number(),
145
+ maxScore: z.number(),
146
+ comment: z.string(),
147
+ })),
148
+ suggestions: z.array(z.string()),
149
+ }),
150
+ }),
151
+ });
@@ -20,7 +20,7 @@ import { createSectionContentAgent } from './section-content-generator.js';
20
20
  export default async function generatePageContentMultiAgent(input) {
21
21
  const { context = '', question = '', locale = '', outputSchema, onProgress, checkConnection, sectionsRules, modelName = 'gpt-4o-mini', } = input;
22
22
  try {
23
- logger.info('Starting multi-agent page content generation');
23
+ logger.info(`[${modelName}] Starting multi-agent page content generation`);
24
24
  // 检查客户端连接状态的辅助函数
25
25
  const isClientConnected = () => {
26
26
  // 如果没有提供检查函数,默认认为客户端仍连接
@@ -41,10 +41,10 @@ export default async function generatePageContentMultiAgent(input) {
41
41
  });
42
42
  }
43
43
  // 2. 初始页面结构规划
44
- logger.info('Planning initial page structure');
44
+ logger.info(`[${modelName}] Planning initial page structure`);
45
45
  // 检查客户端是否仍然连接
46
46
  if (!isClientConnected()) {
47
- logger.warn('Client disconnected during structure planning, aborting generation');
47
+ logger.warn(`[${modelName}] Client disconnected during structure planning, aborting generation`);
48
48
  throw new Error('Client disconnected');
49
49
  }
50
50
  let structurePlanMap = {};
@@ -54,7 +54,7 @@ export default async function generatePageContentMultiAgent(input) {
54
54
  model,
55
55
  agents: [pageStructurePlannerAgent, pageStructureValidatorAgent],
56
56
  limits: {
57
- maxAgentInvokes: 20,
57
+ maxAgentInvokes: 30,
58
58
  },
59
59
  });
60
60
  await planningEngine.invoke(pageStructurePlannerAgent, {
@@ -70,8 +70,7 @@ export default async function generatePageContentMultiAgent(input) {
70
70
  });
71
71
  const structurePlanResult = await planningEngine.subscribe(UserOutputTopic);
72
72
  const { structurePlan } = structurePlanResult.message;
73
- // const { structurePlan } = structurePlanResult;
74
- logger.info('Initial structure plan generated', structurePlan);
73
+ logger.info(`[${modelName}] Initial structure plan generated`, structurePlan);
75
74
  await planningEngine.shutdown();
76
75
  // 触发页面结构生成成功事件
77
76
  if (onProgress) {
@@ -100,7 +99,7 @@ export default async function generatePageContentMultiAgent(input) {
100
99
  logger.warn(`Client disconnected before generating section ${sectionName}, aborting generation`);
101
100
  return null;
102
101
  }
103
- logger.info(`Generating content for section: ${sectionName}`);
102
+ logger.info(`[${modelName}] Generating content for section: ${sectionName}`);
104
103
  // 通知当前正在处理的section
105
104
  if (onProgress) {
106
105
  onProgress({
@@ -125,7 +124,7 @@ export default async function generatePageContentMultiAgent(input) {
125
124
  // eslint-disable-next-line no-await-in-loop
126
125
  const sectionContent = await engine.invoke(sectionContentAgent, sectionContentInput);
127
126
  sectionsData[sectionName] = sectionContent;
128
- logger.info(`Content generated for section: ${sectionName}`);
127
+ logger.info(`[${modelName}] Content generated for section: ${sectionName}`);
129
128
  // 通知该section已生成完成
130
129
  if (onProgress) {
131
130
  const sectionDataObj = {};
@@ -139,10 +138,10 @@ export default async function generatePageContentMultiAgent(input) {
139
138
  }
140
139
  }
141
140
  // 6. 生成页面元数据
142
- logger.info('Generating page metadata');
141
+ logger.info(`[${modelName}] Generating page metadata`);
143
142
  // 检查客户端是否仍然连接
144
143
  if (!isClientConnected()) {
145
- logger.warn('Client disconnected before generating metadata, aborting generation');
144
+ logger.warn(`[${modelName}] Client disconnected before generating metadata, aborting generation`);
146
145
  throw new Error('Client disconnected');
147
146
  }
148
147
  const metadata = await engine.invoke(generatePageMetadataAgent, {
@@ -157,11 +156,11 @@ export default async function generatePageContentMultiAgent(input) {
157
156
  description: metadata.description,
158
157
  sectionsData,
159
158
  };
160
- logger.info('Multi-agent page content generation completed');
159
+ logger.info(`[${modelName}] Multi-agent page content generation completed`);
161
160
  return result;
162
161
  }
163
162
  catch (error) {
164
- logger.error('Error in multi-agent page content generation:', error);
163
+ logger.error(`[${modelName}] Error in multi-agent page content generation:`, error);
165
164
  throw error;
166
165
  }
167
166
  }
@@ -69,7 +69,6 @@ export const pageStructurePlannerAgent = AIAgent.from({
69
69
  logger.info('page structure planner output', JSON.stringify(output.structurePlan));
70
70
  return 'validation_request';
71
71
  },
72
- // publishTopic: UserOutputTopic,
73
72
  instructions: prompt,
74
73
  inputSchema: z.object({
75
74
  context: z.string(),