@blocklet/pages-kit-agents 0.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.env.local.example +8 -0
  2. package/LICENSE +13 -0
  3. package/README.md +10 -0
  4. package/cli.ts +354 -0
  5. package/index.ts +14 -0
  6. package/lib/cjs/agents/multi-agent-page-writer.d.ts +48 -0
  7. package/lib/cjs/agents/multi-agent-page-writer.js +203 -0
  8. package/lib/cjs/agents/page-metadata-generator.d.ts +10 -0
  9. package/lib/cjs/agents/page-metadata-generator.js +43 -0
  10. package/lib/cjs/agents/page-structure-planner.d.ts +55 -0
  11. package/lib/cjs/agents/page-structure-planner.js +106 -0
  12. package/lib/cjs/agents/page-structure-validator.d.ts +30 -0
  13. package/lib/cjs/agents/page-structure-validator.js +124 -0
  14. package/lib/cjs/agents/sample-html-generator.d.ts +6 -0
  15. package/lib/cjs/agents/sample-html-generator.js +35 -0
  16. package/lib/cjs/agents/section-content-generator.d.ts +19 -0
  17. package/lib/cjs/agents/section-content-generator.js +97 -0
  18. package/lib/cjs/index.d.ts +7 -0
  19. package/lib/cjs/index.js +30 -0
  20. package/lib/cjs/tsconfig.tsbuildinfo +1 -0
  21. package/lib/cjs/utils/agent-utils.d.ts +3 -0
  22. package/lib/cjs/utils/agent-utils.js +44 -0
  23. package/lib/cjs/utils/file-utils.d.ts +5 -0
  24. package/lib/cjs/utils/file-utils.js +90 -0
  25. package/lib/cjs/utils/index.d.ts +12 -0
  26. package/lib/cjs/utils/index.js +48 -0
  27. package/lib/cjs/utils/logger.d.ts +1 -0
  28. package/lib/cjs/utils/logger.js +4 -0
  29. package/lib/cjs/utils/template-utils.d.ts +28 -0
  30. package/lib/cjs/utils/template-utils.js +74 -0
  31. package/lib/esm/agents/multi-agent-page-writer.d.ts +48 -0
  32. package/lib/esm/agents/multi-agent-page-writer.js +199 -0
  33. package/lib/esm/agents/page-metadata-generator.d.ts +10 -0
  34. package/lib/esm/agents/page-metadata-generator.js +40 -0
  35. package/lib/esm/agents/page-structure-planner.d.ts +55 -0
  36. package/lib/esm/agents/page-structure-planner.js +103 -0
  37. package/lib/esm/agents/page-structure-validator.d.ts +30 -0
  38. package/lib/esm/agents/page-structure-validator.js +121 -0
  39. package/lib/esm/agents/sample-html-generator.d.ts +6 -0
  40. package/lib/esm/agents/sample-html-generator.js +32 -0
  41. package/lib/esm/agents/section-content-generator.d.ts +19 -0
  42. package/lib/esm/agents/section-content-generator.js +94 -0
  43. package/lib/esm/index.d.ts +7 -0
  44. package/lib/esm/index.js +8 -0
  45. package/lib/esm/tsconfig.tsbuildinfo +1 -0
  46. package/lib/esm/utils/agent-utils.d.ts +3 -0
  47. package/lib/esm/utils/agent-utils.js +38 -0
  48. package/lib/esm/utils/file-utils.d.ts +5 -0
  49. package/lib/esm/utils/file-utils.js +61 -0
  50. package/lib/esm/utils/index.d.ts +12 -0
  51. package/lib/esm/utils/index.js +28 -0
  52. package/lib/esm/utils/logger.d.ts +1 -0
  53. package/lib/esm/utils/logger.js +1 -0
  54. package/lib/esm/utils/template-utils.d.ts +28 -0
  55. package/lib/esm/utils/template-utils.js +67 -0
  56. package/lib/types/agents/multi-agent-page-writer.d.ts +48 -0
  57. package/lib/types/agents/page-metadata-generator.d.ts +10 -0
  58. package/lib/types/agents/page-structure-planner.d.ts +55 -0
  59. package/lib/types/agents/page-structure-validator.d.ts +30 -0
  60. package/lib/types/agents/sample-html-generator.d.ts +6 -0
  61. package/lib/types/agents/section-content-generator.d.ts +19 -0
  62. package/lib/types/index.d.ts +7 -0
  63. package/lib/types/tsconfig.tsbuildinfo +1 -0
  64. package/lib/types/utils/agent-utils.d.ts +3 -0
  65. package/lib/types/utils/file-utils.d.ts +5 -0
  66. package/lib/types/utils/index.d.ts +12 -0
  67. package/lib/types/utils/logger.d.ts +1 -0
  68. package/lib/types/utils/template-utils.d.ts +28 -0
  69. package/package.json +46 -0
  70. package/types.d.ts +4 -0
