Skip to content

Feat: 添加 LLM 反垃圾检测#1017

Merged
imaegoo merged 15 commits into
twikoojs:mainfrom
duzhuoshanwai:feat/llm-spam-detection
Jul 7, 2026
Merged

Feat: 添加 LLM 反垃圾检测#1017
imaegoo merged 15 commits into
twikoojs:mainfrom
duzhuoshanwai:feat/llm-spam-detection

Conversation

@duzhuoshanwai

@duzhuoshanwai duzhuoshanwai commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

改动详情

1. 新增依赖

  • openai SDK

2. 检测逻辑

  • spam.js:新增 checkByLLM() 函数
    • 将评论正文 + 昵称 + 网址发送给 LLM 进行综合判定
    • 支持通过 LLM_SPAM_PROMPT 配置自定义提示词
    • 内置提示词
    • 兼容任意 OpenAI 兼容 API(DeepSeek、OpenAI、Cloudflare Workers AI 等)
  • postCheckSpam 的最后新增 LLM 分支:
    • 优先级:腾讯云内容安全 → Akismet → LLM → 不检测

3. 管理后台配置 UI

  • TkAdminConfig.vue:在「反垃圾」分类下新增 4 个配置项:
    • LLM_API_KEY — API 密钥
    • LLM_API_ENDPOINT — API 端点地址
    • LLM_MODEL — 模型名称
    • LLM_SPAM_PROMPT — 自定义检测提示词

TODO

  • TkAdminConfig.vue 中的 desc 还是硬编码英文,i18n 未支持 已完成
  • 相关文档还没写 已完成
  • 需要实现校验和重试逻辑,大模型输出常含格式瑕疵或多余内容 已完成

Summary by Sourcery

在评论审核中新增可选的基于 LLM 的垃圾信息检测功能,并在管理后台中开放其配置。

新功能:

  • 引入基于 LLM 的垃圾检测,通过兼容 OpenAI 的 API 将评论分类为垃圾(spam)或正常(ham)。
  • 在管理后台中新增 LLM 垃圾检测的配置选项,包括 API 密钥、接口地址(endpoint)、模型(model)以及自定义提示词(prompt)。

改进:

  • 将 LLM 垃圾检测集成到现有的垃圾检测流程中,当启用时,其优先级排在腾讯云和 Akismet 之后。

构建:

  • 新增 OpenAI SDK 作为用于垃圾检测的服务器端依赖。
Original summary in English

Summary by Sourcery

Add optional LLM-based spam detection to comment moderation and expose its configuration in the admin UI.

New Features:

  • Introduce an LLM-based spam check that classifies comments as spam or ham using an OpenAI-compatible API.
  • Add admin configuration options for LLM spam detection, including API key, endpoint, model, and custom prompt.

Enhancements:

  • Integrate the LLM spam check into the existing spam detection flow with priority after Tencent Cloud and Akismet when enabled.

Build:

  • Add the OpenAI SDK as a new server-side dependency for spam detection.

Summary by Sourcery

在评论系统中添加可选的基于 LLM 的垃圾内容检测,并在管理后台中提供相关配置选项。

New Features:

  • 引入基于 LLM 的垃圾检测功能,通过兼容 OpenAI 的 API 将评论分类为垃圾(spam)或正常(ham)。
  • 在管理后台中新增 LLM 垃圾检测的配置项,包括 API Key、Endpoint、Model、自定义 Prompt,以及最大重试次数。
  • 在管理配置界面中为长文本字段(如自定义垃圾检测 Prompt 和邮件模板)提供 textarea 输入支持。

Enhancements:

  • 将 LLM 垃圾检测集成到现有的垃圾检测优先级流程中,置于腾讯云和 Akismet 之后,并为模型返回结果增加 JSON 校验和重试处理逻辑。

Build:

  • 新增 OpenAI SDK 作为服务端依赖,并提供用于基于 LLM 的垃圾检测的客户端封装。

Documentation:

  • 在英文和中文 FAQ 中补充如何配置基于 LLM 的反垃圾功能的说明。
Original summary in English

Summary by Sourcery

Add optional LLM-based spam detection for comments and expose its configuration in the admin panel.

New Features:

  • Introduce an LLM-based spam check that classifies comments as spam or ham via an OpenAI-compatible API.
  • Add admin configuration options for LLM spam detection, including API key, endpoint, model, custom prompt, and max retry count.
  • Support textarea inputs in the admin config UI for long text fields such as custom spam prompts and mail templates.

Enhancements:

  • Integrate LLM spam detection into the existing spam checking priority flow after Tencent Cloud and Akismet, with JSON validation and retry handling for model responses.

Build:

  • Add the OpenAI SDK as a new server-side dependency and client wrapper for LLM-based spam detection.

Documentation:

  • Document how to configure LLM-based anti-spam in both English and Chinese FAQs.
Original summary in English

Summary by Sourcery

在评论系统中添加可选的基于 LLM 的垃圾内容检测,并在管理后台中提供相关配置选项。

New Features:

  • 引入基于 LLM 的垃圾检测功能,通过兼容 OpenAI 的 API 将评论分类为垃圾(spam)或正常(ham)。
  • 在管理后台中新增 LLM 垃圾检测的配置项,包括 API Key、Endpoint、Model、自定义 Prompt,以及最大重试次数。
  • 在管理配置界面中为长文本字段(如自定义垃圾检测 Prompt 和邮件模板)提供 textarea 输入支持。

Enhancements:

  • 将 LLM 垃圾检测集成到现有的垃圾检测优先级流程中,置于腾讯云和 Akismet 之后,并为模型返回结果增加 JSON 校验和重试处理逻辑。

Build:

  • 新增 OpenAI SDK 作为服务端依赖,并提供用于基于 LLM 的垃圾检测的客户端封装。

Documentation:

  • 在英文和中文 FAQ 中补充如何配置基于 LLM 的反垃圾功能的说明。
Original summary in English

Summary by Sourcery

Add optional LLM-based spam detection for comments and expose its configuration in the admin panel.

New Features:

  • Introduce an LLM-based spam check that classifies comments as spam or ham via an OpenAI-compatible API.
  • Add admin configuration options for LLM spam detection, including API key, endpoint, model, custom prompt, and max retry count.
  • Support textarea inputs in the admin config UI for long text fields such as custom spam prompts and mail templates.

