AI-Assistent
Doggy stellt einen globalen AI-Assistenten bereit, der als schwebender Button + seitliches Panel auf allen Seiten verfügbar ist, über einen Kontext-Registrierungsmechanismus tief in die Seiten integriert wird und eine ReAct-Inferenzschleife sowie Tool-Aufrufe unterstützt.
Übersicht
Der AI-Assistent basiert auf dem Technologie-Stack symfony/ai-agent + symfony/ai-platform und bindet an das eigene LlmRouter-Gateway-System der Plattform an:
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 responseGlobaler Einstiegspunkt
Das Panel des AI-Assistenten wird in templates/base.html.twig bedingungslos eingebunden:
<body>
{% block body %}{% endblock %}
{% include 'admin/ai_chat.html.twig' %}
</body>- Öffnen/Schließen: Schwebender Button unten rechts
toggle-ai-chat/ai-chat-close - Eingabe: Enter sendet, Shift+Enter erzeugt einen Zeilenumbruch
- Ladezustand: Die Eingabe wird während der Anfrage deaktiviert, Anzeige "Denke nach..."
- Stil:
public/sunui/admin/ai_chat.css
Kontext-Registrierung der Seiten
Jede Seite registriert über window.__AI_CONTEXT__ einen Kontext beim Assistenten:
window.__AI_CONTEXT__ = {
name: 'view_editor', // 后端 AiContextProviderInterface::getName()
buildPrompt: function(msg) { // 可选,构造发送给后端的消息
return '[当前视图ID: xxx]\n\n' + msg;
},
};Registrierungsbeispiel des View-Editors:
<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>System der Kontext-Anbieter
AiContextProviderInterface definiert die Kontext-Anbieter:
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 对象数组
}Registrierung über das app.ai_context-Tag:
App\Service\AI\Context\ViewEditorAiContext:
tags:
- { name: 'app.ai_context' }Seitenkontext hinzufügen
- Klasse erstellen, die
AiContextProviderInterfaceimplementiert - In
services.yamltags: [{ name: 'app.ai_context' }]hinzufügen - Im Seiten-Template
window.__AI_CONTEXT__ = { name: 'your_name' }setzen
Agent-Laufzeit
AiAssistant ist eine generalisierte Agent-Factory, die bei jedem chat() eine unabhängige Agent-Instanz erstellt, um Zustandsverschmutzung zu vermeiden:
$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-Schleife
AgentProcessor fungiert gleichzeitig als Ein- und Ausgabeprozessor und realisiert die Tool-Aufruf-Schleife:
- processInput: Injiziert die Tool-Metadaten des Toolbox
- processOutput: Wenn das Ergebnis
ToolCallResultist:- ToolCall-Array in die MessageBag aufnehmen
- ToolCall ausführen → ToolResult in die MessageBag zurückschreiben
- Rekursiv aufrufen, bis ein
TextResultzurückgegeben wird
Tool-System
Tool-Methoden werden über das Attribut #[AsTool] von symfony/ai-agent markiert:
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 { ... }
}Aktuelle View-Editor-Tools
| Tool | Funktion |
|---|---|
view.getInfo | Ansichtsinformationen abrufen (Entität, Felder, Layout) |
view.updateSectionConfig | Layout-Konfiguration aktualisieren |
view.updateFieldConfig | Feldkonfiguration aktualisieren |
view.listEntityFields | Entitätsfelder auflisten |
Design-Prinzipien
- Jedes Tool ist eine Methode mit einer einzigen Verantwortung
- Parameternamen stimmen mit den JSON-Schema-Parameternamen von
#[AsTool]überein (automatische Zuordnung) - Gibt
arrayzurück (wird in Tool-Ergebnistext umgewandelt)
API-Endpunkte
POST /api/admin/ai/chat
{
"context": "view_editor",
"message": "帮我改成三列布局"
}Erfolgreiche Antwort:
{
"code": 200,
"message": "success",
"data": { "reply": "已修改为三列布局。" }
}Zugehörige Dateien
| Datei | Verantwortung |
|---|---|
AiChatController.php | Empfängt {context, message} und delegiert an den passenden Provider |
AiContextRegistry.php | Sammelt app.ai_context-Tag-Dienste, indexiert nach Name |
AiAssistant.php | Generalisierte Agent-Factory |
LlmRouterPlatform.php | PlatformInterface-Brücke (MessageBag ↔ Array) |
ChatResultConverter.php | Antwortdaten → TextResult/ToolCallResult |
ViewEditorToolProvider.php | Tool-Methoden des View-Editors |
ChromeDevToolsToolProvider.php | CDP-Browser-Debugging-Tools |
ViewFileToolProvider.php | Lese-/Schreibtools für Ansichtsdateien |