Refine agent text mode workflow
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-13 11:35:07 +08:00
parent ec988be30a
commit 67d834f0dd
3 changed files with 136 additions and 5 deletions

View File

@ -98,6 +98,11 @@ import type {
} from "@/lib/ai/context-memory/types";
import { ChatSDKError } from "@/lib/errors";
import { extractImageUrlsFromText } from "@/lib/image-url";
import {
buildPromptMemoryUpdate,
extractPromptMemoryFromWorkingState,
isPromptCarryForwardRequest,
} from "@/lib/ai/prompt-memory";
import { uploadFile } from "@/lib/storage/service";
import { getVideoStoryboardConfig } from "@/lib/video-agent/config";
import { runRuxaNineGridPipeline } from "@/lib/video-agent/pipeline/ruxa-nine-grid-pipeline";
@ -3059,6 +3064,7 @@ export async function POST(request: Request) {
| Awaited<ReturnType<typeof getConversationWorkingStateByChatId>>
| null = null;
let storyboardPreferenceMemory = null;
let reusablePromptMemory = null;
let smartAutoHistoryMessagesFromDb: DBMessage[] = [];
let preferredAgentModelId: string | undefined;
let pendingGridConfirmation:
@ -3186,6 +3192,7 @@ export async function POST(request: Request) {
return null;
});
storyboardPreferenceMemory = extractStoryboardPreferenceMemory(requestWorkingState);
reusablePromptMemory = extractPromptMemoryFromWorkingState(requestWorkingState);
pendingGridConfirmation =
extractPendingGridConfirmationFromWorkingState(requestWorkingState);
@ -3234,9 +3241,22 @@ export async function POST(request: Request) {
chatId: id,
});
}
confirmedPromptForGeneration = extractConfirmedPromptFromWorkingState(requestWorkingState);
confirmedPromptForGeneration =
extractConfirmedPromptFromWorkingState(requestWorkingState) ||
reusablePromptMemory?.optimizedPrompt;
const hasImageInputForPromptCarryForward =
message.parts.some((part: any) =>
part?.type === "file" || part?.type === "image" || part?.type === "image_url"
);
const isPromptCarryForwardExecution =
Boolean(confirmedPromptForGeneration) &&
isPromptCarryForwardRequest({
text: requestTextContent,
hasImage: hasImageInputForPromptCarryForward,
});
forcePromptDrivenImageGeneration =
isConfirmedPromptGenerationRequest(requestTextContent) &&
(isConfirmedPromptGenerationRequest(requestTextContent) ||
isPromptCarryForwardExecution) &&
Boolean(confirmedPromptForGeneration);
if (!requestResolvedMediaInput) {
requestResolvedMediaInput = resolveChatMediaInput({
@ -3457,7 +3477,10 @@ export async function POST(request: Request) {
availableTools: [],
};
smartAutoFriendlyMessage =
smartAutoFriendlyMessage || "收到,我会按你刚确认的提示词直接生图。";
smartAutoFriendlyMessage ||
(isPromptCarryForwardExecution
? "收到,我会使用上一轮优化后的提示词,结合你这轮图片继续执行。"
: "收到,我会按你刚确认的提示词直接生图。");
}
if (smartAutoPlan) {
@ -4689,6 +4712,12 @@ export async function POST(request: Request) {
resolutionMode: "prompt_only",
confidence: 90,
optimizedPrompt: promptOnlyOptimizedPrompt.slice(0, 1000),
promptMemory: buildPromptMemoryUpdate({
optimizedPrompt: promptOnlyOptimizedPrompt.slice(0, 1000),
sourceIntent: "image_gen",
sourceText: requestTextContent.slice(0, 500),
reusable: true,
}),
imageSummary: promptOnlyImageSummary.slice(0, 1200),
sourceImageUrls: promptOnlySourceImageUrls,
},

92
lib/ai/prompt-memory.ts Normal file
View File

@ -0,0 +1,92 @@
import "server-only";
export interface PromptMemoryEntry {
optimizedPrompt: string;
sourceIntent?: string;
sourceText?: string;
reusable?: boolean;
}
function normalizeText(value: unknown, max = 2000): string | undefined {
const normalized = String(value || "").trim();
if (!normalized) return undefined;
return normalized.slice(0, max);
}
export function extractPromptMemoryFromWorkingState(
workingState?: {
lastTurnSummary?: unknown;
} | null,
): PromptMemoryEntry | null {
const summary = workingState?.lastTurnSummary;
if (!summary || typeof summary !== "object") return null;
const summaryRecord = summary as Record<string, unknown>;
const memory = summaryRecord.promptMemory;
if (memory && typeof memory === "object") {
const memoryRecord = memory as Record<string, unknown>;
const optimizedPrompt = normalizeText(memoryRecord.optimizedPrompt, 4000);
if (!optimizedPrompt) return null;
return {
optimizedPrompt,
sourceIntent: normalizeText(memoryRecord.sourceIntent, 64),
sourceText: normalizeText(memoryRecord.sourceText, 500),
reusable:
typeof memoryRecord.reusable === "boolean" ? memoryRecord.reusable : true,
};
}
const resolutionMode = normalizeText(summaryRecord.resolutionMode, 64);
const optimizedPrompt = normalizeText(summaryRecord.optimizedPrompt, 4000);
if (resolutionMode === "prompt_only" && optimizedPrompt) {
return {
optimizedPrompt,
sourceIntent: "image_gen",
sourceText: normalizeText(summaryRecord.text, 500),
reusable: true,
};
}
return null;
}
export function buildPromptMemoryUpdate(params: {
optimizedPrompt: string;
sourceIntent?: string;
sourceText?: string;
reusable?: boolean;
}): PromptMemoryEntry {
return {
optimizedPrompt: normalizeText(params.optimizedPrompt, 4000) || "",
sourceIntent: normalizeText(params.sourceIntent, 64),
sourceText: normalizeText(params.sourceText, 500),
reusable: params.reusable !== false,
};
}
export function isPromptCarryForwardRequest(params: {
text: string;
hasImage: boolean;
}): boolean {
const normalized = String(params.text || "").toLowerCase().replace(/\s+/g, "");
if (!normalized) return false;
const refersPreviousPrompt =
/根据(上一个|上一句|刚才|上面|之前|那个)?提示词|按(上一个|上一句|刚才|上面|之前|那个)?提示词|用(上一个|上一句|刚才|上面|之前|那个)?提示词|用刚才输出的提示词|按刚才输出的提示词|按上面的提示词|沿用上个提示词|继续用那个提示词/.test(
normalized,
);
if (!refersPreviousPrompt) return false;
const wantsExecution =
/生图|生成图片|出图|画出来|做图|继续生成|继续做|重新做|重绘|优化这张图|优化这个图|处理这张图|处理这个图|按这个做|照这个做|按此生成/.test(
normalized,
);
if (wantsExecution) return true;
if (params.hasImage) {
return /优化|处理|重绘|参考这张图|结合这张图|基于这张图/.test(normalized);
}
return false;
}

View File

@ -2517,7 +2517,12 @@ export async function createAgentPreset(params: {
slots: params.slots,
});
const requiredUploadCount = validatedSlots.some((slot) => slot.requireUpload !== false) ? 3 : 0;
const requiredUploadCount =
params.type === "text"
? 0
: validatedSlots.some((slot) => slot.requireUpload !== false)
? 3
: 0;
const avatarAssetId = String(params.avatarAssetId || "").trim() || null;
if (avatarAssetId) {
await validateOwnedReferenceAssets({
@ -2705,7 +2710,12 @@ export async function updateAgentPreset(params: {
userId: params.userId,
slots: normalizedSlotsInput,
});
const requiredUploadCount = validatedSlots.some((slot) => slot.requireUpload !== false) ? 3 : 0;
const requiredUploadCount =
nextType === "text"
? 0
: validatedSlots.some((slot) => slot.requireUpload !== false)
? 3
: 0;
const nextAvatarAssetId =
params.avatarAssetId !== undefined
? String(params.avatarAssetId || "").trim() || null