Enhancements:

  • Integrate LLM spam detection into the existing spam checking priority flow after Tencent Cloud and Akismet, with JSON validation and retry handling for model responses.

Build:

  • Add the OpenAI SDK as a new server-side dependency and client wrapper for LLM-based spam detection.

Documentation:

  • Document how to configure LLM-based anti-spam in both English and Chinese FAQs.

新功能:

  • 引入基于 LLM 的垃圾内容检查,使用兼容 OpenAI 的 API 将评论分类为垃圾(spam)或正常(ham)。
  • 在管理后台添加用于配置 LLM 垃圾检测的设置,包括 API 密钥、端点、模型以及可自定义的提示词(prompt)。

增强:

  • 将 LLM 垃圾检测集成到现有的垃圾检查优先级流程中,排在腾讯云与 Akismet 之后,并为模型响应增加重试机制和 JSON 校验处理。
  • 扩展管理配置界面 UI,支持用于长文本字段(如自定义垃圾检测提示词和邮件模版)的多行文本输入框(textarea)。

构建:

  • 新增 OpenAI SDK 作为服务器端的垃圾检测依赖。

文档:

  • 在英文和中文 FAQ 中记录如何配置基于 LLM 的反垃圾服务。
Original summary in English

Summary by Sourcery

在评论系统中添加可选的基于 LLM 的垃圾内容检测,并在管理后台中提供相关配置选项。

New Features:

  • 引入基于 LLM 的垃圾检测功能,通过兼容 OpenAI 的 API 将评论分类为垃圾(spam)或正常(ham)。
  • 在管理后台中新增 LLM 垃圾检测的配置项,包括 API Key、Endpoint、Model、自定义 Prompt,以及最大重试次数。
  • 在管理配置界面中为长文本字段(如自定义垃圾检测 Prompt 和邮件模板)提供 textarea 输入支持。

Enhancements:

  • 将 LLM 垃圾检测集成到现有的垃圾检测优先级流程中,置于腾讯云和 Akismet 之后,并为模型返回结果增加 JSON 校验和重试处理逻辑。

Build:

  • 新增 OpenAI SDK 作为服务端依赖,并提供用于基于 LLM 的垃圾检测的客户端封装。

Documentation:

  • 在英文和中文 FAQ 中补充如何配置基于 LLM 的反垃圾功能的说明。
Original summary in English

Summary by Sourcery

Add optional LLM-based spam detection for comments and expose its configuration in the admin panel.

New Features:

  • Introduce an LLM-based spam check that classifies comments as spam or ham via an OpenAI-compatible API.
  • Add admin configuration options for LLM spam detection, including API key, endpoint, model, custom prompt, and max retry count.
  • Support textarea inputs in the admin config UI for long text fields such as custom spam prompts and mail templates.

Enhancements:

  • Integrate LLM spam detection into the existing spam checking priority flow after Tencent Cloud and Akismet, with JSON validation and retry handling for model responses.

Build:

  • Add the OpenAI SDK as a new server-side dependency and client wrapper for LLM-based spam detection.

Documentation:

  • Document how to configure LLM-based anti-spam in both English and Chinese FAQs.
Original summary in English

Summary by Sourcery

在评论系统中添加可选的基于 LLM 的垃圾内容检测,并在管理后台中提供相关配置选项。

New Features:

  • 引入基于 LLM 的垃圾检测功能,通过兼容 OpenAI 的 API 将评论分类为垃圾(spam)或正常(ham)。
  • 在管理后台中新增 LLM 垃圾检测的配置项,包括 API Key、Endpoint、Model、自定义 Prompt,以及最大重试次数。
  • 在管理配置界面中为长文本字段(如自定义垃圾检测 Prompt 和邮件模板)提供 textarea 输入支持。

Enhancements:

  • 将 LLM 垃圾检测集成到现有的垃圾检测优先级流程中,置于腾讯云和 Akismet 之后,并为模型返回结果增加 JSON 校验和重试处理逻辑。

Build:

  • 新增 OpenAI SDK 作为服务端依赖,并提供用于基于 LLM 的垃圾检测的客户端封装。

Documentation:

  • 在英文和中文 FAQ 中补充如何配置基于 LLM 的反垃圾功能的说明。
Original summary in English

Summary by Sourcery

Add optional LLM-based spam detection for comments and expose its configuration in the admin panel.

New Features:

  • Introduce an LLM-based spam check that classifies comments as spam or ham via an OpenAI-compatible API.
  • Add admin configuration options for LLM spam detection, including API key, endpoint, model, custom prompt, and max retry count.
  • Support textarea inputs in the admin config UI for long text fields such as custom spam prompts and mail templates.

Enhancements:

  • Integrate LLM spam detection into the existing spam checking priority flow after Tencent Cloud and Akismet, with JSON validation and retry handling for model responses.

Build:

  • Add the OpenAI SDK as a new server-side dependency and client wrapper for LLM-based spam detection.

Documentation:

  • Document how to configure LLM-based anti-spam in both English and Chinese FAQs.

@sourcery-ai

sourcery-ai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

添加一个可选的、基于 LLM 的垃圾评论检测层,使用兼容 OpenAI 的 SDK,在现有的垃圾检测流程中接入到腾讯云与 Akismet 之后;在管理后台 UI / i18n 中暴露相关配置,并在常见问题(FAQs)中编写使用文档。

使用 LLM 的更新版垃圾检测流程时序图

sequenceDiagram
  actor Commenter
  participant TwikooServer
  participant TencentCloud
  participant Akismet
  participant OpenAI_LLM

  Commenter->>TwikooServer: postCheckSpam(comment, config)
  alt config.TENCENT_SECRET_ID && config.TENCENT_SECRET_KEY
    TwikooServer->>TencentCloud: getTencentCloud()
    TwikooServer->>TencentCloud: moderate(comment)
  else config.AKISMET_KEY
    TwikooServer->>Akismet: getAkismetClient()
    TwikooServer->>Akismet: checkSpam(comment)
  else config.LLM_API_KEY
    TwikooServer->>TwikooServer: checkByLLM(comment, config)
    TwikooServer->>TwikooServer: getOpenAI(config)
    TwikooServer->>OpenAI_LLM: openai.chat.completions.create(model, messages)
    OpenAI_LLM-->>TwikooServer: choices[0].message.content
    TwikooServer->>TwikooServer: extractJson(rawText)
    TwikooServer->>TwikooServer: repairJson(jsonStr)
    TwikooServer->>TwikooServer: validateJson(jsonStr)
    note over TwikooServer: Retry loop up to config.LLM_MAX_RETRIES
  else no spam provider configured
    TwikooServer->>TwikooServer: isSpam = false
  end
  TwikooServer-->>Commenter: isSpam
