agency-orchestrator 0.6.8 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/dist/cli/compose.d.ts +16 -0
- package/dist/cli/compose.js +239 -72
- package/dist/cli.js +24 -4
- package/dist/index.js +4 -1
- package/dist/mcp/server.js +4 -2
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -345,7 +345,7 @@ Cursor (`.cursor/mcp.json`):
|
|
|
345
345
|
| `llm.provider` | string | Yes | `claude-code` / `gemini-cli` / `copilot-cli` / `codex-cli` / `openclaw-cli` / `hermes-cli` / `ollama` / `claude` / `deepseek` / `openai` |
|
|
346
346
|
| `llm.model` | string | Yes | Model name |
|
|
347
347
|
| `llm.max_tokens` | number | No | Default 4096 |
|
|
348
|
-
| `llm.timeout` | number | No | Step timeout in ms (default 120000) |
|
|
348
|
+
| `llm.timeout` | number | No | Step timeout in ms (default API 120000 / CLI/ollama 600000). Automatically extends x1.5 on timeout retry up to 3600000. `0` means no timeout |
|
|
349
349
|
| `llm.retry` | number | No | Retry count (default 3) |
|
|
350
350
|
| `concurrency` | number | No | Max parallel steps (default 2) |
|
|
351
351
|
| `inputs` | array | No | Input variable definitions |
|
package/README.md
CHANGED
|
@@ -366,7 +366,7 @@ ao serve --verbose # 带调试日志
|
|
|
366
366
|
| `llm.provider` | string | 是 | `claude-code` / `gemini-cli` / `copilot-cli` / `codex-cli` / `openclaw-cli` / `hermes-cli` / `ollama` / `claude` / `deepseek` / `openai` |
|
|
367
367
|
| `llm.model` | string | 是 | 模型名称 |
|
|
368
368
|
| `llm.max_tokens` | number | 否 | 默认 4096 |
|
|
369
|
-
| `llm.timeout` | number | 否 | 步骤超时毫秒数(默认 120000
|
|
369
|
+
| `llm.timeout` | number | 否 | 步骤超时毫秒数(默认 API 120000 / CLI/ollama 600000)。因超时重试时自动 x1.5 递增,上限 3600000。`0` 表示不限时 |
|
|
370
370
|
| `llm.retry` | number | 否 | 重试次数(默认 3) |
|
|
371
371
|
| `concurrency` | number | 否 | 最大并行步骤数(默认 2) |
|
|
372
372
|
| `inputs` | array | 否 | 输入变量定义 |
|
package/dist/cli/compose.d.ts
CHANGED
|
@@ -64,3 +64,19 @@ export declare function composeWorkflow(options: {
|
|
|
64
64
|
relativePath: string;
|
|
65
65
|
warnings: string[];
|
|
66
66
|
}>;
|
|
67
|
+
/**
|
|
68
|
+
* 自动修复 compose 生成 YAML 中的变量引用错误。
|
|
69
|
+
*
|
|
70
|
+
* 核心约束:替换目标必须在当前 step 的 DAG 上游(递归 depends_on 闭包)内的 step.output 集合里。
|
|
71
|
+
* 这避免了"早期 step 引用下游 step output"的拓扑错误(旧版策略 2 模糊匹配会把
|
|
72
|
+
* personal_assessment 错误地指向 final_report 这种最终汇总 output)。
|
|
73
|
+
*
|
|
74
|
+
* 如果某 step 没有上游或上游 output 都对不上,bad var 留给 LLM 二次修复(repairWithLLM)。
|
|
75
|
+
*/
|
|
76
|
+
export declare function autoFixVariableRefs(yamlPath: string): Promise<{
|
|
77
|
+
fixed: number;
|
|
78
|
+
details: {
|
|
79
|
+
from: string;
|
|
80
|
+
to: string;
|
|
81
|
+
}[];
|
|
82
|
+
}>;
|
package/dist/cli/compose.js
CHANGED
|
@@ -149,6 +149,8 @@ ${catalog}
|
|
|
149
149
|
|
|
150
150
|
- The role value must strictly use paths from the role catalog (e.g., "engineering/engineering-code-reviewer") — do NOT make up role paths
|
|
151
151
|
- **Variable names must use underscores**, no spaces. Correct: "market_analysis", "tech_report". Wrong: "market analysis", "tech report". All id, output, and depends_on values must be snake_case
|
|
152
|
+
- **Variables must have a source**: every \`{{X}}\` referenced in a step's task MUST appear either as an \`inputs\` name OR as the \`output\` field of an earlier step. Do NOT invent variable names that no step produces
|
|
153
|
+
- **Merge / aggregation steps**: if a step references \`{{a}}\`, \`{{b}}\`, \`{{c}}\` from upstream, its \`depends_on\` MUST list every upstream step that produces those outputs. Cross-check before emitting
|
|
152
154
|
- Only output the YAML code block, nothing else
|
|
153
155
|
- Set concurrency to the maximum number of parallel steps
|
|
154
156
|
- **Important: Split large tasks**. When writing long articles, don't let one step generate more than 800 words. Split by sections into multiple parallel steps (e.g., write_ch1, write_ch2, write_ch3), then use a merge step to rewrite into a coherent complete article
|
|
@@ -241,6 +243,8 @@ ${catalog}
|
|
|
241
243
|
|
|
242
244
|
- role 的值必须严格使用角色目录中的 path(如 "engineering/engineering-code-reviewer"),不要自己编造
|
|
243
245
|
- **变量名必须用下划线**,不能有空格。正确:"market_analysis"、"tech_report"。错误:"market analysis"、"tech report"。id、output、depends_on 中的值都必须用 snake_case
|
|
246
|
+
- **变量必须有来源**:每个 task 中的 \`{{X}}\` 引用,X 必须是 \`inputs\` 中的某个 name,或者是前面某个 step 的 \`output\` 字段。不要凭空写一个没有任何 step 产生的变量名
|
|
247
|
+
- **合并/汇总类步骤**:如果一个步骤的 task 里引用了 \`{{a}}\`、\`{{b}}\`、\`{{c}}\` 这些上游变量,它的 \`depends_on\` 必须列出所有产生这些 output 的上游 step。生成完后请逐一核对一遍
|
|
244
248
|
- 只输出 YAML 代码块,不要输出其他内容
|
|
245
249
|
- concurrency 设为并行步骤的最大数量
|
|
246
250
|
- **重要:拆分大任务**。写长文章时,不要让一个步骤生成超过 800 字的内容。应该按章节拆分成多个并行步骤(如 write_ch1、write_ch2、write_ch3),最后用一个合并步骤重写为连贯的完整文章
|
|
@@ -386,52 +390,77 @@ export async function composeWorkflow(options) {
|
|
|
386
390
|
if (retryYaml && retryYaml.includes('steps:')) {
|
|
387
391
|
writeFileSync(savedPath, retryYaml + '\n', 'utf-8');
|
|
388
392
|
const second = await validateGenerated(savedPath);
|
|
389
|
-
// 重试后仍有变量引用错误 →
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
console.log(` 自动修复了 ${fixResult.fixed} 个变量引用:`);
|
|
395
|
-
for (const f of fixResult.details)
|
|
396
|
-
console.log(` {{${f.from}}} → {{${f.to}}}`);
|
|
397
|
-
const afterFix = await validateGenerated(savedPath);
|
|
398
|
-
warnings.push(...afterFix.errors);
|
|
399
|
-
const fixedYaml = readFileSync(savedPath, 'utf-8').trim();
|
|
400
|
-
return { yaml: fixedYaml, savedPath, relativePath, warnings };
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
warnings.push(...second.errors);
|
|
404
|
-
return { yaml: retryYaml, savedPath, relativePath, warnings };
|
|
393
|
+
// 重试后仍有变量引用错误 → 走 fix 链(autoFix → LLM 二次修复)
|
|
394
|
+
const finalErrors = await runVariableFixChain(savedPath, second.errors, validateGenerated, llmConfig, lang);
|
|
395
|
+
warnings.push(...finalErrors);
|
|
396
|
+
const fixedYaml = readFileSync(savedPath, 'utf-8').trim();
|
|
397
|
+
return { yaml: fixedYaml, savedPath, relativePath, warnings };
|
|
405
398
|
}
|
|
406
399
|
}
|
|
407
400
|
catch (err) {
|
|
408
401
|
warnings.push(`自动修正失败(保留原始输出): ${err instanceof Error ? err.message : err}`);
|
|
409
402
|
}
|
|
410
403
|
}
|
|
411
|
-
//
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
404
|
+
// 首次生成无幻觉角色 → 直接走变量 fix 链
|
|
405
|
+
const finalErrors = await runVariableFixChain(savedPath, first.errors, validateGenerated, llmConfig, lang);
|
|
406
|
+
warnings.push(...finalErrors);
|
|
407
|
+
const finalYaml = readFileSync(savedPath, 'utf-8').trim();
|
|
408
|
+
return { yaml: finalYaml, savedPath, relativePath, warnings };
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 变量引用错误的修复链:autoFix(启发式)→ LLM 二次修复 → 重新校验。
|
|
412
|
+
* 返回最终仍未解决的 errors。
|
|
413
|
+
*/
|
|
414
|
+
async function runVariableFixChain(savedPath, initialErrors, validateGenerated, llmConfig, lang) {
|
|
415
|
+
const hasVarError = (errs) => errs.some(e => e.includes('未定义的变量') || e.toLowerCase().includes('undefined variable'));
|
|
416
|
+
if (!hasVarError(initialErrors))
|
|
417
|
+
return initialErrors;
|
|
418
|
+
// 阶段 1: autoFix(启发式,只在 DAG 上游内替换)
|
|
419
|
+
const fixResult = await autoFixVariableRefs(savedPath);
|
|
420
|
+
if (fixResult.fixed > 0) {
|
|
421
|
+
console.log(` 自动修复了 ${fixResult.fixed} 个变量引用:`);
|
|
422
|
+
for (const f of fixResult.details)
|
|
423
|
+
console.log(` {{${f.from}}} → {{${f.to}}}`);
|
|
424
|
+
}
|
|
425
|
+
let current = await validateGenerated(savedPath);
|
|
426
|
+
if (!hasVarError(current.errors))
|
|
427
|
+
return current.errors;
|
|
428
|
+
// 阶段 2: LLM 二次修复(autoFix 修不动的让 LLM 决策)
|
|
429
|
+
const remainingVars = extractUndefinedVarNames(current.errors);
|
|
430
|
+
if (remainingVars.length === 0)
|
|
431
|
+
return current.errors;
|
|
432
|
+
console.log(` ${remainingVars.length} 个变量启发式修不动,调 LLM 二次修复...`);
|
|
433
|
+
const repair = await repairWithLLM(savedPath, remainingVars, llmConfig, lang);
|
|
434
|
+
if (repair.replaced) {
|
|
435
|
+
current = await validateGenerated(savedPath);
|
|
436
|
+
if (!hasVarError(current.errors)) {
|
|
437
|
+
console.log(' LLM 修复成功');
|
|
438
|
+
return current.errors;
|
|
425
439
|
}
|
|
440
|
+
console.log(` LLM 修复后仍有 ${remainingVars.length} 个变量未解决,需人工检查`);
|
|
441
|
+
}
|
|
442
|
+
return current.errors;
|
|
443
|
+
}
|
|
444
|
+
/** 从 "step \"X\" 引用了未定义的变量: {{Y}}" 这类消息里提取变量名 Y */
|
|
445
|
+
function extractUndefinedVarNames(errors) {
|
|
446
|
+
const names = new Set();
|
|
447
|
+
for (const e of errors) {
|
|
448
|
+
const m = e.match(/\{\{(\w+)\}\}/);
|
|
449
|
+
if (m)
|
|
450
|
+
names.add(m[1]);
|
|
426
451
|
}
|
|
427
|
-
|
|
428
|
-
return { yaml, savedPath, relativePath, warnings };
|
|
452
|
+
return [...names];
|
|
429
453
|
}
|
|
430
454
|
/**
|
|
431
455
|
* 自动修复 compose 生成 YAML 中的变量引用错误。
|
|
432
|
-
*
|
|
456
|
+
*
|
|
457
|
+
* 核心约束:替换目标必须在当前 step 的 DAG 上游(递归 depends_on 闭包)内的 step.output 集合里。
|
|
458
|
+
* 这避免了"早期 step 引用下游 step output"的拓扑错误(旧版策略 2 模糊匹配会把
|
|
459
|
+
* personal_assessment 错误地指向 final_report 这种最终汇总 output)。
|
|
460
|
+
*
|
|
461
|
+
* 如果某 step 没有上游或上游 output 都对不上,bad var 留给 LLM 二次修复(repairWithLLM)。
|
|
433
462
|
*/
|
|
434
|
-
async function autoFixVariableRefs(yamlPath) {
|
|
463
|
+
export async function autoFixVariableRefs(yamlPath) {
|
|
435
464
|
const { parseWorkflow } = await import('../core/parser.js');
|
|
436
465
|
const content = readFileSync(yamlPath, 'utf-8');
|
|
437
466
|
let workflow;
|
|
@@ -442,59 +471,86 @@ async function autoFixVariableRefs(yamlPath) {
|
|
|
442
471
|
return { fixed: 0, details: [] };
|
|
443
472
|
}
|
|
444
473
|
const inputNames = new Set((workflow.inputs || []).map((i) => i.name));
|
|
445
|
-
const
|
|
446
|
-
const
|
|
474
|
+
const stepById = new Map();
|
|
475
|
+
const allOutputs = new Set();
|
|
447
476
|
for (const step of workflow.steps) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
477
|
+
stepById.set(step.id, step);
|
|
478
|
+
if (step.output)
|
|
479
|
+
allOutputs.add(step.output);
|
|
480
|
+
}
|
|
481
|
+
const allDefined = new Set([...inputNames, ...allOutputs, '_loop_iteration']);
|
|
482
|
+
// 计算 stepId 的 DAG 上游 step ids(递归 depends_on 闭包,不含自身)
|
|
483
|
+
function upstreamStepIds(stepId) {
|
|
484
|
+
const out = new Set();
|
|
485
|
+
const stack = [stepId];
|
|
486
|
+
while (stack.length > 0) {
|
|
487
|
+
const cur = stack.pop();
|
|
488
|
+
const step = stepById.get(cur);
|
|
489
|
+
if (!step)
|
|
490
|
+
continue;
|
|
491
|
+
for (const dep of step.depends_on || []) {
|
|
492
|
+
if (out.has(dep))
|
|
493
|
+
continue;
|
|
494
|
+
out.add(dep);
|
|
495
|
+
stack.push(dep);
|
|
496
|
+
}
|
|
451
497
|
}
|
|
498
|
+
return out;
|
|
452
499
|
}
|
|
453
|
-
const allDefined = new Set([...inputNames, ...outputNames, '_loop_iteration']);
|
|
454
500
|
const replacements = [];
|
|
455
501
|
let fixedContent = content;
|
|
456
|
-
//
|
|
457
|
-
const
|
|
502
|
+
// 同名 bad var 在多个 step 里只处理一次(避免重复全局 replace)
|
|
503
|
+
const globallyHandled = new Set();
|
|
458
504
|
for (const step of workflow.steps) {
|
|
459
505
|
const refs = step.task?.match(/\{\{(\w+)\}\}/g) || [];
|
|
506
|
+
const badVarsInStep = [];
|
|
460
507
|
for (const ref of refs) {
|
|
461
508
|
const varName = ref.slice(2, -2);
|
|
462
|
-
if (!allDefined.has(varName)) {
|
|
463
|
-
|
|
509
|
+
if (!allDefined.has(varName) && !globallyHandled.has(varName)) {
|
|
510
|
+
badVarsInStep.push(varName);
|
|
464
511
|
}
|
|
465
512
|
}
|
|
466
|
-
|
|
467
|
-
for (const badVar of undefinedVars) {
|
|
468
|
-
// 策略1:badVar 是某个 step id,该 step 有 output → 替换为 output
|
|
469
|
-
if (stepIdToOutput.has(badVar)) {
|
|
470
|
-
const goodVar = stepIdToOutput.get(badVar);
|
|
471
|
-
fixedContent = fixedContent.replace(new RegExp(`\\{\\{${badVar}\\}\\}`, 'g'), `{{${goodVar}}}`);
|
|
472
|
-
replacements.push({ from: badVar, to: goodVar });
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
// 策略2:模糊匹配 — 找子串包含关系最强的 output 变量
|
|
476
|
-
const best = findBestMatch(badVar, [...outputNames]);
|
|
477
|
-
if (best) {
|
|
478
|
-
fixedContent = fixedContent.replace(new RegExp(`\\{\\{${badVar}\\}\\}`, 'g'), `{{${best}}}`);
|
|
479
|
-
replacements.push({ from: badVar, to: best });
|
|
513
|
+
if (badVarsInStep.length === 0)
|
|
480
514
|
continue;
|
|
515
|
+
// 当前 step 的 DAG 上游 outputs(仅这些是合法替换目标)
|
|
516
|
+
const upStepIds = upstreamStepIds(step.id);
|
|
517
|
+
const upstreamOutputs = [];
|
|
518
|
+
for (const id of upStepIds) {
|
|
519
|
+
const s = stepById.get(id);
|
|
520
|
+
if (s?.output)
|
|
521
|
+
upstreamOutputs.push(s.output);
|
|
481
522
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
523
|
+
if (upstreamOutputs.length === 0)
|
|
524
|
+
continue; // 无上游可用 → 跳过,等 LLM 修
|
|
525
|
+
const usedInThisStep = new Set();
|
|
526
|
+
for (const badVar of badVarsInStep) {
|
|
527
|
+
let goodVar;
|
|
528
|
+
// 策略 1:badVar 等于某个上游 step.id(含传递闭包),用该 step.output
|
|
529
|
+
if (upStepIds.has(badVar)) {
|
|
530
|
+
const depStep = stepById.get(badVar);
|
|
531
|
+
if (depStep?.output)
|
|
532
|
+
goodVar = depStep.output;
|
|
533
|
+
}
|
|
534
|
+
// 策略 2:在上游 outputs 中模糊匹配(已被本 step 用过的优先级降低)
|
|
535
|
+
if (!goodVar) {
|
|
536
|
+
const candidates = upstreamOutputs.filter(o => !usedInThisStep.has(o));
|
|
537
|
+
const pool = candidates.length > 0 ? candidates : upstreamOutputs;
|
|
538
|
+
const match = findBestMatch(badVar, pool);
|
|
539
|
+
if (match)
|
|
540
|
+
goodVar = match;
|
|
541
|
+
}
|
|
542
|
+
// 策略 3:上游里第一个还没被本 step 占用的 output
|
|
543
|
+
if (!goodVar) {
|
|
544
|
+
goodVar = upstreamOutputs.find(o => !usedInThisStep.has(o));
|
|
545
|
+
}
|
|
546
|
+
if (goodVar && goodVar !== badVar) {
|
|
547
|
+
const re = new RegExp(`\\{\\{${escapeRegex(badVar)}\\}\\}`, 'g');
|
|
548
|
+
if (re.test(fixedContent)) {
|
|
549
|
+
fixedContent = fixedContent.replace(re, `{{${goodVar}}}`);
|
|
495
550
|
replacements.push({ from: badVar, to: goodVar });
|
|
551
|
+
usedInThisStep.add(goodVar);
|
|
552
|
+
globallyHandled.add(badVar);
|
|
496
553
|
}
|
|
497
|
-
break;
|
|
498
554
|
}
|
|
499
555
|
}
|
|
500
556
|
}
|
|
@@ -503,6 +559,9 @@ async function autoFixVariableRefs(yamlPath) {
|
|
|
503
559
|
}
|
|
504
560
|
return { fixed: replacements.length, details: replacements };
|
|
505
561
|
}
|
|
562
|
+
function escapeRegex(s) {
|
|
563
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
564
|
+
}
|
|
506
565
|
/**
|
|
507
566
|
* 模糊匹配:找子串包含、前缀/后缀重叠最多的候选
|
|
508
567
|
*/
|
|
@@ -542,3 +601,111 @@ function longestCommonSubstring(a, b) {
|
|
|
542
601
|
}
|
|
543
602
|
return max;
|
|
544
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* 当 autoFix 修不全时,调一次 LLM 让它修剩余的未定义变量。
|
|
606
|
+
* Prompt 给完整 YAML + 错误清单 + 可用 inputs/outputs,让 LLM 决定:
|
|
607
|
+
* - 改 task 里的 {{X}} 引用
|
|
608
|
+
* - 给某 step 加 output 字段
|
|
609
|
+
* - 补 merge step 的 depends_on
|
|
610
|
+
*/
|
|
611
|
+
async function repairWithLLM(yamlPath, undefinedVars, llmConfig, lang) {
|
|
612
|
+
const { parseWorkflow } = await import('../core/parser.js');
|
|
613
|
+
const currentYaml = readFileSync(yamlPath, 'utf-8');
|
|
614
|
+
let workflow;
|
|
615
|
+
try {
|
|
616
|
+
workflow = parseWorkflow(yamlPath);
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
return { ok: false, replaced: false };
|
|
620
|
+
}
|
|
621
|
+
const inputNames = (workflow.inputs || []).map((i) => i.name);
|
|
622
|
+
const outputNames = [];
|
|
623
|
+
const stepIds = [];
|
|
624
|
+
for (const step of workflow.steps) {
|
|
625
|
+
stepIds.push(step.id);
|
|
626
|
+
if (step.output)
|
|
627
|
+
outputNames.push(step.output);
|
|
628
|
+
}
|
|
629
|
+
const prompt = lang === 'en'
|
|
630
|
+
? `Your previously generated workflow YAML has unresolved variable references. Fix and output the complete YAML.
|
|
631
|
+
|
|
632
|
+
# Current YAML
|
|
633
|
+
|
|
634
|
+
\`\`\`yaml
|
|
635
|
+
${currentYaml}
|
|
636
|
+
\`\`\`
|
|
637
|
+
|
|
638
|
+
# Undefined variables
|
|
639
|
+
|
|
640
|
+
These \`{{X}}\` references appear in some step's task but no step produces them as \`output\`:
|
|
641
|
+
${undefinedVars.map(v => ` - {{${v}}}`).join('\n')}
|
|
642
|
+
|
|
643
|
+
# Available names
|
|
644
|
+
|
|
645
|
+
inputs: ${inputNames.length > 0 ? inputNames.join(', ') : '(none)'}
|
|
646
|
+
existing step.output: ${outputNames.length > 0 ? outputNames.join(', ') : '(none)'}
|
|
647
|
+
existing step.id: ${stepIds.join(', ')}
|
|
648
|
+
|
|
649
|
+
# How to fix (pick whichever fits each case)
|
|
650
|
+
|
|
651
|
+
1. If the reference meant an existing upstream output → rename \`{{X}}\` to that output name in the task
|
|
652
|
+
2. If a step should be producing this output but lacks the field → add \`output: X\` to that step
|
|
653
|
+
3. If a merge/aggregation step's \`depends_on\` is incomplete → add the upstream step ids that produce the referenced outputs
|
|
654
|
+
|
|
655
|
+
# Output rules
|
|
656
|
+
|
|
657
|
+
- Output ONLY the corrected complete YAML code block, nothing else
|
|
658
|
+
- Preserve roles, descriptions, structure as much as possible
|
|
659
|
+
- DO NOT add commentary
|
|
660
|
+
`
|
|
661
|
+
: `你之前生成的工作流 YAML 中有未解决的变量引用错误。请修正后输出完整的 YAML。
|
|
662
|
+
|
|
663
|
+
# 当前 YAML
|
|
664
|
+
|
|
665
|
+
\`\`\`yaml
|
|
666
|
+
${currentYaml}
|
|
667
|
+
\`\`\`
|
|
668
|
+
|
|
669
|
+
# 未定义的变量
|
|
670
|
+
|
|
671
|
+
以下 \`{{X}}\` 在某个 step 的 task 中被引用,但没有任何 step 用 \`output\` 字段产生它们:
|
|
672
|
+
${undefinedVars.map(v => ` - {{${v}}}`).join('\n')}
|
|
673
|
+
|
|
674
|
+
# 可用的名字
|
|
675
|
+
|
|
676
|
+
inputs: ${inputNames.length > 0 ? inputNames.join('、') : '(无)'}
|
|
677
|
+
已有的 step.output: ${outputNames.length > 0 ? outputNames.join('、') : '(无)'}
|
|
678
|
+
已有的 step.id: ${stepIds.join('、')}
|
|
679
|
+
|
|
680
|
+
# 修复方式(按场景任选)
|
|
681
|
+
|
|
682
|
+
1. 如果引用的本意是上游某个已存在的 output → 把 task 里的 \`{{X}}\` 改成该 output 的名字
|
|
683
|
+
2. 如果某个 step 本应产生这个 output 但少写了字段 → 给该 step 加 \`output: X\`
|
|
684
|
+
3. 如果合并/汇总类 step 的 \`depends_on\` 不全 → 补上产生这些 output 的上游 step.id
|
|
685
|
+
|
|
686
|
+
# 输出要求
|
|
687
|
+
|
|
688
|
+
- 只输出修正后的完整 YAML 代码块,不要输出其他文字
|
|
689
|
+
- 保持角色、描述、结构尽量不变
|
|
690
|
+
- 不要加解释
|
|
691
|
+
`;
|
|
692
|
+
const systemPrompt = lang === 'en'
|
|
693
|
+
? 'You are a YAML workflow repair assistant. Output only the corrected YAML code block.'
|
|
694
|
+
: '你是一个 YAML 工作流修复助手。只输出修正后的 YAML 代码块。';
|
|
695
|
+
try {
|
|
696
|
+
const connector = createConnector(llmConfig);
|
|
697
|
+
const result = await connector.chat(systemPrompt, prompt, {
|
|
698
|
+
...llmConfig,
|
|
699
|
+
max_tokens: llmConfig.max_tokens || 4096,
|
|
700
|
+
});
|
|
701
|
+
const fixedYaml = extractYamlFromResponse(result.content);
|
|
702
|
+
if (!fixedYaml || !fixedYaml.includes('steps:')) {
|
|
703
|
+
return { ok: false, replaced: false };
|
|
704
|
+
}
|
|
705
|
+
writeFileSync(yamlPath, fixedYaml + '\n', 'utf-8');
|
|
706
|
+
return { ok: true, replaced: true };
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
return { ok: false, replaced: false };
|
|
710
|
+
}
|
|
711
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { readFileSync, existsSync } from 'node:fs';
|
|
12
12
|
import { resolve, join, dirname } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
13
14
|
import { execSync } from 'node:child_process';
|
|
14
15
|
import { parseWorkflow, validateWorkflow } from './core/parser.js';
|
|
15
16
|
import { buildDAG, formatDAG } from './core/dag.js';
|
|
@@ -321,10 +322,27 @@ async function handleCompose() {
|
|
|
321
322
|
console.log('');
|
|
322
323
|
}
|
|
323
324
|
if (autoRun) {
|
|
324
|
-
// --run
|
|
325
|
-
|
|
325
|
+
// --run 模式:校验有严重问题时不进入 run 阶段
|
|
326
|
+
// run() 内部会重新 validate 并 hard-fail,提前拦下来给用户更清晰的提示
|
|
327
|
+
const fatalParse = warnings.some(w => w.includes('解析失败') || w.toLowerCase().includes('parse failed'));
|
|
328
|
+
const fatalUndefVar = warnings.some(w => w.includes('未定义的变量') || w.toLowerCase().includes('undefined variable'));
|
|
329
|
+
const fatalInvalidRole = warnings.some(w => w.includes('不存在于角色库') || w.toLowerCase().includes('does not exist in catalog'));
|
|
330
|
+
if (fatalParse || fatalUndefVar || fatalInvalidRole) {
|
|
326
331
|
console.error(` ${t('compose.retry_yaml_bad')}`);
|
|
327
|
-
console.error(
|
|
332
|
+
console.error('');
|
|
333
|
+
console.error(' 以下问题需要先解决 / The following issues need to be resolved:');
|
|
334
|
+
for (const w of warnings) {
|
|
335
|
+
if (w.includes('未定义的变量') || w.includes('解析失败') || w.includes('不存在于角色库')
|
|
336
|
+
|| w.toLowerCase().includes('undefined variable') || w.toLowerCase().includes('parse failed')
|
|
337
|
+
|| w.toLowerCase().includes('does not exist in catalog')) {
|
|
338
|
+
console.error(` - ${w}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
console.error('');
|
|
342
|
+
console.error(' 建议 / Suggestions:');
|
|
343
|
+
console.error(' 1. 重新生成 / Regenerate: ao compose "..." --run');
|
|
344
|
+
console.error(` 2. 手动修改 / Manually edit: ${relativePath}`);
|
|
345
|
+
console.error(` 然后用 / then run: ao run ${relativePath}`);
|
|
328
346
|
process.exit(1);
|
|
329
347
|
}
|
|
330
348
|
console.log('─'.repeat(50));
|
|
@@ -611,7 +629,9 @@ function parseInputArgs() {
|
|
|
611
629
|
*/
|
|
612
630
|
function resolveAgentsDir(preferLang) {
|
|
613
631
|
// scriptDir = dist/ inside the installed package
|
|
614
|
-
|
|
632
|
+
// Windows: 必须用 fileURLToPath,不能用 new URL(url).pathname,
|
|
633
|
+
// 否则会得到 "/C:/Users/..." 非法路径,包内置 / node_modules 候选都失效
|
|
634
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
615
635
|
const zhFirst = [
|
|
616
636
|
'./agency-agents-zh',
|
|
617
637
|
'./agency-agents',
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { loadAgent } from './agents/loader.js';
|
|
|
24
24
|
import { saveResults, printStepResult, printStepRunning, clearRunningLine, printSummary, loadPreviousContext, getCompletedStepIds } from './output/reporter.js';
|
|
25
25
|
import { existsSync, readFileSync } from 'node:fs';
|
|
26
26
|
import { resolve, dirname, join } from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
27
28
|
/**
|
|
28
29
|
* 一行运行工作流(高级 API)
|
|
29
30
|
*/
|
|
@@ -220,7 +221,9 @@ function resolveAgentsDir(agentsDir, workflowPath) {
|
|
|
220
221
|
// 3. 按用户指定的 agents_dir 名字,在常见位置查找同名目录
|
|
221
222
|
// (尊重用户意图:指定 "agency-agents" 不会 fallback 到 "agency-agents-zh")
|
|
222
223
|
const baseName = agentsDir.replace(/[\/\\]+$/, '').split(/[\/\\]/).pop() || agentsDir;
|
|
223
|
-
|
|
224
|
+
// Windows: 必须用 fileURLToPath,不能用 new URL(url).pathname,
|
|
225
|
+
// 否则会得到 "/C:/Users/..." 非法路径,所有 scriptDir 相关候选都失效
|
|
226
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
224
227
|
const sameNameCandidates = [
|
|
225
228
|
resolve(baseName),
|
|
226
229
|
resolve('..', baseName),
|
package/dist/mcp/server.js
CHANGED
|
@@ -12,6 +12,7 @@ import { z } from 'zod';
|
|
|
12
12
|
import { resolve, relative, dirname } from 'node:path';
|
|
13
13
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
15
16
|
import * as yaml from 'js-yaml';
|
|
16
17
|
import { run } from '../index.js';
|
|
17
18
|
import { parseWorkflow, validateWorkflow } from '../core/parser.js';
|
|
@@ -30,8 +31,9 @@ function findAgentsDir(hint) {
|
|
|
30
31
|
resolve('agents'),
|
|
31
32
|
resolve('node_modules/agency-agents-zh'),
|
|
32
33
|
resolve('node_modules/agency-agents'),
|
|
33
|
-
|
|
34
|
-
resolve(dirname(
|
|
34
|
+
// Windows: 必须用 fileURLToPath,不能用 new URL(url).pathname(会得到 "/C:/..." 非法路径)
|
|
35
|
+
resolve(dirname(fileURLToPath(import.meta.url)), '../../node_modules/agency-agents-zh'),
|
|
36
|
+
resolve(dirname(fileURLToPath(import.meta.url)), '../../node_modules/agency-agents'),
|
|
35
37
|
];
|
|
36
38
|
for (const dir of candidates) {
|
|
37
39
|
if (existsSync(dir))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agency-orchestrator",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
4
|
"description": "Multi-agent YAML workflow engine — 211 AI roles, auto DAG parallelism, zero code. One sentence → multiple AI roles collaborate → complete plan in minutes. 10 LLM providers, 7 need no API key.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"multi-agent",
|