Assistente IA
O Doggy fornece um assistente IA global, presente em todas as páginas sob a forma de botão flutuante + painel deslizante lateral, profundamente integrado com as páginas através de um mecanismo de registo de contexto, e que suporta o ciclo de raciocínio ReAct e chamadas de ferramentas.
Visão geral
O assistente IA é construído sobre a stack symfony/ai-agent + symfony/ai-platform, ligando-se ao sistema de gateway LlmRouter da plataforma:
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 (injeta Tool)
→ LlmRouterPlatform::invoke → LlmRouter → LlmGatewayFactory
→ AgentProcessor::processOutput (ciclo ReAct)
← resposta em stringEntrada global
O painel do assistente IA é incluído incondicionalmente em templates/base.html.twig:
<body>
{% block body %}{% endblock %}
{% include 'admin/ai_chat.html.twig' %}
</body>- Abrir/fechar: botão flutuante no canto inferior direito
toggle-ai-chat/ai-chat-close - Entrada: Enter envia, Shift+Enter cria nova linha
- Estado de carregamento: a entrada é desativada durante o pedido, exibindo "Pensando..."
- Estilos:
public/sunui/admin/ai_chat.css
Registo de contexto de página
Cada página regista o seu contexto no assistente através de window.__AI_CONTEXT__:
window.__AI_CONTEXT__ = {
name: 'view_editor', // AiContextProviderInterface::getName() no backend
buildPrompt: function(msg) { // opcional, constrói a mensagem enviada ao backend
return '[当前视图ID: xxx]\n\n' + msg;
},
};Exemplo de registo do editor de vistas:
<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>Sistema de fornecedores de contexto
AiContextProviderInterface define o fornecedor de contexto:
namespace App\Service\AI\Runtime;
interface AiContextProviderInterface
{
public function getName(): string; // identificador de contexto
public function getRoleCode(): string; // código de papel do LlmRouter
public function getSystemPrompt(): string; // prompt de sistema do Agent
public function getToolProviders(): array; // array de objetos Tool
}Registo através da etiqueta app.ai_context:
App\Service\AI\Context\ViewEditorAiContext:
tags:
- { name: 'app.ai_context' }Adicionar contexto de página
- Criar uma classe que implemente
AiContextProviderInterface - Adicionar
tags: [{ name: 'app.ai_context' }]emservices.yaml - Definir
window.__AI_CONTEXT__ = { name: 'your_name' }no template da página
Runtime do Agent
AiAssistant é uma fábrica de Agent genérica; cada chamada chat() cria uma instância de Agent independente, garantindo que não há poluição de estado:
$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',
);Ciclo ReAct
AgentProcessor atua simultaneamente como processador de entrada e saída, implementando o ciclo de chamadas de ferramentas:
- processInput: injeta os metadados das ferramentas do Toolbox
- processOutput: quando o resultado é
ToolCallResult:- Adiciona o array de ToolCall ao MessageBag
- Executa ToolCall → escreve ToolResult de volta no MessageBag
- Invoca recursivamente até retornar
TextResult
Sistema de ferramentas
Baseado no atributo #[AsTool] de symfony/ai-agent para marcar métodos de ferramenta:
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 { ... }
}Ferramentas atuais do editor de vistas
| Ferramenta | Função |
|---|---|
view.getInfo | Obtém informações da vista (entidade, campos, layout) |
view.updateSectionConfig | Atualiza a configuração do layout |
view.updateFieldConfig | Atualiza a configuração dos campos |
view.listEntityFields | Lista os campos da entidade |
Princípios de design
- Cada ferramenta é um método com uma única responsabilidade
- Os nomes dos parâmetros coincidem com os do Schema JSON do
#[AsTool](mapeamento automático) - Retorna
array(convertido em texto de resultado da ferramenta)
Endpoint da API
POST /api/admin/ai/chat
{
"context": "view_editor",
"message": "帮我改成三列布局"
}Resposta de sucesso:
{
"code": 200,
"message": "success",
"data": { "reply": "已修改为三列布局。" }
}Ficheiros relacionados
| Ficheiro | Responsabilidade |
|---|---|
AiChatController.php | Recebe {context, message}, delega no Provider correspondente |
AiContextRegistry.php | Recolhe os serviços com a etiqueta app.ai_context, indexa por nome |
AiAssistant.php | Fábrica de Agent genérica |
LlmRouterPlatform.php | Ponte PlatformInterface (MessageBag ↔ array) |
ChatResultConverter.php | Dados de resposta → TextResult/ToolCallResult |
ViewEditorToolProvider.php | Métodos de ferramenta do editor de vistas |
ChromeDevToolsToolProvider.php | Ferramentas de depuração de browser CDP |
ViewFileToolProvider.php | Ferramentas de leitura/escrita de ficheiros de vista |