Loading

File-Level Changes

Change Details Files
引入基于 OpenAI 的 LLM 垃圾检测,并将其集成到服务端垃圾检测流水线中,包含健壮的提示构造、JSON 解析/校验和重试处理。
  • 添加懒加载初始化的 OpenAI 客户端工厂,从配置中读取 LLM 相关配置(API key、endpoint),并缓存客户端以及最近使用的配置。
  • 实现辅助工具,用于从 LLM 响应中提取、修复和校验 JSON,强制要求存在布尔类型的 spam 字段。
  • 添加 buildMessages 用于构建 system/user 消息,支持默认的内容审核提示语,以及可选的自定义提示语和用于重试的错误反馈。
  • 实现 checkByLLM,调用 OpenAI chat completion API 要求仅返回 JSON,应用解析/校验辅助方法,记录决策日志,并在网络/格式错误时进行重试,最终在失败时回退为正常(ham)。
  • 扩展 postCheckSpam,当未配置腾讯云和 Akismet 但已设置 LLM_API_KEY 时调用 LLM 垃圾检测,同时保留现有优先级顺序。
src/server/function/twikoo/utils/spam.js
src/server/function/twikoo/utils/lib.js
在管理后台 UI 中暴露 LLM 垃圾检测配置和 textarea 支持,并在 i18n 层本地化新的选项。
  • 为 LLM 相关配置键(API key、endpoint、model、自定义垃圾检测提示语、最大重试次数)添加多语言 i18n 字符串。
  • 扩展 TkAdminConfig 表单渲染以支持用于长文本设置的 textarea 输入类型。
  • 在“反垃圾”配置中新增 LLM API key、endpoint、model、自定义垃圾检测提示语(textarea)以及最大重试次数等配置项。
  • 将邮件模板配置项修改为使用 textarea 输入类型,以便更好地编辑长文本。
src/client/utils/i18n/i18n.js
src/client/view/components/TkAdminConfig.vue
编写基于 LLM 的反垃圾配置文档,并添加新功能所需的 OpenAI SDK 依赖。
  • 在 FAQ(中英文 EN/ZH)中新增章节,说明如何配置 LLM 反垃圾,包括密钥、endpoint、模型、重试次数以及可选的自定义提示语。
  • 在服务端依赖中添加 openai,并提供一个辅助方法根据配置的 API key 和 base URL 构造 OpenAI 客户端。
docs/en/faq.md
docs/faq.md
src/server/function/twikoo/package.json

Assessment against linked issues

Issue Objective Addressed Explanation
#986 为评论内容和用户昵称(以及相关字段)添加基于 LLM/AI 的审核,用于补充/替代传统的关键词垃圾检测。
#986 在管理后台 UI 和文档中暴露配置选项(API key、endpoint、model、自定义提示语),以便管理员自定义审核规则。
#986 针对用户邮箱字段实现专门的 AI/LLM 审核,作为对评论和用户名之外的补充。 LLM 检测使用的是评论文本、昵称和站点(comment.comment, comment.nick, comment.link),未在输入或逻辑中包含用户邮箱字段。

Possibly linked issues

  • #Feature: AI 评论/邮箱/用户名审核: 该 PR 实现了针对评论、昵称和网站的基于 LLM 的垃圾检测,并支持自定义提示语,与请求的 AI 审核功能相匹配。

Tips and commands

Interacting with Sourcery

  • 触发新的评审: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 回复 Sourcery 的评审评论,要求其从该评论创建一个 issue。你也可以回复评审评论并添加 @sourcery-ai issue 来从中创建 issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题的任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 Pull Request 摘要: 在 Pull Request 正文的任意位置写上 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在 Pull Request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成评审者指南: 在 Pull Request 中评论 @sourcery-ai guide,即可随时(重新)生成评审者指南。
  • 解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,可标记所有 Sourcery 评论为已解决。如果你已经处理完所有评论且不希望再看到它们,这非常有用。
  • 关闭所有 Sourcery 评审: 在 Pull Request 中评论 @sourcery-ai dismiss,可关闭所有现有的 Sourcery 评审。尤其适用于希望从一次全新评审开始的场景——别忘了再评论 @sourcery-ai review 来触发新的评审!

Customizing Your Experience

访问你的 dashboard 来:

  • 启用或停用评审功能,例如 Sourcery 自动生成的 Pull Request 摘要、评审者指南等。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审指令。
  • 调整其他评审设置。

Getting Help

Original review guide in English

Reviewer's Guide

Add an optional LLM-based spam detection layer using an OpenAI-compatible SDK, wire it into the existing spam-check flow after Tencent Cloud and Akismet, expose configuration in the admin UI/i18n, and document usage in FAQs.

Sequence diagram for updated spam detection flow with LLM

sequenceDiagram
  actor Commenter
  participant TwikooServer
  participant TencentCloud
  participant Akismet
  participant OpenAI_LLM

  Commenter->>TwikooServer: postCheckSpam(comment, config)
  alt config.TENCENT_SECRET_ID && config.TENCENT_SECRET_KEY
    TwikooServer->>TencentCloud: getTencentCloud()
    TwikooServer->>TencentCloud: moderate(comment)
  else config.AKISMET_KEY
    TwikooServer->>Akismet: getAkismetClient()
    TwikooServer->>Akismet: checkSpam(comment)
  else config.LLM_API_KEY
    TwikooServer->>TwikooServer: checkByLLM(comment, config)
    TwikooServer->>TwikooServer: getOpenAI(config)
    TwikooServer->>OpenAI_LLM: openai.chat.completions.create(model, messages)
    OpenAI_LLM-->>TwikooServer: choices[0].message.content
    TwikooServer->>TwikooServer: extractJson(rawText)
    TwikooServer->>TwikooServer: repairJson(jsonStr)
    TwikooServer->>TwikooServer: validateJson(jsonStr)
    note over TwikooServer: Retry loop up to config.LLM_MAX_RETRIES
  else no spam provider configured
    TwikooServer->>TwikooServer: isSpam = false
  end
  TwikooServer-->>Commenter: isSpam
