From 0f39945f4363501f9d4e15498d5bb85b650c4d38 Mon Sep 17 00:00:00 2001 From: Rodribm10 Date: Sat, 2 May 2026 11:55:13 -0300 Subject: [PATCH] =?UTF-8?q?feat(captain/mcp):=20get=5Fassistant=5Fscenario?= =?UTF-8?q?=20tool=20=E2=80=94=20Construtor=20copia=20identidade=20de=20ou?= =?UTF-8?q?tro=20agente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Construtor atende 'copiar maps/endereço/telefone/wifi da Lara' sem o admin redigitar. Tool retorna o markdown bruto do scenario (default Daniela_Reservas) do assistant fonte; LLM extrai os campos relevantes. Cobre o gap entre get_assistant_pricing (preços estruturados) e get_assistant_faqs (Q&As): essa retorna prompt CRU pra LLM interpretar campos não estruturados (contatos, links, wifi, persona). Hot-patched + USR1 no Puma e Construtor. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../app/services/captain/mcp/tool_registry.rb | 1 + .../mcp/tools/get_assistant_scenario_tool.rb | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 enterprise/app/services/captain/mcp/tools/get_assistant_scenario_tool.rb diff --git a/enterprise/app/services/captain/mcp/tool_registry.rb b/enterprise/app/services/captain/mcp/tool_registry.rb index 2d791d321..78c71acd0 100644 --- a/enterprise/app/services/captain/mcp/tool_registry.rb +++ b/enterprise/app/services/captain/mcp/tool_registry.rb @@ -22,6 +22,7 @@ class Captain::Mcp::ToolRegistry Captain::Mcp::Tools::ListAssistantsTool, Captain::Mcp::Tools::GetAssistantPricingTool, Captain::Mcp::Tools::GetAssistantFaqsTool, + Captain::Mcp::Tools::GetAssistantScenarioTool, Captain::Mcp::Tools::SaveAgentSpecTool # Captain::Mcp::Tools::HandoffTool — fluxo via automation hoje, MCP futuro ].freeze diff --git a/enterprise/app/services/captain/mcp/tools/get_assistant_scenario_tool.rb b/enterprise/app/services/captain/mcp/tools/get_assistant_scenario_tool.rb new file mode 100644 index 000000000..7308365e2 --- /dev/null +++ b/enterprise/app/services/captain/mcp/tools/get_assistant_scenario_tool.rb @@ -0,0 +1,69 @@ +# Tool MCP: retorna o texto completo de um cenário (instruction) de um +# assistente existente. +# +# Caso de uso: Construtor pergunta "copiar identidade/maps/wifi/telefone +# da Lara?". Tool retorna o markdown bruto do scenario solicitado pra o +# Construtor (LLM) extrair os campos relevantes. +# +# Diferente de get_assistant_pricing (que parseia preços) e +# get_assistant_faqs (que lista responses): essa retorna o RAW PROMPT +# pra LLM interpretar livremente. +class Captain::Mcp::Tools::GetAssistantScenarioTool < Captain::Mcp::Tools::BaseTool + DEFAULT_SCENARIO_TITLE = 'Daniela_Reservas'.freeze + MAX_CHARS = 20_000 + + class << self + def name + 'get_assistant_scenario' + end + + def description + 'Retorna o texto MARKDOWN completo de um cenário (prompt) de um ' \ + 'assistente existente. Use pra copiar identidade da unidade ' \ + '(endereço, telefone, WhatsApp, maps, wifi, etc), persona ou ' \ + 'qualquer outro detalhe que esteja no prompt do agente fonte. ' \ + 'Default scenario_title="Daniela_Reservas".' + end + + def input_schema + { + type: 'object', + properties: { + assistant_id: { + type: 'integer', + description: 'ID do assistente fonte.' + }, + scenario_title: { + type: 'string', + description: "Título do cenário (default: '#{DEFAULT_SCENARIO_TITLE}'). Use list_assistants pra ver opções.", + default: DEFAULT_SCENARIO_TITLE + } + }, + required: ['assistant_id'] + } + end + end + + def call(args, context:) # rubocop:disable Lint/UnusedMethodArgument, Metrics/AbcSize + assistant = Captain::Assistant.find_by(id: args['assistant_id']) + return error_response("Assistente #{args['assistant_id']} não encontrado.") if assistant.blank? + + title = args['scenario_title'].to_s.presence || DEFAULT_SCENARIO_TITLE + scenario = assistant.scenarios.find_by(title: title) + + if scenario.blank? + avail = assistant.scenarios.pluck(:title) + return error_response( + "Cenário '#{title}' não existe em #{assistant.name}. Disponíveis: #{avail.join(', ')}." + ) + end + + text = scenario.instruction.to_s + text = "#{text.first(MAX_CHARS)}\n\n... [truncado em #{MAX_CHARS} chars] ..." if text.size > MAX_CHARS + + text_response("# Cenário '#{title}' de #{assistant.name}\n\n#{text}") + rescue StandardError => e + Rails.logger.error("[Captain::Mcp::GetAssistantScenarioTool] error: #{e.class}: #{e.message}") + error_response("Erro ao buscar cenário: #{e.message}") + end +end