AI 助手
Doggy 提供全域 AI 助手,以浮動按鈕 + 側滑面板形式存在於所有頁面,透過上下文註冊機制與頁面深度整合,支援 ReAct 推理迴圈和工具呼叫。
概述
AI 助手基於 symfony/ai-agent + symfony/ai-platform 技術堆疊建置,串接平台自有的 LlmRouter 閘道體系:
Client POST /api/admin/ai/chat { context, message }
→ AiChatController
→ AiContextRegistry::get(context)
→ AiContextProviderInterface (e.g. ViewEditorAiContext)
→ AiAssistant::chat(roleCode, systemPrompt, toolProviders, message)
→ Agent::call(MessageBag, options)
→ AgentProcessor::processInput (注入 Tool)
→ LlmRouterPlatform::invoke → LlmRouter → LlmGatewayFactory
→ AgentProcessor::processOutput (ReAct 循环)
← string response全域入口
AI 助手面板在 templates/base.html.twig 中無條件引入:
twig
<body>
{% block body %}{% endblock %}
{% include 'admin/ai_chat.html.twig' %}
</body>- 開啟/關閉:右下角浮動按鈕
toggle-ai-chat/ai-chat-close - 輸入:Enter 傳送,Shift+Enter 換行
- 載入狀態:請求期間停用輸入,顯示「思考中...」
- 樣式:
public/sunui/admin/ai_chat.css
頁面上下文註冊
每個頁面透過 window.__AI_CONTEXT__ 向助手註冊上下文:
js
window.__AI_CONTEXT__ = {
name: 'view_editor', // 后端 AiContextProviderInterface::getName()
buildPrompt: function(msg) { // 可选,构造发送给后端的消息
return '[当前视图ID: xxx]\n\n' + msg;
},
};視圖編輯器註冊範例:
twig
<script>
window.__VIEW_ID__ = {{ id|json_encode|raw }};
window.__AI_CONTEXT__ = {
name: 'view_editor',
buildPrompt: function(msg) {
var vid = window.__VIEW_ID__;
return vid ? '[当前视图ID: ' + vid + ']\n\n' + msg : msg;
},
};
</script>上下文提供者系統
AiContextProviderInterface 定義上下文提供者:
php
namespace App\Service\AI\Runtime;
interface AiContextProviderInterface
{
public function getName(): string; // 上下文标识
public function getRoleCode(): string; // LlmRouter 角色代码
public function getSystemPrompt(): string; // Agent 系统提示词
public function getToolProviders(): array; // Tool 对象数组
}透過 app.ai_context 標籤註冊:
yaml
App\Service\AI\Context\ViewEditorAiContext:
tags:
- { name: 'app.ai_context' }新增頁面上下文
- 建立類別實作
AiContextProviderInterface - 在
services.yaml中新增tags: [{ name: 'app.ai_context' }] - 在頁面範本中設定
window.__AI_CONTEXT__ = { name: 'your_name' }
Agent 執行階段
AiAssistant 是泛化 Agent 工廠,每次 chat() 建立獨立的 Agent 實例,確保無狀態污染:
php
$platform = new LlmRouterPlatform($this->router, $this->converter);
$toolbox = new Toolbox($toolProviders);
$agent = new Agent(
platform: $platform,
model: $roleCode,
inputProcessors: [
new SystemPromptInputProcessor($systemPrompt),
new AgentProcessor($toolbox),
],
outputProcessors: [
new AgentProcessor($toolbox),
],
name: 'ai-assistant',
);ReAct 迴圈
AgentProcessor 同時作為輸入/輸出處理器,實作工具呼叫迴圈:
- processInput:注入 Toolbox 的 Tool 中繼資料
- processOutput:偵測結果為
ToolCallResult時:- 將 ToolCall 陣列加入 MessageBag
- 執行 ToolCall → 將 ToolResult 寫回 MessageBag
- 遞迴呼叫直到回傳
TextResult
工具體系
基於 symfony/ai-agent 的 #[AsTool] 屬性標記工具方法:
php
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
class ViewEditorToolProvider
{
#[AsTool(name: 'view.getInfo', description: '获取视图的完整信息')]
public function getViewInfo(string $viewId): array { ... }
#[AsTool(name: 'view.updateSectionConfig', description: '更新视图的布局配置')]
public function updateSectionConfig(string $viewId, string $contentWidth, ...): array { ... }
}目前的視圖編輯器工具
| Tool | 功能 |
|---|---|
view.getInfo | 取得檢視資訊(實體、欄位、版面配置) |
view.updateSectionConfig | 更新版面配置設定 |
view.updateFieldConfig | 更新欄位設定 |
view.listEntityFields | 列出實體欄位 |
設計原則
- 每個工具是單一職責的方法
- 參數名稱與
#[AsTool]JSON Schema 參數名稱一致(自動對應) - 回傳
array(轉為工具結果文字)
API 端點
POST /api/admin/ai/chat
json
{
"context": "view_editor",
"message": "帮我改成三列布局"
}成功回應:
json
{
"code": 200,
"message": "success",
"data": { "reply": "已修改为三列布局。" }
}相關檔案
| 檔案 | 職責 |
|---|---|
AiChatController.php | 接收 {context, message},委派對應的 Provider |
AiContextRegistry.php | 收集 app.ai_context 標籤服務,依 name 索引 |
AiAssistant.php | 泛化 Agent 工廠 |
LlmRouterPlatform.php | PlatformInterface 橋接(MessageBag ↔ 陣列) |
ChatResultConverter.php | 回應資料 → TextResult/ToolCallResult |
ViewEditorToolProvider.php | 視圖編輯器 Tool 方法 |
ChromeDevToolsToolProvider.php | CDP 瀏覽器除錯工具 |
ViewFileToolProvider.php | 視圖檔案讀寫工具 |