Loading

File-Level Changes

Change Details Files
Introduce OpenAI-based LLM spam detection and integrate it into the server-side spam checking pipeline with robust prompt building, JSON parsing/validation, and retry handling.
  • Add lazy-initialized OpenAI client factory that reads LLM-related config (API key, endpoint) and caches client plus last-used config.
  • Implement helper utilities to extract, repair, and validate JSON from LLM responses, enforcing a boolean spam field.
  • Add buildMessages to construct system/user messages, supporting a default moderation prompt plus optional custom prompt and error feedback for retries.
  • Implement checkByLLM which calls the OpenAI chat completion API with JSON-only responses, applies parsing/validation helpers, logs decisions, and retries on network/format errors before falling back to ham.
  • Extend postCheckSpam to invoke LLM spam detection when Tencent Cloud and Akismet are not configured but LLM_API_KEY is set, keeping the existing priority order.
src/server/function/twikoo/utils/spam.js
src/server/function/twikoo/utils/lib.js
Expose LLM spam detection configuration and textarea support in the admin UI, and localize new options in the i18n layer.
  • Add i18n strings for LLM-related config keys (API key, endpoint, model, custom spam prompt, max retries) in multiple languages.
  • Extend TkAdminConfig form rendering to support a textarea input type for long-text settings.
  • Add new "Anti-Spam" config items for LLM API key, endpoint, model, custom spam prompt (textarea), and max retries.
  • Change mail template config items to use textarea input type for better long-text editing.
src/client/utils/i18n/i18n.js
src/client/view/components/TkAdminConfig.vue
Document LLM-based anti-spam configuration and add OpenAI SDK dependency required by the new feature.
  • Add FAQ sections (EN/ZH) describing how to configure LLM anti-spam, including keys, endpoint, model, retries, and optional custom prompt.
  • Add openai as a server-side dependency and provide a helper to construct the OpenAI client with configured API key and base URL.
docs/en/faq.md
docs/faq.md
src/server/function/twikoo/package.json

Assessment against linked issues

Issue Objective Addressed Explanation
#986 Add LLM/AI-based moderation for comment content and user nickname (and related fields) to complement/replace traditional keyword-based spam checks.
#986 Expose configuration options (API key, endpoint, model, custom prompt) for the LLM-based moderation feature in the admin UI and documentation so administrators can customize review rules.
#986 Implement AI/LLM-based moderation specifically for the user email field in addition to comments and username. The LLM check uses comment text, nickname, and website (comment.comment, comment.nick, comment.link) but does not include the user email field in its inputs or logic.

Possibly linked issues

  • #Feature: AI 评论/邮箱/用户名审核: PR implements LLM-based spam detection for comments, nickname, and website with custom prompts, matching the requested AI审核功能。

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@duzhuoshanwai duzhuoshanwai changed the title Feat: 添加 LLM 反垃圾检测 Feat: 添加 LLM 反垃圾检测(draft) Jul 3, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 3 个问题,并给出了一些整体性反馈:

  • postCheckSpam 中的垃圾信息检测优先级(腾讯 → Akismet → LLM)与 PR 描述以及管理后台 UI 中提示的「LLM 优先」不一致;请让实际分支执行顺序与文档/界面提示保持一致。
  • checkByLLM 中使用 answer.includes('SPAM'),会导致像 "NOT SPAM" 这样的回复被错误地判定为垃圾信息;建议改为使用严格相等(例如 answer === 'SPAM')以符合提示语中的要求。
  • 管理配置中对 LLM_API_ENDPOINT 的描述称其默认使用 OpenAI,但实现中使用的是 https://api.deepseek.com 作为后备 base URL;请更新代码或说明文字,使默认行为保持一致且不具误导性。
给 AI Agents 的提示
Please address the comments from this code review:

## Overall Comments
- `postCheckSpam` 中的垃圾信息检测优先级(腾讯 → Akismet → LLM)与 PR 描述以及管理后台 UI 中提示的「LLM 优先」不一致;请让实际分支执行顺序与文档/界面提示保持一致。
-`checkByLLM` 中使用 `answer.includes('SPAM')`,会导致像 `"NOT SPAM"` 这样的回复被错误地判定为垃圾信息;建议改为使用严格相等(例如 `answer === 'SPAM'`)以符合提示语中的要求。
- 管理配置中对 `LLM_API_ENDPOINT` 的描述称其默认使用 OpenAI,但实现中使用的是 `https://api.deepseek.com` 作为后备 base URL;请更新代码或说明文字,使默认行为保持一致且不具误导性。

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="68-69" />
<code_context>
+      model: config.LLM_MODEL || 'deepseek-v4-pro',
+      messages: [{ role: 'user', content }]
+    })
+    const answer = (chatCompletion.choices[0].message.content || '').trim().toUpperCase()
+    if (answer.includes('SPAM')) {
+      logger.info(`LLM 判定为 SPAM: id="${comment.id}" nick="${comment.nick}"`)
+    }
</code_context>
<issue_to_address>
**issue (bug_risk):** 使用 `includes('SPAM')` 会把像 `NOT SPAM` 这样的响应也判定为垃圾信息。

由于模型有可能合理地返回类似 `NOT SPAM` 的短语,使用 `answer.includes('SPAM')` 会错误地将这些结果标记为垃圾信息。为了避免这类误报,建议采用更严格的检查方式,例如 `answer === 'SPAM'`,或使用更受限的匹配模式(例如 `answer.startsWith('SPAM')`,同时显式排除 `NOT SPAM`/`HAM`)。
</issue_to_address>

### Comment 2
<location path="src/client/view/components/TkAdminConfig.vue" line_range="138" />
<code_context>
-          ]
+            { key: 'NOTIFY_SPAM', desc: t('ADMIN_CONFIG_ITEM_NOTIFY_SPAM'), ph: `${t('ADMIN_CONFIG_EXAMPLE')}false`, value: '' },
+            { key: 'LLM_API_KEY', desc: 'LLM API Key, used for AI spam comment detection. Takes precedence over Akismet and Tencent Cloud when configured.', ph: 'sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', value: '', secret: true },
+            { key: 'LLM_API_ENDPOINT', desc: 'LLM API endpoint URL, defaults to OpenAI', ph: 'https://api.deepseek.com', value: '' },
+            { key: 'LLM_MODEL', desc: 'LLM model name', ph: 'deepseek-v4-flash', value: '' },
+            { key: 'LLM_SPAM_PROMPT', desc: 'LLM spam detection prompt (optional, uses default if left empty)', ph: '', value: '' }          ]
</code_context>
<issue_to_address>
**suggestion:** 端点描述和默认值与后端默认配置不一致。

