Перейти к содержимому

А давайте встроим ии в powershell. Часть вторая. Поисковик спецификаций.

  • автор:

В прошлый раз мы увидели, как с помощью PowerShell можно взаимодействовать с моделью Gemini через интерфейс командной строки. В этой статье я покажу, как извлечь пользу из наших знаний. Мы превратим консоль в интерактивный справочник, который будет принимать на вход идентификатор компонента (марка, модель, категория, артикул и т. п.) и возвращать интерактивную таблицу с характеристиками, полученную от модели Gemini.

Что мы будем использовать:
Gemini-CLI
OutConsoleGridView

Инженеры, разработчики и другие специалисты часто сталкиваются с задачей: нужно быстро узнать точные характеристики — будь то материнская плата, автомат в электрощитке или сетевой коммутатор. Наш интерактивный справочник всегда под рукой: по запросу он соберёт сведения, уточнит параметры в интернете и вернёт готовую таблицу. В таблице можно выбрать интересующие параметры и, при необходимости, запустить углублённый поиск. В дальнейшем мы научимся передавать результат по конвейеру для дальнейшей обработки: экспорта в Excel или Google Таблицы, хранения в базе данных или интеграции с другими программами. А если запрос окажется неудачным, модель подскажет, какие данные стоит уточнить.

Впрочем, лучше один раз увидеть:

Шаг 1: Настройка

# --- Шаг 1: Настройка ---
$env:GEMINI_API_KEY = "AIzaSyCbq8bkt5Xr2hlE-73MIXFpdFYH-rLBd0k"
if (-not $env:GEMINI_API_KEY) { Write-Error "..."; return }

$scriptRoot = Get-Location
# --- ИЗМЕНЕНИЕ: Переменная переименована ---
$HistoryDir = Join-Path $scriptRoot ".gemini/.chat_history"
# --- КОНЕЦ ИЗМЕНЕНИЯ ---
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$historyFileName = "ai_session_$timestamp.jsonl"
$historyFilePath = Join-Path $HistoryDir $historyFileName

Назначение строк:

  • $env:GEMINI_API_KEY = "..." — устанавливает API ключ для доступа к Gemini AI
  • if (-not $env:GEMINI_API_KEY) — проверяет наличие ключа, завершает работу если его нет
  • $scriptRoot = Get-Location — получает текущую рабочую директорию
  • $HistoryDir = Join-Path... — формирует путь к папке для хранения истории диалогов (.gemini/.chat_history)
  • $timestamp = Get-Date... — создает временную метку в формате 2025-08-26_14-30-15
  • $historyFileName = "ai_session_$timestamp.jsonl" — генерирует уникальное имя файла сессии
  • $historyFilePath = Join-Path... — создает полный путь к файлу истории текущей сессии

Проверка окружения — что должно быть установлено.

# --- Шаг 2: Проверка окружения ---
try { Get-Command gemini -ErrorAction Stop | Out-Null } 
catch { Write-Error "Команда 'gemini' не найдена..."; return }

if (-not (Test-Path (Join-Path $scriptRoot ".gemini/GEMINI.md"))) { 
    Write-Warning "Файл системного промпта .gemini/GEMINI.md не найден..." 
}
if (-not (Test-Path (Join-Path $scriptRoot ".gemini/ShowHelp.md"))) { 
    Write-Warning "Файл справки .gemini/ShowHelp.md не найден..." 
}

Что проверяется:

  • Наличие Gemini CLI в системе — без него скрипт не работает
  • Файл GEMINI.md — содержит системный промпт (инструкции для AI)
  • Файл ShowHelp.md — справка для пользователя (команда ?)

Основная функция взаимодействия с AI.

function Invoke-GeminiPrompt {
    param([string]$Prompt, [string]$Model)
    try {
        $output = & gemini -m $Model -p $Prompt 2>&1
        if (-not $?) { $output | ForEach-Object { Write-Warning $_.ToString() }; return $null }
        
        $outputString = ($output -join [Environment]::NewLine).Trim()
        $cleanedOutput = $outputString -replace "(?m)^Data collection is disabled\.`r?`n" , ""
        $cleanedOutput = $cleanedOutput -replace "(?m)^Loaded cached credentials\.`r?`n", ""
        
        return $cleanedOutput.Trim()
    }
    catch { Write-Error "Критическая ошибка при вызове Gemini CLI: $_"; return $null }
}

Задачи функции:

  • Вызывает Gemini CLI с указанной моделью и промптом
  • Захватывает все выводы (включая ошибки)
  • Очищает результат от служебных сообщений CLI
  • Возвращает чистый ответ AI или $null при ошибке

Функции управления историей.

