修正记忆重复问题

This commit is contained in:
ACT丶流星雨 2026-03-30 23:45:58 +08:00
parent 8e10813dc8
commit de362e0312
4 changed files with 115 additions and 105 deletions

View File

@ -23,7 +23,7 @@ function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
export default router.get("/", async (req, res) => { export default router.get("/", async (req, res) => {
const isolationKey = "test"; const isolationKey = "1:productionAgent:1";
const input = "你好" const input = "你好"
const memory = new Memory("productionAgent", isolationKey); const memory = new Memory("productionAgent", isolationKey);

View File

@ -3,7 +3,6 @@ import u from "@/utils";
import { Namespace, Socket } from "socket.io"; import { Namespace, Socket } from "socket.io";
import * as agent from "@/agents/productionAgent/index"; import * as agent from "@/agents/productionAgent/index";
import ResTool from "@/socket/resTool"; import ResTool from "@/socket/resTool";
import Memory from "@/utils/agent/memory";
async function verifyToken(rawToken: string): Promise<Boolean> { async function verifyToken(rawToken: string): Promise<Boolean> {
const setting = await u.db("o_setting").where("key", "tokenKey").select("value").first(); const setting = await u.db("o_setting").where("key", "tokenKey").select("value").first();
@ -57,7 +56,6 @@ export default (nsp: Namespace) => {
abortController?.abort(); abortController?.abort();
abortController = new AbortController(); abortController = new AbortController();
const currentController = abortController; const currentController = abortController;
const memory = new Memory("scriptAgent", isolationKey);
const msg = resTool.newMessage("assistant", "视频策划"); const msg = resTool.newMessage("assistant", "视频策划");
const ctx: agent.AgentContext = { const ctx: agent.AgentContext = {
@ -70,26 +68,16 @@ export default (nsp: Namespace) => {
msg, msg,
}; };
try {
const textStream = await agent.decisionAI(ctx); const textStream = await agent.decisionAI(ctx);
let currentMsg = ctx.msg; let currentMsg = ctx.msg;
let text = currentMsg.text(); let text = currentMsg.text();
let currentContent = "";
const persistCurrentMessage = async () => { const syncCurrentMessage = () => {
if (!currentContent.trim()) return;
await memory.add("assistant:decision", currentContent, {
name: "视频策划",
createTime: new Date(currentMsg.datetime).getTime(),
});
currentContent = "";
};
const syncCurrentMessage = async () => {
if (ctx.msg === currentMsg) return; if (ctx.msg === currentMsg) return;
text.complete(); text.complete();
currentMsg.complete(); currentMsg.complete();
await persistCurrentMessage();
currentMsg = ctx.msg; currentMsg = ctx.msg;
text = currentMsg.text(); text = currentMsg.text();
}; };
@ -97,9 +85,8 @@ export default (nsp: Namespace) => {
let aborted = false; let aborted = false;
try { try {
for await (const chunk of textStream) { for await (const chunk of textStream) {
await syncCurrentMessage(); syncCurrentMessage();
text.append(chunk); text.append(chunk);
currentContent += chunk;
} }
} catch (err: any) { } catch (err: any) {
if (err.name === "AbortError" || currentController.signal.aborted) { if (err.name === "AbortError" || currentController.signal.aborted) {
@ -108,7 +95,7 @@ export default (nsp: Namespace) => {
throw err; throw err;
} }
} finally { } finally {
await syncCurrentMessage(); syncCurrentMessage();
if (aborted) { if (aborted) {
text.append("[已停止]"); text.append("[已停止]");
text.complete(); text.complete();
@ -117,7 +104,15 @@ export default (nsp: Namespace) => {
text.complete(); text.complete();
currentMsg.complete(); currentMsg.complete();
} }
await persistCurrentMessage(); }
} catch (err: any) {
if (err.name !== "AbortError" && !currentController.signal.aborted) {
const errorMsg = u.error(err).message;
console.error("[productionAgent] chat error:", errorMsg);
ctx.msg.text(errorMsg).complete();
ctx.msg.error();
}
} finally {
if (abortController === currentController) { if (abortController === currentController) {
abortController = null; abortController = null;
} }

View File

@ -3,7 +3,6 @@ import u from "@/utils";
import { Namespace, Socket } from "socket.io"; import { Namespace, Socket } from "socket.io";
import * as agent from "@/agents/scriptAgent/index"; import * as agent from "@/agents/scriptAgent/index";
import ResTool from "@/socket/resTool"; import ResTool from "@/socket/resTool";
import Memory from "@/utils/agent/memory";
async function verifyToken(rawToken: string): Promise<Boolean> { async function verifyToken(rawToken: string): Promise<Boolean> {
const setting = await u.db("o_setting").where("key", "tokenKey").select("value").first(); const setting = await u.db("o_setting").where("key", "tokenKey").select("value").first();
@ -46,7 +45,6 @@ export default (nsp: Namespace) => {
abortController?.abort(); abortController?.abort();
abortController = new AbortController(); abortController = new AbortController();
const currentController = abortController; const currentController = abortController;
const memory = new Memory("scriptAgent", isolationKey);
const msg = resTool.newMessage("assistant", "统筹"); const msg = resTool.newMessage("assistant", "统筹");
const ctx: agent.AgentContext = { const ctx: agent.AgentContext = {
@ -59,26 +57,16 @@ export default (nsp: Namespace) => {
msg, msg,
}; };
try {
const textStream = await agent.decisionAI(ctx); const textStream = await agent.decisionAI(ctx);
let currentMsg = ctx.msg; let currentMsg = ctx.msg;
let text = currentMsg.text(); let text = currentMsg.text();
let currentContent = "";
const persistCurrentMessage = async () => { const syncCurrentMessage = () => {
if (!currentContent.trim()) return;
await memory.add("assistant:decision", currentContent, {
name: "统筹",
createTime: new Date(currentMsg.datetime).getTime(),
});
currentContent = "";
};
const syncCurrentMessage = async () => {
if (ctx.msg === currentMsg) return; if (ctx.msg === currentMsg) return;
text.complete(); text.complete();
currentMsg.complete(); currentMsg.complete();
await persistCurrentMessage();
currentMsg = ctx.msg; currentMsg = ctx.msg;
text = currentMsg.text(); text = currentMsg.text();
}; };
@ -86,9 +74,8 @@ export default (nsp: Namespace) => {
let aborted = false; let aborted = false;
try { try {
for await (const chunk of textStream) { for await (const chunk of textStream) {
await syncCurrentMessage(); syncCurrentMessage();
text.append(chunk); text.append(chunk);
currentContent += chunk;
} }
} catch (err: any) { } catch (err: any) {
if (err.name === "AbortError" || currentController.signal.aborted) { if (err.name === "AbortError" || currentController.signal.aborted) {
@ -97,7 +84,7 @@ export default (nsp: Namespace) => {
throw err; throw err;
} }
} finally { } finally {
await syncCurrentMessage(); syncCurrentMessage();
if (aborted) { if (aborted) {
text.complete(); text.complete();
currentMsg.stop(); currentMsg.stop();
@ -105,7 +92,15 @@ export default (nsp: Namespace) => {
text.complete(); text.complete();
currentMsg.complete(); currentMsg.complete();
} }
await persistCurrentMessage(); }
} catch (err: any) {
if (err.name !== "AbortError" && !currentController.signal.aborted) {
const errorMsg = u.error(err).message;
console.error("[scriptAgent] chat error:", errorMsg);
ctx.msg.text(errorMsg).complete();
ctx.msg.error();
}
} finally {
if (abortController === currentController) { if (abortController === currentController) {
abortController = null; abortController = null;
} }

View File

@ -25,7 +25,12 @@ async function getVendorTemplateFn(fnName: FnName, modelName: `${string}:${strin
const selectedModel = modelList.find((i: any) => i.modelName == name); const selectedModel = modelList.find((i: any) => i.modelName == name);
if (!selectedModel) throw new Error(`未找到模型 ${name} id=${id}`); if (!selectedModel) throw new Error(`未找到模型 ${name} id=${id}`);
const jsCode = transform(vendorConfigData.code!, { transforms: ["typescript"] }).code; const jsCode = transform(vendorConfigData.code!, { transforms: ["typescript"] }).code;
const fn = u.vm(jsCode)[fnName]; const running = u.vm(jsCode);
if (running.vendor) {
Object.assign(running.vendor.inputValues, JSON.parse(vendorConfigData.inputValues ?? "{}"));
running.vendor.models = modelList;
}
const fn = running[fnName];
if (!fn) throw new Error(`未找到供应商配置中的函数 ${fnName} id=${id}`); if (!fn) throw new Error(`未找到供应商配置中的函数 ${fnName} id=${id}`);
if (fnName == "textRequest") return fn(selectedModel); if (fnName == "textRequest") return fn(selectedModel);
else return <T>(input: T) => fn(input, selectedModel); else return <T>(input: T) => fn(input, selectedModel);
@ -115,13 +120,18 @@ class AiImage {
constructor(key: `${string}:${string}`) { constructor(key: `${string}:${string}`) {
this.key = key; this.key = key;
} }
async run(input: ImageConfig, taskRecord: TaskRecord) { async run(input: ImageConfig, taskRecord?: TaskRecord) {
return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, async (modelName) => { const modelName = await resolveModelName(this.key);
const fn = await getVendorTemplateFn("imageRequest", modelName); const exec = async (mn: `${string}:${string}`) => {
const fn = await getVendorTemplateFn("imageRequest", mn);
this.result = await fn(input); this.result = await fn(input);
if (this.result.startsWith("http")) this.result = await urlToBase64(this.result); if (this.result.startsWith("http")) this.result = await urlToBase64(this.result);
return this; return this;
}); };
if (taskRecord) {
return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, exec);
}
return exec(modelName);
} }
async save(path: string) { async save(path: string) {
await u.oss.writeFile(path, this.result); await u.oss.writeFile(path, this.result);
@ -144,13 +154,18 @@ class AiVideo {
constructor(key: `${string}:${string}`) { constructor(key: `${string}:${string}`) {
this.key = key; this.key = key;
} }
async run(input: VideoConfig, taskRecord: TaskRecord) { async run(input: VideoConfig, taskRecord?: TaskRecord) {
return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, async (modelName) => { const modelName = await resolveModelName(this.key);
const fn = await getVendorTemplateFn("videoRequest", modelName); const exec = async (mn: `${string}:${string}`) => {
const fn = await getVendorTemplateFn("videoRequest", mn);
this.result = await fn(input); this.result = await fn(input);
if (this.result.startsWith("http")) this.result = await urlToBase64(this.result); if (this.result.startsWith("http")) this.result = await urlToBase64(this.result);
return this; return this;
}); };
if (taskRecord) {
return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, exec);
}
return exec(modelName);
} }
async save(path: string) { async save(path: string) {
await u.oss.writeFile(path, this.result); await u.oss.writeFile(path, this.result);
@ -163,13 +178,18 @@ class AiAudio {
constructor(key: `${string}:${string}`) { constructor(key: `${string}:${string}`) {
this.key = key; this.key = key;
} }
async run(input: VideoConfig, taskRecord: TaskRecord) { async run(input: VideoConfig, taskRecord?: TaskRecord) {
return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, async (modelName) => { const modelName = await resolveModelName(this.key);
const fn = await getVendorTemplateFn("ttsRequest", modelName); const exec = async (mn: `${string}:${string}`) => {
const fn = await getVendorTemplateFn("ttsRequest", mn);
this.result = await fn(input); this.result = await fn(input);
if (this.result.startsWith("http")) this.result = await urlToBase64(this.result); if (this.result.startsWith("http")) this.result = await urlToBase64(this.result);
return this; return this;
}); };
if (taskRecord) {
return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, exec);
}
return exec(modelName);
} }
async save(path: string) { async save(path: string) {
await u.oss.writeFile(path, this.result); await u.oss.writeFile(path, this.result);