From 011f722999cef56b308d1778cf4b6f0219830d1c Mon Sep 17 00:00:00 2001 From: eaiadmin Date: Thu, 24 Sep 2026 23:01:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=93=E5=91=98=20JSON=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=BD=93=E7=B3=BB=20+=20=E9=85=8D=E5=9B=BE=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E8=AF=8D=E8=A7=84=E5=88=99=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增配图提示词 JSON 规则(image_prompt.json):封面图 + 每 500 字按字数分布 - 新增 Go 配置加载器(weixin_public_account_image_prompt_config.go):从嵌入的 spec 目录读取 JSON 配置 - 新增 weixin_public_account 完整 spec 目录:skill.md, workflow.json, domains.json, policy.json, validators/ - 新增 pj0012 参考文件(pj0012-wechat-image-spec.md):配图规则 + 专员 JSON 定义体系 - 产物 Tab 添加下载按钮 + 整体导出区 - 修复前端 Markdown 渲染 bug + 步骤消息分段问题 所有专员定义向 JSON 配置迁移,以 weixin_public_account 为样板。 --- .env.example | 54 +++ README.md | 52 +++ .../api/spec/config/domains.json | 42 +++ .../api/spec/config/image_prompt.json | 54 +++ .../api/spec/config/policy.json | 39 +++ .../prompts/image_prompt_generate.prompt.md | 27 ++ .../api/spec/config/skill.md | 62 ++++ .../spec/config/validators/image_prompt.json | 32 ++ .../api/spec/config/workflow.json | 157 +++++++++ ...ixin_public_account_image_prompt_config.go | 309 ++++++++++++++++++ .../api/weixin_public_account_workflow.go | 1 + .../weixin_public_account/spec/README.md | 60 ++++ .../spec/config/domains.json | 42 +++ .../spec/config/image_prompt.json | 54 +++ .../spec/config/policy.json | 39 +++ .../prompts/image_prompt_generate.prompt.md | 27 ++ .../spec/config/skill.md | 62 ++++ .../spec/config/validators/image_prompt.json | 32 ++ .../spec/config/workflow.json | 157 +++++++++ .../WeixinPublicAccountSpecialistPanel.vue | 82 ++++- .../views/workbench/SmartAssistantPage.vue | 44 ++- .../reference/pj0012-wechat-image-spec.md | 224 +++++++++++++ start_dev_10231_10232.sh | 8 + 23 files changed, 1642 insertions(+), 18 deletions(-) create mode 100644 .env.example create mode 100644 README.md create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/domains.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/image_prompt.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/policy.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/prompts/image_prompt_generate.prompt.md create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/skill.md create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/validators/image_prompt.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/workflow.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_image_prompt_config.go create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/README.md create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/domains.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/image_prompt.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/policy.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/prompts/image_prompt_generate.prompt.md create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/skill.md create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/validators/image_prompt.json create mode 100644 eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/workflow.json create mode 100644 internal/specialists/reference/pj0012-wechat-image-spec.md mode change 100644 => 100755 start_dev_10231_10232.sh diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..73e171d --- /dev/null +++ b/.env.example @@ -0,0 +1,54 @@ +# ================================================================ +# EAI AgentPlatform 环境变量配置 +# ================================================================ +# 复制此文件为 .env 并填入实际值: +# cp .env.example .env +# ================================================================ + +# ---------- 后端服务 ---------- +# 后端 API 端口 +PORT=8080 + +# SQLite 数据库路径 +DB_PATH=./data/eai_agentplatform.db + +# JWT 密钥(生产环境请修改) +JWT_SECRET=change-this-to-a-strong-secret-in-production +JWT_EXPIRE_MINUTES=480 + +# ---------- AI 服务 ---------- +# LLM 服务地址(llamacpp 默认 8080) +LLM_BASE_URL=http://127.0.0.1:8080/v1 +LLM_API_KEY=local +LLM_MODEL=Qwen3.5-4B-Q4_K_M.gguf + +# 向量嵌入模型 +EMBED_MODEL=bge-m3 + +# ---------- 知识库 ---------- +KB_DATA_DIR=./data/kb_data +KNOWLEDGE_SOURCE_DIR=./assets/knowledge/source +TRAINING_MATERIALS_DIR=./assets/training/materials + +# ---------- 备份 ---------- +BACKUP_ENABLED=true +BACKUP_DIR=./data/backups +BACKUP_KEEP=7 +BACKUP_INTERVAL_HOURS=24 + +# ---------- 文件上传限制 ---------- +FILE_MAX_SIZE_DOC=209715200 +FILE_MAX_SIZE_VIDEO=2147483648 +CHUNK_THRESHOLD=104857600 + +# ---------- 文档转换 ---------- +LIBREOFFICE_BIN=libreoffice +PDFTOTEXT_BIN=pdftotext + +# ---------- 金蝶集成 ---------- +# KINGDEE_BASE_URL= +# KINGDEE_ACCOUNT_ID= +# KINGDEE_USERNAME= +# KINGDEE_PASSWORD= +# KINGDEE_LCID=2052 +# KINGDEE_TIMEOUT_SEC=15 diff --git a/README.md b/README.md new file mode 100644 index 0000000..6931721 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# EAI AgentPlatform + +智能 Agent 平台 — 基于 Go + Vue 3 构建。 + +## 快速启动 + +```bash +# 开发环境启动(前端 10231 + 后端 10232) +./start_dev_10231_10232.sh +``` + +## 端口说明 + +| 端口 | 服务 | 说明 | +|--------|--------------------------------|----------------------------| +| 10231 | 前端 Vite 开发服务器 | 本地开发,自动代理 API 请求 | +| 10232 | 后端 Go API 服务 | 本地开发端口 | +| 8080 | 外部 LLM 服务 (llamacpp) | AI 对话 API 入口 | +| 8081 | 外部 Embed 服务 (llamacpp) | 向量嵌入服务 | +| 11434 | 外部 Ollama 服务(可选) | 如使用 Ollama 路由则需运行 | + +> **注意**: 8080 和 8081 是外部 AI 服务的端口,平台后端会调用它们获取 LLM 对话和向量嵌入能力。 + +## 配置 + +复制环境变量模板并修改: + +```bash +cp .env.example .env +``` + +主要配置项见 [.env.example](.env.example)。 + +## 启动脚本参数 + +``` +--force 强制释放端口(包括非本项目进程) +--skip-preflight 跳过预检查 +--skip-smoke 跳过管理员登录冒烟测试 +--skip-build 不重新编译后端 +--help 显示帮助 +``` + +## 环境变量 + +| 变量 | 默认值 | 说明 | +|-------------------------|-----------------------------------------|-----------------------| +| PORT | 8080 | 后端 API 端口 | +| EAI_DEV_BIND_HOST | 127.0.0.1 | 开发绑定地址 | +| EAI_DEV_PUBLIC_HOST | 自动检测 | 公开访问地址 | +| LLM_BASE_URL | http://127.0.0.1:8080/v1 | LLM 服务地址 | +| EMBED_MODEL | bge-m3 | 向量嵌入模型名 | diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/domains.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/domains.json new file mode 100644 index 0000000..ee3fca1 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/domains.json @@ -0,0 +1,42 @@ +{ + "description": "业务域配置。每个业务域有对应的标签和默认配置。", + "version": "1.0.0", + "domains": { + "AI": { + "label": "AI", + "image_domain_label": "AI 与人工智能", + "hotspot_sources": ["hackernews", "techcrunch", "arxiv"], + "default_keywords": ["大语言模型", "AI Agent", "多模态", "AIGC"] + }, + "medical": { + "label": "医疗", + "image_domain_label": "医疗健康", + "hotspot_sources": ["medical_news_today", "statnews"], + "default_keywords": ["新药研发", "临床实验", "健康管理", "医疗AI"] + }, + "insurance": { + "label": "保险", + "image_domain_label": "保险金融", + "hotspot_sources": ["reuters", "bloomberg"], + "default_keywords": ["健康险", "车险", "理赔", "保险科技"] + }, + "hr": { + "label": "HR", + "image_domain_label": "人力资源", + "hotspot_sources": ["linkedin", "shrm"], + "default_keywords": ["招聘", "培训", "绩效考核", "薪酬福利"] + }, + "legal": { + "label": "法务", + "image_domain_label": "法律合规", + "hotspot_sources": ["reuters", "bloomberg"], + "default_keywords": ["数据合规", "知识产权", "劳动法", "合同法"] + }, + "general": { + "label": "通用", + "image_domain_label": "通用主题", + "hotspot_sources": ["google_trending", "reddit"], + "default_keywords": [] + } + } +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/image_prompt.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/image_prompt.json new file mode 100644 index 0000000..ada66d0 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/image_prompt.json @@ -0,0 +1,54 @@ +{ + "description": "配图提示词生成规则。配图按字数均匀分布,封面图在标题下方,其余配图按 500 字间隔放置在所属小节开始处。", + "version": "1.0.0", + "distribution": { + "mode": "word_count", + "word_count_threshold": 500, + "cover_image": { + "enabled": true, + "key": "cover", + "slot_label": "TITLE_PIC", + "section_label": "封面配图", + "position": "after_title", + "size": "landscape_16_9" + }, + "content_images": { + "enabled": true, + "key_prefix": "section", + "size": "landscape_4_3", + "placement": "section_start", + "calculation": "accumulate_across_sections" + } + }, + "prompt_template": { + "description": "配图提示词模板。{index} 为序号(从 1 开始),{section_title} 为小节标题,{section_summary} 为段落摘要(截断至 120 字)。", + "format": "chinese", + "min_chars": 40, + "max_chars": 70, + "template": "真实人物纪实摄影,公众号文章配图,第{index}张配图核心画面,{section_summary},构图清晰,主体突出,电影感光影,细节丰富,高清" + }, + "bracket_format": { + "description": "配图提示词在正文中的嵌入格式。", + "cover": "[TITLE_PIC: {prompt}, size: {size}]", + "content": "[section_{{index:02d}}: {prompt}, size: {size}]" + }, + "validation": { + "checks": [ + "section_mapping_completeness", + "required_visual_elements", + "prompt_length", + "no_forbidden_elements" + ], + "required_elements": { + "subject": true, + "scene": true, + "lighting": true + }, + "forbidden_elements": [ + "text_overlay", + "collage_style" + ], + "retry_max": 2, + "retry_feedback_template": "配图提示词校验失败:{failed_checks}。请重新生成全部提示词并修复问题,保持段落映射不变。" + } +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/policy.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/policy.json new file mode 100644 index 0000000..07a4675 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/policy.json @@ -0,0 +1,39 @@ +{ + "description": "重试和降级策略。", + "version": "1.0.0", + "retries": { + "default_max_retries": 2, + "step_overrides": { + "topic_recommendation": 1, + "title_generation": 2, + "outline_generation": 2, + "content_creation": 2, + "image_prompt_generation": 2, + "image_generation": 2, + "preview_export": 0, + "page_markdown": 0 + }, + "policy": { + "regenerate_fully": true, + "include_error_message_in_feedback": true, + "allow_partial_patch": false + } + }, + "fallback": { + "mark_fallback_in_logs": true, + "step_overrides": { + "topic_recommendation": { + "allow_fallback": true, + "fallback_note": "使用关键词直接生成选题。" + }, + "title_generation": { + "allow_fallback": true, + "fallback_note": "使用标题模板生成。" + }, + "content_creation": { + "allow_fallback": false, + "fallback_note": "不降级,返回明确错误。" + } + } + } +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/prompts/image_prompt_generate.prompt.md b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/prompts/image_prompt_generate.prompt.md new file mode 100644 index 0000000..28b87ff --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/prompts/image_prompt_generate.prompt.md @@ -0,0 +1,27 @@ +# Role +你是 `image_prompt_generate` 步骤的执行者,负责为公众号文章生成配图提示词。 + +# Goal +基于正文内容,按字数分布规则生成配图提示词,并将提示词以括号标记格式嵌入正文。 + +# 配图规则 +- 封面图:标题下方 1 张,`[TITLE_PIC: 提示词, size: landscape_16_9]` +- 正文配图:从正文开头累计字数,每 500 字生成 1 张 +- 每张配图放在所属小节(##)的开始处 +- 如果某节超过 500 字,则该节内按 500 字间隔插入多张 +- 提示词格式:`[section_{{index:02d}}: 提示词, size: landscape_4_3]` + +# 提示词模板 +``` +真实人物纪实摄影,公众号文章配图,第{N}张配图核心画面,{段落摘要120字},构图清晰,主体突出,电影感光影,细节丰富,高清 +``` + +# 约束 +- 每个提示词必须包含:主体、场景、光线 +- 每个提示词长度 40-70 个中文字 +- 禁止文字覆盖、拼贴风格 +- 段落映射必须完整(配图数 = 需要配图的小节数) + +# 输出 +- `image_prompts`: 配图提示词数组 [{key, section, prompt, size}] +- `content_with_prompts`: 带括号标记的完整正文 diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/skill.md b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/skill.md new file mode 100644 index 0000000..3965eeb --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/skill.md @@ -0,0 +1,62 @@ +# 公众号创作专员技能规范 + +## 技能标识 +- **Key**: weixin-public-account +- **Label**: 公众号创作专员 +- **Type**: longform-writing + +## 目的 +完成公众号文章的端到端创作:从选题、标题、提纲、正文、配图提示词、图片生成、预览导出到分页 Markdown。 + +## 适用场景 +- 热点选题驱动的内容创作 +- 关键词驱动的通用文章生成 +- 多业务域(AI/医疗/保险/HR/法务/通用)适配 + +## 工作流步骤 + +| 序号 | Step Key | 标签 | 前置条件 | 输出 | +|------|----------|------|----------|------| +| 0 | task_configuration | 任务配置 | — | 业务域、关键词、受众、语调等表单参数 | +| 1 | topic_recommendation | 选题推荐 | 配置完成 | 3 个选题建议(标题 + 理由 + 热度) | +| 2 | title_generation | 标题生成 | 有选题 | 3 个标题建议(偏点击/偏稳重/偏平衡) | +| 3 | outline_generation | 提纲生成 | 有标题 | Markdown 提纲结构 | +| 4 | content_creation | 正文创作 | 有提纲 | Markdown 正文(含 ## 小节标题) | +| 5 | image_prompt_generation | 配图提示词 | 有正文 | 配图提示词 + 带标记正文 | +| 6 | image_generation | 图片生成 | 有配图提示词 | 封面图 + 段落图 | +| 7 | preview_export | 预览导出 | 有正文 + 图片 | 预览 MD + 预览 HTML | +| 8 | page_markdown | 分页 Markdown | 有预览稿 | 分页 MD(含图片尺寸标注) | + +## 配图规则 + +### 分布规则 +1. 标题下方生成 1 张封面图(`cover` / `TITLE_PIC`),16:9 +2. 从正文开头累计字数,每 500 字生成 1 张配图 +3. 每张配图放在其所属小节(##)的**开始处** +4. 如果某节很长(>500 字),则该节内按 500 字间隔插入多张 +5. 如果某节不足 500 字,与下节合并累计,但不跨节重复 + +### 提示词模板 +``` +真实人物纪实摄影,公众号文章配图,第{N}张配图核心画面,{段落摘要120字},构图清晰,主体突出,电影感光影,细节丰富,高清 +``` + +### 括号标记格式 +``` +[TITLE_PIC: 提示词, size: landscape_16_9] +[section_01: 提示词, size: landscape_4_3] +``` + +## 业务域 +AI、医疗、保险、HR、法务、通用 + +## 重试策略 +- 热点抓取:最多 1 次重试 +- 选题推荐:最多 1 次重试 +- 标题/提纲/正文/配图:最多 2 次重试 +- 预览/分页:0 次重试 + +## 验证规则 +- 提纲:至少 3 个小节,每小节至少 2 个要点 +- 正文:至少 800 字,每个小节有实质性内容 +- 配图提示词:段落数 = 提示词数,每个提示词 40-70 字,必须包含主体/场景/光线 diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/validators/image_prompt.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/validators/image_prompt.json new file mode 100644 index 0000000..295aeaa --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/validators/image_prompt.json @@ -0,0 +1,32 @@ +{ + "description": "配图提示词验证规则。", + "version": "1.0.0", + "step_id": "image_prompt_generation", + "checks": [ + "section_mapping_completeness", + "required_visual_elements", + "prompt_length", + "no_forbidden_elements", + "bracket_format_valid" + ], + "section_mapping": { + "description": "配图提示词与正文段落的映射必须完整。", + "cover_required": true, + "content_images_required": true + }, + "visual_elements": { + "required": ["subject", "scene", "lighting"], + "forbidden": ["text_overlay", "collage_style"] + }, + "prompt_length": { + "min_chars": 40, + "max_chars": 70, + "count_mode": "chinese_characters" + }, + "bracket_format": { + "cover_pattern": "^\\[TITLE_PIC: .* size: landscape_16_9\\]$", + "content_pattern": "^\\[section_\\d{2}: .* size: landscape_4_3\\]$" + }, + "retry_feedback_template": "配图提示词校验失败:{failed_checks}。请重新生成全部提示词并修复问题。", + "retry_max": 2 +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/workflow.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/workflow.json new file mode 100644 index 0000000..c26c344 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/spec/config/workflow.json @@ -0,0 +1,157 @@ +{ + "description": "公众号创作工作流步骤定义。步骤按顺序执行,前一步骤完成方可进入下一步骤。", + "version": "1.0.0", + "steps": [ + { + "key": "task_configuration", + "label": "任务配置", + "type": "form", + "order": 0, + "is_entry_point": true, + "description": "配置业务域、关键词、受众、语调等参数。" + }, + { + "key": "topic_recommendation", + "label": "选题推荐", + "type": "ai", + "order": 1, + "preconditions": ["task_configuration"], + "description": "基于业务域和关键词生成 3 个选题建议。" + }, + { + "key": "title_generation", + "label": "标题生成", + "type": "ai", + "order": 2, + "preconditions": ["topic_recommendation"], + "description": "基于选定选题生成 3 个标题建议(偏点击/偏稳重/偏平衡)。" + }, + { + "key": "outline_generation", + "label": "提纲生成", + "type": "ai", + "order": 3, + "preconditions": ["title_generation"], + "description": "基于选定标题生成 Markdown 提纲结构。" + }, + { + "key": "content_creation", + "label": "正文创作", + "type": "ai", + "order": 4, + "preconditions": ["outline_generation"], + "description": "基于选定提纲和配置参数生成正文。" + }, + { + "key": "image_prompt_generation", + "label": "配图提示词", + "type": "ai", + "order": 5, + "preconditions": ["content_creation"], + "description": "基于正文按字数分布生成配图提示词,并嵌入带标记正文。" + }, + { + "key": "image_generation", + "label": "图片生成", + "type": "external", + "order": 6, + "preconditions": ["image_prompt_generation"], + "description": "基于配图提示词调用外部图片生成 API。" + }, + { + "key": "preview_export", + "label": "预览导出", + "type": "render", + "order": 7, + "preconditions": ["content_creation", "image_generation"], + "description": "生成预览 MD、预览 HTML 和分页 MD。" + }, + { + "key": "page_markdown", + "label": "分页 Markdown", + "type": "render", + "order": 8, + "preconditions": ["preview_export"], + "description": "生成带图片尺寸标注的分页 Markdown。" + } + ], + "forms": { + "task_configuration": { + "fields": [ + { + "key": "business_domain", + "label": "业务域", + "type": "select", + "required": true, + "options": ["AI", "医疗", "保险", "HR", "法务", "通用"] + }, + { + "key": "keyword", + "label": "关键词", + "type": "text", + "required": true + }, + { + "key": "audience", + "label": "目标受众", + "type": "text", + "default": "公众号读者", + "required": false + }, + { + "key": "goal", + "label": "写作目标", + "type": "text", + "required": false + }, + { + "key": "tone", + "label": "语调风格", + "type": "select", + "default": "专业但好懂", + "options": ["专业但好懂", "轻松有趣", "严肃权威", "故事叙述", "数据驱动"] + }, + { + "key": "requirements", + "label": "特殊要求", + "type": "textarea", + "required": false + }, + { + "key": "outline_style", + "label": "提纲风格", + "type": "select", + "default": "问题拆解型", + "options": ["问题拆解型", "对比分析型", "案例驱动型", "步骤教程型"] + }, + { + "key": "outline_words", + "label": "提纲字数", + "type": "number", + "default": 800, + "min": 300, + "max": 2000, + "required": false + }, + { + "key": "content_target_words", + "label": "正文目标字数", + "type": "number", + "default": 1400, + "min": 500, + "max": 5000, + "required": false + }, + { + "key": "max_content_images", + "label": "最大配图数(可选,0 表示无上限)", + "type": "number", + "default": 0, + "min": 0, + "max": 20, + "required": false + } + ] + } + } +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_image_prompt_config.go b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_image_prompt_config.go new file mode 100644 index 0000000..3922192 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_image_prompt_config.go @@ -0,0 +1,309 @@ +package weixinpublicaccountapi + +import ( + "embed" + "encoding/json" + "fmt" + "strings" +) + +//go:embed spec/config/*.json spec/config/validators/*.json spec/config/prompts/*.md +var specConfigFS embed.FS + +// imagePromptConfig 从 JSON 配置文件读取的配图提示词规则。 +type imagePromptConfig struct { + Distribution distributionConfig `json:"distribution"` + PromptTemplate promptTemplateConfig `json:"prompt_template"` + BracketFormat bracketFormatConfig `json:"bracket_format"` + Validation validationConfig `json:"validation"` +} + +type distributionConfig struct { + Mode string `json:"mode"` + WordCount int `json:"word_count_threshold"` + CoverImage coverImageConfig `json:"cover_image"` + ContentImages contentImagesConfig `json:"content_images"` +} + +type coverImageConfig struct { + Enabled bool `json:"enabled"` + Key string `json:"key"` + SlotLabel string `json:"slot_label"` + SectionLbl string `json:"section_label"` + Position string `json:"position"` + Size string `json:"size"` +} + +type contentImagesConfig struct { + Enabled bool `json:"enabled"` + KeyPrefix string `json:"key_prefix"` + Size string `json:"size"` + Placement string `json:"placement"` + Calculation string `json:"calculation"` +} + +type promptTemplateConfig struct { + Format string `json:"format"` + MinChars int `json:"min_chars"` + MaxChars int `json:"max_chars"` + Template string `json:"template"` +} + +type bracketFormatConfig struct { + Cover string `json:"cover"` + Content string `json:"content"` +} + +type validationConfig struct { + Checks []string `json:"checks"` + RequiredElements map[string]bool `json:"required_elements"` + ForbiddenElements []string `json:"forbidden_elements"` + RetryMax int `json:"retry_max"` + RetryFeedbackTemplate string `json:"retry_feedback_template"` +} + +// imagePromptRule 是运行时的实例化规则(含业务域标签、关键词等运行时参数)。 +type imagePromptRule struct { + Keyword string + BusinessDomain string + Template promptTemplateConfig + Distribution distributionConfig + BracketFormat bracketFormatConfig +} + +var weixinPublicAccountImagePromptConfig *imagePromptConfig +var weixinPublicAccountImagePromptRuleCache map[string]*imagePromptRule + +func init() { + weixinPublicAccountImagePromptConfig = loadWeixinPublicAccountImagePromptConfig() + weixinPublicAccountImagePromptRuleCache = make(map[string]*imagePromptRule) +} + +// loadWeixinPublicAccountImagePromptConfig 从嵌入的 spec 目录加载 JSON 配置。 +func loadWeixinPublicAccountImagePromptConfig() *imagePromptConfig { + data, err := specConfigFS.ReadFile("spec/config/image_prompt.json") + if err != nil { + // 如果 spec 目录不存在或配置缺失,回退到内联默认配置 + return defaultImagePromptConfig() + } + var cfg imagePromptConfig + if err := json.Unmarshal(data, &cfg); err != nil { + return defaultImagePromptConfig() + } + return &cfg +} + +func defaultImagePromptConfig() *imagePromptConfig { + return &imagePromptConfig{ + Distribution: distributionConfig{ + Mode: "word_count", + WordCount: 500, + CoverImage: coverImageConfig{ + Enabled: true, + Key: "cover", + SlotLabel: "TITLE_PIC", + SectionLbl: "封面配图", + Position: "after_title", + Size: "landscape_16_9", + }, + ContentImages: contentImagesConfig{ + Enabled: true, + KeyPrefix: "section", + Size: "landscape_4_3", + Placement: "section_start", + Calculation: "accumulate_across_sections", + }, + }, + PromptTemplate: promptTemplateConfig{ + Format: "chinese", + MinChars: 40, + MaxChars: 70, + Template: "真实人物纪实摄影,公众号文章配图,第{N}张配图核心画面,{段落摘要120字},构图清晰,主体突出,电影感光影,细节丰富,高清", + }, + BracketFormat: bracketFormatConfig{ + Cover: "[TITLE_PIC: {prompt}, size: {size}]", + Content: "[section_{{index:02d}}: {prompt}, size: {size}]", + }, + Validation: validationConfig{ + Checks: []string{ + "section_mapping_completeness", + "required_visual_elements", + "prompt_length", + "no_forbidden_elements", + }, + RequiredElements: map[string]bool{ + "subject": true, + "scene": true, + "lighting": true, + }, + ForbiddenElements: []string{ + "text_overlay", + "collage_style", + }, + RetryMax: 2, + }, + } +} + +// getWeixinPublicAccountImagePromptRule 按业务域获取缓存的规则。 +func getWeixinPublicAccountImagePromptRule(workflow *weixinPublicAccountWorkflowState) *imagePromptRule { + key := workflow.Form.BusinessDomain + if rule, ok := weixinPublicAccountImagePromptRuleCache[key]; ok { + return rule + } + cfg := weixinPublicAccountImagePromptConfig + rule := &imagePromptRule{ + Keyword: workflow.Form.Keyword, + BusinessDomain: workflow.Form.BusinessDomain, + Template: cfg.PromptTemplate, + Distribution: cfg.Distribution, + BracketFormat: cfg.BracketFormat, + } + weixinPublicAccountImagePromptRuleCache[key] = rule + return rule +} + +// buildWeixinPublicAccountImagePromptsV2 新版配图提示词生成逻辑,基于 JSON 配置。 +func buildWeixinPublicAccountImagePromptsV2(workflow *weixinPublicAccountWorkflowState) []weixinPublicAccountImagePrompt { + rule := getWeixinPublicAccountImagePromptRule(workflow) + cfg := &rule.Distribution + prompts := []weixinPublicAccountImagePrompt{} + + // 1. 封面图 + if cfg.CoverImage.Enabled { + prompts = append(prompts, weixinPublicAccountImagePrompt{ + Key: cfg.CoverImage.Key, + Section: cfg.CoverImage.SectionLbl, + Prompt: buildCoverImagePrompt(workflow, rule), + Size: cfg.CoverImage.Size, + }) + } + + // 2. 正文配图(按 500 字分布) + sections := extractWeixinPublicAccountSections(workflow.Shared.Content) + contentImageIndex := 1 + wordCountAccumulator := 0 + + // 根据计算模式决定如何遍历 + if cfg.ContentImages.Calculation == "accumulate_across_sections" { + // 从正文开头累计字数,每 500 字生成一张配图,放在小节开始处 + for _, section := range sections { + sectionWordCount := countChineseWords(section.Content) + + // 如果这是一个新的配图分配点 + if sectionWordCount > 0 { + wordCountAccumulator += sectionWordCount + // 计算这之前应该有多少张配图 + expectedImages := wordCountAccumulator / cfg.WordCount + // 添加配图直到达到 expectedImages + for wordCountAccumulator >= expectedImages*cfg.WordCount && contentImageIndex <= expectedImages { + prompt := buildContentImagePrompt(workflow, section, contentImageIndex, rule) + prompts = append(prompts, weixinPublicAccountImagePrompt{ + Key: fmt.Sprintf("%s_%02d", cfg.ContentImages.KeyPrefix, contentImageIndex), + Section: section.Title, + Prompt: prompt, + Size: cfg.ContentImages.Size, + }) + contentImageIndex++ + } + } + } + } + + return prompts +} + +// buildCoverImagePrompt 生成封面图提示词。 +func buildCoverImagePrompt(workflow *weixinPublicAccountWorkflowState, rule *imagePromptRule) string { + title := firstNonEmpty(workflow.Shared.SelectedTitle, workflow.Form.Keyword, "公众号文章") + + // 从 JSON 模板构建(当前回退到硬编码模板) + template := rule.Template.Template + if template == "" { + // 回退到当前硬编码的英文模板 + return fmt.Sprintf("realistic editorial illustration, Chinese business article cover, %s, focus on %s, %s, clean composition, natural lighting, brand-safe, no text, no watermark", + weixinPublicAccountBusinessDomainLabel(workflow.Form.BusinessDomain), + title, + "公众号封面主视觉", + ) + } + + return fmt.Sprintf(template, 1, title, fmt.Sprintf("%s(截断至120字)", title)) +} + +// buildContentImagePrompt 生成正文配图提示词。 +func buildContentImagePrompt(workflow *weixinPublicAccountWorkflowState, section weixinPublicAccountSection, index int, rule *imagePromptRule) string { + summary := truncateChinese(strings.Join(section.Content, " "), 120) + + // 从 JSON 模板构建 + template := rule.Template.Template + if template == "" { + // 回退到当前硬编码的英文模板 + return fmt.Sprintf("realistic editorial illustration, Chinese business article cover, %s, focus on %s, %s, clean composition, natural lighting, brand-safe, no text, no watermark", + weixinPublicAccountBusinessDomainLabel(workflow.Form.BusinessDomain), + firstNonEmpty(section.Title, workflow.Form.Keyword), + firstNonEmpty(summary, workflow.Form.Tone), + ) + } + + return fmt.Sprintf(template, index, firstNonEmpty(section.Title, workflow.Form.Keyword), summary) +} + +// countChineseWords 计算内容的中文字数。 +func countChineseWords(content []string) int { + text := strings.Join(content, "\n") + count := 0 + for _, r := range text { + if r >= '\u4e00' && r <= '\u9fff' { + count++ + } + } + return count +} + +// truncateChinese 截断中文字符串到指定长度。 +func truncateChinese(s string, maxLen int) string { + runes := []rune(s) + if len(runes) > maxLen { + return string(runes[:maxLen]) + } + return s +} + +// renderWeixinPublicAccountImagePromptFromJSON 基于 JSON 配置渲染带标记的正文。 +func renderWeixinPublicAccountImagePromptFromJSON(content string, prompts []weixinPublicAccountImagePrompt) string { + cfg := weixinPublicAccountImagePromptConfig + content = strings.TrimSpace(content) + if content == "" { + return "" + } + blocks := []string{content, "", "## 配图提示词"} + + // 使用 JSON 配置中的括号格式 + sections := extractWeixinPublicAccountSections(content) + sectionIdx := 0 + + for _, prompt := range prompts { + if prompt.Key == cfg.Distribution.CoverImage.Key { + // 封面图:插入在标题下方 + block := replacePlaceholder(cfg.BracketFormat.Cover, "{prompt}", prompt.Prompt) + block = replacePlaceholder(block, "{size}", cfg.Distribution.CoverImage.Size) + blocks = append(blocks, block) + } else if strings.HasPrefix(prompt.Key, cfg.Distribution.ContentImages.KeyPrefix) { + // 正文配图:插入在小节开始处 + if sectionIdx < len(sections) { + block := replacePlaceholder(cfg.BracketFormat.Content, "{prompt}", prompt.Prompt) + block = replacePlaceholder(block, "{size}", cfg.Distribution.ContentImages.Size) + block = replacePlaceholder(block, "{index}", fmt.Sprintf("%02d", sectionIdx+1)) + blocks = append(blocks, block) + } + sectionIdx++ + } + } + + return strings.Join(blocks, "\n") +} + +func replacePlaceholder(s, placeholder, value string) string { + return strings.ReplaceAll(s, placeholder, value) +} diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_workflow.go b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_workflow.go index a63d6e7..49542ec 100644 --- a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_workflow.go +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/api/weixin_public_account_workflow.go @@ -153,6 +153,7 @@ type weixinPublicAccountImagePrompt struct { Key string `json:"key"` Section string `json:"section"` Prompt string `json:"prompt"` + Size string `json:"size"` } type weixinPublicAccountImageRef struct { diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/README.md b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/README.md new file mode 100644 index 0000000..9a0c2cf --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/README.md @@ -0,0 +1,60 @@ +# 公众号创作专员 - 规范定义 + +> 这是 pj0235 的第一个"配置驱动"专员样板。参考 pj0012 的 JSON 配置体系设计。 + +## 目录结构 + +``` +spec/ +├── config/ +│ ├── skill.md # 技能描述 + 执行策略(人可读 + LLM 可读) +│ ├── workflow.json # 工作流步骤定义(步骤 key、标签、前置条件、表单) +│ ├── image_prompt.json # 配图提示词规则(分布、模板、验证) +│ ├── domains.json # 业务域配置 +│ └── policy.json # 重试和降级策略 +├── config/prompts/ +│ └── image_prompt_generate.prompt.md # 配图提示词生成 prompt 模板 +├── config/validators/ +│ └── image_prompt.json # 配图提示词验证规则 +└── README.md +``` + +## 设计原则 + +1. **三层分离**: + - `skill.md` — 技能描述层(人可读 + LLM 可读) + - `*.json` — 数据配置层(机器可读,热更新) + - `*.prompt.md` — 提示词模板层(YAML 变量注入) + +2. **代码是运行时适配器**: + - 按路径读取 JSON/文本 + - 将运行时参数注入模板 + - 执行验证器逻辑 + - 控制重试/降级循环 + - 调用 LLM + +3. **不硬编码业务内容**: + - 业务规则变更通过修改 JSON/Markdown 实现 + - 代码层只做通用适配 + +## 配图规则(核心示例) + +配图提示词规则完全外置在 `image_prompt.json` 中,包括: + +- 分布规则:封面图 + 每 500 字 1 张 +- 提示词模板:中文模板 + 最小/最大字数 +- 括号标记格式:`[TITLE_PIC: 提示词, size: landscape_16_9]` +- 验证规则:主体/场景/光线必填,文字覆盖/拼贴禁用 +- 重试策略:最多 2 次 + +## 如何参考 + +完整的学习分析见:`../../../../reference/pj0012-wechat-image-spec.md` + +## 使用方式 + +Go 代码通过 `embed.FS` 嵌入这些文件,运行时: +1. 读取 JSON 配置 → 获取业务规则 +2. 读取 prompt 模板 → 注入运行时参数 → 组装最终 prompt +3. 读取验证器 → 执行验证逻辑 +4. 读取策略 → 控制重试/降级循环 diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/domains.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/domains.json new file mode 100644 index 0000000..ee3fca1 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/domains.json @@ -0,0 +1,42 @@ +{ + "description": "业务域配置。每个业务域有对应的标签和默认配置。", + "version": "1.0.0", + "domains": { + "AI": { + "label": "AI", + "image_domain_label": "AI 与人工智能", + "hotspot_sources": ["hackernews", "techcrunch", "arxiv"], + "default_keywords": ["大语言模型", "AI Agent", "多模态", "AIGC"] + }, + "medical": { + "label": "医疗", + "image_domain_label": "医疗健康", + "hotspot_sources": ["medical_news_today", "statnews"], + "default_keywords": ["新药研发", "临床实验", "健康管理", "医疗AI"] + }, + "insurance": { + "label": "保险", + "image_domain_label": "保险金融", + "hotspot_sources": ["reuters", "bloomberg"], + "default_keywords": ["健康险", "车险", "理赔", "保险科技"] + }, + "hr": { + "label": "HR", + "image_domain_label": "人力资源", + "hotspot_sources": ["linkedin", "shrm"], + "default_keywords": ["招聘", "培训", "绩效考核", "薪酬福利"] + }, + "legal": { + "label": "法务", + "image_domain_label": "法律合规", + "hotspot_sources": ["reuters", "bloomberg"], + "default_keywords": ["数据合规", "知识产权", "劳动法", "合同法"] + }, + "general": { + "label": "通用", + "image_domain_label": "通用主题", + "hotspot_sources": ["google_trending", "reddit"], + "default_keywords": [] + } + } +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/image_prompt.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/image_prompt.json new file mode 100644 index 0000000..ada66d0 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/image_prompt.json @@ -0,0 +1,54 @@ +{ + "description": "配图提示词生成规则。配图按字数均匀分布,封面图在标题下方,其余配图按 500 字间隔放置在所属小节开始处。", + "version": "1.0.0", + "distribution": { + "mode": "word_count", + "word_count_threshold": 500, + "cover_image": { + "enabled": true, + "key": "cover", + "slot_label": "TITLE_PIC", + "section_label": "封面配图", + "position": "after_title", + "size": "landscape_16_9" + }, + "content_images": { + "enabled": true, + "key_prefix": "section", + "size": "landscape_4_3", + "placement": "section_start", + "calculation": "accumulate_across_sections" + } + }, + "prompt_template": { + "description": "配图提示词模板。{index} 为序号(从 1 开始),{section_title} 为小节标题,{section_summary} 为段落摘要(截断至 120 字)。", + "format": "chinese", + "min_chars": 40, + "max_chars": 70, + "template": "真实人物纪实摄影,公众号文章配图,第{index}张配图核心画面,{section_summary},构图清晰,主体突出,电影感光影,细节丰富,高清" + }, + "bracket_format": { + "description": "配图提示词在正文中的嵌入格式。", + "cover": "[TITLE_PIC: {prompt}, size: {size}]", + "content": "[section_{{index:02d}}: {prompt}, size: {size}]" + }, + "validation": { + "checks": [ + "section_mapping_completeness", + "required_visual_elements", + "prompt_length", + "no_forbidden_elements" + ], + "required_elements": { + "subject": true, + "scene": true, + "lighting": true + }, + "forbidden_elements": [ + "text_overlay", + "collage_style" + ], + "retry_max": 2, + "retry_feedback_template": "配图提示词校验失败:{failed_checks}。请重新生成全部提示词并修复问题,保持段落映射不变。" + } +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/policy.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/policy.json new file mode 100644 index 0000000..07a4675 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/policy.json @@ -0,0 +1,39 @@ +{ + "description": "重试和降级策略。", + "version": "1.0.0", + "retries": { + "default_max_retries": 2, + "step_overrides": { + "topic_recommendation": 1, + "title_generation": 2, + "outline_generation": 2, + "content_creation": 2, + "image_prompt_generation": 2, + "image_generation": 2, + "preview_export": 0, + "page_markdown": 0 + }, + "policy": { + "regenerate_fully": true, + "include_error_message_in_feedback": true, + "allow_partial_patch": false + } + }, + "fallback": { + "mark_fallback_in_logs": true, + "step_overrides": { + "topic_recommendation": { + "allow_fallback": true, + "fallback_note": "使用关键词直接生成选题。" + }, + "title_generation": { + "allow_fallback": true, + "fallback_note": "使用标题模板生成。" + }, + "content_creation": { + "allow_fallback": false, + "fallback_note": "不降级,返回明确错误。" + } + } + } +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/prompts/image_prompt_generate.prompt.md b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/prompts/image_prompt_generate.prompt.md new file mode 100644 index 0000000..28b87ff --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/prompts/image_prompt_generate.prompt.md @@ -0,0 +1,27 @@ +# Role +你是 `image_prompt_generate` 步骤的执行者,负责为公众号文章生成配图提示词。 + +# Goal +基于正文内容,按字数分布规则生成配图提示词,并将提示词以括号标记格式嵌入正文。 + +# 配图规则 +- 封面图:标题下方 1 张,`[TITLE_PIC: 提示词, size: landscape_16_9]` +- 正文配图:从正文开头累计字数,每 500 字生成 1 张 +- 每张配图放在所属小节(##)的开始处 +- 如果某节超过 500 字,则该节内按 500 字间隔插入多张 +- 提示词格式:`[section_{{index:02d}}: 提示词, size: landscape_4_3]` + +# 提示词模板 +``` +真实人物纪实摄影,公众号文章配图,第{N}张配图核心画面,{段落摘要120字},构图清晰,主体突出,电影感光影,细节丰富,高清 +``` + +# 约束 +- 每个提示词必须包含:主体、场景、光线 +- 每个提示词长度 40-70 个中文字 +- 禁止文字覆盖、拼贴风格 +- 段落映射必须完整(配图数 = 需要配图的小节数) + +# 输出 +- `image_prompts`: 配图提示词数组 [{key, section, prompt, size}] +- `content_with_prompts`: 带括号标记的完整正文 diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/skill.md b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/skill.md new file mode 100644 index 0000000..3965eeb --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/skill.md @@ -0,0 +1,62 @@ +# 公众号创作专员技能规范 + +## 技能标识 +- **Key**: weixin-public-account +- **Label**: 公众号创作专员 +- **Type**: longform-writing + +## 目的 +完成公众号文章的端到端创作:从选题、标题、提纲、正文、配图提示词、图片生成、预览导出到分页 Markdown。 + +## 适用场景 +- 热点选题驱动的内容创作 +- 关键词驱动的通用文章生成 +- 多业务域(AI/医疗/保险/HR/法务/通用)适配 + +## 工作流步骤 + +| 序号 | Step Key | 标签 | 前置条件 | 输出 | +|------|----------|------|----------|------| +| 0 | task_configuration | 任务配置 | — | 业务域、关键词、受众、语调等表单参数 | +| 1 | topic_recommendation | 选题推荐 | 配置完成 | 3 个选题建议(标题 + 理由 + 热度) | +| 2 | title_generation | 标题生成 | 有选题 | 3 个标题建议(偏点击/偏稳重/偏平衡) | +| 3 | outline_generation | 提纲生成 | 有标题 | Markdown 提纲结构 | +| 4 | content_creation | 正文创作 | 有提纲 | Markdown 正文(含 ## 小节标题) | +| 5 | image_prompt_generation | 配图提示词 | 有正文 | 配图提示词 + 带标记正文 | +| 6 | image_generation | 图片生成 | 有配图提示词 | 封面图 + 段落图 | +| 7 | preview_export | 预览导出 | 有正文 + 图片 | 预览 MD + 预览 HTML | +| 8 | page_markdown | 分页 Markdown | 有预览稿 | 分页 MD(含图片尺寸标注) | + +## 配图规则 + +### 分布规则 +1. 标题下方生成 1 张封面图(`cover` / `TITLE_PIC`),16:9 +2. 从正文开头累计字数,每 500 字生成 1 张配图 +3. 每张配图放在其所属小节(##)的**开始处** +4. 如果某节很长(>500 字),则该节内按 500 字间隔插入多张 +5. 如果某节不足 500 字,与下节合并累计,但不跨节重复 + +### 提示词模板 +``` +真实人物纪实摄影,公众号文章配图,第{N}张配图核心画面,{段落摘要120字},构图清晰,主体突出,电影感光影,细节丰富,高清 +``` + +### 括号标记格式 +``` +[TITLE_PIC: 提示词, size: landscape_16_9] +[section_01: 提示词, size: landscape_4_3] +``` + +## 业务域 +AI、医疗、保险、HR、法务、通用 + +## 重试策略 +- 热点抓取:最多 1 次重试 +- 选题推荐:最多 1 次重试 +- 标题/提纲/正文/配图:最多 2 次重试 +- 预览/分页:0 次重试 + +## 验证规则 +- 提纲:至少 3 个小节,每小节至少 2 个要点 +- 正文:至少 800 字,每个小节有实质性内容 +- 配图提示词:段落数 = 提示词数,每个提示词 40-70 字,必须包含主体/场景/光线 diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/validators/image_prompt.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/validators/image_prompt.json new file mode 100644 index 0000000..295aeaa --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/validators/image_prompt.json @@ -0,0 +1,32 @@ +{ + "description": "配图提示词验证规则。", + "version": "1.0.0", + "step_id": "image_prompt_generation", + "checks": [ + "section_mapping_completeness", + "required_visual_elements", + "prompt_length", + "no_forbidden_elements", + "bracket_format_valid" + ], + "section_mapping": { + "description": "配图提示词与正文段落的映射必须完整。", + "cover_required": true, + "content_images_required": true + }, + "visual_elements": { + "required": ["subject", "scene", "lighting"], + "forbidden": ["text_overlay", "collage_style"] + }, + "prompt_length": { + "min_chars": 40, + "max_chars": 70, + "count_mode": "chinese_characters" + }, + "bracket_format": { + "cover_pattern": "^\\[TITLE_PIC: .* size: landscape_16_9\\]$", + "content_pattern": "^\\[section_\\d{2}: .* size: landscape_4_3\\]$" + }, + "retry_feedback_template": "配图提示词校验失败:{failed_checks}。请重新生成全部提示词并修复问题。", + "retry_max": 2 +} \ No newline at end of file diff --git a/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/workflow.json b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/workflow.json new file mode 100644 index 0000000..c26c344 --- /dev/null +++ b/eai_agentplatform/backend-go/internal/specialists/packages/weixin_public_account/spec/config/workflow.json @@ -0,0 +1,157 @@ +{ + "description": "公众号创作工作流步骤定义。步骤按顺序执行,前一步骤完成方可进入下一步骤。", + "version": "1.0.0", + "steps": [ + { + "key": "task_configuration", + "label": "任务配置", + "type": "form", + "order": 0, + "is_entry_point": true, + "description": "配置业务域、关键词、受众、语调等参数。" + }, + { + "key": "topic_recommendation", + "label": "选题推荐", + "type": "ai", + "order": 1, + "preconditions": ["task_configuration"], + "description": "基于业务域和关键词生成 3 个选题建议。" + }, + { + "key": "title_generation", + "label": "标题生成", + "type": "ai", + "order": 2, + "preconditions": ["topic_recommendation"], + "description": "基于选定选题生成 3 个标题建议(偏点击/偏稳重/偏平衡)。" + }, + { + "key": "outline_generation", + "label": "提纲生成", + "type": "ai", + "order": 3, + "preconditions": ["title_generation"], + "description": "基于选定标题生成 Markdown 提纲结构。" + }, + { + "key": "content_creation", + "label": "正文创作", + "type": "ai", + "order": 4, + "preconditions": ["outline_generation"], + "description": "基于选定提纲和配置参数生成正文。" + }, + { + "key": "image_prompt_generation", + "label": "配图提示词", + "type": "ai", + "order": 5, + "preconditions": ["content_creation"], + "description": "基于正文按字数分布生成配图提示词,并嵌入带标记正文。" + }, + { + "key": "image_generation", + "label": "图片生成", + "type": "external", + "order": 6, + "preconditions": ["image_prompt_generation"], + "description": "基于配图提示词调用外部图片生成 API。" + }, + { + "key": "preview_export", + "label": "预览导出", + "type": "render", + "order": 7, + "preconditions": ["content_creation", "image_generation"], + "description": "生成预览 MD、预览 HTML 和分页 MD。" + }, + { + "key": "page_markdown", + "label": "分页 Markdown", + "type": "render", + "order": 8, + "preconditions": ["preview_export"], + "description": "生成带图片尺寸标注的分页 Markdown。" + } + ], + "forms": { + "task_configuration": { + "fields": [ + { + "key": "business_domain", + "label": "业务域", + "type": "select", + "required": true, + "options": ["AI", "医疗", "保险", "HR", "法务", "通用"] + }, + { + "key": "keyword", + "label": "关键词", + "type": "text", + "required": true + }, + { + "key": "audience", + "label": "目标受众", + "type": "text", + "default": "公众号读者", + "required": false + }, + { + "key": "goal", + "label": "写作目标", + "type": "text", + "required": false + }, + { + "key": "tone", + "label": "语调风格", + "type": "select", + "default": "专业但好懂", + "options": ["专业但好懂", "轻松有趣", "严肃权威", "故事叙述", "数据驱动"] + }, + { + "key": "requirements", + "label": "特殊要求", + "type": "textarea", + "required": false + }, + { + "key": "outline_style", + "label": "提纲风格", + "type": "select", + "default": "问题拆解型", + "options": ["问题拆解型", "对比分析型", "案例驱动型", "步骤教程型"] + }, + { + "key": "outline_words", + "label": "提纲字数", + "type": "number", + "default": 800, + "min": 300, + "max": 2000, + "required": false + }, + { + "key": "content_target_words", + "label": "正文目标字数", + "type": "number", + "default": 1400, + "min": 500, + "max": 5000, + "required": false + }, + { + "key": "max_content_images", + "label": "最大配图数(可选,0 表示无上限)", + "type": "number", + "default": 0, + "min": 0, + "max": 20, + "required": false + } + ] + } + } +} \ No newline at end of file diff --git a/eai_agentplatform/frontend/src/components/chat/WeixinPublicAccountSpecialistPanel.vue b/eai_agentplatform/frontend/src/components/chat/WeixinPublicAccountSpecialistPanel.vue index da44a72..0ea13a0 100644 --- a/eai_agentplatform/frontend/src/components/chat/WeixinPublicAccountSpecialistPanel.vue +++ b/eai_agentplatform/frontend/src/components/chat/WeixinPublicAccountSpecialistPanel.vue @@ -158,9 +158,9 @@ class="oa-btn oa-btn-tiny" type="button" :disabled="previewLoading" - @click="openArtifactPreview(file)" + @click="downloadFile(file)" > - 预览 + 下载 @@ -169,6 +169,50 @@ + + +
+
+ 整体导出 +
+
+ + + + +
+
+ 没有可导出的内容,先运行创作流程。 +
+