首页 / 文章 / 实用提示:构建→测试→修复→重复:实现Laravel开发的自动化

实用提示:构建→测试→修复→重复:实现Laravel开发的自动化

《实用笔记:构建→测试→修复→重复:自动化Laravel开发》的操作指南:为采用该开发模式的团队提供的契约、检查机制以及即插即用代码模块。

3634 词

本指南将逐步构建从原材料到可运行系统的完整流程,遵循“构建→测试→修复→重复”的循环:利用 AI 智能体实现 Laravel 开发自动化。重点在于可操作的步骤、明确的检查点,以及可直接放入代码仓库的代码,无需猜测其用途。 在概览阶段,应在修改代码之前明确输入参数、各步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需推测隐藏的状态。 需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及错误处理都是产品不可或缺的部分,而非后续需要补充的功能。

在继续之前,先了解一些术语

在完成“前置概念”阶段时,首先写下合约的详细内容:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 相比冗长的脚本,更应选择小型且可测试的单元。当某个步骤失败时,故障应指向单一的责任模块,而非复杂的流程链。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的大型语言模型。

为何“AI能写代码吗”已不再是有趣的问题

在处理“为什么AI能写作”这一阶段时,首先需列出相关契约:所需输入、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将这一阶段视为输入与验证后输出之间的契约。为相关成果命名,明确成功判定标准,杜绝无声的半完成状态。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的LLM接口。

Developer → AI → Developer → Test → Developer → AI → Developer → Test → ...
Developer → Orchestrator → Build → Test → (failed? → Fix → Test again) → Review → Done

认识两大核心工具

在处理“Meet the Two Main”阶段时,首先写下契约内容:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 为每次调用记录工具名称、参数哈希值、延迟时间以及最终结果。没有这些记录,调试代理将陷入无休止的循环,耗费大量时间。

Laravel Boost——连接人工智能与应用程序的桥梁

在处理 Laravel Boost 的翻译阶段时,首先需明确相关规范:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改始终符合要求。 将配置信息置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放,以便操作员无需查看全部代码即可进行审核。 在耗时较高的步骤之后设置检查点。当操作员重新执行后续节点时,恢复流程不应再次调用相同的大型语言模型。

composer require laravel/boost --dev

php artisan boost:install

OpenCode —— 智能体实际执行任务的地方

在处理 OpenCode 中的智能体阶段时,首先需写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的组成部分,而非后续需要补充的功能。 在成本较高的步骤之后设置检查点。当操作员重新尝试某个节点时,恢复流程不应再次调用相同的大型语言模型。

实践操作:将 Boost 集成到 OpenCode 中

在逐步实现 Hands-On Wiring Boost 功能时,首先列出需求清单:所需的输入参数、成功信号以及部分功能失效时的处理方式。这样的检查表能确保后续的代码修改不会偏离原定方向。 建议采用小型、可测试的单元而非庞大的脚本。当某个步骤失败时,故障应能指向具体的责任模块,而非复杂的流程链。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的 LLM 接口。

1. 将 OpenCode 与 Boost 相连

在完成“将OpenCode连接到阶段”这一步骤时,首先需写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合约定。 将这一阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功判定标准,并杜绝无声的半完成状态。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的大型语言模型。

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "laravel-boost": {
      "type": "local",
      "command": ["php", "artisan", "boost:mcp"],
      "enabled": true
    }
  }
}
opencode mcp list

2. 创建具有特定职责的智能体

在完成“带有阶段的两个代理创建”任务时,首先需明确相关规范:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 在耗时的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次收取相同的LLM调用费用。 在完成“带有阶段的两个代理创建”任务时,首先需明确相关规范:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及死信处理都是产品功能的一部分,而非后续需要补充的内容。

---
description: Build Agent for new features
mode: primary
tools:
  write: true
  edit: true
---
You are the Build Agent.
Use Laravel Boost tools to understand the application structure
before writing any code. Never assume the database structure -
always check the schema first. Write tests for every new behavior.
At the end of your work, return ONLY JSON in this format:
{"status": "success", "files_changed": [...], "summary": "..."}
opencode agent list

3. 先尝试手动运行

“尝试运行”这一阶段若被视为可度量的指标,效果会更好。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某一步骤失败时,故障应能指向某个具体的责任模块,而非复杂的流程链。 保持图表状态简洁且具有类型定义。嵌套的数据结构会掩盖哪个节点编写了哪个字段的信息,还会在流程中断后导致无法继续执行。

