Phase システムをエージェントに注入する
This commit is contained in:
parent
bed836f08b
commit
62de1ede3c
@ -503,6 +503,121 @@ describe('instruction-builder', () => {
|
|||||||
|
|
||||||
expect(result).toContain('- Step Iteration: 3(このステップの実行回数)');
|
expect(result).toContain('- Step Iteration: 3(このステップの実行回数)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should include workflow structure when workflowSteps is provided', () => {
|
||||||
|
const step = createMinimalStep('Do work');
|
||||||
|
step.name = 'implement';
|
||||||
|
const context = createMinimalContext({
|
||||||
|
language: 'en',
|
||||||
|
workflowSteps: [
|
||||||
|
{ name: 'plan' },
|
||||||
|
{ name: 'implement' },
|
||||||
|
{ name: 'review' },
|
||||||
|
],
|
||||||
|
currentStepIndex: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
expect(result).toContain('This workflow consists of 3 steps:');
|
||||||
|
expect(result).toContain('- Step 1: plan');
|
||||||
|
expect(result).toContain('- Step 2: implement');
|
||||||
|
expect(result).toContain('← current');
|
||||||
|
expect(result).toContain('- Step 3: review');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mark current step with marker', () => {
|
||||||
|
const step = createMinimalStep('Do work');
|
||||||
|
step.name = 'plan';
|
||||||
|
const context = createMinimalContext({
|
||||||
|
language: 'en',
|
||||||
|
workflowSteps: [
|
||||||
|
{ name: 'plan' },
|
||||||
|
{ name: 'implement' },
|
||||||
|
],
|
||||||
|
currentStepIndex: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
expect(result).toContain('- Step 1: plan ← current');
|
||||||
|
expect(result).not.toContain('- Step 2: implement ← current');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include description in parentheses when provided', () => {
|
||||||
|
const step = createMinimalStep('Do work');
|
||||||
|
step.name = 'plan';
|
||||||
|
const context = createMinimalContext({
|
||||||
|
language: 'ja',
|
||||||
|
workflowSteps: [
|
||||||
|
{ name: 'plan', description: 'タスクを分析し実装計画を作成する' },
|
||||||
|
{ name: 'implement' },
|
||||||
|
],
|
||||||
|
currentStepIndex: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
expect(result).toContain('- Step 1: plan(タスクを分析し実装計画を作成する) ← 現在');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip workflow structure when workflowSteps is not provided', () => {
|
||||||
|
const step = createMinimalStep('Do work');
|
||||||
|
const context = createMinimalContext({ language: 'en' });
|
||||||
|
|
||||||
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
expect(result).not.toContain('This workflow consists of');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip workflow structure when workflowSteps is empty', () => {
|
||||||
|
const step = createMinimalStep('Do work');
|
||||||
|
const context = createMinimalContext({
|
||||||
|
language: 'en',
|
||||||
|
workflowSteps: [],
|
||||||
|
currentStepIndex: -1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
expect(result).not.toContain('This workflow consists of');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render workflow structure in Japanese', () => {
|
||||||
|
const step = createMinimalStep('Do work');
|
||||||
|
step.name = 'plan';
|
||||||
|
const context = createMinimalContext({
|
||||||
|
language: 'ja',
|
||||||
|
workflowSteps: [
|
||||||
|
{ name: 'plan' },
|
||||||
|
{ name: 'implement' },
|
||||||
|
],
|
||||||
|
currentStepIndex: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
expect(result).toContain('このワークフローは2ステップで構成されています:');
|
||||||
|
expect(result).toContain('← 現在');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show current marker when currentStepIndex is -1', () => {
|
||||||
|
const step = createMinimalStep('Do work');
|
||||||
|
step.name = 'sub-step';
|
||||||
|
const context = createMinimalContext({
|
||||||
|
language: 'en',
|
||||||
|
workflowSteps: [
|
||||||
|
{ name: 'plan' },
|
||||||
|
{ name: 'implement' },
|
||||||
|
],
|
||||||
|
currentStepIndex: -1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
expect(result).toContain('This workflow consists of 2 steps:');
|
||||||
|
expect(result).not.toContain('← current');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildInstruction report-free (phase separation)', () => {
|
describe('buildInstruction report-free (phase separation)', () => {
|
||||||
|
|||||||
@ -133,6 +133,7 @@ export const ParallelSubStepRawSchema = z.object({
|
|||||||
/** Workflow step schema - raw YAML format */
|
/** Workflow step schema - raw YAML format */
|
||||||
export const WorkflowStepRawSchema = z.object({
|
export const WorkflowStepRawSchema = z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
|
description: z.string().optional(),
|
||||||
/** Agent is required for normal steps, optional for parallel container steps */
|
/** Agent is required for normal steps, optional for parallel container steps */
|
||||||
agent: z.string().optional(),
|
agent: z.string().optional(),
|
||||||
/** Session handling for this step */
|
/** Session handling for this step */
|
||||||
|
|||||||
@ -53,6 +53,8 @@ export interface ReportObjectConfig {
|
|||||||
/** Single step in a workflow */
|
/** Single step in a workflow */
|
||||||
export interface WorkflowStep {
|
export interface WorkflowStep {
|
||||||
name: string;
|
name: string;
|
||||||
|
/** Brief description of this step's role in the workflow */
|
||||||
|
description?: string;
|
||||||
/** Agent name, path, or inline prompt as specified in workflow YAML. Undefined when step runs without an agent. */
|
/** Agent name, path, or inline prompt as specified in workflow YAML. Undefined when step runs without an agent. */
|
||||||
agent?: string;
|
agent?: string;
|
||||||
/** Session handling for this step */
|
/** Session handling for this step */
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export interface StepExecutorDeps {
|
|||||||
readonly getReportDir: () => string;
|
readonly getReportDir: () => string;
|
||||||
readonly getLanguage: () => Language | undefined;
|
readonly getLanguage: () => Language | undefined;
|
||||||
readonly getInteractive: () => boolean;
|
readonly getInteractive: () => boolean;
|
||||||
|
readonly getWorkflowSteps: () => ReadonlyArray<{ name: string; description?: string }>;
|
||||||
readonly detectRuleIndex: (content: string, stepName: string) => number;
|
readonly detectRuleIndex: (content: string, stepName: string) => number;
|
||||||
readonly callAiJudge: (
|
readonly callAiJudge: (
|
||||||
agentOutput: string,
|
agentOutput: string,
|
||||||
@ -52,6 +53,7 @@ export class StepExecutor {
|
|||||||
task: string,
|
task: string,
|
||||||
maxIterations: number,
|
maxIterations: number,
|
||||||
): string {
|
): string {
|
||||||
|
const workflowSteps = this.deps.getWorkflowSteps();
|
||||||
return new InstructionBuilder(step, {
|
return new InstructionBuilder(step, {
|
||||||
task,
|
task,
|
||||||
iteration: state.iteration,
|
iteration: state.iteration,
|
||||||
@ -64,6 +66,8 @@ export class StepExecutor {
|
|||||||
reportDir: join(this.deps.getProjectCwd(), this.deps.getReportDir()),
|
reportDir: join(this.deps.getProjectCwd(), this.deps.getReportDir()),
|
||||||
language: this.deps.getLanguage(),
|
language: this.deps.getLanguage(),
|
||||||
interactive: this.deps.getInteractive(),
|
interactive: this.deps.getInteractive(),
|
||||||
|
workflowSteps,
|
||||||
|
currentStepIndex: workflowSteps.findIndex(s => s.name === step.name),
|
||||||
}).build();
|
}).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -101,6 +101,7 @@ export class WorkflowEngine extends EventEmitter {
|
|||||||
getReportDir: () => this.reportDir,
|
getReportDir: () => this.reportDir,
|
||||||
getLanguage: () => this.options.language,
|
getLanguage: () => this.options.language,
|
||||||
getInteractive: () => this.options.interactive === true,
|
getInteractive: () => this.options.interactive === true,
|
||||||
|
getWorkflowSteps: () => this.config.steps.map(s => ({ name: s.name, description: s.description })),
|
||||||
detectRuleIndex: this.detectRuleIndex,
|
detectRuleIndex: this.detectRuleIndex,
|
||||||
callAiJudge: this.callAiJudge,
|
callAiJudge: this.callAiJudge,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export function isReportObjectConfig(report: string | ReportConfig[] | ReportObj
|
|||||||
/** Shape of localized section strings */
|
/** Shape of localized section strings */
|
||||||
interface SectionStrings {
|
interface SectionStrings {
|
||||||
workflowContext: string;
|
workflowContext: string;
|
||||||
|
workflowStructure: string;
|
||||||
|
currentStepMarker: string;
|
||||||
iteration: string;
|
iteration: string;
|
||||||
iterationWorkflowWide: string;
|
iterationWorkflowWide: string;
|
||||||
stepIteration: string;
|
stepIteration: string;
|
||||||
@ -127,12 +129,23 @@ export class InstructionBuilder {
|
|||||||
|
|
||||||
private renderWorkflowContext(language: Language): string {
|
private renderWorkflowContext(language: Language): string {
|
||||||
const s = getPromptObject<SectionStrings>('instruction.sections', language);
|
const s = getPromptObject<SectionStrings>('instruction.sections', language);
|
||||||
const lines: string[] = [
|
const lines: string[] = [s.workflowContext];
|
||||||
s.workflowContext,
|
|
||||||
`- ${s.iteration}: ${this.context.iteration}/${this.context.maxIterations}${s.iterationWorkflowWide}`,
|
// Workflow structure (if workflow steps info is available)
|
||||||
`- ${s.stepIteration}: ${this.context.stepIteration}${s.stepIterationTimes}`,
|
if (this.context.workflowSteps && this.context.workflowSteps.length > 0) {
|
||||||
`- ${s.step}: ${this.step.name}`,
|
lines.push(s.workflowStructure.replace('{count}', String(this.context.workflowSteps.length)));
|
||||||
];
|
this.context.workflowSteps.forEach((ws, index) => {
|
||||||
|
const isCurrent = index === this.context.currentStepIndex;
|
||||||
|
const marker = isCurrent ? ` ← ${s.currentStepMarker}` : '';
|
||||||
|
const desc = ws.description ? `(${ws.description})` : '';
|
||||||
|
lines.push(`- Step ${index + 1}: ${ws.name}${desc}${marker}`);
|
||||||
|
});
|
||||||
|
lines.push('');
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(`- ${s.iteration}: ${this.context.iteration}/${this.context.maxIterations}${s.iterationWorkflowWide}`);
|
||||||
|
lines.push(`- ${s.stepIteration}: ${this.context.stepIteration}${s.stepIterationTimes}`);
|
||||||
|
lines.push(`- ${s.step}: ${this.step.name}`);
|
||||||
|
|
||||||
// If step has report config, include Report Directory path and phase note
|
// If step has report config, include Report Directory path and phase note
|
||||||
if (this.step.report && this.context.reportDir) {
|
if (this.step.report && this.context.reportDir) {
|
||||||
|
|||||||
@ -34,6 +34,10 @@ export interface InstructionContext {
|
|||||||
language?: Language;
|
language?: Language;
|
||||||
/** Whether interactive-only rules are enabled */
|
/** Whether interactive-only rules are enabled */
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
|
/** Top-level workflow steps for workflow structure display */
|
||||||
|
workflowSteps?: ReadonlyArray<{ name: string; description?: string }>;
|
||||||
|
/** Index of the current step in workflowSteps (0-based) */
|
||||||
|
currentStepIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Execution environment metadata prepended to agent instructions */
|
/** Execution environment metadata prepended to agent instructions */
|
||||||
|
|||||||
@ -185,6 +185,7 @@ function normalizeStepFromRaw(step: RawStep, workflowDir: string): WorkflowStep
|
|||||||
|
|
||||||
const result: WorkflowStep = {
|
const result: WorkflowStep = {
|
||||||
name: step.name,
|
name: step.name,
|
||||||
|
description: step.description,
|
||||||
agent: agentSpec,
|
agent: agentSpec,
|
||||||
session: step.session,
|
session: step.session,
|
||||||
agentDisplayName: step.agent_name || (agentSpec ? extractAgentDisplayName(agentSpec) : step.name),
|
agentDisplayName: step.agent_name || (agentSpec ? extractAgentDisplayName(agentSpec) : step.name),
|
||||||
|
|||||||
@ -133,6 +133,8 @@ instruction:
|
|||||||
|
|
||||||
sections:
|
sections:
|
||||||
workflowContext: "## Workflow Context"
|
workflowContext: "## Workflow Context"
|
||||||
|
workflowStructure: "This workflow consists of {count} steps:"
|
||||||
|
currentStepMarker: "current"
|
||||||
iteration: "Iteration"
|
iteration: "Iteration"
|
||||||
iterationWorkflowWide: "(workflow-wide)"
|
iterationWorkflowWide: "(workflow-wide)"
|
||||||
stepIteration: "Step Iteration"
|
stepIteration: "Step Iteration"
|
||||||
|
|||||||
@ -146,6 +146,8 @@ instruction:
|
|||||||
|
|
||||||
sections:
|
sections:
|
||||||
workflowContext: "## Workflow Context"
|
workflowContext: "## Workflow Context"
|
||||||
|
workflowStructure: "このワークフローは{count}ステップで構成されています:"
|
||||||
|
currentStepMarker: "現在"
|
||||||
iteration: "Iteration"
|
iteration: "Iteration"
|
||||||
iterationWorkflowWide: "(ワークフロー全体)"
|
iterationWorkflowWide: "(ワークフロー全体)"
|
||||||
stepIteration: "Step Iteration"
|
stepIteration: "Step Iteration"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user