Plugin phức tạp chính tắc. Walked end-to-end.

Mọi khái niệm covered trong phần còn lại của các doc này — flow boot-and-load, bốn hook pattern, hook layout REGISTRY, migration plugin-isolated, flow indirect translation, bốn lifecycle state, pattern testsuite-per-plugin — đều được exercise trong storage/app/plugins/acelle/ai/. Plugin ship tám Eloquent model, mười bốn migration, mười tám locale, hơn sáu mươi Blade template, ba universal anonymous component, mọi hook layout-injection, và hơn một trăm Pest test trong testsuite riêng. Trang này là công thức đọc — nhìn cái gì trước, nhìn cái gì cuối, và làm sao học từ nó mà không lạc trong production-grade plumbing.

Tại sao plugin này là reference chính tắc

Hầu hết plugin trong production đều nhỏ. Một sending driver là một class cộng một blade connection. Một payment gateway là một service cộng một redirect controller. Một add-on sidebar đơn giản là một call Hook::add. Không cái nào trong số đó exercise toàn bộ plugin SDK — và plugin nhỏ không phải là bài tập đọc đúng cho author đang cố hiểu plugin có thể grow lớn cỡ nào một cách trách nhiệm.

acelle/ai tồn tại ở đầu kia của phổ. Nó là một subsystem AI self-contained: chatbox agent, universal text-rewrite component, KB-grounded coach persona, và admin observability dashboard. Activate plugin trong install AcelleMail add toàn bộ surface AI mà không động vào core code; deactivate remove nó sạch sẽ. Plugin sử dụng mọi khái niệm phần còn lại của doc này cover, trong production. Đọc nó là cách nhanh nhất để thấy pattern combine ra sao khi feature là non-trivial.

File tree at a glance

Từ README riêng của plugin:

