新增 去除think标签 方法 (未使用),
This commit is contained in:
parent
9ffc181822
commit
86dbd14493
@ -123,6 +123,7 @@ function runSubAgent(parentCtx: AgentContext) {
|
|||||||
prompt: z.string().max(100).describe("交给子Agent的任务简约描述"),
|
prompt: z.string().max(100).describe("交给子Agent的任务简约描述"),
|
||||||
}),
|
}),
|
||||||
execute: async ({ agent, prompt }) => {
|
execute: async ({ agent, prompt }) => {
|
||||||
|
//todo 传入md有问题
|
||||||
const fn = [executionAI, supervisionAI][subAgentList.indexOf(agent)];
|
const fn = [executionAI, supervisionAI][subAgentList.indexOf(agent)];
|
||||||
//运行子Agent
|
//运行子Agent
|
||||||
const subTextStream = await fn({ ...parentCtx, text: prompt });
|
const subTextStream = await fn({ ...parentCtx, text: prompt });
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export async function decisionAI(ctx: AgentContext) {
|
|||||||
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
|
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
|
||||||
|
|
||||||
const projectData = await u.db("o_project").where("id", resTool.data.projectId).first();
|
const projectData = await u.db("o_project").where("id", resTool.data.projectId).first();
|
||||||
const novelData = await u.db("o_novel").select("id", "chapterIndex as index");
|
const novelData = await u.db("o_novel").where("projectId", resTool.data.projectId).select("id", "chapterIndex as index");
|
||||||
|
|
||||||
const projectInfo = [
|
const projectInfo = [
|
||||||
"## 项目信息",
|
"## 项目信息",
|
||||||
@ -70,6 +70,7 @@ export async function decisionAI(ctx: AgentContext) {
|
|||||||
...useTools(ctx.resTool),
|
...useTools(ctx.resTool),
|
||||||
},
|
},
|
||||||
onFinish: async (completion) => {
|
onFinish: async (completion) => {
|
||||||
|
console.log("%c Line:73 🍧 completion", "background:#93c0a4", completion);
|
||||||
await memory.add("assistant:decision", completion.text);
|
await memory.add("assistant:decision", completion.text);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -99,6 +100,7 @@ export async function executionAI(ctx: AgentContext) {
|
|||||||
...useTools(ctx.resTool),
|
...useTools(ctx.resTool),
|
||||||
},
|
},
|
||||||
onFinish: async (completion) => {
|
onFinish: async (completion) => {
|
||||||
|
console.log("%c Line:102 🍻 completion", "background:#fca650", completion);
|
||||||
await memory.add("assistant:execution", completion.text);
|
await memory.add("assistant:execution", completion.text);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -125,6 +127,7 @@ export async function supervisionAI(ctx: AgentContext) {
|
|||||||
...useTools(ctx.resTool),
|
...useTools(ctx.resTool),
|
||||||
},
|
},
|
||||||
onFinish: async (completion) => {
|
onFinish: async (completion) => {
|
||||||
|
console.log("%c Line:129 🍣 completion", "background:#3f7cff", completion);
|
||||||
await memory.add("assistant:supervision", completion.text);
|
await memory.add("assistant:supervision", completion.text);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -149,6 +152,7 @@ function runSubAgent(parentCtx: AgentContext) {
|
|||||||
let fullResponse = "";
|
let fullResponse = "";
|
||||||
|
|
||||||
for await (const chunk of subTextStream) {
|
for await (const chunk of subTextStream) {
|
||||||
|
console.log("%c Line:155 🥛 chunk", "background:#fca650", chunk);
|
||||||
msg.send(chunk);
|
msg.send(chunk);
|
||||||
fullResponse += chunk;
|
fullResponse += chunk;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export const AssetSchema = z.object({
|
|||||||
type: z.enum(["role", "tool", "scene", "clip"]).describe("资产类型"),
|
type: z.enum(["role", "tool", "scene", "clip"]).describe("资产类型"),
|
||||||
});
|
});
|
||||||
export const ScriptSchema = z.object({
|
export const ScriptSchema = z.object({
|
||||||
id: z.number().describe("剧本ID,如果新增则为空").optional(),
|
id: z.number().describe("剧本ID"),
|
||||||
name: z.string().describe("剧本名称"),
|
name: z.string().describe("剧本名称"),
|
||||||
content: z.string().describe("剧本内容"),
|
content: z.string().describe("剧本内容"),
|
||||||
});
|
});
|
||||||
@ -42,7 +42,7 @@ export default (resTool: ResTool, toolsNames?: string[]) => {
|
|||||||
console.log("[tools] get_novel_events", ids);
|
console.log("[tools] get_novel_events", ids);
|
||||||
const data = await u
|
const data = await u
|
||||||
.db("o_novel")
|
.db("o_novel")
|
||||||
.where("projectId",resTool.data.projectId)
|
.where("projectId", resTool.data.projectId)
|
||||||
.select("id", "chapterIndex as index", "reel", "chapter", "chapterData", "event", "eventState")
|
.select("id", "chapterIndex as index", "reel", "chapter", "chapterData", "event", "eventState")
|
||||||
.whereIn("id", ids);
|
.whereIn("id", ids);
|
||||||
const eventString = data.map((i: any) => [`第${i.index}章,标题:${i.chapter},事件:${i.event}`].join("\n")).join("\n");
|
const eventString = data.map((i: any) => [`第${i.index}章,标题:${i.chapter},事件:${i.event}`].join("\n")).join("\n");
|
||||||
@ -91,41 +91,48 @@ export default (resTool: ResTool, toolsNames?: string[]) => {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
insert_script_to_sqlite: tool({
|
update_script_to_sqlite: tool({
|
||||||
description: "将剧本内容插入sqlite数据库,供后续业务使用",
|
description: "更新剧本,修改数据库对应剧本,供后续业务使用",
|
||||||
inputSchema: z.object({
|
inputSchema: z.object({
|
||||||
script: ScriptSchema,
|
script: ScriptSchema,
|
||||||
// assetsList: z.array(AssetSchema).describe("剧本所使用资产列表,注意不要包含剧本内容,仅为所使用到的 道具、人物、场景、素材"),
|
|
||||||
}),
|
}),
|
||||||
execute: async ({ script }) => {
|
execute: async ({ script }) => {
|
||||||
console.log("%c Line:103 🍷 script", "background:#42b983", script);
|
console.log("%c Line:103 🍷 script", "background:#42b983", script);
|
||||||
// console.log("[tools] insert_script_to_sqlite", assetsList);
|
await u.db("o_script").where({ id: script.id }).update({
|
||||||
|
name: script.name,
|
||||||
|
content: script.content,
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.emit("setPlanData", { key: "script", value: script.id });
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
insert_script_to_sqlite: tool({
|
||||||
|
description: "新增剧本,将剧本内容插入sqlite数据库,供后续业务使用",
|
||||||
|
inputSchema: z.object({
|
||||||
|
script: ScriptSchema.omit({ id: true }),
|
||||||
|
}),
|
||||||
|
execute: async ({ script }) => {
|
||||||
|
console.log("%c Line:103 🍷 script", "background:#42b983", script);
|
||||||
|
|
||||||
const [scriptId] = await u.db("o_script").insert({
|
const [scriptId] = await u.db("o_script").insert({
|
||||||
name: script.name,
|
name: script.name,
|
||||||
content: script.content,
|
content: script.content,
|
||||||
projectId: resTool.data.projectId,
|
projectId: resTool.data.projectId,
|
||||||
createTime: Date.now(),
|
createTime: Date.now(),
|
||||||
});
|
});
|
||||||
// if (assetsList && assetsList.length) {
|
socket.emit("setPlanData", { key: "script", value: scriptId });
|
||||||
// const assetId = [];
|
return true;
|
||||||
// for (const i of assetsList) {
|
},
|
||||||
// if (i.id) {
|
}),
|
||||||
// assetId.push(i.id);
|
delete_script_to_sqlite: tool({
|
||||||
// continue;
|
description: "删除剧本,将剧本内容从sqlite数据库中删除",
|
||||||
// }
|
inputSchema: z.object({
|
||||||
// const [id] = await u.db("o_assets").insert({
|
scriptId: z.string().describe("剧本id"),
|
||||||
// name: i.name,
|
}),
|
||||||
// prompt: i.prompt,
|
execute: async ({ scriptId }) => {
|
||||||
// type: i.type,
|
console.log("[tools] delete_script_to_sqlite", scriptId);
|
||||||
// describe: i.desc,
|
await u.db("o_script").where({ id: scriptId }).delete();
|
||||||
// projectId: resTool.data.projectId,
|
|
||||||
// startTime: Date.now(),
|
|
||||||
// });
|
|
||||||
// assetId.push(id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// await u.db("o_scriptAssets").insert(assetId.map((i) => ({ scriptId, assetId: i })));
|
|
||||||
// }
|
|
||||||
socket.emit("setPlanData", { key: "script", value: scriptId });
|
socket.emit("setPlanData", { key: "script", value: scriptId });
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -122,6 +122,7 @@ export default router.post(
|
|||||||
messages: [{ role: "user", content: "小说原文" + novelText }],
|
messages: [{ role: "user", content: "小说原文" + novelText }],
|
||||||
tools: skill.tools,
|
tools: skill.tools,
|
||||||
})) as any;
|
})) as any;
|
||||||
|
|
||||||
if (!_output) return res.status(500).send("失败");
|
if (!_output) return res.status(500).send("失败");
|
||||||
await u.db("o_assets").where("id", assetsId).update({ prompt: _output });
|
await u.db("o_assets").where("id", assetsId).update({ prompt: _output });
|
||||||
|
|
||||||
|
|||||||
34
src/types/database.d.ts
vendored
34
src/types/database.d.ts
vendored
@ -1,6 +1,36 @@
|
|||||||
// @db-hash d807205fbb27fc5ddb04cae060fb4430
|
// @db-hash 579a004cc745580469a24ee71f5f51c3
|
||||||
//该文件由脚本自动生成,请勿手动修改
|
//该文件由脚本自动生成,请勿手动修改
|
||||||
|
|
||||||
|
export interface _o_project_old_20260326 {
|
||||||
|
'artStyle'?: string | null;
|
||||||
|
'createTime'?: number | null;
|
||||||
|
'id'?: number | null;
|
||||||
|
'intro'?: string | null;
|
||||||
|
'name'?: string | null;
|
||||||
|
'projectType'?: string | null;
|
||||||
|
'type'?: string | null;
|
||||||
|
'userId'?: number | null;
|
||||||
|
'videoRatio'?: string | null;
|
||||||
|
}
|
||||||
|
export interface _o_storyboard_old_20260325 {
|
||||||
|
'camera'?: string | null;
|
||||||
|
'createTime'?: number | null;
|
||||||
|
'description'?: string | null;
|
||||||
|
'duration'?: string | null;
|
||||||
|
'filePath'?: string | null;
|
||||||
|
'frameMode'?: string | null;
|
||||||
|
'id'?: number;
|
||||||
|
'lines'?: string | null;
|
||||||
|
'mode'?: string | null;
|
||||||
|
'model'?: string | null;
|
||||||
|
'prompt'?: string | null;
|
||||||
|
'reason'?: string | null;
|
||||||
|
'resolution'?: string | null;
|
||||||
|
'scriptId'?: number | null;
|
||||||
|
'sound'?: string | null;
|
||||||
|
'state'?: string | null;
|
||||||
|
'title'?: string | null;
|
||||||
|
}
|
||||||
export interface memories {
|
export interface memories {
|
||||||
'content': string;
|
'content': string;
|
||||||
'createTime': number;
|
'createTime': number;
|
||||||
@ -220,6 +250,8 @@ export interface o_videoConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DB {
|
export interface DB {
|
||||||
|
"_o_project_old_20260326": _o_project_old_20260326;
|
||||||
|
"_o_storyboard_old_20260325": _o_storyboard_old_20260325;
|
||||||
"memories": memories;
|
"memories": memories;
|
||||||
"o_agentDeploy": o_agentDeploy;
|
"o_agentDeploy": o_agentDeploy;
|
||||||
"o_agentWorkData": o_agentWorkData;
|
"o_agentWorkData": o_agentWorkData;
|
||||||
|
|||||||
106
src/utils/stripThink.ts
Normal file
106
src/utils/stripThink.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* 去除深度思考模型输出的 <think>...</think> 标签及其内容
|
||||||
|
*
|
||||||
|
* 1. stripThink(text) — 用于非流式,直接去除完整文本中的 <think> 块
|
||||||
|
* 2. createThinkStreamFilter() — 用于流式,返回有状态的过滤器,逐 chunk 过滤
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非流式:去除完整文本中的 <think>...</think>
|
||||||
|
*/
|
||||||
|
export function stripThink(text: string): string {
|
||||||
|
return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流式:创建一个有状态的 chunk 过滤器
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* ```ts
|
||||||
|
* const filter = createThinkStreamFilter();
|
||||||
|
* for await (const chunk of textStream) {
|
||||||
|
* const filtered = filter.push(chunk);
|
||||||
|
* if (filtered) msg.send(filtered);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function createThinkStreamFilter() {
|
||||||
|
let insideThink = false;
|
||||||
|
let buffer = "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* 输入一个 chunk,返回过滤后需要输出的文本(可能为空字符串)
|
||||||
|
*/
|
||||||
|
push(chunk: string): string {
|
||||||
|
let output = "";
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < chunk.length) {
|
||||||
|
if (insideThink) {
|
||||||
|
// 正在 <think> 内部,寻找 </think>
|
||||||
|
const closeIdx = chunk.indexOf("</think>", i);
|
||||||
|
if (closeIdx !== -1) {
|
||||||
|
// 找到闭合标签,跳过标签内容
|
||||||
|
insideThink = false;
|
||||||
|
i = closeIdx + "</think>".length;
|
||||||
|
} else {
|
||||||
|
// 整个剩余 chunk 都在 think 内,全部丢弃
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 不在 <think> 内部
|
||||||
|
const openIdx = chunk.indexOf("<think>", i);
|
||||||
|
if (openIdx !== -1) {
|
||||||
|
// 找到开启标签,输出标签之前的内容
|
||||||
|
output += buffer + chunk.slice(i, openIdx);
|
||||||
|
buffer = "";
|
||||||
|
insideThink = true;
|
||||||
|
i = openIdx + "<think>".length;
|
||||||
|
} else {
|
||||||
|
// 没有发现 <think>,但可能 chunk 末尾是不完整的 "<thi..."
|
||||||
|
// 缓冲末尾可能是 "<" 开头的不完整标签片段
|
||||||
|
const potentialStart = findPartialTag(chunk, i);
|
||||||
|
if (potentialStart !== -1) {
|
||||||
|
output += buffer + chunk.slice(i, potentialStart);
|
||||||
|
buffer = chunk.slice(potentialStart);
|
||||||
|
} else {
|
||||||
|
output += buffer + chunk.slice(i);
|
||||||
|
buffer = "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流结束时调用,刷出缓冲区中残留的内容
|
||||||
|
*/
|
||||||
|
flush(): string {
|
||||||
|
const remaining = buffer;
|
||||||
|
buffer = "";
|
||||||
|
return remaining;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查 chunk[startIdx..] 的末尾是否包含 "<think>" 的不完整前缀
|
||||||
|
* 如 "<", "<t", "<th", "<thi", "<thin", "<think"
|
||||||
|
* 返回不完整前缀的起始位置,未找到则返回 -1
|
||||||
|
*/
|
||||||
|
function findPartialTag(chunk: string, startIdx: number): number {
|
||||||
|
const tag = "<think>";
|
||||||
|
// 只需检查末尾最多 tag.length - 1 个字符
|
||||||
|
const searchStart = Math.max(startIdx, chunk.length - (tag.length - 1));
|
||||||
|
for (let i = searchStart; i < chunk.length; i++) {
|
||||||
|
const remaining = chunk.slice(i);
|
||||||
|
if (tag.startsWith(remaining)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user