Files
memind/wechat/image-generation-policy.mjs
john cb62b36cd5
Memind CI / Test, build, and release guards (pull_request) Successful in 5m1s
fix: recover WeChat page thumbnail delivery
2026-07-22 11:51:48 +08:00

96 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
IMAGE_GENERATION_MODE,
resolveImageGenerationDecision,
} from '../chat-intent-router.mjs';
export const WECHAT_PAGE_THUMBNAIL_MODE = {
AUTO: 'auto',
REQUIRED_FRESH: 'required_fresh',
};
const INLINE_IMAGE_REQUIRED_PATTERNS = [
/(?:正文|内容|段落|章节).{0,12}(?:配图|插图|图片)/u,
/(?:每段|每章|文中|文章中).{0,12}(?:配图|插图|图片)/u,
/(?:多张|若干张|几张).{0,12}(?:配图|插图|图片)/u,
];
export function resolveWechatImageGenerationPolicy({
text,
isPageGenerate = false,
requireFreshPageThumbnail = false,
} = {}) {
const normalizedText = String(text ?? '').trim();
const requested = resolveImageGenerationDecision({ text: normalizedText });
const pageThumbnailMode = isPageGenerate && requireFreshPageThumbnail
? WECHAT_PAGE_THUMBNAIL_MODE.REQUIRED_FRESH
: WECHAT_PAGE_THUMBNAIL_MODE.AUTO;
const inlineImageMode = requested.mode === IMAGE_GENERATION_MODE.DISABLED
? IMAGE_GENERATION_MODE.DISABLED
: INLINE_IMAGE_REQUIRED_PATTERNS.some((pattern) => pattern.test(normalizedText))
? IMAGE_GENERATION_MODE.REQUIRED
: IMAGE_GENERATION_MODE.AUTO;
const standaloneImageMode = isPageGenerate
? IMAGE_GENERATION_MODE.AUTO
: requested.mode;
return {
channel: 'wechat_mp',
imageGenerationMode: pageThumbnailMode === WECHAT_PAGE_THUMBNAIL_MODE.REQUIRED_FRESH
? IMAGE_GENERATION_MODE.REQUIRED
: standaloneImageMode,
pageThumbnailMode,
inlineImageMode,
standaloneImageMode,
requested,
};
}
export function buildWechatImageRunMetadata(policy) {
if (!policy) return { channel: 'wechat_mp' };
return {
channel: 'wechat_mp',
imageGenerationMode: policy.imageGenerationMode,
pageThumbnailMode: policy.pageThumbnailMode,
inlineImageMode: policy.inlineImageMode,
};
}
export function buildWechatStandaloneImageInstruction(policy, { sourceMessageId = '' } = {}) {
if (!policy || policy.standaloneImageMode === IMAGE_GENERATION_MODE.AUTO) return '';
if (policy.standaloneImageMode === IMAGE_GENERATION_MODE.DISABLED) {
return '【图片策略】用户明确不要生成图片。本轮禁止调用 generate_image,只返回文字。';
}
const idempotencyKey = sourceMessageId
? `wechat-${String(sourceMessageId).replace(/[^a-zA-Z0-9._-]+/g, '-').slice(0, 80)}-image`
: '';
return [
'【微信服务号生图任务】用户明确要求生成一张新图片。',
'必须先调用 `load_skill` → `image-generation`,再调用一次 `sandbox-fs__generate_image`purpose=`inline_image`。',
'必须使用本轮新返回的 PNG/JPEG/WebP 资产;禁止复用历史图片、SVG、CSS 绘图、占位图或网页图片。',
idempotencyKey ? `调用 generate_image 时传入 idempotency_key=${idempotencyKey}` : '',
'成功后简短说明结果,不要输出内部工具步骤;失败时如实报告,禁止声称已经生成。',
].filter(Boolean).join('\n');
}
export function buildWechatPageImageInstruction(policy, { sourceMessageId = '' } = {}) {
if (policy?.pageThumbnailMode !== WECHAT_PAGE_THUMBNAIL_MODE.REQUIRED_FRESH) return '';
const keyPrefix = sourceMessageId
? `wechat-${String(sourceMessageId).replace(/[^a-zA-Z0-9._-]+/g, '-').slice(0, 72)}-page`
: 'wechat-page';
const inlineInstruction = policy.inlineImageMode === IMAGE_GENERATION_MODE.DISABLED
? '用户明确不要正文图片:页面正文和 Hero 不展示图片,但缩略图源图仍必须生成并只写入 mindspace-cover.cover。'
: policy.inlineImageMode === IMAGE_GENERATION_MODE.REQUIRED
? '用户明确要求正文配图:缩略图源图之外,只有正文确实需要不同画面时才可按需调用 inline_image。'
: '正文独立插图按意图判断;没有明确必要时不要额外调用 inline_image。';
return [
'【服务号页面新缩略图硬要求】每个本轮交付给用户的正式内容页面都必须使用本轮新生成、与主题一致的位图作为缩略图源图。',
'写 HTML 前先调用 `load_skill` → `image-generation`,再为每个正式内容页面各调用一次 `sandbox-fs__generate_image`,优先 purpose=`hero`。',
`幂等键使用 ${keyPrefix}-<页面序号>-thumbnail;不同正式页面不得共用同一张图。`,
'同一正式页面在 Agent 层只调用一次 generate_image;工具内部会完成语义重试。返回失败后禁止自行更换提示词并重复调用,交由服务号交付层统一重试。',
'生成成功后,把返回的 asset.htmlSrc 原样写入对应页面的 mindspace-cover.cover。页面需要主视觉时复用同一张图作为 Hero,禁止再调用 card_cover/feed_cover。',
'管理后台、密码查看、重定向、下载包装和错误辅助页不属于正式内容页面,不要求单独生图;这类页面必须在 head 写 `<meta name="mindspace-page-role" content="auxiliary">` 供交付守卫识别。',
inlineInstruction,
'当前轮没有新的成功 generate_image、没有新 jobId,或正式页面未引用本轮资产时,禁止交付页面链接或声称完成。',
].join('\n');
}