新增 去除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的任务简约描述"),
|
||||
}),
|
||||
execute: async ({ agent, prompt }) => {
|
||||
//todo 传入md有问题
|
||||
const fn = [executionAI, supervisionAI][subAgentList.indexOf(agent)];
|
||||
//运行子Agent
|
||||
const subTextStream = await fn({ ...parentCtx, text: prompt });
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
},
|
||||
|
||||
@ -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 });
|
||||
|
||||
|
||||
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 {
|
||||
'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
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