2026-05-30 23:26:24 +08:00
|
|
|
|
// HLDP-ZY://agents/zhuyuan-dev-agent/modules/plan-expander.js
|
|
|
|
|
|
// @guardian: ICE-GL-ZY001 · 铸渊
|
|
|
|
|
|
// @sovereign: TCS-0002∞ · 冰朔
|
|
|
|
|
|
// @copyright: 国作登字-2026-A-00037559
|
|
|
|
|
|
// 铸渊开发Agent · 任务补全引擎 v2.0
|
2026-05-30 15:42:13 +08:00
|
|
|
|
// 高层面规划 → LLM补全 → 详细可执行步骤
|
2026-05-30 23:26:24 +08:00
|
|
|
|
// v2.0: 修复LLM提示词缺少target/content → 增加光湖术语上下文 → 退化继承机制
|
2026-05-30 15:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
const fs = require("fs");
|
|
|
|
|
|
const path = require("path");
|
|
|
|
|
|
|
|
|
|
|
|
async function expandTask(task, modelCaller) {
|
2026-05-30 23:26:24 +08:00
|
|
|
|
const taskInfo = [
|
|
|
|
|
|
"【原任务】",
|
|
|
|
|
|
" 任务ID: " + task.id,
|
|
|
|
|
|
" 动作: " + (task.action || "未知"),
|
|
|
|
|
|
" 目标路径/命令: " + (task.target || "未指定"),
|
|
|
|
|
|
" 写入内容: " + (task.content || "未指定"),
|
2026-05-30 15:42:13 +08:00
|
|
|
|
" 描述: " + (task.description || "无"),
|
|
|
|
|
|
" 上下文: " + (task.context || "无"),
|
|
|
|
|
|
"",
|
2026-05-30 23:26:24 +08:00
|
|
|
|
"【光湖术语解释】",
|
|
|
|
|
|
" 铸渊 = AI开发Agent,运行在Linux服务器上,可执行Shell命令和写文件",
|
|
|
|
|
|
" ice-core = 冰核域,冰朔主权的核心域",
|
|
|
|
|
|
" BS-SG-002 = 新加坡服务器,代码face节点",
|
|
|
|
|
|
" HLDP = 光湖开发协议,所有模块代码必须包含@guardian @sovereign标签",
|
|
|
|
|
|
" 光湖语言世界 = AI原生操作系统,铸渊是其开发Agent",
|
|
|
|
|
|
"",
|
|
|
|
|
|
"【规则 · 必须遵守】",
|
|
|
|
|
|
"1. substep的target字段必须等于原任务的target字段(完整路径/完整命令),不得修改、不得用占位符替代",
|
|
|
|
|
|
"2. substep的action字段必须等于原任务的action字段",
|
|
|
|
|
|
"3. 如果原任务为 write_file 且提供了content,substep只需用相同的content",
|
|
|
|
|
|
"4. 如果原任务为 run_cmd,substep的target必须是具体的Shell命令(如 'ls -la /opt'),不能是占位符如'待指定'或'无'",
|
|
|
|
|
|
"5. 如果原任务信息已经足够具体,只返回一个substep,内容与原任务完全一致",
|
|
|
|
|
|
"6. 返回纯JSON数组,不要任何解释文字",
|
|
|
|
|
|
"",
|
|
|
|
|
|
'格式: [{"step":1,"action":"write_file|run_cmd|call_api|check","target":"文件路径或命令","detail":"具体做什么","content":"写入内容","depends_on":null}]',
|
|
|
|
|
|
"",
|
|
|
|
|
|
"现在请补全以上原任务的详细步骤:"
|
2026-05-30 15:42:13 +08:00
|
|
|
|
].join("\n");
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2026-05-30 23:26:24 +08:00
|
|
|
|
const raw = await modelCaller(taskInfo);
|
2026-05-30 15:42:13 +08:00
|
|
|
|
const jsonMatch = raw.match(/\[[\s\S]*\]/);
|
|
|
|
|
|
if (jsonMatch) {
|
2026-05-30 23:26:24 +08:00
|
|
|
|
let substeps = JSON.parse(jsonMatch[0]);
|
|
|
|
|
|
substeps = substeps.map(ss => {
|
|
|
|
|
|
if (!ss.target || ss.target === "待指定文件路径" || ss.target === "待指定" || ss.target === "无" || ss.target === "未指定") {
|
|
|
|
|
|
console.warn("[Expander] LLM返回模糊target='" + ss.target + "', 回退到原任务target='" + task.target + "'");
|
|
|
|
|
|
ss.target = task.target || "";
|
|
|
|
|
|
}
|
|
|
|
|
|
if ((!ss.content || ss.content === "未指定") && task.content) {
|
|
|
|
|
|
ss.content = task.content;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!ss.detail || ss.detail.includes("需要补充") || ss.detail.includes("待指定")) {
|
|
|
|
|
|
ss.detail = task.description || ("执行: " + ss.action + " -> " + (ss.target || "?"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return ss;
|
|
|
|
|
|
});
|
2026-05-30 15:42:13 +08:00
|
|
|
|
return { ...task, substeps, expanded: true, expanded_at: new Date().toISOString() };
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn("[Expander] LLM补全失败,退化为单步模式: " + e.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
...task,
|
2026-05-30 23:26:24 +08:00
|
|
|
|
substeps: [{
|
|
|
|
|
|
step: 1,
|
|
|
|
|
|
action: task.action || "unknown",
|
|
|
|
|
|
target: task.target || "",
|
|
|
|
|
|
content: task.content || "",
|
|
|
|
|
|
detail: task.description || ("执行: " + (task.action || "?") + " -> " + (task.target || "?")),
|
|
|
|
|
|
depends_on: null
|
|
|
|
|
|
}],
|
2026-05-30 15:42:13 +08:00
|
|
|
|
expanded: false,
|
|
|
|
|
|
expanded_at: new Date().toISOString()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function expandPlan(plan, modelCaller, plansDir) {
|
2026-05-30 23:26:24 +08:00
|
|
|
|
console.log("[Expander v2] 开始补全规划: " + plan.plan_id + " · " + (plan.tasks?.length || 0) + "个任务");
|
2026-05-30 15:42:13 +08:00
|
|
|
|
const expandedTasks = [];
|
2026-05-30 23:26:24 +08:00
|
|
|
|
for (let i = 0; i < (plan.tasks || []).length; i++) {
|
2026-05-30 15:42:13 +08:00
|
|
|
|
const task = plan.tasks[i];
|
2026-05-30 23:26:24 +08:00
|
|
|
|
console.log("[Expander v2] 补全 " + (i + 1) + "/" + plan.tasks.length + ": " + task.id);
|
2026-05-30 15:42:13 +08:00
|
|
|
|
const expanded = await expandTask(task, modelCaller);
|
|
|
|
|
|
expandedTasks.push(expanded);
|
|
|
|
|
|
}
|
|
|
|
|
|
plan.tasks = expandedTasks;
|
|
|
|
|
|
plan._status = "expanded";
|
|
|
|
|
|
plan._expanded_at = new Date().toISOString();
|
|
|
|
|
|
const planFile = path.join(plansDir, plan.plan_id + ".json");
|
|
|
|
|
|
fs.writeFileSync(planFile, JSON.stringify(plan, null, 2), "utf-8");
|
2026-05-30 23:26:24 +08:00
|
|
|
|
const totalSubsteps = expandedTasks.reduce((s, t) => s + (t.substeps?.length || 0), 0);
|
|
|
|
|
|
console.log("[Expander v2] 补全完成 · 共 " + totalSubsteps + " 个子步骤");
|
2026-05-30 15:42:13 +08:00
|
|
|
|
return plan;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = { expandPlan, expandTask };
|