73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
|
|
import { z } from "zod";
|
|||
|
|
import type { ProviderApiKeys } from "./keys";
|
|||
|
|
import { withAgentSystem } from "./agent";
|
|||
|
|
import { generateStructuredObject } from "./generate-structured";
|
|||
|
|
import { HASHTAG_USER_REMINDER, HASHTAG_WRITING_RULES } from "./hashtag-rules";
|
|||
|
|
import { buildPersonaPromptBlock } from "./persona";
|
|||
|
|
import { getModel } from "./provider";
|
|||
|
|
import { THREADS_MAX_CHARS } from "@/lib/utils";
|
|||
|
|
import type { ViralAnalysis } from "@/lib/types/viral";
|
|||
|
|
|
|||
|
|
const replicaSchema = z.object({
|
|||
|
|
angle: z.string(),
|
|||
|
|
hook: z.string(),
|
|||
|
|
text: z.string().max(THREADS_MAX_CHARS),
|
|||
|
|
imageBrief: z.string(),
|
|||
|
|
rationale: z.string(),
|
|||
|
|
structureNotes: z.string(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
export interface ReplicateViralInput {
|
|||
|
|
originalText: string;
|
|||
|
|
authorName?: string | null;
|
|||
|
|
viralAnalysis: ViralAnalysis;
|
|||
|
|
topicLabel: string;
|
|||
|
|
topicBrief?: string | null;
|
|||
|
|
persona?: string | null;
|
|||
|
|
aiProvider: string;
|
|||
|
|
aiModel: string;
|
|||
|
|
apiKeys?: ProviderApiKeys;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function replicateViralPost(input: ReplicateViralInput) {
|
|||
|
|
const model = getModel(input.aiProvider, input.aiModel, input.apiKeys ?? {});
|
|||
|
|
|
|||
|
|
const object = await generateStructuredObject({
|
|||
|
|
model,
|
|||
|
|
provider: input.aiProvider,
|
|||
|
|
modelId: input.aiModel,
|
|||
|
|
schema: replicaSchema,
|
|||
|
|
system: withAgentSystem(`你是 Threads 爆款複製策略師。根據爆款分析報告,為使用者撰寫「同結構、同節奏、但完全原創」的貼文。
|
|||
|
|
|
|||
|
|
${buildPersonaPromptBlock(input.persona)}
|
|||
|
|
|
|||
|
|
規則:
|
|||
|
|
- 複製的是「爆款公式」(hook 手法、情緒節奏、圖文配合),不是抄襲原文
|
|||
|
|
- 文筆必須像創作者本人,不是只複製爆款語氣
|
|||
|
|
- text ≤ 500 字(含 #話題標籤)
|
|||
|
|
${HASHTAG_WRITING_RULES}
|
|||
|
|
- imageBrief 描述要產出的配圖:版面、配色、圖上文字、風格(繁體中文台灣用語,具體可執行)`),
|
|||
|
|
prompt: `主題:${input.topicLabel}
|
|||
|
|
${input.topicBrief ? `Brief:${input.topicBrief}` : ""}
|
|||
|
|
|
|||
|
|
原文參考(@${input.authorName ?? "匿名"},只學結構不抄內容):
|
|||
|
|
${input.originalText}
|
|||
|
|
|
|||
|
|
爆款分析:
|
|||
|
|
- 爆紅原因:${input.viralAnalysis.whyViral.join(";")}
|
|||
|
|
- Hook 手法:${input.viralAnalysis.hookPattern}
|
|||
|
|
- 結構:${input.viralAnalysis.structurePattern}
|
|||
|
|
- 情緒觸發:${input.viralAnalysis.emotionalTrigger}
|
|||
|
|
- 留言共鳴:${input.viralAnalysis.commentInsights.whyPeopleEngage}
|
|||
|
|
- 複製策略:${input.viralAnalysis.replicationStrategy}
|
|||
|
|
- 圖文策略:${input.viralAnalysis.visualAnalysis.replicationTips.join(";")}
|
|||
|
|
- 繪圖 prompt 參考:${input.viralAnalysis.visualAnalysis.imageGenPrompt}
|
|||
|
|
|
|||
|
|
請產出一篇可發布的複製版貼文 + 配圖製作說明。${HASHTAG_USER_REMINDER}`,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
...object,
|
|||
|
|
text: object.text.slice(0, THREADS_MAX_CHARS),
|
|||
|
|
};
|
|||
|
|
}
|