@@ -0,0 +1,3 @@
1
+ import { ClaudeChatModel } from '@aigne/core/models/claude-chat-model.js';
2
+ import { OpenAIChatModel } from '@aigne/core/models/openai-chat-model.js';
3
+ export declare function getModel(modelName?: string): OpenAIChatModel | ClaudeChatModel | undefined;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getModel = getModel;
7
+ const claude_chat_model_js_1 = require("@aigne/core/models/claude-chat-model.js");
8
+ const gemini_chat_model_js_1 = require("@aigne/core/models/gemini-chat-model.js");
9
+ const openai_chat_model_js_1 = require("@aigne/core/models/openai-chat-model.js");
10
+ const node_assert_1 = __importDefault(require("node:assert"));
11
+ const { OPENAI_API_KEY, GEMINI_API_KEY, CLAUDE_API_KEY } = process.env;
12
+ function getModel(modelName = 'openai') {
13
+ if (modelName?.includes('gpt')) {
14
+ (0, node_assert_1.default)(OPENAI_API_KEY, 'Please set the OPENAI_API_KEY environment variable');
15
+ const openai = new openai_chat_model_js_1.OpenAIChatModel({
16
+ apiKey: OPENAI_API_KEY,
17
+ model: modelName,
18
+ modelOptions: {
19
+ temperature: 0.8,
20
+ },
21
+ });
22
+ return openai;
23
+ }
24
+ if (modelName?.includes('gemini')) {
25
+ (0, node_assert_1.default)(GEMINI_API_KEY, 'Please set the GEMINI_API_KEY environment variable');
26
+ const gemini = new gemini_chat_model_js_1.GeminiChatModel({
27
+ apiKey: GEMINI_API_KEY,
28
+ model: modelName,
29
+ });
30
+ return gemini;
31
+ }
32
+ if (modelName?.includes('claude')) {
33
+ (0, node_assert_1.default)(CLAUDE_API_KEY, 'Please set the CLAUDE_API_KEY environment variable');
34
+ const model = new claude_chat_model_js_1.ClaudeChatModel({
35
+ apiKey: CLAUDE_API_KEY,
36
+ model: modelName,
37
+ modelOptions: {
38
+ temperature: 0.7,
39
+ },
40
+ });
41
+ return model;
42
+ }
43
+ return undefined;
44
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Updates the file list JSON by scanning the sample-output directory
3
+ * and generating a list of all HTML files in each subfolder
4
+ */
5
+ export declare function updateFileList(): Promise<void>;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.updateFileList = updateFileList;
30
+ const fs_1 = require("fs");
31
+ const fs = __importStar(require("fs/promises"));
32
+ const path_1 = __importDefault(require("path"));
33
+ /**
34
+ * Updates the file list JSON by scanning the sample-output directory
35
+ * and generating a list of all HTML files in each subfolder
36
+ */
37
+ async function updateFileList() {
38
+ const outputDir = path_1.default.join(process.cwd(), 'sample-output');
39
+ const indexPath = path_1.default.join(outputDir, 'index.html');
40
+ // 存储所有子文件夹及其HTML文件信息
41
+ const fileList = [];
42
+ try {
43
+ // 读取sample-output目录下的所有子文件夹
44
+ const folders = await fs.readdir(outputDir, { withFileTypes: true });
45
+ // 过滤出目录,并排除特定文件
46
+ const dirNames = folders
47
+ .filter((dirent) => dirent.isDirectory())
48
+ .map((dirent) => dirent.name)
49
+ .filter((name) => name !== '.git' && !name.startsWith('.'));
50
+ // 按照文件夹名称排序(时间戳格式,新的在前)
51
+ dirNames.sort().reverse();
52
+ // 处理每个子文件夹
53
+ for (const folderName of dirNames) {
54
+ const folderPath = path_1.default.join(outputDir, folderName);
55
+ // eslint-disable-next-line no-await-in-loop
56
+ const folderFiles = await fs.readdir(folderPath, { withFileTypes: true });
57
+ // 过滤出HTML文件
58
+ const htmlFiles = folderFiles
59
+ .filter((file) => file.isFile() && file.name.endsWith('.html'))
60
+ .map((file) => {
61
+ const fileName = file.name.replace(/\.html$/, '');
62
+ return {
63
+ name: fileName,
64
+ path: `${folderName}/${file.name}`,
65
+ createdAt: new Date().toISOString(),
66
+ };
67
+ });
68
+ // 将文件夹信息添加到列表
69
+ fileList.push({
70
+ folder: folderName,
71
+ files: htmlFiles,
72
+ });
73
+ }
74
+ // 更新index.html,内联JSON数据
75
+ if ((0, fs_1.existsSync)(indexPath)) {
76
+ 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');
84
+ }
85
+ }
86
+ }
87
+ catch (error) {
88
+ console.error('Error updating file list:', error);
89
+ }
90
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 读取指定路径的文件并返回字符串内容
3
+ * @param filePath 文件路径,可以是相对路径或绝对路径
4
+ * @param encoding 文件编码,默认为 'utf-8'
5
+ * @returns 文件内容字符串
6
+ * @throws 如果文件不存在或读取失败则抛出异常
7
+ */
8
+ export declare function readFile(filePath: string, encoding?: BufferEncoding): Promise<string>;
9
+ export * from './agent-utils';
10
+ export * from './logger';
11
+ export * from './template-utils';
12
+ export * from './file-utils';
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.readFile = readFile;
21
+ const promises_1 = require("node:fs/promises");
22
+ const node_path_1 = __importDefault(require("node:path"));
23
+ /**
24
+ * 读取指定路径的文件并返回字符串内容
25
+ * @param filePath 文件路径,可以是相对路径或绝对路径
26
+ * @param encoding 文件编码,默认为 'utf-8'
27
+ * @returns 文件内容字符串
28
+ * @throws 如果文件不存在或读取失败则抛出异常
29
+ */
30
+ async function readFile(filePath, encoding = 'utf-8') {
31
+ try {
32
+ // 如果是相对路径,则从当前工作目录解析
33
+ const resolvedPath = node_path_1.default.isAbsolute(filePath) ? filePath : node_path_1.default.resolve(process.cwd(), filePath);
34
+ // 读取文件内容
35
+ const content = await (0, promises_1.readFile)(resolvedPath, { encoding });
36
+ return content;
37
+ }
38
+ catch (error) {
39
+ // 捕获并重新抛出异常,添加更明确的错误信息
40
+ const err = error;
41
+ throw new Error(`Failed to read file at path ${filePath}: ${err.message}`);
42
+ }
43
+ }
44
+ // 导出所有模块功能
45
+ __exportStar(require("./agent-utils"), exports);
46
+ __exportStar(require("./logger"), exports);
47
+ __exportStar(require("./template-utils"), exports);
48
+ __exportStar(require("./file-utils"), exports);
@@ -0,0 +1 @@
1
+ export declare const logger: Console;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logger = void 0;
4
+ exports.logger = console;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 根据JSON数据更新HTML模板并保存到指定目录
3
+ * @param jsonData 需要应用到模板的JSON数据
4
+ * @param templatePath 模板HTML文件路径,默认为assets目录下的sample.html
5
+ * @param outputFolder 输出文件夹名称,将在output目录下创建
6
+ * @param outputFileName 输出HTML文件名称,不包含扩展名
7
+ * @returns 生成的HTML文件的完整路径
8
+ * @throws 如果模板文件不存在或无法创建输出目录则抛出异常
9
+ */
10
+ export declare function generateHtmlFromTemplate({ jsonData, outputFolder, outputFileName, templatePath, }: {
11
+ jsonData: Record<string, any>;
12
+ outputFolder: string;
13
+ outputFileName: string;
14
+ templatePath?: string;
15
+ }): Promise<string>;
16
+ /**
17
+ * 保存HTML内容到指定目录
18
+ * @param htmlContent 要保存的HTML内容
19
+ * @param outputFolder 输出文件夹路径
20
+ * @param outputFileName 输出HTML文件名称,不包含扩展名
21
+ * @returns 生成的HTML文件的完整路径
22
+ * @throws 如果无法创建输出目录或写入文件则抛出异常
23
+ */
24
+ export declare function saveHtmlContent({ htmlContent, outputFolder, outputFileName, }: {
25
+ htmlContent: string;
26
+ outputFolder: string;
27
+ outputFileName: string;
28
+ }): Promise<string>;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateHtmlFromTemplate = generateHtmlFromTemplate;
7
+ exports.saveHtmlContent = saveHtmlContent;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const promises_1 = require("node:fs/promises");
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ /**
12
+ * 根据JSON数据更新HTML模板并保存到指定目录
13
+ * @param jsonData 需要应用到模板的JSON数据
14
+ * @param templatePath 模板HTML文件路径,默认为assets目录下的sample.html
15
+ * @param outputFolder 输出文件夹名称,将在output目录下创建
16
+ * @param outputFileName 输出HTML文件名称,不包含扩展名
17
+ * @returns 生成的HTML文件的完整路径
18
+ * @throws 如果模板文件不存在或无法创建输出目录则抛出异常
19
+ */
20
+ async function generateHtmlFromTemplate({ jsonData, outputFolder, outputFileName, templatePath = 'assets/sample2.html', }) {
21
+ try {
22
+ // 读取模板文件
23
+ const resolvedTemplatePath = node_path_1.default.isAbsolute(templatePath)
24
+ ? templatePath
25
+ : node_path_1.default.resolve(process.cwd(), templatePath);
26
+ const templateContent = await (0, promises_1.readFile)(resolvedTemplatePath, { encoding: 'utf-8' });
27
+ // 将JSON数据转换为字符串
28
+ const jsonDataString = JSON.stringify(jsonData);
29
+ // 替换模板中的jsonData变量
30
+ const updatedContent = templateContent.replace(/const jsonData = \{[\s\S]*?\};/, `const jsonData = ${jsonDataString};`);
31
+ // 创建输出目录
32
+ const outputDir = node_path_1.default.resolve(process.cwd(), 'sample-output', outputFolder);
33
+ // 确保输出目录存在
34
+ if (!node_fs_1.default.existsSync(outputDir)) {
35
+ node_fs_1.default.mkdirSync(outputDir, { recursive: true });
36
+ }
37
+ // 构建输出文件的完整路径
38
+ const outputFilePath = node_path_1.default.join(outputDir, `${outputFileName}.html`);
39
+ // 写入更新后的HTML内容
40
+ node_fs_1.default.writeFileSync(outputFilePath, updatedContent, 'utf-8');
41
+ return outputFilePath;
42
+ }
43
+ catch (error) {
44
+ const err = error;
45
+ throw new Error(`Failed to generate HTML from template: ${err.message}`);
46
+ }
47
+ }
48
+ /**
49
+ * 保存HTML内容到指定目录
50
+ * @param htmlContent 要保存的HTML内容
51
+ * @param outputFolder 输出文件夹路径
52
+ * @param outputFileName 输出HTML文件名称,不包含扩展名
53
+ * @returns 生成的HTML文件的完整路径
54
+ * @throws 如果无法创建输出目录或写入文件则抛出异常
55
+ */
56
+ async function saveHtmlContent({ htmlContent, outputFolder, outputFileName, }) {
57
+ try {
58
+ // 创建输出目录
59
+ const outputDir = node_path_1.default.resolve(process.cwd(), 'sample-output', outputFolder);
60
+ // 确保输出目录存在
61
+ if (!node_fs_1.default.existsSync(outputDir)) {
62
+ node_fs_1.default.mkdirSync(outputDir, { recursive: true });
63
+ }
64
+ // 构建输出文件的完整路径
65
+ const outputFilePath = node_path_1.default.join(outputDir, `${outputFileName}.html`);
66
+ // 写入HTML内容
67
+ node_fs_1.default.writeFileSync(outputFilePath, htmlContent, 'utf-8');
68
+ return outputFilePath;
69
+ }
70
+ catch (error) {
71
+ const err = error;
72
+ throw new Error(`Failed to save HTML content: ${err.message}`);
73
+ }
74
+ }
@@ -0,0 +1,48 @@
1
+ import type { z } from 'zod';
2
+ export interface GeneratePageContentInput {
3
+ context: string;
4
+ question: string;
5
+ sectionsRules?: Record<string, string>;
6
+ locale: string;
7
+ outputSchema: z.ZodObject<any>;
8
+ modelName?: string;
9
+ onProgress?: (params: {
10
+ sectionName?: string;
11
+ sectionIndex?: number;
12
+ totalSections: number;
13
+ sectionData?: Record<string, any>;
14
+ eventType?: 'progress' | 'section' | 'structure_start' | 'structure_complete';
15
+ structureData?: any;
16
+ }) => void;
17
+ checkConnection?: () => boolean;
18
+ }
19
+ /**
20
+ * 多Agent协作生成页面内容
21
+ *
22
+ * 工作流程:
23
+ * 1. 使用页面结构规划Agent分析内容,规划各section内容
24
+ * 2. 使用结构验证Agent检查规划的完整性和有效性
25
+ * 3. 如果验证不通过,将反馈提供给规划Agent进行修正
26
+ * 4. 逐个生成各section内容
27
+ * 5. 基于所有section内容生成页面元数据
28
+ * 6. 整合所有内容返回完整页面数据
29
+ */
30
+ export default function generatePageContentMultiAgent(input: GeneratePageContentInput): Promise<{
31
+ title: string;
32
+ description: string;
33
+ sectionsData: Record<string, any>;
34
+ } | null>;
35
+ /**
36
+ * 单独生成特定section的内容
37
+ * 支持独立调用,为特定section重新生成内容
38
+ */
39
+ export declare function generateSingleSectionContent({ context, question, locale, sectionName, sectionSchema, sectionPlan, existingSections, modelName, }: {
40
+ context: string;
41
+ question: string;
42
+ locale: string;
43
+ sectionName: string;
44
+ sectionSchema: any;
45
+ sectionPlan?: string;
46
+ existingSections?: Record<string, any>;
47
+ modelName?: string;
48
+ }): Promise<import("@aigne/core").Message>;
@@ -0,0 +1,199 @@
1
+ import { AIGNE, UserOutputTopic } from '@aigne/core';
2
+ import { zodSchemaToJsonSchema } from '@blocklet/pages-kit-block-studio/zod-utils';
3
+ import { getModel } from '../utils/agent-utils.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import { generatePageMetadataAgent } from './page-metadata-generator.js';
6
+ import { pageStructurePlannerAgent } from './page-structure-planner.js';
7
+ import { pageStructureValidatorAgent } from './page-structure-validator.js';
8
+ import { createSectionContentAgent } from './section-content-generator.js';
9
+ /**
10
+ * 多Agent协作生成页面内容
11
+ *
12
+ * 工作流程:
13
+ * 1. 使用页面结构规划Agent分析内容,规划各section内容
14
+ * 2. 使用结构验证Agent检查规划的完整性和有效性
15
+ * 3. 如果验证不通过,将反馈提供给规划Agent进行修正
16
+ * 4. 逐个生成各section内容
17
+ * 5. 基于所有section内容生成页面元数据
18
+ * 6. 整合所有内容返回完整页面数据
19
+ */
20
+ export default async function generatePageContentMultiAgent(input) {
21
+ const { context = '', question = '', locale = '', outputSchema, onProgress, checkConnection, sectionsRules, modelName = 'gpt-4o-mini', } = input;
22
+ try {
23
+ logger.info('Starting multi-agent page content generation');
24
+ // 检查客户端连接状态的辅助函数
25
+ const isClientConnected = () => {
26
+ // 如果没有提供检查函数,默认认为客户端仍连接
27
+ if (!checkConnection)
28
+ return true;
29
+ // 否则调用提供的检查函数
30
+ return checkConnection();
31
+ };
32
+ // 1. 从outputSchema提取sectionsData结构
33
+ const outputSchemaJson = zodSchemaToJsonSchema(outputSchema);
34
+ const sectionsSchema = outputSchema.shape.sectionsData;
35
+ const sectionNames = Object.keys(sectionsSchema.shape);
36
+ // 触发页面结构开始生成事件
37
+ if (onProgress) {
38
+ onProgress({
39
+ totalSections: sectionNames.length,
40
+ eventType: 'structure_start',
41
+ });
42
+ }
43
+ // 2. 初始页面结构规划
44
+ logger.info('Planning initial page structure');
45
+ // 检查客户端是否仍然连接
46
+ if (!isClientConnected()) {
47
+ logger.warn('Client disconnected during structure planning, aborting generation');
48
+ throw new Error('Client disconnected');
49
+ }
50
+ let structurePlanMap = {};
51
+ const model = getModel(modelName);
52
+ // 3-4 使用反思工作流生成页面结构
53
+ const planningEngine = new AIGNE({
54
+ model,
55
+ agents: [pageStructurePlannerAgent, pageStructureValidatorAgent],
56
+ limits: {
57
+ maxAgentInvokes: 20,
58
+ },
59
+ });
60
+ await planningEngine.invoke(pageStructurePlannerAgent, {
61
+ context,
62
+ question,
63
+ locale,
64
+ initialStructurePlan: Object.entries(sectionsRules || {}).map(([sectionName, sectionContentPlan]) => ({
65
+ sectionName,
66
+ sectionContentPlan,
67
+ })),
68
+ outputSchema: JSON.stringify(outputSchemaJson),
69
+ expectedSections: JSON.stringify(sectionNames),
70
+ });
71
+ const structurePlanResult = await planningEngine.subscribe(UserOutputTopic);
72
+ const { structurePlan } = structurePlanResult.message;
73
+ // const { structurePlan } = structurePlanResult;
74
+ logger.info('Initial structure plan generated', structurePlan);
75
+ await planningEngine.shutdown();
76
+ // 触发页面结构生成成功事件
77
+ if (onProgress) {
78
+ onProgress({
79
+ totalSections: sectionNames.length,
80
+ eventType: 'structure_complete',
81
+ structureData: {
82
+ structurePlan,
83
+ },
84
+ });
85
+ }
86
+ structurePlanMap = Object.fromEntries(structurePlan.map((item) => [item.sectionName, item.sectionContentPlan]));
87
+ const engine = new AIGNE({
88
+ model,
89
+ });
90
+ // 5. 逐个生成各section内容
91
+ const sectionsData = {};
92
+ for (let i = 0; i < sectionNames.length; i++) {
93
+ const sectionName = sectionNames[i];
94
+ if (!sectionName) {
95
+ // eslint-disable-next-line no-continue
96
+ continue;
97
+ }
98
+ // 每个section生成前检查客户端连接状态
99
+ if (!isClientConnected()) {
100
+ logger.warn(`Client disconnected before generating section ${sectionName}, aborting generation`);
101
+ return null;
102
+ }
103
+ logger.info(`Generating content for section: ${sectionName}`);
104
+ // 通知当前正在处理的section
105
+ if (onProgress) {
106
+ onProgress({
107
+ sectionName,
108
+ sectionIndex: i,
109
+ totalSections: sectionNames.length,
110
+ });
111
+ }
112
+ const sectionSchema = sectionsSchema.shape[sectionName];
113
+ const sectionPlan = structurePlanMap[sectionName] || `Generate content for ${sectionName}`;
114
+ const sectionContentInput = {
115
+ context,
116
+ question,
117
+ locale,
118
+ sectionName,
119
+ sectionSchema,
120
+ sectionPlan,
121
+ generatedSections: JSON.stringify(sectionsData), // 传递已生成的sections
122
+ };
123
+ // eslint-disable-next-line no-await-in-loop
124
+ const sectionContentAgent = await createSectionContentAgent(sectionContentInput);
125
+ // eslint-disable-next-line no-await-in-loop
126
+ const sectionContent = await engine.invoke(sectionContentAgent, sectionContentInput);
127
+ sectionsData[sectionName] = sectionContent;
128
+ logger.info(`Content generated for section: ${sectionName}`);
129
+ // 通知该section已生成完成
130
+ if (onProgress) {
131
+ const sectionDataObj = {};
132
+ sectionDataObj[sectionName] = sectionContent;
133
+ onProgress({
134
+ sectionName,
135
+ sectionIndex: i,
136
+ totalSections: sectionNames.length,
137
+ sectionData: sectionDataObj,
138
+ });
139
+ }
140
+ }
141
+ // 6. 生成页面元数据
142
+ logger.info('Generating page metadata');
143
+ // 检查客户端是否仍然连接
144
+ if (!isClientConnected()) {
145
+ logger.warn('Client disconnected before generating metadata, aborting generation');
146
+ throw new Error('Client disconnected');
147
+ }
148
+ const metadata = await engine.invoke(generatePageMetadataAgent, {
149
+ context,
150
+ question,
151
+ locale,
152
+ sectionsData: JSON.stringify(sectionsData),
153
+ });
154
+ // 7. 组装完整页面数据
155
+ const result = {
156
+ title: metadata.title,
157
+ description: metadata.description,
158
+ sectionsData,
159
+ };
160
+ logger.info('Multi-agent page content generation completed');
161
+ return result;
162
+ }
163
+ catch (error) {
164
+ logger.error('Error in multi-agent page content generation:', error);
165
+ throw error;
166
+ }
167
+ }
168
+ /**
169
+ * 单独生成特定section的内容
170
+ * 支持独立调用,为特定section重新生成内容
171
+ */
172
+ export async function generateSingleSectionContent({ context, question, locale, sectionName, sectionSchema, sectionPlan, existingSections = {}, modelName = 'gpt-4o-mini', }) {
173
+ try {
174
+ logger.info(`Starting single section content generation for: ${sectionName}`);
175
+ const actualSectionPlan = sectionPlan || `Generate content for ${sectionName}`;
176
+ const sectionContentInput = {
177
+ context,
178
+ question,
179
+ locale,
180
+ sectionName,
181
+ sectionSchema,
182
+ sectionPlan: actualSectionPlan,
183
+ generatedSections: JSON.stringify(existingSections),
184
+ };
185
+ const sectionContentAgent = await createSectionContentAgent(sectionContentInput);
186
+ const model = getModel(modelName);
187
+ const engine = new AIGNE({
188
+ model,
189
+ });
190
+ // eslint-disable-next-line no-await-in-loop
191
+ const sectionContent = await engine.invoke(sectionContentAgent, sectionContentInput);
192
+ logger.info(`Single section content generated for: ${sectionName}`);
193
+ return sectionContent;
194
+ }
195
+ catch (error) {
196
+ logger.error(`Error generating content for section ${sectionName}:`, error);
197
+ throw error;
198
+ }
199
+ }
@@ -0,0 +1,10 @@
1
+ import { AIAgent } from '@aigne/core';
2
+ export declare const generatePageMetadataAgent: AIAgent<{
3
+ context: string;
4
+ question: string;
5
+ locale: string;
6
+ sectionsData: string;
7
+ }, {
8
+ description: string;
9
+ title: string;
10
+ }>;
@@ -0,0 +1,40 @@
1
+ import { AIAgent } from '@aigne/core';
2
+ import { z } from 'zod';
3
+ const prompt = `您是一名专业的SEO和内容编辑专家,擅长创建有吸引力和描述性强的页面元数据。
4
+
5
+ 您的职责
6
+ 1. 基于已生成的页面内容,创建准确、吸引人的页面标题
7
+ 2. 创建简洁而信息丰富的页面描述,适合在搜索结果和社交媒体分享中显示
8
+ 3. 确保标题和描述准确反映页面内容
9
+ 4. 优化元数据以提高页面的SEO价值
10
+
11
+ ## 输入上下文:
12
+ {{context}}
13
+
14
+ ## 用户指令:
15
+ {{question}}
16
+
17
+ ## 已生成的页面内容:
18
+ {{sectionsData}}
19
+
20
+ ## 要求
21
+ 1. 页面标题:简洁有力,准确描述页面内容,长度在5-10个词之间
22
+ 2. 页面描述:提供页面内容的简明概述,长度不超过160个字符
23
+ 3. 标题和描述必须基于实际生成的页面内容
24
+ 4. 标题和描述应使用 {{locale}} 语言
25
+
26
+ 请为以上页面内容生成合适的标题和描述。`;
27
+ export const generatePageMetadataAgent = AIAgent.from({
28
+ name: 'Page Metadata Generator',
29
+ instructions: prompt,
30
+ inputSchema: z.object({
31
+ context: z.string(),
32
+ question: z.string(),
33
+ locale: z.string(),
34
+ sectionsData: z.string(),
35
+ }),
36
+ outputSchema: z.object({
37
+ title: z.string().describe('页面标题'),
38
+ description: z.string().describe('页面描述'),
39
+ }),
40
+ });