opencode run --agent builder "Add a task assignment feature to TaskFlow according to the requirements in TASK.md"

实际案例:“TaskFlow”中的任务分配功能

将“真实案例分析”阶段视为可测量的界面来处理效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 把这一阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,绝不允许默默地部分完成任务。 保持图表状态简洁且具有类型约束。嵌套的数据块会掩盖哪个节点修改了哪个字段,还会在任务中断后导致无法继续处理。

1. A task has an assignee column (belongs to a User).
2. A user can only assign tasks within a project they're a member of.
3. A user must not assign or access tasks from other projects.
4. All of the above must be covered by automated tests.

为何我们需要为该流程建立“日志本”(数据库)

“我们为何需要阶段”这一概念在被视为可度量的对象时效果最佳。在扩大范围之前,先记录一份理想的操作流程、一个故障案例以及回滚说明。 在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,就能避免在从演示环境过渡到共享环境时出现意外账单。 保持图表状态简洁且具有类型定义。嵌套的数据块会掩盖哪个节点修改了哪个字段,还会在中断后导致流程无法继续。 “我们为何需要阶段”这一概念在被视为可度量的对象时效果最佳。在扩大范围之前,先记录一份理想的操作流程、一个故障案例以及回滚说明。 需同时记录正常流程与恢复流程。重试机制、人工审核环节以及死信处理都是产品的一部分,而非后续需要补充的内容。

Schema::create('ai_tasks', function (Blueprint $table) {
    $table->id();
    $table->string('type');
    $table->string('status')->default('pending');
    $table->text('prompt');
    $table->json('result')->nullable();
    $table->unsignedTinyInteger('iteration')->default(0);
    $table->timestamps();
});
class AiTask extends Model
{
    protected $fillable = [
        'type', 'status', 'prompt', 'result', 'iteration',
    ];

    protected function casts(): array
    {
        return ['result' => 'array'];
    }
}
pending → building → testing → (fixing → testing)* → reviewing → completed / failed

将OpenCode集成到我们的PHP代码中

在将 OpenCode 整合到我们的平台时,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏的状态。 相比庞大的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任点,而非复杂的流程链。 对于涉及资金支出或修改生产数据的操作,必须经过人工审批。编译时的连接方式并不等同于业务功能的完整性。

interface AgentRunner
{
    public function run(string $agent, string $prompt): array;
}
class OpenCodeAgentRunner implements AgentRunner
{
    public function run(string $agent, string $prompt): array
    {
        $result = Process::timeout(600)->run([
            'opencode', 'run',
            '--agent', $agent,
            '--format', 'json',
            $prompt,
        ]);

        if (! $result->successful()) {
            return [
                'status' => 'error',
                'error' => $result->errorOutput(),
            ];
        }

        return json_decode($result->output(), true) ?? [
            'status' => 'error',
            'error' => 'Agent output was not valid JSON',
        ];
    }
}
$this->app->bind(AgentRunner::class, OpenCodeAgentRunner::class);

每个阶段都有对应的任务

对于“每个阶段都有相应任务”的原则,在修改代码之前需明确输入内容、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功检测标准,并杜绝默许的半完成状态。 对于涉及资金支出或修改生产数据的操作,必须经过人工审批。编译时的配置并不等同于业务上的完整性。

构建任务

在构建任务阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测其中的隐藏状态。 应在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外账单。 对于会产生费用或修改生产数据的操作,必须经过人工审批。编译时的配置并不等同于业务功能的完整性。

class BuildFeature implements ShouldQueue
{
    public function __construct(protected AiTask $task) {}

    public function handle(AgentRunner $agent): void
    {
        $result = $agent->run(
            agent: 'builder',
            prompt: $this->task->prompt,
        );

        $this->task->update([
            'status' => 'testing',
            'result' => $result,
        ]);

        RunTests::dispatch($this->task);
    }
}

在构建任务阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测其中的隐藏状态。 需同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理措施都是产品不可或缺的部分,而非后续需要补充的内容。

测试任务

在处理测试任务阶段时,首先写下相关约定:所需的输入参数、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 优先选择小型、可测试的单元,而非庞大的脚本。当某个步骤失败时,故障应指向单一的责任模块,而非复杂的流程链。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的大型语言模型。

class RunTests implements ShouldQueue
{
    public function __construct(protected AiTask $task) {}