function Add-History { 
    param([string]$UserPrompt, [string]$ModelResponse)
    if (-not (Test-Path $HistoryDir)) { New-Item -Path $HistoryDir -ItemType Directory | Out-Null }
    @{ user = $UserPrompt } | ConvertTo-Json -Compress | Add-Content -Path $historyFilePath
    @{ model = $ModelResponse } | ConvertTo-Json -Compress | Add-Content -Path $historyFilePath
}

function Show-History {
    if (-not (Test-Path $historyFilePath)) { Write-Host "История текущей сессии пуста." -ForegroundColor Yellow; return }
    Write-Host "`n--- История текущей сессии ---" -ForegroundColor Cyan
    Get-Content -Path $historyFilePath
    Write-Host "------------------------------------`n" -ForegroundColor Cyan
}

function Clear-History {
    if (Test-Path $historyFilePath) {
        Remove-Item -Path $historyFilePath -Force -ErrorAction Stop
        Write-Host "История текущей сессии ($historyFileName) была удалена." -ForegroundColor Yellow
    }
}

Назначение:

  • Add-History — сохраняет пары «вопрос-ответ» в JSONL формате
  • Show-History — показывает содержимое файла истории
  • Clear-History — удаляет файл истории текущей сессии

Функция отображения выбранных данных

function Show-SelectionTable {
    param([array]$SelectedData)
    
    if ($null -eq $SelectedData -or $SelectedData.Count -eq 0) { return }
    
    Write-Host "`n--- ВЫБРАННЫЕ ДАННЫЕ ---" -ForegroundColor Yellow
    
    # Получить все уникальные свойства из выбранных объектов
    $allProperties = @()
    foreach ($item in $SelectedData) {
        if ($item -is [PSCustomObject]) {
            $properties = $item | Get-Member -MemberType Properties | Select-Object -ExpandProperty Name
            $allProperties = $allProperties + $properties | Sort-Object -Unique
        }
    }
    
    # Показать таблицу или список
    if ($allProperties.Count -gt 0) {
        $SelectedData | Format-Table -Property $allProperties -AutoSize -Wrap
    } else {
        for ($i = 0; $i -lt $SelectedData.Count; $i++) {
            Write-Host "[$($i + 1)] $($SelectedData[$i])" -ForegroundColor White
        }
    }
    
    Write-Host "-------------------------" -ForegroundColor Yellow
    Write-Host "Выбрано элементов: $($SelectedData.Count)" -ForegroundColor Magenta
}

Задача функции: После выбора элементов в Out-ConsoleGridView показывает их в консоли в виде аккуратной таблицы, чтобы пользователь видел, что именно выбрал.

Основной рабочий цикл.

while ($true) {
    # Показ приглашения с индикацией состояния
    if ($selectionContextJson) {
        Write-Host -NoNewline -ForegroundColor Green "🤖AI [Выборка активна] :) > "
    } else {
        Write-Host -NoNewline -ForegroundColor Green "🤖AI :) > "
    }
    
    $UserPrompt = Read-Host
    
    # Обработка служебных команд
    $commandResult = Command-Handler -Command $UserPrompt
    if ($commandResult -eq 'break') { break }
    if ($commandResult -eq 'continue') { continue }
    
    # Формирование полного промпта с контекстом
    $fullPrompt = @"
### ИСТОРИЯ ДИАЛОГА (КОНТЕКСТ)
$historyContent

### ДАННЫЕ ИЗ ВЫБОРКИ (ДЛЯ АНАЛИЗА)
$selectionContextJson

### НОВАЯ ЗАДАЧА
$UserPrompt
"@
    
    # Вызов AI и обработка ответа
    $ModelResponse = Invoke-GeminiPrompt -Prompt $fullPrompt -Model $Model
    
    # Попытка парсинга JSON и показ интерактивной таблицы
    try {
        $jsonObject = $jsonToParse | ConvertFrom-Json
        $gridSelection = $jsonObject | Out-ConsoleGridView -Title "Выберите строки..." -OutputMode Multiple
        
        if ($null -ne $gridSelection) {
            Show-SelectionTable -SelectedData $gridSelection
            $selectionContextJson = $gridSelection | ConvertTo-Json -Compress -Depth 10
        }
    }
    catch {
        Write-Host $ModelResponse -ForegroundColor Cyan
    }
    
    Add-History -UserPrompt $UserPrompt -ModelResponse $ModelResponse
}

Ключевые особенности:

  • Индикатор [Выборка активна] показывает, что есть данные для анализа
  • Каждый запрос включает всю историю диалога для поддержания контекста
  • AI получает и историю, и выбранные пользователем данные
  • Результат пытается отобразиться как интерактивная таблица
  • При неудаче парсинга JSON показывается обычный текст

Структура рабочей директории.

