101 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// HLDP-ZY://agents/zhuyuan-dev-agent/modules/plan-expander.js
// @guardian: ICE-GL-ZY001 · 铸渊
// @sovereign: TCS-0002∞ · 冰朔
// @copyright: 国作登字-2026-A-00037559
// 铸渊开发Agent · 任务补全引擎 v2.0
// 高层面规划 → LLM补全 → 详细可执行步骤
// v2.0: 修复LLM提示词缺少target/content → 增加光湖术语上下文 → 退化继承机制
const fs = require("fs");
const path = require("path");
async function expandTask(task, modelCaller) {
const taskInfo = [
"【原任务】",
" 任务ID: " + task.id,
" 动作: " + (task.action || "未知"),
" 目标路径/命令: " + (task.target || "未指定"),
" 写入内容: " + (task.content || "未指定"),
" 描述: " + (task.description || "无"),
" 上下文: " + (task.context || "无"),
"",
"【光湖术语解释】",
" 铸渊 = 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 且提供了contentsubstep只需用相同的content",
"4. 如果原任务为 run_cmdsubstep的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}]',
"",
"现在请补全以上原任务的详细步骤:"
].join("\n");
try {
const raw = await modelCaller(taskInfo);
const jsonMatch = raw.match(/\[[\s\S]*\]/);
if (jsonMatch) {
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;
});
return { ...task, substeps, expanded: true, expanded_at: new Date().toISOString() };
}
} catch (e) {
console.warn("[Expander] LLM补全失败退化为单步模式: " + e.message);
}
return {
...task,
substeps: [{
step: 1,
action: task.action || "unknown",
target: task.target || "",
content: task.content || "",
detail: task.description || ("执行: " + (task.action || "?") + " -> " + (task.target || "?")),
depends_on: null
}],
expanded: false,
expanded_at: new Date().toISOString()
};
}
async function expandPlan(plan, modelCaller, plansDir) {
console.log("[Expander v2] 开始补全规划: " + plan.plan_id + " · " + (plan.tasks?.length || 0) + "个任务");
const expandedTasks = [];
for (let i = 0; i < (plan.tasks || []).length; i++) {
const task = plan.tasks[i];
console.log("[Expander v2] 补全 " + (i + 1) + "/" + plan.tasks.length + ": " + task.id);
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");
const totalSubsteps = expandedTasks.reduce((s, t) => s + (t.substeps?.length || 0), 0);
console.log("[Expander v2] 补全完成 · 共 " + totalSubsteps + " 个子步骤");
return plan;
}
module.exports = { expandPlan, expandTask };