From 213e293c066009e586b5d0571aa519885372e882 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:38:49 +0900 Subject: [PATCH] =?UTF-8?q?Phase=201=E3=83=97=E3=83=AD=E3=83=B3=E3=83=97?= =?UTF-8?q?=E3=83=88=E3=81=AB=E3=82=82=E3=82=B9=E3=83=86=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=82=B9=E3=83=AB=E3=83=BC=E3=83=AB=E3=82=92=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=EF=BC=88Phase=203=E3=81=A8=E3=81=AE=E4=BD=B5=E7=94=A8=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buildInstruction()にセクション7を追加し、タグベースのルールがある場合に 判定基準と出力フォーマットをPhase 1のプロンプトに注入する。 ai()/aggregate条件のみの場合はスキップ。 --- src/__tests__/instructionBuilder.test.ts | 46 +++++++++++++----------- src/workflow/instruction-builder.ts | 19 +++++++--- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/__tests__/instructionBuilder.test.ts b/src/__tests__/instructionBuilder.test.ts index 285ad56..5c54322 100644 --- a/src/__tests__/instructionBuilder.test.ts +++ b/src/__tests__/instructionBuilder.test.ts @@ -362,8 +362,8 @@ describe('instruction-builder', () => { }); }); - describe('buildInstruction with rules (Phase 1 — no status tags)', () => { - it('should NOT include status rules even when rules exist (phase separation)', () => { + describe('buildInstruction with rules (Phase 1 — status rules injection)', () => { + it('should include status rules when tag-based rules exist', () => { const step = createMinimalStep('Do work'); step.name = 'plan'; step.rules = [ @@ -374,10 +374,9 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - // Phase 1 should NOT contain status header or criteria - expect(result).not.toContain('Status Output Rules'); - expect(result).not.toContain('Decision Criteria'); - expect(result).not.toContain('[PLAN:'); + expect(result).toContain('Decision Criteria'); + expect(result).toContain('[PLAN:1]'); + expect(result).toContain('[PLAN:2]'); }); it('should not add status rules when rules do not exist', () => { @@ -386,7 +385,6 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); expect(result).not.toContain('Decision Criteria'); }); @@ -397,7 +395,6 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); expect(result).not.toContain('Decision Criteria'); }); }); @@ -859,8 +856,8 @@ describe('instruction-builder', () => { }); }); - describe('phase separation — buildInstruction never includes status rules', () => { - it('should NOT include status rules even with ai() conditions', () => { + describe('status rules injection — skip when all rules are ai()/aggregate', () => { + it('should NOT include status rules when all rules are ai() conditions', () => { const step = createMinimalStep('Do work'); step.rules = [ { condition: 'ai("No issues")', next: 'COMPLETE', isAiCondition: true, aiConditionText: 'No issues' }, @@ -870,12 +867,13 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); + expect(result).not.toContain('Decision Criteria'); expect(result).not.toContain('[TEST-STEP:'); }); - it('should NOT include status rules with mixed regular and ai() conditions', () => { + it('should include status rules with mixed regular and ai() conditions', () => { const step = createMinimalStep('Do work'); + step.name = 'review'; step.rules = [ { condition: 'Error occurred', next: 'ABORT' }, { condition: 'ai("Issues found")', next: 'fix', isAiCondition: true, aiConditionText: 'Issues found' }, @@ -884,11 +882,13 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); + expect(result).toContain('Decision Criteria'); + expect(result).toContain('[REVIEW:1]'); }); - it('should NOT include status rules with regular conditions only', () => { + it('should include status rules with regular conditions only', () => { const step = createMinimalStep('Do work'); + step.name = 'plan'; step.rules = [ { condition: 'Done', next: 'COMPLETE' }, { condition: 'Blocked', next: 'ABORT' }, @@ -897,10 +897,12 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); + expect(result).toContain('Decision Criteria'); + expect(result).toContain('[PLAN:1]'); + expect(result).toContain('[PLAN:2]'); }); - it('should NOT include status rules with aggregate conditions', () => { + it('should NOT include status rules when all rules are aggregate conditions', () => { const step = createMinimalStep('Do work'); step.rules = [ { condition: 'all("approved")', next: 'COMPLETE', isAggregateCondition: true, aggregateType: 'all' as const, aggregateConditionText: 'approved' }, @@ -910,10 +912,10 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); + expect(result).not.toContain('Decision Criteria'); }); - it('should NOT include status rules with mixed ai() and aggregate conditions', () => { + it('should NOT include status rules when all rules are ai() + aggregate', () => { const step = createMinimalStep('Do work'); step.rules = [ { condition: 'all("approved")', next: 'COMPLETE', isAggregateCondition: true, aggregateType: 'all' as const, aggregateConditionText: 'approved' }, @@ -924,11 +926,12 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); + expect(result).not.toContain('Decision Criteria'); }); - it('should NOT include status rules with mixed aggregate and regular conditions', () => { + it('should include status rules with mixed aggregate and regular conditions', () => { const step = createMinimalStep('Do work'); + step.name = 'supervise'; step.rules = [ { condition: 'all("approved")', next: 'COMPLETE', isAggregateCondition: true, aggregateType: 'all' as const, aggregateConditionText: 'approved' }, { condition: 'Error occurred', next: 'ABORT' }, @@ -937,7 +940,8 @@ describe('instruction-builder', () => { const result = buildInstruction(step, context); - expect(result).not.toContain('Status Output Rules'); + expect(result).toContain('Decision Criteria'); + expect(result).toContain('[SUPERVISE:1]'); }); }); diff --git a/src/workflow/instruction-builder.ts b/src/workflow/instruction-builder.ts index 487a991..acdd061 100644 --- a/src/workflow/instruction-builder.ts +++ b/src/workflow/instruction-builder.ts @@ -3,10 +3,12 @@ * * Builds the instruction string for agent execution by: * 1. Auto-injecting standard sections (Execution Context, Workflow Context, - * User Request, Previous Response, Additional User Inputs, Instructions header) + * User Request, Previous Response, Additional User Inputs, Instructions header, + * Status Output Rules) * 2. Replacing template placeholders with actual values * - * Status judgment is handled separately in Phase 3 (buildStatusJudgmentInstruction). + * Status rules are injected into Phase 1 for tag-based detection, + * and also used in Phase 3 (buildStatusJudgmentInstruction) as a dedicated follow-up. */ import type { WorkflowStep, WorkflowRule, AgentResponse, Language, ReportConfig, ReportObjectConfig } from '../models/types.js'; @@ -406,8 +408,7 @@ function replaceTemplatePlaceholders( * 4. Previous Response — if passPreviousResponse and has content, unless template contains {previous_response} * 5. Additional User Inputs — unless template contains {user_inputs} * 6. Instructions header + instruction_template content — always - * - * Status judgment is handled separately in Phase 3 (buildStatusJudgmentInstruction). + * 7. Status Output Rules — when step has tag-based rules (not all ai()/aggregate) * * Template placeholders ({task}, {previous_response}, etc.) are still replaced * within the instruction_template body for backward compatibility. @@ -462,6 +463,16 @@ export function buildInstruction( ); sections.push(`${s.instructions}\n${processedTemplate}`); + // 7. Status Output Rules (for tag-based detection in Phase 1) + // Skip if all rules are ai() or aggregate conditions (no tags needed) + if (step.rules && step.rules.length > 0) { + const allNonTagConditions = step.rules.every((r) => r.isAiCondition || r.isAggregateCondition); + if (!allNonTagConditions) { + const statusRulesPrompt = generateStatusRulesFromRules(step.name, step.rules, language); + sections.push(statusRulesPrompt); + } + } + return sections.join('\n\n'); }