FolderOwns cái gì
src/AIHandler/AI runtime engine — engine, agent loop, tool, settings resolver, observability writer/reader, KB lookup, URL sanitiser.
src/Models/Tám Eloquent model cho substrate audit (AIConversation, AIMessage, AIRequest, AIToolCall, AIFeedback, AIRawBlob, AIDailyRollup, AIToolUndoRecord).
src/Controllers/Admin controller (/rui/admin/ai-*) + public-API controller (/api/v1/ai/*) + PluginDashboardController plugin-landing.
src/Services/PluginStatusReport, AISettingsService, AutomationService, AIObservabilityPolicy, v.v.
src/ServiceProvider.phpEntry point duy nhất — register PSR-4, route, view, lang file, hook, middleware alias, lifecycle listener.
database/migrations/Mười bốn migration substrate-audit. Run lúc activate, rolled back lúc delete.
resources/views/Hơn sáu mươi Blade template admin + ba universal anonymous component (<x-mc-ai-chatbox>, <x-mc-ai-rewrite>, <x-mc-ai-subject-ab-generator>) + JS partial chatbox / sparkle.
resources/assets/CSS (~14 file) + JS (~21 file) publish tới public/plugins/acelle/ai/ qua vendor:publish --tag=plugin --force lúc plugin install.
resources/lang/Mười tám locale × chín lang file = full surface của module AI được dịch.
tests/Hơn một trăm Pest test (Feature + Unit) + base class Acelle\Ai\Tests\PluginTestCase.
routes.phpRoute plugin (admin + public API + dashboard plugin-landing tại /plugins/acelle/ai/dashboard).
composer.jsonMetadata plugin; extra.setting-route point tới PluginDashboardController@index để nút "Settings" trên trang admin Plugins deep-link vào dashboard riêng của plugin.

Không folder nào trong đó là bespoke. Mọi folder map trực tiếp tới một section trong phần còn lại của các doc này. Đọc plugin là quá trình nhận ra cùng pattern shipped ở scale lớn.

Tám Eloquent model — substrate audit

Data layer của module AI được shape quanh auditability: mọi conversation, mọi invocation model, mọi tool call, mọi user feedback, và mọi blob raw provider output được capture cho replay và observability. Tám model cover substrate đó:

ModelTableĐại diện cái gì
AIConversationai_conversationsMột row mỗi session agent / support multi-turn. Carry FK customer + user, task key, route screen, và token / cost total rolled-up.
AIMessageai_messagesMột row mỗi turn user / agent. Role, content JSON, FK tới tool-call, latency, model used.
AIRequestai_requestsMột row mỗi API call upstream. Engine, prompt hash, latency, cost, error status. Bridge AIMessage tới HTTP traffic thực tế.
AIToolCallai_tool_callsInvocation function-call spawn bởi turn agent. Tool name, input/output JSON, source flag.
AIFeedbackai_feedbackThumbs-up/down + free-text feedback mỗi message + mỗi conversation.
AIRawBlobai_raw_blobsRaw provider response gốc, kept cho replay / audit. Table riêng vì table rollup cần stay small.
AIDailyRollupai_daily_rollupAggregate per-day cho dashboard observability admin — token total, cost, error rate. Pre-aggregated để dashboard read cheaply.
AIToolUndoRecordai_tool_undo_recordsTrack reversible tool action cho feature "undo last".

Ba pattern từ list này translate trực tiếp sang plugin khác. Split "raw provider response" thành table riêng khỏi "rolled-up summary" giúp table rollup stay nhỏ đủ để scan. FK nullable tới customersusers giúp cùng row work cho cả traffic authenticated lẫn anonymous. Rollup one-row-per-day cho admin dashboard read cheap mà không cần JOIN nặng vào table activity.

Mười bốn migration one-line mỗi cái

Filename migration trong storage/app/plugins/acelle/ai/database/migrations/ kể câu chuyện riêng của chúng — additive theo thời gian, immediately reversible, không bao giờ là một schema move destructive:

FilenameLàm gì
2026_04_28_000001_create_ai_conversations_table.phpSession chat multi-turn — uid, FK customer_id, status enum, rollup token / cost
2026_04_28_000002_create_ai_messages_table.phpMột turn user / agent — role, content JSON, FK tool-call, latency, model used
2026_04_28_000003_create_ai_requests_table.phpMột row mỗi API call upstream — engine, prompt hash, latency, cost, error
2026_04_28_000004_create_ai_tool_calls_table.phpInvocation function-call spawn bởi turn agent — input / output JSON
2026_04_28_000005_create_ai_feedback_table.phpThumbs-up/down + free-text feedback mỗi message + mỗi conversation
2026_04_28_000006_create_ai_raw_blobs_table.phpRaw provider response gốc, kept cho replay / audit
2026_04_28_000007_create_ai_daily_rollup_table.phpAggregate per-day cho admin dashboard — token total, cost, error rate
2026_04_29_000001_add_client_message_id_to_ai_messages.phpColumn dedup cross-tab — additive, không default
2026_04_30_000002_add_source_to_ai_tool_calls.phpTrack xem một tool call đến từ route agent hay support
2026_05_02_180000_widen_ai_conversations_client_session_uid.phpFix width ULID / UUID — migration column-altering, fully reversible
2026_05_02_200000_create_ai_tool_undo_records_table.phpTrack reversible tool action cho feature "undo last"
2026_05_03_000001_add_url_sanitization_to_ai_requests.phpAdd column JSON cho telemetry URL sanitised
2026_05_04_000001_create_ai_settings_table.phpSettings admin plugin-level — kept riêng khỏi plugins.data để mỗi row có thể indexed

Đọc migration top-to-bottom và bạn có evolution schema của toàn bộ module AI. Mọi migration additive là một feature ship — shape additive là cái làm rollback delete_plugin_* của plugin luôn work, kể cả khi admin uninstall sau một năm feature accretion.

Mọi hook plugin sử dụng

ServiceProvider của plugin exercise mọi hook pattern trừ FILTER. Grep Hook::* trên storage/app/plugins/acelle/ai/src/ServiceProvider.php:

REGISTRY (Hook::add) — sáu contribution

  • Chín entry add_translation_file tại register() line 175-197 — một per translation surface (rewrite, chatbox, prompts, wait, subject AB, settings, admin usage, audit, permissions). Mỗi surface là một file editable riêng trong UI admin Languages.
  • layout.head.assets tại boot() line 688 — contribute CSS + JS chatbox cho mọi page extend master layout app / admin.
  • layout.body.before_close tại boot() line 699 — contribute HTML bubble chatbox và popover sparkle trước </body> của mọi page.
  • admin.sidebar.groups tại boot() line 718 — add group AI với ba hay bốn child link vào sidebar admin.

Cả sáu gate chính nó với aiPluginAvailable() — một helper cuối cùng resolve về Plugin::getByName('acelle/ai')->isActive(). Return null khi plugin bị gate off là cách conventional để biến mất sạch sẽ mà không cần unload service provider.

EVENT (Hook::on) — hai lifecycle listener

  • activate_plugin_acelle/ai tại line 472 — run artisan migrate đối với folder migration của plugin.
  • delete_plugin_acelle/ai tại line 546 — accept flag $keepData, rollback migration khi không set, preserve table audit khi set.

BEHAVIOR (Hook::set) — một override icon URL

  • icon_url_acelle/ai tại line 522 — override hook BEHAVIOR per-plugin để trang admin Plugins render icon riêng của module AI thay vì plugin.svg default của host.

Cùng nhau những cái này là hầu hết hook surface của plugin. Đọc ServiceProvider.php top-to-bottom là cách nhanh nhất để thấy pattern combine ra sao trong production.

UI surface chatbox

Plugin contribute ba universal Blade component trong resources/views/components/ — không cái nào cần host application biết chúng tồn tại:

  • <x-mc-ai-chatbox> — bubble chatbox floating mở vào conversation agent multi-turn. Mount qua hook REGISTRY layout.body.before_close để nó xuất hiện trên mọi page app + admin.
  • <x-mc-ai-rewrite> — affordance "rewrite this text" universal có thể drop bên cạnh bất kỳ textarea nào trong host. Plugin-namespaced, không cần registration central.
  • <x-mc-ai-subject-ab-generator> — generate variant subject-line A/B từ prompt. Used trong campaign editor.

Ba component này show pattern cho "plugin contribute UI cho nhiều host page mà không fork mỗi page": ship component như anonymous Blade component dưới namespace view của plugin của bạn, register nó qua hook layout REGISTRY cho global mounting, hoặc để host page opt-in bằng cách include trực tiếp. Cả hai pattern đều work; plugin AI dùng cả hai.

Chín file × mười tám locale

register() của plugin register chín file translation riêng biệt. Machinery Language::dump() sau đó materialise mỗi cái thành mười bảy folder runtime non-English locale dưới storage/app/data/plugins/acelle/ai/lang/. Result trên disk: 153 runtime translation file (9 file × 17 non-English locale + 9 English original = 162 minus 9 English master = 153 dump-clone), mỗi cái editable riêng qua UI admin Languages của host.

Chín file surface (registered qua loop $aiLangFiles trong ServiceProvider::register()):

  • ai_rewrite — universal text rewrite component
  • ai_chatbox — UI chatbox
  • ai_chatbox_prompts — prompt pre-canned show trong chatbox
  • ai_chatbox_wait — UI smart-wait ("looking up… running tool… composing reply")
  • ai_subject_ab — subject A/B generator
  • ai_settings — label trang settings admin
  • admin_ai_usage — dashboard usage / cost admin
  • admin_ai_audit — UI audit / replay admin
  • admin_ai_permissions — toggle permission per-feature admin

Split này là câu trả lời thực tế cho "làm sao tôi keep file translation đủ nhỏ để translator có thể edit một cái trong một lần ngồi": một master mỗi surface logic, registered riêng, materialised per locale, edit độc lập qua UI admin.

Config file plugin-owned

Plugin own hai file config dưới config/ — registered trong ServiceProvider::boot() qua $this->mergeConfigFrom() và reachable qua helper standard config('ai.*')config('ai-navigation-hints.*'). Config plugin-owned là nơi đúng cho metadata static không change per-install (engine catalog, prompt template, navigation default); setting admin-editable sống trong table ai_settings seeded bởi migration.

Split — config cho default immutable plugin-shipped, row DB cho setting mutable admin-editable — là một pattern port sạch sẽ sang plugin khác. Đặt cả hai trong JSON column plugins.data là tempting nhưng punish UI admin performance; một table indexed dedicated là quyết định đúng.

Test infrastructure

Test directory của plugin follow shape tests/ của host — folder Unit + Feature cộng một base class PluginTestCase tại root:

storage/app/plugins/acelle/ai/tests/
├── PluginTestCase.php          ← seeds the plugin row as active before every test
├── Feature/
│   ├── AIHandler/              ← engines, agent loop, tools, observability writer
│   └── PluginLifecycle/        ← lifecycle integration tests
├── Unit/                       ← isolated unit tests (no Laravel boot for some)
├── Fixtures/                   ← test fixtures + factories
├── Snapshots/                  ← Pest snapshot artefacts
└── Support/                    ← test-only helpers

phpunit.xml của host register testsuite của plugin là <testsuite name="Plugin: acelle/ai">. ./vendor/bin/pest --testsuite="Plugin: acelle/ai" run full suite song song với suite Unit + Feature riêng của host.

PluginTestCase tại root demonstrate trap per-request gate-cache reset mà mọi plugin shipping middleware nên adopt — xem Testing § gate-cache trap cho full pattern. Không có nó, test thứ hai trong suite trở đi observe stale cache state từ boot của test thứ nhất.

Cách học từ nó

Đọc mọi line của một plugin trăm-nghìn-token không phải bài tập đúng. Một công thức bốn-bước hữu ích hơn:

  1. Clone hoặc symlink plugin vào host install. Activate nó. Mở sidebar admin — group AI nên xuất hiện. Click "Settings" trên entry trang admin Plugins — dashboard plugin-landing nên load. Cái này prove UI surface của plugin được wired vào host local của bạn.
  2. Đọc src/ServiceProvider.php top-to-bottom. Bốn mươi phút. Mọi khái niệm covered trong Plugin architecture + Hook system + UI injection + Translations xuất hiện trong một file này ở production scale. Cross-reference với các trang deep-dive khi bạn đi.
  3. Trace một feature end-to-end. Pick bubble chatbox. Tìm entry point (hook layout.body.before_close trong ServiceProvider), follow partial nó return (ai::partials.body_assets), tìm anonymous component render bubble, tìm JS mount nó, tìm route API JS call, tìm controller, follow nó xuống runtime engine AIHandler, watch row AIConversation + AIMessage + AIRequest được insert. Hai-tới-ba giờ, end-to-end. Sau cái này bạn sẽ biết plugin AI dùng pattern nào và không dùng pattern nào.
  4. Adapt một pattern sang plugin của riêng bạn. Pick cái nhỏ nhất map được — thường là loop registration per-translation-surface, hoặc group sidebar admin, hoặc approach table rollup per-day. Strip out code AI-specific; keep pattern structural. Đó là gain productivity day-one của plugin author.

Đi tiếp đâu

Đây là cái cuối trong mười một deep-dive developer. Từ đây, hub index là recap tự nhiên: Documentation index show mọi trang trong cluster organised vào Foundation / Building / Quality / Reference. Trang landing developer là entry point marketing-shaped cho visitor mới đến từ search.

Hai bước kế tiếp thực tế khi bạn ready ship một plugin thực: cycle activate → test → delete từ deep-dive Testing prove hook listener delete_plugin_* của plugin cleanup đúng. Plugin architecture § Recovery from broken state là trang nên bookmark cho production runtime issue — ba failure mode cộng fix path chính xác cho mỗi cái.

Beyond pattern AcelleMail-specific, ecosystem Laravel rộng hơn áp dụng. Code plugin là vanilla Laravel; runtime loader của host là phần non-standard duy nhất. Bất cứ thứ gì bạn biết về Eloquent, Blade, Pest, queue, scheduling, hoặc middleware work bên trong folder plugin unchanged.