├── Find-Spec.ps1
├── .gemini/
│ ├── GEMINI.md # Системный промпт для AI
│ ├── ShowHelp.md # Справка пользователя
│ └── .chat_history/ # Папка с историей сессий
│ ├── ai_session_2025-08-26_10-15-30.jsonl
│ └── ai_session_2025-08-26_14-22-45.jsonl

При запуске скрипт первым делом настраивает рабочее окружение. Он устанавливает API ключ для доступа к Gemini AI, определяет текущую папку как базовую директорию и создает структуру для хранения файлов. Особое внимание уделяется истории диалогов — для каждой сессии создается уникальный файл с временной меткой, например ai_session_2025-08-26_14-30-15.jsonl.

Затем система проверяет, что все необходимые инструменты установлены. Она ищет Gemini CLI в системе, проверяет наличие файлов конфигурации (системный промпт и справка). Если что-то критично важное отсутствует, скрипт предупреждает пользователя или завершает работу.

Запуск интерактивного режима

После успешной инициализации скрипт переходит в интерактивный режим — показывает приветственное сообщение и ждет ввода от пользователя. Приглашение выглядит как 🤖AI :) >  и меняется на 🤖AI [Выборка активна] :) >  когда у системы есть данные для анализа.

Обработка пользовательского ввода

Каждый ввод пользователя сначала проверяется на служебные команды — ? (справка), history (история), clear (очистка), exit (выход). Если это служебная команда, она выполняется немедленно без обращения к AI.

Если это обычный запрос, система начинает формировать контекст для отправки в Gemini. Она читает всю историю текущей сессии из файла, добавляет данные из предыдущей выборки (если есть) и объединяет все это с новым запросом пользователя в единый промпт.

Взаимодействие с искусственным интеллектом

Сформированный промпт отправляется в Gemini через командную строку. Система вызывает gemini -m модель -p промпт, захватывает весь вывод и очищает его от служебных сообщений CLI. Если происходит ошибка на этом этапе, пользователь получает предупреждение, но скрипт продолжает работать.

Обработка ответа AI

Полученный от AI ответ система пытается интерпретировать как JSON. Если парсинг успешен, данные отображаются в интерактивной таблице Out-ConsoleGridView, где пользователь может выбрать интересующие строки. Если JSON не распознается, ответ показывается как обычный текст.

Работа с выборкой данных

Когда пользователь выбирает строки в таблице и нажимает OK, система делает две вещи. Во-первых, показывает выбранные данные прямо в консоли в виде аккуратной таблицы — так пользователь видит, что именно выбрал. Во-вторых, сохраняет эти данные в памяти для использования в следующем запросе.

Выбранные данные превращаются в JSON и становятся частью контекста для последующих обращений к AI. Это позволяет задавать уточняющие вопросы вроде «сравни их», «найди аналоги», «где дешевле» — AI понимает, о чем идет речь.

Ведение истории

Каждая пара «запрос пользователя — ответ AI» сохраняется в файл истории в формате JSONL. Это обеспечивает непрерывность диалога — AI «помнит» весь предыдущий разговор и может ссылаться на ранее обсуждавшиеся темы.

Цикл продолжается

После обработки запроса система возвращается к ожиданию нового ввода. Если у пользователя есть активная выборка, это отражается в приглашении командной строки. Цикл продолжается до тех пор, пока пользователь не введет команду выхода.

Практический пример работы

Представим, что пользователь запускает скрипт и вводит «RTX 4070 Ti Super»:

  1. Подготовка контекста: Система берет системный промпт из файла, добавляет историю (пока пустую) и новый запрос
  2. Обращение к AI: Полный промпт отправляется в Gemini с просьбой найти характеристики видеокарт
  3. Получение данных: AI возвращает JSON с массивом объектов, содержащих информацию о различных моделях RTX 4070 Ti Super
  4. Интерактивная таблица: Пользователь видит таблицу с производителями, характеристиками, ценами и выбирает 2-3 интересующие модели
  5. Отображение выборки: В консоли появляется таблица с выбранными моделями, приглашение меняется на [Выборка активна]
  6. Уточняющий запрос: Пользователь пишет «сравни производительность в играх»
  7. Контекстный анализ: AI получает и исходный запрос, и выбранные модели, и новый вопрос — дает детальное сравнение именно этих карт

Завершение работы

При вводе exit или quit скрипт корректно завершается, сохранив всю историю сессии в файл. Пользователь может в любой момент вернуться к этому диалогу, просмотрев содержимое соответствующего файла в папке .chat_history.

Вся эта сложная логика скрыта от пользователя за простым интерфейсом командной строки. Человек просто задает вопросы и получает структурированные ответы, а система берет на себя всю работу по поддержанию контекста, парсингу данных и управлению состоянием диалога.

Метки:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *