ИИ-агент двойного назначения
- суббота, 30 мая 2026 г. в 00:00:14
В наши дни каждый разработчик, наверняка, пробовал вайбкодить, а некоторые идут дальше и заводят себе целых ИИ агентов. Однако отовсюду доносятся новости о том, как какой-то AI агент удалил базу данных со всеми бэкапами. Поэтому давайте посмотрим исходных код проектов, которые так или иначе связаны с агентной разработкой.

Агентская разработка и различные AI-решения всё теснее входит в разработку программного обеспечения. Поэтому мы решили проверить нашим Go анализатором проекты, которые как-то связаны с нейронными сетями и разработкой с помощью AI. Это различные серверы для запуска локальных моделей, балансировщики нагрузок, прокси-сервисы для подписок и многое другое. К тому же такие проекты достаточно популярные и пользуются спросом.
Если вам интересно собственноручно попробовать наш Go анализатор, то вы можете принять участие в программе раннего доступа. EAP доступно для языков JavaScript, TypeScript и Go.
Как говорится, куй железо, пока горячо, поэтому приступим к проверке проектов.
Начнём с агрегатора и балансировщика нагрузки для множества AI-провайдеров — new-api. Он позволяет управлять десятками API-ключей от разных провайдеров через единый шлюз. Это достаточно крупный и популярный проект (33 тыс. звёзд на GitHub), но даже в таких проектах встречаются баги и подозрительный код. Коммит 22b6b16.
func GetChannel(group string, model string, retry int) (*Channel, error) { var abilities []Ability var err error = nil channelQuery, err := getChannelQuery(group, model, retry) if err != nil { return nil, err } if common.UsingSQLite || common.UsingPostgreSQL { err = channelQuery.Order("weight DESC").Find(&abilities).Error } else { err = channelQuery.Order("weight DESC").Find(&abilities).Error } .... }
Предупреждение PVS-Studio: V8005 The ‘then’ statement is equivalent to the ‘else’ statement. ability.go 114
Анализатор сообщает, что if с условием common.UsingSQLite || common.UsingPostgreSQL по сути бесполезен, поскольку в then и else части выполняется одинаковый код.
Аналогичная проблема была найдена в следующем отрывке кода:
func StreamResponseClaude2OpenAI(....) .... { .... if claudeResponse.Type == "message_start" { .... } else if claudeResponse.Type == "content_block_start" { .... } else if claudeResponse.Type == "content_block_delta" { .... } else if claudeResponse.Type == "message_delta" { .... } else if claudeResponse.Type == "message_stop" { return nil } else { return nil } .... }
Предупреждение PVS-Studio: V8005 The ‘then’ statement is equivalent to the ‘else’ statement. relay-claude.go 474
Сложно понять, действительно ли это ошибка, либо же для claudeResponse.Type == "message_stop" пока попросту отсутствует логика.
Теперь рассмотрим срабатывание в коде tau — open-source платформе для создания автономных облачных инфраструктур. Коммит 1e5036f.
func (p *pluginInstance) makeFunc(....) reflect.Value { .... _out := make([]reflect.Value, len(cOut)) for idx := 0; idx < len(cOut); idx++ { switch retTypes[idx] { case vm.I32Type: _out[idx] = reflect.ValueOf(int32(cOut[idx])) case vm.I64Type: // <= _out[idx] = reflect.ValueOf(int64(cOut[idx])) case vm.F32Type: _out[idx] = reflect.ValueOf(math.Float32frombits(uint32(cOut[idx]))) case vm.I64Type: // <= _out[idx] = reflect.ValueOf(math.Float64frombits(cOut[idx])) } } .... }
Предупреждение PVS-Studio: V8010 Two or more case branches have equivalent expressions. instance.go 93
Здесь мы можем наблюдать, что для разных case используется одно и то же выражение I64Type. В таком коде нет смысла, поскольку код внутри второго кейса никогда не будет выполнен. И, скорее всего, вместо второго I64Type предполагалось использовать F64Type:
switch retTypes[idx] { case vm.I32Type: _out[idx] = reflect.ValueOf(int32(cOut[idx])) case vm.I64Type: _out[idx] = reflect.ValueOf(int64(cOut[idx])) case vm.F32Type: _out[idx] = reflect.ValueOf(math.Float32frombits(uint32(cOut[idx]))) case vm.F64Type: _out[idx] = reflect.ValueOf(math.Float64frombits(cOut[idx])) }
Кстати, подобный паттерн ошибок также был найден в коде популярного блокировщика рекламы AdGuardHome. Об этом мы писали в статье “Go vet не поможет. Статический анализ Golang проектов с помощью PVS-Studio”.
Перейдём к прокси-серверу для AI-сервисов — Sub2API. Коммит 0f03393.
func cleanJSONSchemaRecursive(value any) any { .... if hasKey(schemaMap, "properties") { schemaMap["type"] = "object" } else { // 默认为 string ? or object? Gemini 通常需要明确 type schemaMap["type"] = "object" } .... }
Предупреждение PVS-Studio: V8005 The ‘then’ statement is equivalent to the ‘else’ statement. schema_cleaner.go 310
В этом фрагменте кода в then или else частях, возможно, есть ошибка, поскольку они одинаковые. В лучшем случае if является лишним и приводит к тому, что код тяжелее читать.
Рассмотрим другой фрагмент:
func classifyOpsPhase(errType, message, code string) string { .... switch errType { case "authentication_error": return "auth" case "billing_error", "subscription_error": return "request" // <= case "rate_limit_error": if .... { return "request" } return "upstream" case "invalid_request_error": return "request" // <= case "upstream_error", "overloaded_error": return "upstream" case "api_error": if strings.Contains(msg, opsErrNoAvailableAccounts) { return "routing" } return "internal" default: return "internal" } }
Предупреждение PVS-Studio: V8009 Two or more case branches perform the same actions. ops_error_logger.go 1125
Анализатор нашёл ветви конструкции switch с одинаковым телом. Срабатывания этого диагностического правила встречаются достаточно часто, поскольку для некоторых кейсов нужно иметь одинаковый исполняемый код, и некоторые не любят использовать запятую для перечисления условий.
Видим, что первый кейс, который возвращает значение request, содержит в себе несколько выражений case "billing_error", "subscription_error". И в таком случае было бы логично через запятую написать выражение case "invalid_request_error" из второго кейса, если они всегда должны приводить к одному и тому же исходу.
Однако стоит обратить внимание на другой кейс:
case "rate_limit_error": if .... { return "request" } return "upstream"
Отсюда также может быть возвращено значение request, но здесь есть дополнительная обработка. Это приводит нас к мысли, что, возможно, во втором кейсе с одинаковым телом также должна быть какая-то дополнительная обработка. Но из контекста сложно понять, что именно должно быть, поэтому анализатор подсвечивает странный фрагмент кода.
Похожий случай в функции classifyopsErrorSource:
func classifyOpsErrorSource(phase string, message string) string { // Standardized sources: client_request|upstream_http|gateway switch phase { case "upstream": return "upstream_http" case "network": return "gateway" // <= case "request", "auth": return "client_request" case "routing", "internal": return "gateway" // <= default: if strings.Contains(strings.ToLower(message), "upstream") { return "upstream_http" } return "gateway" } }
Предупреждение PVS-Studio: V8009 Two or more case branches perform the same actions. ops_error_logger.go 1220
С одной стороны, возможно, это не ошибка и стоит объединить case "routing", "internal" с case "network". А с другой стороны, есть вероятность, что не хватает какого-то дополнительного кода, как в кейсе default.
PhotoPristm — self-hosted менеджер фотографий с открытым исходным кодом. Проект интересен тем, что обладает встроенным AI и REST API, который позволяет агенту управлять фотобиблиотекой. Коммит 93bb435.
func (c *opticsClusterer) extract() { .... switch { case math.Abs(d) <= c.xi: cs = areas[j].start ce = ue case d > c.xi: for k := areas[j].end; k > areas[j].end; k-- { // <= if ....{ cs = k break } } ce = ue default: cs = areas[j].start for k := i; k < e; k++ { if ....{ ce = k break } } } .... }
Предупреждение PVS-Studio: V8016 The loop condition will never be met. Inspect initial and final values in the ‘for’ loop. optics.go 321
Анализатор сообщает, что условие for никогда не будет выполнено, поскольку начальное и конечное значение равны areas[j].end. Скорее всего, здесь опечатка, и условие должно выглядеть следующим образом:
for k := areas[j].end; k > areas[j].start; k-- { .... }
Ещё один прокси — axonhub. Коммит bfc11e01.
func AggregateStreamChunks(....) ([]byte, llm.ResponseMeta, error) { .... if event.Delta.Thinking != nil { if contentBlocks[index].Type == "thinking" { if contentBlocks[index].Thinking == nil { contentBlocks[index].Thinking = lo.ToPtr("") } *contentBlocks[index].Thinking += *event.Delta.Thinking } else { // Convert to thinking block if it's not already contentBlocks[index].Type = "thinking" contentBlocks[index].Thinking = event.Delta.Thinking } } if event.Delta.Signature != nil { // <= // Handle signature delta - append to thinking block signature if contentBlocks[index].Type == "thinking" { if event.Delta.Signature != nil { // <= if contentBlocks[index].Signature == nil { contentBlocks[index].Signature = event.Delta.Signature } else { contentBlocks[index].Signature = lo.ToPtr(....) } } } else { // Convert to thinking block if it's not already contentBlocks[index].Type = "thinking" contentBlocks[index].Signature = event.Delta.Signature } } .... }
Предупреждение PVS-Studio: V8020 Recurring check. The ‘event.Delta.Signature != nil’ condition was already verified on line 96. aggregator.go 96
Анализатор обнаружил рекурсивную проверку event.Delta.Signature != nil. Повторная проверка бессмысленна, поскольку она всегда будет истинной.
На очереди LocalAI — открытый движок для локального использования моделей искусственного интеллекта. Коммит dd8e74a.
func (r *RunCMD) Run(ctx *cliContext.Context) error { .... if r.DisableMetricsEndpoint { opts = append(opts, config.DisableMetricsEndpoint) } if r.DisableRuntimeSettings { opts = append(opts, config.DisableRuntimeSettings) } if r.EnableTracing { opts = append(opts, config.EnableTracing) } if r.EnableTracing { opts = append(opts, config.EnableTracing) } opts = append(opts, config.WithTracingMaxItems(r.TracingMaxItems)) .... }
Предупреждение PVS-Studio: V8017 The conditions of the ‘if’ statements situated alongside each other are equivalent. Check lines: 158, 162. run.go 158
Можно заметить, что в функции Run дублируются строчки:
if r.EnableTracing { opts = append(opts, config.EnableTracing) }
С большой вероятностью это ошибка, которая может привести к тому, что opts = append(opts, config.EnableTracing) будет выполнена два раза, если r.EnableTracing имеет значение true.
Также возможно, что вместо второго r.EnableTracing и config.EnableTracing должно быть что-то другое, и часть функционала попросту утеряна. Скорее всего, эта ошибка допущена в результате copy-paste.
Перейдём к следующему срабатыванию:
func parseXMLWithFormat(s string, format *XMLToolCallFormat) (....) { .... for _, match := range toolCallMatches { if len(match) < 3 { continue } .... var functionContent string if len(match) >= 3 { if format.ToolSep == "" && format.KeyStart != "" { functionContent = match[2] } else { functionContent = match[2] } } .... } .... }
Предупреждение PVS-Studio: V8005 The ‘then’ statement is equivalent to the ‘else’ statement. parse.go 902
Невооружённым глазом видно, что then и else блоки идентичны, и в таком случае нет смысла использовать if.
Скорее всего, это ошибка copy-paste, и во второй строке functionContent = match[2] забыли изменить индекс.
И сразу три срабатывания V8005 было выдано на три похожих фрагмента:
func ChatEndpoint(....) echo.HandlerFunc { .... if len(cleanedContent) > len(lastEmittedCleanedContent) && strings.HasPrefix(cleanedContent, lastEmittedCleanedContent) { deltaContent = cleanedContent[len(lastEmittedCleanedContent):] lastEmittedCleanedContent = cleanedContent } else if cleanedContent != lastEmittedCleanedContent { // If cleaned content changed but not in a simple append, // extract delta from cleaned content // This handles cases where thinking tags are removed mid-stream if lastEmittedCleanedContent == "" { deltaContent = cleanedContent // <= lastEmittedCleanedContent = cleanedContent // <= } else { // Content changed in non-append way, use the new cleaned content deltaContent = cleanedContent // <= lastEmittedCleanedContent = cleanedContent // <= } } .... }
Предупреждение PVS-Studio: V8005 The ‘then’ statement is equivalent to the ‘else’ statement. chat.go 85
Опять then и else имеют одинаковый код. Однако в этот раз таких фрагментов три. Это наводит на мысль, что код мог быть сгенерирован, а затем растаскан по другим функциям.
На этом всё. Если вам интересны разборы проектов, предназначенных для использования AI и агентной разработки, то можете написать об этом в комментариях, и мы учтём ваше мнение :)
Напомню, что недавно в PVS-Studio появилась программа раннего доступа к нашим новым анализаторам, в которой вы можете принять участие! EAP доступно для языков JavaScript, TypeScript и Go.
Если вы хотите самостоятельно проверить проект с помощью PVS-Studio, то попробовать анализатор можете по ссылке!
Берегите себя и свой код!
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Georgii Tormozov. Double AI agents: What’s hiding in your Golang code.