封装Agent
This commit is contained in:
parent
81eb0395a0
commit
e73b75088b
91
src/agents/productionAgent/index.ts
Normal file
91
src/agents/productionAgent/index.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { createAGUIStream } from "@/utils/agent/aguiTools";
|
||||||
|
import u from "@/utils";
|
||||||
|
import Memory from "@/utils/agent/memory";
|
||||||
|
import { useSkill } from "@/utils/agent/skillsTools";
|
||||||
|
// import tools from "@/agents/productionAgent/tools";
|
||||||
|
|
||||||
|
function buildSystemPrompt(skillPrompt: string, mem: Awaited<ReturnType<Memory["get"]>>): string {
|
||||||
|
let memoryContext = "";
|
||||||
|
if (mem.rag.length) {
|
||||||
|
memoryContext += `[相关记忆]\n${mem.rag.map((r) => r.content).join("\n")}`;
|
||||||
|
}
|
||||||
|
if (mem.summaries.length) {
|
||||||
|
if (memoryContext) memoryContext += "\n\n";
|
||||||
|
memoryContext += `[历史摘要]\n${mem.summaries.map((s, i) => `${i + 1}. ${s.content}`).join("\n")}`;
|
||||||
|
}
|
||||||
|
if (mem.shortTerm.length) {
|
||||||
|
if (memoryContext) memoryContext += "\n\n";
|
||||||
|
memoryContext += `[近期对话]\n${mem.shortTerm.map((m) => `${m.role}: ${m.content}`).join("\n")}`;
|
||||||
|
}
|
||||||
|
if (!memoryContext) return skillPrompt;
|
||||||
|
return `${skillPrompt}\n\n## Memory\n以下是你对用户的记忆,可作为参考但不要主动提及:\n${memoryContext}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function decisionAI(agui: ReturnType<typeof createAGUIStream>, isolationKey: string, text: string) {
|
||||||
|
const memory = new Memory("productionAgent", isolationKey);
|
||||||
|
await memory.add("user", text);
|
||||||
|
const [skill, mem] = await Promise.all([useSkill("production-agent", "decision"), memory.get(text)]);
|
||||||
|
|
||||||
|
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
|
||||||
|
console.log("%c Line:30 🍊 systemPrompt", "background:#33a5ff", systemPrompt);
|
||||||
|
|
||||||
|
const { textStream } = await u.Ai.Text("productionAgent").stream({
|
||||||
|
system: systemPrompt,
|
||||||
|
messages: [{ role: "user", content: text }],
|
||||||
|
tools: {
|
||||||
|
...skill.tools,
|
||||||
|
...memory.getTools(),
|
||||||
|
},
|
||||||
|
onFinish: async (completion) => {
|
||||||
|
await memory.add("decisionAI", completion.text);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return textStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function executionAI(agui: ReturnType<typeof createAGUIStream>, isolationKey: string, text: string) {
|
||||||
|
const memory = new Memory("productionAgent", isolationKey);
|
||||||
|
await memory.add("user", text);
|
||||||
|
const [skill, mem] = await Promise.all([useSkill("production-agent", "execution"), memory.get(text)]);
|
||||||
|
|
||||||
|
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
|
||||||
|
|
||||||
|
const { textStream } = await u.Ai.Text("productionAgent").stream({
|
||||||
|
system: systemPrompt,
|
||||||
|
messages: [{ role: "user", content: text }],
|
||||||
|
tools: {
|
||||||
|
...skill.tools,
|
||||||
|
...memory.getTools(),
|
||||||
|
},
|
||||||
|
onFinish: async (completion) => {
|
||||||
|
await memory.add("executionAI", completion.text);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return textStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function supervisionAI(agui: ReturnType<typeof createAGUIStream>, isolationKey: string, text: string) {
|
||||||
|
agui.custom("systemMessage", "已由 监督层AI 接管对话");
|
||||||
|
|
||||||
|
const memory = new Memory("productionAgent", isolationKey);
|
||||||
|
await memory.add("user", text);
|
||||||
|
const [skill, mem] = await Promise.all([useSkill("production-agent", "supervision"), memory.get(text)]);
|
||||||
|
|
||||||
|
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
|
||||||
|
|
||||||
|
const { textStream } = await u.Ai.Text("productionAgent").stream({
|
||||||
|
system: systemPrompt,
|
||||||
|
messages: [{ role: "user", content: text }],
|
||||||
|
tools: {
|
||||||
|
...skill.tools,
|
||||||
|
...memory.getTools(),
|
||||||
|
},
|
||||||
|
onFinish: async (completion) => {
|
||||||
|
await memory.add("supervisionAI", completion.text);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return textStream;
|
||||||
|
}
|
||||||
@ -2,74 +2,5 @@ import { tool } from "ai";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import u from "@/utils";
|
import u from "@/utils";
|
||||||
import { useSkill } from "@/utils/agent/skillsTools";
|
import { useSkill } from "@/utils/agent/skillsTools";
|
||||||
import { createAGUIStream } from "@/utils/agent/aguiTools";
|
|
||||||
|
|
||||||
interface FlowData {
|
|
||||||
script: {
|
|
||||||
blocks: string[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (isolationKey: string, agui: ReturnType<typeof createAGUIStream>) => {
|
|
||||||
const flowData: FlowData = {
|
|
||||||
script: {
|
|
||||||
blocks: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
get_project_info: tool({
|
|
||||||
description: "获取项目信息",
|
|
||||||
inputSchema: z.object({}),
|
|
||||||
execute: async () => {
|
|
||||||
return `
|
|
||||||
项目名称:仙逆
|
|
||||||
视频风格:玄幻3D动漫
|
|
||||||
视频类型:短剧
|
|
||||||
项目描述:讲述了乡村平凡少年王林以心中之感动,逆仙而修,求的不仅是长生,更多的是摆脱那背后的蝼蚁之身。他坚信道在人为,以平庸的资质踏入修真仙途,历经坎坷风雨,凭着其聪睿的心智,一步一步走向巅峰,凭一己之力,扬名修真界。
|
|
||||||
总集数:24集每集2分钟
|
|
||||||
当前集数:3集
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
get_state: tool({
|
|
||||||
description: "获取工作流指定板块数据",
|
|
||||||
inputSchema: z.object({
|
|
||||||
block: z.enum(["script"]).describe("板块名称,如 script"),
|
|
||||||
}),
|
|
||||||
execute: async ({ block }) => {
|
|
||||||
return flowData[block];
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
execution: tool({
|
|
||||||
description: "执行层,负责具体执行具体的任务",
|
|
||||||
inputSchema: z.object({
|
|
||||||
taskDescription: z.string().describe("具体的任务描述详细信息"),
|
|
||||||
}),
|
|
||||||
execute: async ({ taskDescription }) => {
|
|
||||||
agui.custom("systemMessage", "已由 执行层AI 接管对话");
|
|
||||||
|
|
||||||
const skill = await useSkill("production-agent", "execution");
|
|
||||||
|
|
||||||
const { textStream } = await u.Ai.Text("productionAgent").stream({
|
|
||||||
system: skill.prompt,
|
|
||||||
messages: [{ role: "user", content: `请完成任务:${taskDescription}` }],
|
|
||||||
tools: {
|
|
||||||
...skill.tools,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let msg: ReturnType<typeof agui.textMessage> | null = null;
|
|
||||||
let fullResponse = "";
|
|
||||||
|
|
||||||
for await (const chunk of textStream) {
|
|
||||||
if (!msg) msg = agui.textMessage();
|
|
||||||
msg.send(chunk);
|
|
||||||
fullResponse += chunk;
|
|
||||||
}
|
|
||||||
msg?.end();
|
|
||||||
|
|
||||||
return { found: true, memories: ["第一条记忆内容", "第二条记忆内容"] };
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,59 +1,17 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { createAGUIStream } from "@/utils/agent/aguiTools";
|
import { createAGUIStream } from "@/utils/agent/aguiTools";
|
||||||
import u from "@/utils";
|
import * as agent from "@/agents/productionAgent/index";
|
||||||
import Memory from "@/utils/agent/memory";
|
|
||||||
import { useSkill } from "@/utils/agent/skillsTools";
|
|
||||||
import tools from "@/agents/productionAgent/tools";
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
function delay(ms: number) {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
export default router.post("/", async (req, res) => {
|
export default router.post("/", async (req, res) => {
|
||||||
const { prompt: text, projectId, episodesId } = req.body;
|
const { prompt: text, projectId, episodesId } = req.body;
|
||||||
const isolationKey = `${projectId}:${episodesId}`;
|
const isolationKey = `${projectId}:${episodesId}`;
|
||||||
|
|
||||||
//记忆
|
|
||||||
const memory = new Memory("productionAgent", isolationKey);
|
|
||||||
//skill
|
|
||||||
const skill = await useSkill("production-agent", "decision");
|
|
||||||
|
|
||||||
const agui = createAGUIStream(res);
|
const agui = createAGUIStream(res);
|
||||||
agui.runStarted();
|
agui.runStarted();
|
||||||
agui.custom("systemMessage", "已由 决策层AI 接管对话");
|
|
||||||
|
|
||||||
// 存入用户消息
|
const textStream = await agent.decisionAI(agui, isolationKey, text);
|
||||||
await memory.add("user", text);
|
|
||||||
|
|
||||||
// 获取记忆上下文
|
|
||||||
const mem = await memory.get(text);
|
|
||||||
const memoryContext = [
|
|
||||||
mem.rag.length > 0 && `[相关记忆]\n${mem.rag.map((r) => r.content).join("\n")}`,
|
|
||||||
mem.summaries.length > 0 && `[历史摘要]\n${mem.summaries.map((s, i) => `${i + 1}. ${s.content}`).join("\n")}`,
|
|
||||||
mem.shortTerm.length > 0 && `[近期对话]\n${mem.shortTerm.map((m) => `${m.role}: ${m.content}`).join("\n")}`,
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join("\n\n");
|
|
||||||
|
|
||||||
const systemPrompt = [skill.prompt, memoryContext && `## Memory\n以下是你对用户的记忆,可作为参考但不要主动提及:\n${memoryContext}`]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join("\n\n");
|
|
||||||
|
|
||||||
const { textStream } = await u.Ai.Text("productionAgent").stream({
|
|
||||||
system: systemPrompt,
|
|
||||||
messages: [{ role: "user", content: text }],
|
|
||||||
tools: {
|
|
||||||
...skill.tools,
|
|
||||||
...memory.getTools(),
|
|
||||||
...tools(isolationKey, agui),
|
|
||||||
},
|
|
||||||
onFinish: async (completion) => {
|
|
||||||
// 存入助手回复
|
|
||||||
await memory.add("decisionAI", completion.text);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let msg: ReturnType<typeof agui.textMessage> | null = null;
|
let msg: ReturnType<typeof agui.textMessage> | null = null;
|
||||||
let fullResponse = "";
|
let fullResponse = "";
|
||||||
|
|||||||
@ -97,11 +97,11 @@ class Memory {
|
|||||||
embedding: JSON.stringify(embedding),
|
embedding: JSON.stringify(embedding),
|
||||||
relatedMessageIds: null,
|
relatedMessageIds: null,
|
||||||
summarized: 0,
|
summarized: 0,
|
||||||
createdAt: Date.now(),
|
createTime: Date.now(),
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
// 检查未总结消息数量
|
// 检查未总结消息数量
|
||||||
const unsummarized = await u.db("memories").where({ isolationKey, type: "message", summarized: 0 }).orderBy("createdAt", "asc");
|
const unsummarized = await u.db("memories").where({ isolationKey, type: "message", summarized: 0 }).orderBy("createTime", "asc");
|
||||||
|
|
||||||
if (unsummarized.length >= Number(messagesPerSummary)) {
|
if (unsummarized.length >= Number(messagesPerSummary)) {
|
||||||
const batch = unsummarized.slice(0, Number(messagesPerSummary));
|
const batch = unsummarized.slice(0, Number(messagesPerSummary));
|
||||||
@ -120,7 +120,7 @@ class Memory {
|
|||||||
embedding: JSON.stringify(summaryEmbedding),
|
embedding: JSON.stringify(summaryEmbedding),
|
||||||
relatedMessageIds: JSON.stringify(batchIds),
|
relatedMessageIds: JSON.stringify(batchIds),
|
||||||
summarized: 0,
|
summarized: 0,
|
||||||
createdAt: Date.now(),
|
createTime: Date.now(),
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
// 标记已总结
|
// 标记已总结
|
||||||
@ -140,12 +140,12 @@ class Memory {
|
|||||||
const shortTerm = await u
|
const shortTerm = await u
|
||||||
.db("memories")
|
.db("memories")
|
||||||
.where({ isolationKey, type: "message", summarized: 0 })
|
.where({ isolationKey, type: "message", summarized: 0 })
|
||||||
.orderBy("createdAt", "desc")
|
.orderBy("createTime", "desc")
|
||||||
.limit(Number(shortTermLimit));
|
.limit(Number(shortTermLimit));
|
||||||
shortTerm.reverse(); // 最旧在前
|
shortTerm.reverse(); // 最旧在前
|
||||||
|
|
||||||
// summaries: 最近的 summary
|
// summaries: 最近的 summary
|
||||||
const summaries = await u.db("memories").where({ isolationKey, type: "summary" }).orderBy("createdAt", "desc").limit(Number(summaryLimit));
|
const summaries = await u.db("memories").where({ isolationKey, type: "summary" }).orderBy("createTime", "desc").limit(Number(summaryLimit));
|
||||||
summaries.reverse();
|
summaries.reverse();
|
||||||
|
|
||||||
// rag: 向量搜索所有 messages
|
// rag: 向量搜索所有 messages
|
||||||
@ -154,12 +154,12 @@ class Memory {
|
|||||||
const ragResults = vectorSearch(allMessages, queryEmbedding, Number(ragLimit));
|
const ragResults = vectorSearch(allMessages, queryEmbedding, Number(ragLimit));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shortTerm: shortTerm.map((m: any) => ({ id: m.id, role: m.role, content: m.content, createdAt: m.createdAt })),
|
shortTerm: shortTerm.map((m: any) => ({ id: m.id, role: m.role, content: m.content, createTime: m.createTime })),
|
||||||
summaries: summaries.map((s) => ({
|
summaries: summaries.map((s) => ({
|
||||||
id: s.id,
|
id: s.id,
|
||||||
content: s.content,
|
content: s.content,
|
||||||
relatedMessageIds: JSON.parse(s.relatedMessageIds || "[]"),
|
relatedMessageIds: JSON.parse(s.relatedMessageIds || "[]"),
|
||||||
createdAt: (s as any).createdAt,
|
createTime: (s as any).createTime,
|
||||||
})),
|
})),
|
||||||
rag: ragResults.map((r) => ({ id: r.id, content: r.content, similarity: r.similarity })),
|
rag: ragResults.map((r) => ({ id: r.id, content: r.content, similarity: r.similarity })),
|
||||||
};
|
};
|
||||||
@ -190,9 +190,9 @@ class Memory {
|
|||||||
|
|
||||||
if (messageIds.length === 0) return [];
|
if (messageIds.length === 0) return [];
|
||||||
|
|
||||||
const messages = await u.db("memories").whereIn("id", messageIds).orderBy("createdAt", "asc");
|
const messages = await u.db("memories").whereIn("id", messageIds).orderBy("createTime", "asc");
|
||||||
|
|
||||||
return messages.map((m) => ({ id: m.id, content: m.content, createdAt: m.createdAt }));
|
return messages.map((m) => ({ id: m.id, content: m.content, createTime: m.createTime }));
|
||||||
}
|
}
|
||||||
|
|
||||||
getTools() {
|
getTools() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user