说明中写的是端点“默认为 OpenAI”,但占位符和 `checkByLLM` 中默认的 `baseURL` 都是 `https://api.deepseek.com`。请将说明与实际默认值对齐(例如注明 DeepSeek),或者如果默认应为 OpenAI,则更新后端默认值,以确保配置行为清晰明确、不产生歧义。

```suggestion
            { key: 'LLM_API_ENDPOINT', desc: 'LLM API endpoint URL, defaults to DeepSeek', ph: 'https://api.deepseek.com', value: '' },
```
</issue_to_address>

### Comment 3
<location path="src/client/view/components/TkAdminConfig.vue" line_range="139" />
<code_context>
+            { key: 'NOTIFY_SPAM', desc: t('ADMIN_CONFIG_ITEM_NOTIFY_SPAM'), ph: `${t('ADMIN_CONFIG_EXAMPLE')}false`, value: '' },
+            { key: 'LLM_API_KEY', desc: 'LLM API Key, used for AI spam comment detection. Takes precedence over Akismet and Tencent Cloud when configured.', ph: 'sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', value: '', secret: true },
+            { key: 'LLM_API_ENDPOINT', desc: 'LLM API endpoint URL, defaults to OpenAI', ph: 'https://api.deepseek.com', value: '' },
+            { key: 'LLM_MODEL', desc: 'LLM model name', ph: 'deepseek-v4-flash', value: '' },
+            { key: 'LLM_SPAM_PROMPT', desc: 'LLM spam detection prompt (optional, uses default if left empty)', ph: '', value: '' }          ]
         },
</code_context>
<issue_to_address>
**suggestion:** UI 中的 LLM 模型占位符与后端默认模型不一致。

在 `checkByLLM` 中,当 `config.LLM_MODEL` 未设置时,默认模型为 `deepseek-v4-pro`,但管理后台 UI 中的占位符却是 `deepseek-v4-flash`。这种不一致可能会误导管理员对实际使用模型的判断。请更新后端默认值或 UI 占位符,使二者保持一致。

```suggestion
            { key: 'LLM_MODEL', desc: 'LLM model name', ph: 'deepseek-v4-pro', value: '' },
```
</issue_to_address>

Sourcery 对开源项目免费使用——如果你觉得这些评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点选 👍 或 👎,我会根据你的反馈改进后续的评审质量。
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • The spam-check priority in postCheckSpam (Tencent → Akismet → LLM) conflicts with both the PR description and the admin UI hint that LLM takes precedence; align the actual branching order with the documented/UX expectation.
  • In checkByLLM, using answer.includes('SPAM') means a reply like "NOT SPAM" will be treated as spam; consider enforcing strict equality (e.g., answer === 'SPAM') to match the prompt instructions.
  • The admin config description for LLM_API_ENDPOINT says it defaults to OpenAI, but the implementation uses https://api.deepseek.com as the fallback base URL; update either the code or the label so that the default behavior is consistent and not misleading.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The spam-check priority in `postCheckSpam` (Tencent → Akismet → LLM) conflicts with both the PR description and the admin UI hint that LLM takes precedence; align the actual branching order with the documented/UX expectation.
- In `checkByLLM`, using `answer.includes('SPAM')` means a reply like `"NOT SPAM"` will be treated as spam; consider enforcing strict equality (e.g., `answer === 'SPAM'`) to match the prompt instructions.
- The admin config description for `LLM_API_ENDPOINT` says it defaults to OpenAI, but the implementation uses `https://api.deepseek.com` as the fallback base URL; update either the code or the label so that the default behavior is consistent and not misleading.

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="68-69" />
<code_context>
+      model: config.LLM_MODEL || 'deepseek-v4-pro',
+      messages: [{ role: 'user', content }]
+    })
+    const answer = (chatCompletion.choices[0].message.content || '').trim().toUpperCase()
+    if (answer.includes('SPAM')) {
+      logger.info(`LLM 判定为 SPAM: id="${comment.id}" nick="${comment.nick}"`)
+    }
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `includes('SPAM')` will classify responses like `NOT SPAM` as spam.

Because the model may legitimately return phrases like `NOT SPAM`, using `answer.includes('SPAM')` will incorrectly flag these as spam. To avoid these false positives, use a stricter check such as `answer === 'SPAM'` or a more constrained pattern (e.g. `answer.startsWith('SPAM')` while explicitly excluding `NOT SPAM`/`HAM`).
</issue_to_address>

### Comment 2
<location path="src/client/view/components/TkAdminConfig.vue" line_range="138" />
<code_context>
-          ]
+            { key: 'NOTIFY_SPAM', desc: t('ADMIN_CONFIG_ITEM_NOTIFY_SPAM'), ph: `${t('ADMIN_CONFIG_EXAMPLE')}false`, value: '' },
+            { key: 'LLM_API_KEY', desc: 'LLM API Key, used for AI spam comment detection. Takes precedence over Akismet and Tencent Cloud when configured.', ph: 'sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', value: '', secret: true },
+            { key: 'LLM_API_ENDPOINT', desc: 'LLM API endpoint URL, defaults to OpenAI', ph: 'https://api.deepseek.com', value: '' },
+            { key: 'LLM_MODEL', desc: 'LLM model name', ph: 'deepseek-v4-flash', value: '' },
+            { key: 'LLM_SPAM_PROMPT', desc: 'LLM spam detection prompt (optional, uses default if left empty)', ph: '', value: '' }          ]
</code_context>
<issue_to_address>
**suggestion:** The endpoint description and default value are inconsistent with the backend default.

The description says the endpoint "defaults to OpenAI" but the placeholder and `checkByLLM` default `baseURL` are `https://api.deepseek.com`. Please align the description with the actual default (e.g., mention DeepSeek) or update the backend default if OpenAI is intended, so configuration behavior is unambiguous.

```suggestion
            { key: 'LLM_API_ENDPOINT', desc: 'LLM API endpoint URL, defaults to DeepSeek', ph: 'https://api.deepseek.com', value: '' },
```
</issue_to_address>