    public function handle(): void
    {
        $result = Process::timeout(300)->run('php artisan test --compact');

        if ($result->successful()) {
            $this->task->update(['status' => 'reviewing']);
            ReviewFeature::dispatch($this->task);
            return;
        }

        $this->task->update(['status' => 'failed']);
        AnalyzeFailure::dispatch($this->task, $result->output());
    }
}
Test
 ├── passed → move to Review
 └── failed → move to Analyze

不要将原始错误直接交给智能体

在处理“不要直接传递原始数据”阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将此阶段视为输入与验证后输出之间的契约。为相关成果命名,明确成功判定标准,并杜绝无声的半完成状态。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的大型语言模型。

{
  "status": "failed",
  "failed_tests": [
    {
      "name": "user_cannot_assign_task_from_other_project",
      "error": "Expected response status code [403] but received 200."
    }
  ]
}
You are the Debug Agent.

Analyze the failed tests below. DO NOT modify any files.
Inspect: relevant models, policies, migrations, and tests.
Return JSON with:
- root_cause
- affected_files
- recommended_fix
- risk_of_regression
{
  "status": "analyzed",
  "root_cause": "TaskPolicy does not verify project membership before allowing assignment",
  "affected_files": ["app/Policies/TaskPolicy.php"],
  "recommended_fix": "Add a project membership check inside the assign() method",
  "next_action": "fix"
}

先修复,再重新测试——而非修复后就结束

在“修复后再测试”阶段,首先需写下接口规范:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 在耗时较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次收取相同的LLM调用费用。

class FixFeature implements ShouldQueue
{
    public function __construct(protected AiTask $task, protected array $analysis) {}

    public function handle(AgentRunner $agent): void
    {
        $result = $agent->run(
            agent: 'fixer',
            prompt: json_encode($this->analysis),
        );

        $this->task->increment('iteration');
        $this->task->update([
            'status' => 'testing',
            'result' => $result,
        ]);

        RunTests::dispatch($this->task);
    }
}

在“修复后再测试”阶段,首先需写下接口规范:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及死信处理都是产品功能的一部分,而非后续需要补充的内容。

协调器:决定下一步行动的“交通调度员”

将“协调器·交通”阶段视为可度量的对象使用效果最佳。在扩大范围之前,先记录一份优秀的测试案例、一个故障实例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某一步骤失败时,故障应指向单一责任点,而非复杂的流程链。 保持图表状态简洁且具有类型定义。嵌套的数据块会掩盖哪个节点编写了哪个字段的信息,还会在中断后导致流程无法继续。

Bus::chain([
    new BuildFeature($task),
    new RunTests($task),
    new ReviewFeature($task),
])->dispatch();
class AgentOrchestrator
{
    public function next(AiTask $task): void
    {
        match ($task->status) {
            'pending'   => BuildFeature::dispatch($task),
            'testing'   => RunTests::dispatch($task),
            'failed'    => AnalyzeFailure::dispatch($task),
            'fixing'    => RunTests::dispatch($task),
            'reviewing' => ReviewFeature::dispatch($task),
            'completed', 'stopped' => null,
            default => throw new LogicException("Unknown status: {$task->status}"),
        };
    }
}

限制重试次数——避免无限循环

将“重试次数上限”阶段视为可度量的工作面时,其效果最佳。在扩大范围之前,先记录一份完美的测试用例、一个失败案例以及回滚说明。 把这一阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声的半完成状态。 保持图结构简洁且类型明确。嵌套的数据块会掩盖哪个节点修改了哪个字段的信息,还会在中断后导致无法继续处理。

Fix → Test fails → Fix → Test fails → Fix → Test fails → ...
class RunTests implements ShouldQueue
{
    protected const MAX_ITERATIONS = 5;

    public function handle(): void
    {
        if ($this->task->iteration >= self::MAX_ITERATIONS) {
            $this->task->update(['status' => 'needs_human_review']);
            return;
        }
        // ... run tests as usual
    }
}

测试通过≠功能已完成

“绿色测试:已完成”阶段若被视为可度量的指标,效果最佳。在扩大范围之前,需记录一份理想运行日志、一个失败案例以及回滚说明。 在功能测试结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,可避免从演示环境过渡到共享环境时出现意外费用。 保持图表状态简洁且类型明确。嵌套的数据块会掩盖具体是哪个节点修改了哪个字段,还会在中断后导致流程无法继续。 “绿色测试:已完成”阶段若被视为可度量的指标,效果最佳。在扩大范围之前,需记录一份理想运行日志、一个失败案例以及回滚说明。 需同时记录正常流程与故障恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的组成部分,而非后续需要补充的功能。

public function assign(User $user, Task $task): bool
{
    return $user->isAdmin();
}
Build → Tests pass → Deploy immediately
Build → Test → (if failed: Debug → Fix → Test again) → Human review → Deploy

若多个代理并行工作,需注意文件冲突问题

在“多个代理并行工作”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 建议使用小型、可测试的单元而非庞大的脚本。当某个步骤失败时,故障应能指向单一责任点,而非复杂的流程链。 对于涉及资金支出或修改生产数据的操作,必须经过人工审批。编译时的逻辑连接并不等同于业务功能的完整性。

Agent A → writes Task.php
Agent B → reads Task.php (nearly at the same time)
Agent A → writes Task.php again
Research Agent   → read-only
Review Agent     → read-only
Security Agent   → read-only
Build Agent      → write access
Fix Agent        → write access

不要让一个代理承担所有工作

在“不要创建”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与已验证输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 对于涉及资金支出或修改生产数据的操作,必须经过人工审批。编译时的连接方式并不等同于业务上的完整性。

Planner   → designs the approach
Builder   → writes the new feature's code
Tester    → runs the test suite
Debugger  → diagnoses failures (read-only)
Fixer     → executes the fix
Reviewer  → final quality check (read-only)

实际使用前的最低检查清单

在“A Minimal Checklist Before”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 在功能结果旁记录执行时间以及令牌或查询成本。提前显示成本可避免在流程从演示环境转向共享环境时出现意外费用。 对于会耗费资金或修改生产数据的操作,必须经过人工审批。编译时的配置并不等同于业务功能的完整性。 在“A Minimal Checklist Before”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理措施都是产品本身的一部分,而非后续需要补充的内容。

在处理“Laravel到底有什么用”这一阶段时,首先要明确相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 应优先选择小型且可测试的单元,而非庞大的脚本。当某个步骤失败时,故障应指向单一的责任模块,而非复杂的流程链。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的大型语言模型接口。

Developer (us)
    │
    ▼
Orchestrator (traffic controller, running on Laravel Queue)
    │
    ├── Build Agent
    ├── Test Agent
    ├── Debug Agent
    └── Fix Agent
          │
          ▼
      OpenCode (where the agent does its work)
          │
          ▼
    Laravel Boost (context translator, via MCP)
          │
          ▼
    Our Laravel application

结论:问题已经发生变化

在处理“结论:问题所在”阶段时,首先写下契约内容:所需的输入参数、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合约定。 将此阶段视为输入与验证后输出之间的契约。为相关成果命名,明确成功判定标准,杜绝默许部分完成的情况。 在成本较高的步骤之后设置检查点。当操作员重新尝试后续节点时,恢复流程不应再次调用相同的大型语言模型。

操作检查清单

若将“操作检查清单”阶段视为可度量的对象,其效果会更好。在扩大范围之前,先记录一份最佳示例、一个失败案例以及回滚说明。 将配置信息置于应用程序代码之外。环境文件、密钥存储和功能开关应集中存放于一个位置,以便操作员无需查看整个系统结构即可进行审计。

保持图状态扁平且具有类型约束。嵌套的数据块会隐藏是哪个节点修改了哪个字段,还会在中断后导致流程无法继续。

只要预算允许,就在持续集成过程中使用测试用例而非真实的付费 API 来执行关键路径的冒烟测试。

需同时记录正常流程与故障恢复流程。重试机制、人工审核环节以及死信处理都是产品不可或缺的部分,而非后续才需要补充的功能。

保持图状态扁平且具有类型约束。嵌套的数据块会隐藏是哪个节点修改了哪个字段,还会在中断后导致流程无法继续。

在升级技术栈之前,应先冻结版本,为关键路径生成标准操作记录,并确认回滚步骤。共享环境需要设置速率限制、进行租户身份验证,同时明确密钥轮换的责任人。与其追求花哨的一次性演示,不如注重扎实的可靠性。

关于815696fa9b90的批处理说明:不要将提供者密钥放入代码仓库,为每个会话设置令牌上限,并将转录内容存储在评估测试用例的旁边,以便后续更换模型时仍能保持可比性。