新增 去除think标签 方法 (未使用),

This commit is contained in:
zhishi 2026-03-27 01:36:21 +08:00
parent 9ffc181822
commit 86dbd14493
6 changed files with 179 additions and 28 deletions

View File

@ -123,6 +123,7 @@ function runSubAgent(parentCtx: AgentContext) {
prompt: z.string().max(100).describe("交给子Agent的任务简约描述"),
}),
execute: async ({ agent, prompt }) => {
//todo 传入md有问题
const fn = [executionAI, supervisionAI][subAgentList.indexOf(agent)];
//运行子Agent
const subTextStream = await fn({ ...parentCtx, text: prompt });

View File

@ -46,7 +46,7 @@ export async function decisionAI(ctx: AgentContext) {
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
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 = [
"## 项目信息",
@ -70,6 +70,7 @@ export async function decisionAI(ctx: AgentContext) {
...useTools(ctx.resTool),
},
onFinish: async (completion) => {
console.log("%c Line:73 🍧 completion", "background:#93c0a4", completion);
await memory.add("assistant:decision", completion.text);
},
});
@ -99,6 +100,7 @@ export async function executionAI(ctx: AgentContext) {
...useTools(ctx.resTool),
},
onFinish: async (completion) => {
console.log("%c Line:102 🍻 completion", "background:#fca650", completion);
await memory.add("assistant:execution", completion.text);
},
});
@ -125,6 +127,7 @@ export async function supervisionAI(ctx: AgentContext) {
...useTools(ctx.resTool),
},
onFinish: async (completion) => {
console.log("%c Line:129 🍣 completion", "background:#3f7cff", completion);
await memory.add("assistant:supervision", completion.text);
},
});
@ -149,6 +152,7 @@ function runSubAgent(parentCtx: AgentContext) {
let fullResponse = "";
for await (const chunk of subTextStream) {
console.log("%c Line:155 🥛 chunk", "background:#fca650", chunk);
msg.send(chunk);
fullResponse += chunk;
}

View File

@ -12,7 +12,7 @@ export const AssetSchema = z.object({
type: z.enum(["role", "tool", "scene", "clip"]).describe("资产类型"),
});
export const ScriptSchema = z.object({
id: z.number().describe("剧本ID,如果新增则为空").optional(),
id: z.number().describe("剧本ID"),
name: z.string().describe("剧本名称"),
content: z.string().describe("剧本内容"),
});
@ -42,7 +42,7 @@ export default (resTool: ResTool, toolsNames?: string[]) => {
console.log("[tools] get_novel_events", ids);
const data = await u
.db("o_novel")
.where("projectId",resTool.data.projectId)
.where("projectId", resTool.data.projectId)
.select("id", "chapterIndex as index", "reel", "chapter", "chapterData", "event", "eventState")
.whereIn("id", ids);
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;
},
}),
insert_script_to_sqlite: tool({
description: "将剧本内容插入sqlite数据库,供后续业务使用",
update_script_to_sqlite: tool({
description: "更新剧本,修改数据库对应剧本,供后续业务使用",
inputSchema: z.object({
script: ScriptSchema,
// assetsList: z.array(AssetSchema).describe("剧本所使用资产列表,注意不要包含剧本内容,仅为所使用到的 道具、人物、场景、素材"),
}),
execute: async ({ 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({
name: script.name,
content: script.content,
projectId: resTool.data.projectId,
createTime: Date.now(),
});
// if (assetsList && assetsList.length) {
// const assetId = [];
// for (const i of assetsList) {
// if (i.id) {
// assetId.push(i.id);
// continue;
// }
// const [id] = await u.db("o_assets").insert({
// name: i.name,
// prompt: i.prompt,
// type: i.type,
// describe: i.desc,
// 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 });
return true;
},
}),
delete_script_to_sqlite: tool({
description: "删除剧本,将剧本内容从sqlite数据库中删除",
inputSchema: z.object({
scriptId: z.string().describe("剧本id"),
}),
execute: async ({ scriptId }) => {
console.log("[tools] delete_script_to_sqlite", scriptId);
await u.db("o_script").where({ id: scriptId }).delete();
socket.emit("setPlanData", { key: "script", value: scriptId });
return true;
},

View File

@ -122,6 +122,7 @@ export default router.post(
messages: [{ role: "user", content: "小说原文" + novelText }],
tools: skill.tools,
})) as any;
if (!_output) return res.status(500).send("失败");
await u.db("o_assets").where("id", assetsId).update({ prompt: _output });

View File

@ -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 {
'content': string;
'createTime': number;
@ -220,6 +250,8 @@ export interface o_videoConfig {
}
export interface DB {
"_o_project_old_20260326": _o_project_old_20260326;
"_o_storyboard_old_20260325": _o_storyboard_old_20260325;
"memories": memories;
"o_agentDeploy": o_agentDeploy;
"o_agentWorkData": o_agentWorkData;

106
src/utils/stripThink.ts Normal file
View 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;
}