### Comment 3
<location path="src/client/view/components/TkAdminConfig.vue" line_range="139" />
<code_context>
+            { key: 'NOTIFY_SPAM', desc: t('ADMIN_CONFIG_ITEM_NOTIFY_SPAM'), ph: `${t('ADMIN_CONFIG_EXAMPLE')}false`, value: '' },
+            { key: 'LLM_API_KEY', desc: 'LLM API Key, used for AI spam comment detection. Takes precedence over Akismet and Tencent Cloud when configured.', ph: 'sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', value: '', secret: true },
+            { key: 'LLM_API_ENDPOINT', desc: 'LLM API endpoint URL, defaults to OpenAI', ph: 'https://api.deepseek.com', value: '' },
+            { key: 'LLM_MODEL', desc: 'LLM model name', ph: 'deepseek-v4-flash', value: '' },
+            { key: 'LLM_SPAM_PROMPT', desc: 'LLM spam detection prompt (optional, uses default if left empty)', ph: '', value: '' }          ]
         },
</code_context>
<issue_to_address>
**suggestion:** LLM model placeholder in the UI does not match the backend default model.

In `checkByLLM`, the default model is `deepseek-v4-pro` when `config.LLM_MODEL` is unset, but the admin UI placeholder shows `deepseek-v4-flash`. This inconsistency can mislead admins about which model is actually used. Please update either the backend default or the placeholder so they match.

