fix: Codex 自动压缩不触发 —— 接管时链接模型目录(绝对路径)进 config.toml (hot 1.5.83)
This commit is contained in:
+75
-18
@@ -475,12 +475,18 @@ function reconcileTeam(team) {
|
||||
return team;
|
||||
}
|
||||
|
||||
function normalizeTeam(team, model, requirement) {
|
||||
function normalizeTeam(team, modelOrOptions, requirement) {
|
||||
const options = modelOrOptions && typeof modelOrOptions === 'object'
|
||||
? modelOrOptions
|
||||
: { analysisModel: modelOrOptions, orchestratorModel: modelOrOptions, agentModel: modelOrOptions };
|
||||
const analysisModel = String(options.analysisModel || '').trim();
|
||||
const orchestratorModel = String(options.orchestratorModel || analysisModel).trim();
|
||||
const agentModel = String(options.agentModel || analysisModel).trim();
|
||||
team.stack = asArr(team.stack);
|
||||
team.employees = asArr(team.employees);
|
||||
|
||||
team.employees.forEach((e) => {
|
||||
e.model = model; // 员工统一用运行模型,避免模型名不一致
|
||||
e.model = agentModel; // 执行角色统一使用成本模型;总控单独使用 orchestratorModel
|
||||
e.constraints = ensureConstraints(e);
|
||||
e.responsibilities = asArr(e.responsibilities);
|
||||
e.handoff_in = e.handoff_in || '';
|
||||
@@ -488,12 +494,20 @@ function normalizeTeam(team, model, requirement) {
|
||||
});
|
||||
|
||||
if (team.orchestrator) {
|
||||
team.orchestrator.model = model;
|
||||
team.orchestrator.model = orchestratorModel;
|
||||
team.orchestrator.constraints = ensureConstraints(team.orchestrator);
|
||||
team.orchestrator.responsibilities = asArr(team.orchestrator.responsibilities);
|
||||
team.orchestrator.workflow = ORCHESTRATOR_WORKFLOW; // 注入调度状态机
|
||||
}
|
||||
|
||||
team.model_routing = {
|
||||
generation_model: analysisModel,
|
||||
orchestrator_model: orchestratorModel,
|
||||
worker_model: agentModel,
|
||||
analysis_model: analysisModel,
|
||||
capability_policy: '需要识图、截图对比、视频理解、音频语义分析、复杂架构或最终审计的任务由总控处理;执行角色只接收结构化证据和明确验收标准',
|
||||
};
|
||||
|
||||
// 结构性归一(id / 路径互斥 / 引用收敛 / 总控只读)——与 /api/pack 共用同一套
|
||||
reconcileTeam(team);
|
||||
|
||||
@@ -550,26 +564,28 @@ function sharedProtocol(team, isLead) {
|
||||
(asArr(team.git_rules).length ? team.git_rules : TEAM_CONVENTIONS.git_rules).forEach((x) => L.push(`- ${x}`));
|
||||
L.push('\n### 四、任务交接协议');
|
||||
TEAM_CONVENTIONS.handoff_protocol.forEach((x) => L.push(`- ${x}`));
|
||||
L.push(`\n### 五、接口契约`);
|
||||
L.push('\n### 五、模型与能力路由');
|
||||
TEAM_CONVENTIONS.model_routing.forEach((x) => L.push(`- ${x}`));
|
||||
L.push(`\n### 六、接口契约`);
|
||||
L.push(`- 契约 owner:${ownerName(team)},只有 owner 能定稿 / 变更契约。`);
|
||||
L.push(`- 契约约定:${team.contract_spec}`);
|
||||
L.push(`- 其他角色需遵循契约,有异议先与 owner 或总控确认,不擅自改动。`);
|
||||
if (team.security_checklist.length) {
|
||||
L.push('\n### 六、安全 / 权限审核清单(相关改动必须自检)');
|
||||
L.push('\n### 七、安全 / 权限审核清单(相关改动必须自检)');
|
||||
team.security_checklist.forEach((x) => L.push(`- ${x}`));
|
||||
}
|
||||
if (isLead) {
|
||||
L.push('\n### 七、调度流程(总控按此状态机推进)');
|
||||
L.push('\n### 八、调度流程(总控按此状态机推进)');
|
||||
((team.orchestrator && team.orchestrator.workflow) || ORCHESTRATOR_WORKFLOW).forEach((x, i) => L.push(`${i + 1}. ${x}`));
|
||||
L.push('\n遇到跨角色冲突、职责归属不清、契约争议,由你(总控)裁决并驱动返工闭环。你是只读角色,不写任何文件。');
|
||||
L.push('\n### 八、范围纪律(防跑飞——总控是第一道闸)');
|
||||
L.push('\n### 九、范围纪律(防跑飞——总控是第一道闸)');
|
||||
L.push('- 分派任何任务前,先产出该任务的《任务契约》(套 templates/task-charter.md),其中「明确不做 / out of scope」至少写 2 条:根据**本次客户需求**推导「这个任务明确不该碰的东西」。写不出 out of scope,说明任务还没想清楚,不许分派。');
|
||||
L.push('- 审核与打回一律对照该任务的《任务契约》:产出必须能在「目标」里找到依据、且未命中「out of scope」。打回时理由必须引用《任务契约》或 DoD 的**具体条款**,并写入 docs/coordination/task-ledger.md,不凭感觉打回。');
|
||||
L.push('- 同一任务打回 ≥ 2 次时,不再简单退回,改为重新澄清《任务契约》(判断是不是任务边界本身没定义清楚),避免无限返工。');
|
||||
L.push('- 任务状态在 docs/coordination/task-ledger.md 流转(assigned → in-progress → review → rework → accepted),仅当状态为 accepted 才算关闭。');
|
||||
} else {
|
||||
L.push('\n> 遇到跨角色冲突或职责归属不清,提交总控 Agent 裁决;跨职责修改务必走报告 + 复核(在完成报告中说明并请对应 owner 复核)。');
|
||||
L.push('\n### 七、范围纪律(防跑飞——动工前必读)');
|
||||
L.push('\n### 八、范围纪律(防跑飞——动工前必读)');
|
||||
L.push('- 动工前先读本任务的《任务契约》(templates/task-charter.md 的本任务实例)。只做「目标」授权的事。');
|
||||
L.push('- 判定跑飞的通用标准:你即将做的事若无法在《任务契约》的「目标」里找到依据,或命中了「明确不做 / out of scope」,即为跑飞——**立即停下**,把情况写进 docs/coordination/task-ledger.md 并交总控裁决,不要自行扩大范围。');
|
||||
L.push('- 「顺手」优化 / 重构 / 补功能若不在本任务目标内,一律不做,先报总控。连续两次都要越界才能完成,说明任务定义有问题,同样交总控。');
|
||||
@@ -607,7 +623,10 @@ function toToml(agent, role, protocol) {
|
||||
lines.push(`responsibilities = ${arrToml(agent.responsibilities)}`);
|
||||
lines.push(`constraints = ${arrToml(agent.constraints)}`);
|
||||
lines.push('');
|
||||
lines.push(`prompt = ${multiline((agent.prompt || '') + protocol)}`);
|
||||
const routeHint = agent.model
|
||||
? `\n\n[TD_MODEL_ROUTE model="${agent.model}" role="${role}" agent="${agent.id || role}"; runtime hint only, ignore in task output.]`
|
||||
: '';
|
||||
lines.push(`prompt = ${multiline((agent.prompt || '') + protocol + routeHint)}`);
|
||||
return lines.join('\n') + '\n';
|
||||
}
|
||||
|
||||
@@ -679,6 +698,12 @@ function buildConventions(team) {
|
||||
team.git_rules.forEach((x) => L.push(`- ${x}`));
|
||||
L.push('\n## 任务交接协议');
|
||||
team.handoff_protocol.forEach((x) => L.push(`- ${x}`));
|
||||
L.push('\n## 模型与能力路由');
|
||||
L.push(`- 团队生成模型(仅生成配置):${(team.model_routing && (team.model_routing.generation_model || team.model_routing.analysis_model)) || '未指定'}`);
|
||||
L.push(`- 总控模型:${(team.orchestrator && team.orchestrator.model) || '未指定'}`);
|
||||
const workerModels = Array.from(new Set(team.employees.map((e) => e.model).filter(Boolean)));
|
||||
L.push(`- 执行模型:${workerModels.join('、') || '未指定'}`);
|
||||
TEAM_CONVENTIONS.model_routing.forEach((x) => L.push(`- ${x}`));
|
||||
if (team.auth_scheme && team.auth_scheme !== 'none') L.push(`\n## 鉴权方案(全局唯一)\n- ${team.auth_scheme}`);
|
||||
if (team.package_manager && team.package_manager !== 'none') L.push(`\n## 包管理器(全局唯一)\n- ${team.package_manager}(锁文件、CI 命令、.gitignore 均以此为准)`);
|
||||
L.push('\n## 接口契约');
|
||||
@@ -807,7 +832,11 @@ function buildReadme(team, tool) {
|
||||
if (isClaude) {
|
||||
L.push('Claude Code 的 `model` 字段只认 claude 系(`sonnet` / `opus` / `haiku` / `claude-*`)。生成时若未指定 claude 系模型,agent 文件已**省略 model 字段 = 自动继承会话模型**(推荐)。需要固定某档时在 frontmatter 加一行如 `model: sonnet`。');
|
||||
} else {
|
||||
L.push(`各文件里的 \`model = "${team.employees[0] ? team.employees[0].model : ''}"\` 请改成你本地 Codex 运行器实际支持的模型名,否则可能调用失败。`);
|
||||
const generationModel = (team.model_routing && (team.model_routing.generation_model || team.model_routing.analysis_model)) || '';
|
||||
const leadModel = team.orchestrator && team.orchestrator.model ? team.orchestrator.model : '';
|
||||
const workerModel = team.employees[0] && team.employees[0].model ? team.employees[0].model : '';
|
||||
L.push(`本次团队配置由 \`${generationModel || '未指定'}\` 生成;生成后总控文件使用 \`model = "${leadModel}"\`,执行角色使用 \`model = "${workerModel}"\`。请确保运行模型已在本地路由分组中配置;本包会按模型名自动选择对应分组。`);
|
||||
L.push('涉及识图、截图对比、视频理解、音频语义分析或最终视觉验收时,必须由总控处理,再把结构化证据交给执行角色。');
|
||||
}
|
||||
L.push('\n> 定位:这是一份**多 Agent 协作配置包**(规范 + 角色 + 模板 + 工具化约束),不是可直接运行的项目骨架。`package.json` 等由 **devops-engineer 作为第一个 bootstrap 任务**初始化后,CI 才会真正跑起来(未初始化时 CI 不会硬失败)。');
|
||||
L.push('\n## ⚠️ 解压须知(先看这个)');
|
||||
@@ -1442,6 +1471,13 @@ function packDefaults(result) {
|
||||
result.auth_scheme = result.auth_scheme || '';
|
||||
result.package_manager = result.package_manager || '';
|
||||
if (result.orchestrator) result.orchestrator.workflow = fill(result.orchestrator.workflow, ORCHESTRATOR_WORKFLOW);
|
||||
const workerModel = result.employees.find((e) => e && e.model) || {};
|
||||
result.model_routing = {
|
||||
...(result.model_routing || {}),
|
||||
generation_model: (result.model_routing && (result.model_routing.generation_model || result.model_routing.analysis_model)) || '',
|
||||
orchestrator_model: (result.orchestrator && result.orchestrator.model) || '',
|
||||
worker_model: workerModel.model || '',
|
||||
};
|
||||
// pack 复用完整结构性归一(id 清洗/路径互斥/引用收敛/总控只读,含 orchestrator.id 对齐;不覆写模型)+ 口径强制
|
||||
reconcileTeam(result);
|
||||
enforceConsistency(result);
|
||||
@@ -1473,21 +1509,36 @@ function resolveGroup(groupId) {
|
||||
}
|
||||
|
||||
// 生成员工团队:需求 → 结构化团队
|
||||
async function generate({ requirement, tool, groupId, model, agentModel } = {}) {
|
||||
async function generate({ requirement, tool, groupId, orchestratorGroupId, agentGroupId, model, agentModel, orchestratorModel } = {}) {
|
||||
if (!requirement || !String(requirement).trim()) throw new Error("请先输入项目需求");
|
||||
const g = resolveGroup(groupId);
|
||||
const useModel = (model && String(model).trim()) || g.model;
|
||||
if (tool !== "claude" && (!String(orchestratorModel || "").trim() || !String(agentModel || "").trim())) {
|
||||
throw new Error("Codex 团队必须分别选择总控执行模型和子 Agent 执行模型;团队生成模型只用于生成配置");
|
||||
}
|
||||
const analysisGroup = resolveGroup(groupId);
|
||||
const orchestratorGroup = resolveGroup(orchestratorGroupId || groupId);
|
||||
const workerGroup = resolveGroup(agentGroupId || groupId);
|
||||
const useModel = (model && String(model).trim()) || analysisGroup.model;
|
||||
if (!useModel) throw new Error("未选择模型");
|
||||
const gwBase = chatBase(g.baseUrl);
|
||||
const gwBase = chatBase(analysisGroup.baseUrl);
|
||||
|
||||
const content = await callChat(gwBase, g.key, useModel, [
|
||||
const content = await callChat(gwBase, analysisGroup.key, useModel, [
|
||||
{ role: "system", content: EMPLOYEE_SYSTEM_PROMPT },
|
||||
{ role: "user", content: buildEmployeeInstruction(requirement, tool) },
|
||||
], 5000);
|
||||
|
||||
// 员工运行模型:用户可指定,否则用分析模型
|
||||
const runModel = (agentModel && String(agentModel).trim()) || useModel;
|
||||
const result = normalizeTeam(extractJson(content), runModel, requirement);
|
||||
// 生成模型只负责本次生成;总控/执行模型是生成后的运行时配置,三者彼此独立。
|
||||
const runModel = (agentModel && String(agentModel).trim()) || workerGroup.model || useModel;
|
||||
const leadModel = (orchestratorModel && String(orchestratorModel).trim()) || orchestratorGroup.model || useModel;
|
||||
if (!runModel) throw new Error("未选择子 Agent 模型");
|
||||
if (typeof router.registerModelRoute === "function") {
|
||||
router.registerModelRoute(orchestratorGroupId || groupId || orchestratorGroup.id, leadModel);
|
||||
router.registerModelRoute(agentGroupId || groupId || workerGroup.id, runModel);
|
||||
}
|
||||
const result = normalizeTeam(extractJson(content), {
|
||||
analysisModel: useModel,
|
||||
orchestratorModel: leadModel,
|
||||
agentModel: runModel,
|
||||
}, requirement);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1566,6 +1617,12 @@ function packFiles(result, tool) {
|
||||
package_manager: result.package_manager || "",
|
||||
auth_scheme: result.auth_scheme || "",
|
||||
interface_kind: result.interface_kind || "",
|
||||
model_routing: {
|
||||
generation: (result.model_routing && (result.model_routing.generation_model || result.model_routing.analysis_model)) || "",
|
||||
orchestrator: (result.model_routing && result.model_routing.orchestrator_model) || (result.orchestrator && result.orchestrator.model) || "",
|
||||
workers: Array.from(new Set(result.employees.map((e) => e && e.model).filter(Boolean))),
|
||||
capability_policy: (result.model_routing && result.model_routing.capability_policy) || "",
|
||||
},
|
||||
required_env: asArr(result.env_vars).map((v) => v && v.name).filter(Boolean),
|
||||
roles: result.employees.map((e) => e.id).concat(["orchestrator"]),
|
||||
files: [],
|
||||
|
||||
Reference in New Issue
Block a user