```suggestion
            { key: 'LLM_MODEL', desc: 'LLM model name', ph: 'deepseek-v4-pro', value: '' },
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/server/function/twikoo/utils/spam.js Outdated
Comment thread src/client/view/components/TkAdminConfig.vue Outdated
Comment thread src/client/view/components/TkAdminConfig.vue Outdated
duzhuoshanwai and others added 2 commits July 3, 2026 11:47
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
@imaegoo imaegoo marked this pull request as draft July 3, 2026 03:51
@imaegoo

imaegoo commented Jul 3, 2026

Copy link
Copy Markdown
Member

挺好的想法

@imaegoo imaegoo linked an issue Jul 3, 2026 that may be closed by this pull request
@duzhuoshanwai duzhuoshanwai changed the title Feat: 添加 LLM 反垃圾检测(draft) Feat: 添加 LLM 反垃圾检测 Jul 5, 2026
@duzhuoshanwai duzhuoshanwai marked this pull request as ready for review July 5, 2026 06:28

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体反馈:

  • 建议将 buildMessages 中较长的 system prompt 抽取为可配置或支持 i18n 的常量,这样在不改动代码的前提下更易于维护和本地化。
  • checkByLLM 中的重试行为(固定 maxRetries = 3 且延迟 1 秒)可以通过防垃圾设置进行配置,让运营方可以根据实际环境在延迟和健壮性之间进行调优。
  • 目前在每次调用 checkByLLM 时都会实例化一个新的 OpenAI client;建议考虑复用单例 client 实例,以避免在评论量较高时重复初始化带来的开销。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider extracting the long system prompt in `buildMessages` into a configurable or i18n-able constant so that it’s easier to maintain and localize without touching code.
- The retry behavior in `checkByLLM` (fixed `maxRetries = 3` and 1s delay) could be made configurable through anti-spam settings so operators can tune latency vs robustness for their environment.
- You instantiate a new OpenAI client on every `checkByLLM` call; consider reusing a singleton client instance to avoid repeated setup overhead under high comment traffic.

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="64-73" />
<code_context>
+function buildMessages (commentData, errorMsg = '', customPrompt = '') {
</code_context>
<issue_to_address>
**suggestion:** 自定义 prompt 的占位符替换目前只会替换每个占位符第一次出现的位置,对于更复杂的模板来说有一定限制。

使用 `.replace('{{comment}}', ...)` 只会更新 `LLM_SPAM_PROMPT` 中每个占位符的第一次出现。对于多次引用 `{{comment}}` 的 prompt,后续位置会保持未替换状态。建议改为使用全局替换(例如 `new RegExp('{{comment}}', 'g')``replaceAll`),以确保所有出现的位置都能被一致替换。

建议实现:

```javascript
function buildMessages (commentData, errorMsg = '', customPrompt = '') {
  // 1. system指令(固定不变)
  const systemContent = `You are a blog comment moderation assistant. Analyze ALL fields below and determine if this submission is spam or ham.

Spam includes ANY of the following in ANY field:
- Commercial ads, promotions, or buying/selling offers (e.g., "代开发票", "加微信", "兼职", "办证", "AI中转站").
- Special case: If the comment text is harmless, but the nickname is suspicious (e.g., contains ads or promotions) AND a website link is provided, treat it as SPAM.
- Meaningless gibberish or spammy repetition (e.g., "顶顶顶", "111111", "asdfgh", "好" repeated).
- Abusive language, insults, or offensive Chinese slang.
- Suspicious links or SEO spam in the website field.
- Bot-like automated greetings.

```

```javascript
  const userContent = (customPrompt || LLM_SPAM_PROMPT)
    .replaceAll('{{comment}}', commentData.comment || '')
    .replaceAll('{{nick}}', commentData.nick || '')
    .replaceAll('{{mail}}', commentData.mail || '')
    .replaceAll('{{link}}', commentData.link || '')

```

我目前只看到了 `buildMessages` 函数的部分实现。要完整应用上述建议:

1. 确保在 `buildMessages` 内部(以及它调用的任何辅助函数中)所有占位符替换都使用全局替换(`replaceAll``string.replace(new RegExp('...', 'g'))`),而不是只替换一次的 `replace`2. 如果运行环境可能不支持 `String.prototype.replaceAll`,可以改用:
   ```js
   .replace(/{{comment}}/g, commentData.comment || '')
   ```
   针对每个占位符分别处理。
3. 如果 `LLM_SPAM_PROMPT` 中还定义了除 `comment``nick``mail``link` 之外的其他占位符,也请将这些占位符的替换方式一并更新为全局替换。
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据这些反馈改进后续的代码审查。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Consider extracting the long system prompt in buildMessages into a configurable or i18n-able constant so that it’s easier to maintain and localize without touching code.
  • The retry behavior in checkByLLM (fixed maxRetries = 3 and 1s delay) could be made configurable through anti-spam settings so operators can tune latency vs robustness for their environment.
  • You instantiate a new OpenAI client on every checkByLLM call; consider reusing a singleton client instance to avoid repeated setup overhead under high comment traffic.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider extracting the long system prompt in `buildMessages` into a configurable or i18n-able constant so that it’s easier to maintain and localize without touching code.
- The retry behavior in `checkByLLM` (fixed `maxRetries = 3` and 1s delay) could be made configurable through anti-spam settings so operators can tune latency vs robustness for their environment.
- You instantiate a new OpenAI client on every `checkByLLM` call; consider reusing a singleton client instance to avoid repeated setup overhead under high comment traffic.

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="64-73" />
<code_context>
+function buildMessages (commentData, errorMsg = '', customPrompt = '') {
</code_context>
<issue_to_address>
**suggestion:** Custom prompt substitution only replaces the first occurrence of each placeholder, which is limiting for richer templates.

Using `.replace('{{comment}}', ...)` only updates the first occurrence of each placeholder in `LLM_SPAM_PROMPT`. Prompts that reference `{{comment}}` multiple times will have later instances left untouched. Please switch to a global replacement (e.g. `new RegExp('{{comment}}', 'g')` or `replaceAll`) so all occurrences are substituted consistently.

Suggested implementation:

```javascript
function buildMessages (commentData, errorMsg = '', customPrompt = '') {
  // 1. system指令(固定不变)
  const systemContent = `You are a blog comment moderation assistant. Analyze ALL fields below and determine if this submission is spam or ham.

Spam includes ANY of the following in ANY field:
- Commercial ads, promotions, or buying/selling offers (e.g., "代开发票", "加微信", "兼职", "办证", "AI中转站").
- Special case: If the comment text is harmless, but the nickname is suspicious (e.g., contains ads or promotions) AND a website link is provided, treat it as SPAM.
- Meaningless gibberish or spammy repetition (e.g., "顶顶顶", "111111", "asdfgh", "好" repeated).
- Abusive language, insults, or offensive Chinese slang.
- Suspicious links or SEO spam in the website field.
- Bot-like automated greetings.

```

```javascript
  const userContent = (customPrompt || LLM_SPAM_PROMPT)
    .replaceAll('{{comment}}', commentData.comment || '')
    .replaceAll('{{nick}}', commentData.nick || '')
    .replaceAll('{{mail}}', commentData.mail || '')
    .replaceAll('{{link}}', commentData.link || '')

```

I only see part of the `buildMessages` function. To fully apply the suggestion:

1. Ensure that all placeholder substitutions inside `buildMessages` (or any helper it calls) use global replacement (`replaceAll` or `string.replace(new RegExp('...', 'g'))`) rather than single-occurrence `replace`.
2. If the runtime environment might not support `String.prototype.replaceAll`, you can instead use:
   ```js
   .replace(/{{comment}}/g, commentData.comment || '')
   ```
   for each placeholder.
3. If `LLM_SPAM_PROMPT` defines additional placeholders beyond `comment`, `nick`, `mail`, and `link`, update those substitutions to use global replacement as well.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/server/function/twikoo/utils/spam.js
@duzhuoshanwai duzhuoshanwai marked this pull request as draft July 5, 2026 06:34
@duzhuoshanwai duzhuoshanwai marked this pull request as ready for review July 5, 2026 08:16

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并留下了一些整体反馈:

  • 建议将 checkByLLM 中的 maxRetries 和重试退避策略通过设置配置化,这样管理员可以针对不同的提供商在鲁棒性和延迟之间进行调优。
  • getOpenAI 中,缓存失效目前只检查 API key 和 endpoint;如果管理员更改了模型或用于垃圾检测的提示词,客户端不会被重新创建。因此,最好将客户端缓存与提示词 / 模型相关的配置分离开来,以避免混淆。
  • buildMessages 中的默认 system prompt 比较长,而且把业务规则紧密耦合在代码里;将其提取到单独的常量或配置值会让后续调整更容易,也能减少函数内部的噪音。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider making `maxRetries` and retry backoff in `checkByLLM` configurable via settings so admins can tune robustness vs. latency for different providers.
- In `getOpenAI`, the cache invalidation only checks API key and endpoint; if admins change the model or spam prompt, the client won't be recreated, so it may be clearer to separate client caching from prompt/model-related config to avoid confusion.
- The default system prompt in `buildMessages` is quite long and tightly couples business rules to code; extracting it into a separate constant or config value would make future adjustments easier and reduce noise in the function.

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="121" />
<code_context>
+    { role: 'system', content: systemContent },
+    { role: 'user', content: userContent }
+  ]
+  logger.log('提示词是:', finalPrompt)
+  return finalPrompt
+}
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Logging the full LLM prompt may be noisy and potentially expose user content in logs.

`finalPrompt` contains full user and system content. Logging this at normal level can both leak user data into logs and substantially increase log volume on a busy site. Please either reduce the log level (e.g., debug), redact/summarize the prompt (e.g., only IDs/metadata), or guard this behind a configuration flag for troubleshooting only.

Suggested implementation:

```javascript
  const finalPrompt = [
    { role: 'system', content: systemContent },
    { role: 'user', content: userContent }
  ]

  // 使用 debug 级别并只输出部分内容,避免在日志中泄露完整提示词
  logger.debug('提示词已生成', {
    systemContentPreview: systemContent.slice(0, 200),
    userContentPreview: userContent.slice(0, 200)
  })

  return finalPrompt
}

```

如果你们有统一的配置管理或日志级别控制,可以进一步:
1.`200` 这一截断长度抽成常量或配置项。
2. 如果需要完全关闭这类日志,可以用环境变量或配置开关包裹这一段 `logger.debug` 调用,例如只在开发或故障排查模式下启用。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Consider making maxRetries and retry backoff in checkByLLM configurable via settings so admins can tune robustness vs. latency for different providers.
  • In getOpenAI, the cache invalidation only checks API key and endpoint; if admins change the model or spam prompt, the client won't be recreated, so it may be clearer to separate client caching from prompt/model-related config to avoid confusion.
  • The default system prompt in buildMessages is quite long and tightly couples business rules to code; extracting it into a separate constant or config value would make future adjustments easier and reduce noise in the function.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider making `maxRetries` and retry backoff in `checkByLLM` configurable via settings so admins can tune robustness vs. latency for different providers.
- In `getOpenAI`, the cache invalidation only checks API key and endpoint; if admins change the model or spam prompt, the client won't be recreated, so it may be clearer to separate client caching from prompt/model-related config to avoid confusion.
- The default system prompt in `buildMessages` is quite long and tightly couples business rules to code; extracting it into a separate constant or config value would make future adjustments easier and reduce noise in the function.

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="121" />
<code_context>
+    { role: 'system', content: systemContent },
+    { role: 'user', content: userContent }
+  ]
+  logger.log('提示词是:', finalPrompt)
+  return finalPrompt
+}
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Logging the full LLM prompt may be noisy and potentially expose user content in logs.

`finalPrompt` contains full user and system content. Logging this at normal level can both leak user data into logs and substantially increase log volume on a busy site. Please either reduce the log level (e.g., debug), redact/summarize the prompt (e.g., only IDs/metadata), or guard this behind a configuration flag for troubleshooting only.

Suggested implementation:

```javascript
  const finalPrompt = [
    { role: 'system', content: systemContent },
    { role: 'user', content: userContent }
  ]

  // 使用 debug 级别并只输出部分内容,避免在日志中泄露完整提示词
  logger.debug('提示词已生成', {
    systemContentPreview: systemContent.slice(0, 200),
    userContentPreview: userContent.slice(0, 200)
  })

  return finalPrompt
}

```

如果你们有统一的配置管理或日志级别控制,可以进一步:
1.`200` 这一截断长度抽成常量或配置项。
2. 如果需要完全关闭这类日志,可以用环境变量或配置开关包裹这一段 `logger.debug` 调用,例如只在开发或故障排查模式下启用。
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/server/function/twikoo/utils/spam.js
@duzhuoshanwai

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体性的反馈:

  • 建议为 LLM_MAX_RETRIES 增加一个硬性的最大上限(例如,钳制到一个合理的最大值),以避免由于配置错误导致在故障情况下出现过多的重试循环和过高的延迟。
  • 当前的 JSON 提取/修复流水线(extractJson + repairJson)比较宽松,可能会错误地捕获或修改非 JSON 文本中的大括号;你可能需要收紧这部分逻辑,或者只依赖 response_format: { type: 'json_object' },当模型返回无效内容时快速失败,而不是尝试自动修复。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider adding a hard upper bound on `LLM_MAX_RETRIES` (e.g., clamping to a reasonable max) to avoid misconfiguration leading to excessive retry loops and latency under failure conditions.
- The JSON extraction/repair pipeline (`extractJson` + `repairJson`) is quite permissive and may incorrectly pick up or modify braces in non-JSON text; you might want to tighten this logic or rely solely on `response_format: { type: 'json_object' }` and fail fast when the model returns invalid content instead of trying to auto-repair.

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="126" />
<code_context>
+}
+
+async function checkByLLM (comment, config) {
+  const maxRetries = config.LLM_MAX_RETRIES || 3
+  let lastError = ''
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):**`LLM_MAX_RETRIES` 强制转换为数字,以避免隐蔽的类型问题。

`config.LLM_MAX_RETRIES` 可能是字符串(例如来自环境变量/env 或管理后台/admin UI)。在使用 `const maxRetries = config.LLM_MAX_RETRIES || 3` 时,`maxRetries` 可能是 `'5'`,并且循环依赖于 `attempt <= maxRetries` 中的隐式类型转换。通过显式转换,例如 `const maxRetries = Number(config.LLM_MAX_RETRIES) || 3`,可以避免类型上的意外行为,并确保后续的数值运算更安全。

建议实现:

```javascript
  const maxRetries = Number(config.LLM_MAX_RETRIES) || 3

```

此更改会显式地将 `config.LLM_MAX_RETRIES` 转换为数字,确保即使配置值以字符串形式提供(例如通过环境变量或管理后台),`maxRetries` 也始终为数值类型。只要 `maxRetries` 只在数值场景中使用(如比较或算术运算),通常不需要进一步调整。
</issue_to_address>

Sourcery 对开源项目免费 —— 如果你觉得我们的代码审查有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据这些反馈改进后续的代码审查。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Consider adding a hard upper bound on LLM_MAX_RETRIES (e.g., clamping to a reasonable max) to avoid misconfiguration leading to excessive retry loops and latency under failure conditions.
  • The JSON extraction/repair pipeline (extractJson + repairJson) is quite permissive and may incorrectly pick up or modify braces in non-JSON text; you might want to tighten this logic or rely solely on response_format: { type: 'json_object' } and fail fast when the model returns invalid content instead of trying to auto-repair.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider adding a hard upper bound on `LLM_MAX_RETRIES` (e.g., clamping to a reasonable max) to avoid misconfiguration leading to excessive retry loops and latency under failure conditions.
- The JSON extraction/repair pipeline (`extractJson` + `repairJson`) is quite permissive and may incorrectly pick up or modify braces in non-JSON text; you might want to tighten this logic or rely solely on `response_format: { type: 'json_object' }` and fail fast when the model returns invalid content instead of trying to auto-repair.

## Individual Comments

### Comment 1
<location path="src/server/function/twikoo/utils/spam.js" line_range="126" />
<code_context>
+}
+
+async function checkByLLM (comment, config) {
+  const maxRetries = config.LLM_MAX_RETRIES || 3
+  let lastError = ''
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Coerce `LLM_MAX_RETRIES` to a number to avoid subtle type issues.

`config.LLM_MAX_RETRIES` may be a string (e.g. from env/admin UI). With `const maxRetries = config.LLM_MAX_RETRIES || 3`, `maxRetries` can be `'5'`, and the loop relies on implicit coercion in `attempt <= maxRetries`. Converting explicitly, e.g. `const maxRetries = Number(config.LLM_MAX_RETRIES) || 3`, avoids type surprises and keeps future numeric operations safe.

Suggested implementation:

```javascript
  const maxRetries = Number(config.LLM_MAX_RETRIES) || 3

```

This change explicitly coerces `config.LLM_MAX_RETRIES` to a number, ensuring `maxRetries` is always numeric even if the configuration value is provided as a string (e.g. via environment variables or an admin UI). No further adjustments should be necessary as long as `maxRetries` is only used in numeric contexts (such as comparisons or arithmetic).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/server/function/twikoo/utils/spam.js Outdated
@imaegoo imaegoo merged commit 34d4c92 into twikoojs:main Jul 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 建议增加 AI 评论/用户邮箱/用户名审核功能

2 participants