使用 TensorFlow.js 创建 Github 操作来检测恶意评论
这篇文章最初发布在我的个人网站上
周末,我花了几个小时构建了一个 Github 操作来自动检测潜在的有害评论和 PR 评论。
它使用 TensorFlow.js 及其毒性预训练模型来评估毒性程度,基于以下 7 个类别:
- 身份攻击
- 侮辱
- 猥亵
- 严重毒性
- 色情内容
- 威胁
- 毒性
当用户发布新评论或评论 PR 时,就会触发该操作。如果内容被归类为有害内容的可能性很高,机器人就会创建一条评论,标记作者并建议更新内容。
这是一个快速演示:

设置
在深入研究代码之前,可能需要注意的是,这是一个JavaScript 操作。我了解到操作也可以在 Docker 容器中实现,但为了简单起见,我坚持使用 JS。
首先,我在项目文件夹的根目录下创建了一个 action.yml 文件。
在这个文件中,我写了以下代码:
name: "Safe space"
description: "Detect the potential toxicity of PR comments"
inputs:
GITHUB_TOKEN:
required: true
message:
required: false
toxicity_threshold:
required: false
runs:
using: "node12"
main: "dist/index.js"
前几行不言自明。然后,该inputs
属性包含 3 个不同的元素。
- 这
GITHUB_TOKEN
是工作流程运行中验证身份所需的秘密令牌,会自动生成。 - 该
message
属性是可选的,如果人们想要在操作检测到有害评论时自定义机器人发布的评论内容,则可以使用该属性。 - 该
toxicity_threshold
属性也是可选的,允许人们设置一个自定义阈值,机器学习模型在对评论进行预测时将使用该阈值。
最后,下面的设置runs
表明了我们希望我们的操作运行的 Node.js 版本,以及操作代码所在的文件。
操作代码
要创建 JavaScript 操作,您需要安装并至少需要 2 个 Node.js 模块:@actions/core
和@actions/github
。由于此特定操作使用 TensorFlow.js 模型,因此我还安装并需要@tensorflow-models/toxicity
和@tensorflow/tfjs
。
然后,在我的dist/index.js
文件中,我开始编写我的动作代码。
核心设置可能看起来像这样:
async function run() {
const tf = require("@tensorflow/tfjs");
const toxicity = require("@tensorflow-models/toxicity");
await tf.setBackend("cpu");
try {
const githubToken = core.getInput("GITHUB_TOKEN");
const customMessage = core.getInput("message");
const toxicityThreshold = core.getInput("toxicity_threshold");
const { context } = github;
} catch (error) {
core.setFailed(error.message);
}
}
run();
有一个 mainrun
函数,它需要所需的软件包并设置 TensorFlow.js 的后端。然后在try
/catch
语句中,代码获取了前面提到的 3 个参数,我们很快就会用到它们。
最后,我们获取了触发操作时事件的上下文。
当用户对问题或 PR 发表评论时创建机器人评论
一些不同的事件可以触发 Github 操作。由于此操作旨在获取针对某个问题或 PR 发布的注释,因此我们需要首先查看事件的有效负载,并查看属性是否comment
已定义。然后,我们还可以查看操作的类型(此处为created
和edited
),例如,仅在添加新注释或编辑注释(而非删除注释)时运行预测。
更多详细信息请参阅官方 Github 文档。
然后,我访问请求正确问题或 PR 的评论所需的几个参数,加载机器学习模型,如果match
预测结果之一的属性为真,则意味着该评论已被归类为有毒,并且我会生成带有警告消息的新评论。
if (context.payload.comment) {
if (
context.payload.action === "created" ||
context.payoad.action === "edited"
) {
const issueNumber = context.payload.issue.number;
const repository = context.payload.repository;
const octokit = new github.GitHub(githubToken);
const threshold = toxicityThreshold ? toxicityThreshold : 0.9;
const model = await toxicity.load(threshold);
const comments = [];
const commentsObjects = [];
const latestComment = [context.payload.comment.body];
const latestCommentObject = context.payload.comment;
let toxicComment = undefined;
model.classify(latestComment).then((predictions) => {
predictions.forEach((prediction) => {
if (toxicComment) {
return;
}
prediction.results.forEach((result, index) => {
if (toxicComment) {
return;
}
if (result.match) {
const commentAuthor = latestCommentObject.user.login;
toxicComment = latestComment;
const message = customMessage
? customMessage
: `<img src="https://media.giphy.com/media/3ohzdQ1IynzclJldUQ/giphy.gif" width="400"/> </br>
Hey @${commentAuthor}! 👋 <br/> PRs and issues should be safe environments but your comment: <strong>"${toxicComment}"</strong> was classified as potentially toxic! 😔</br>
Please consider spending a few seconds editing it and feel free to delete me afterwards! 🙂`;
return octokit.issues.createComment({
owner: repository.owner.login,
repo: repository.name,
issue_number: issueNumber,
body: message,
});
}
});
});
});
}
}
当用户提交 PR 审核时创建机器人评论
检查 PR 评论的代码非常相似,主要区别在于前几行。comment
我们不是在有效载荷上查找属性,而是查找review
,而我感兴趣的操作是submitted
。
if (context.payload.review) {
if (context.payload.action === "submitted") {
const issueNumber = context.payload.pull_request.number;
const repository = context.payload.repository;
const octokit = new github.GitHub(githubToken);
const threshold = toxicityThreshold ? toxicityThreshold : 0.9;
const model = await toxicity.load(threshold);
const reviewComment = [context.payload.review.body];
const reviewObject = context.payload.review;
let toxicComment = undefined;
model.classify(reviewComment).then((predictions) => {
predictions.forEach((prediction) => {
if (toxicComment) {
return;
}
prediction.results.forEach((result, index) => {
if (toxicComment) {
return;
}
if (result.match) {
const commentAuthor = reviewObject.user.login;
toxicComment = reviewComment[0];
const message = customMessage
? customMessage
: `<img src="https://media.giphy.com/media/3ohzdQ1IynzclJldUQ/giphy.gif" width="400"/> </br>
Hey @${commentAuthor}! 👋 <br/> PRs and issues should be safe environments but your comment: <strong>"${toxicComment}"</strong> was classified as potentially toxic! 😔</br>
Please consider spending a few seconds editing it and feel free to delete me afterwards! 🙂`;
return octokit.issues.createComment({
owner: repository.owner.login,
repo: repository.name,
issue_number: issueNumber,
body: message,
});
}
});
});
});
}
}
使用动作
要使用存储库中的操作,我们需要创建一个工作流文件。
首先,存储库需要有一个.github
文件夹,其中包含一个workflows
文件夹。然后,我们可以添加一个新.yml
文件,其中包含我们要运行的操作的详细信息。
on: [issue_comment, pull_request_review]
jobs:
toxic_check:
runs-on: ubuntu-latest
name: Safe space
steps:
- uses: actions/checkout@v2
- name: Safe space - action step
uses: charliegerard/safe-space@master
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
在此代码示例中,我们指出当问题评论周围发生事件以及拉取请求审核事件发生时,我们希望触发此操作。
然后,我们添加了需要从使用默认actions/checkout@v2
操作开始的操作,最后添加了此毒性分类操作以及一些附加参数,包括必需GITHUB_TOKEN
参数。
如果您想使用可选属性message
和toxicity_threshold
,您可以这样做:
on: [issue_comment, pull_request_review]
jobs:
toxic_check:
runs-on: ubuntu-latest
name: Safe space
steps:
- uses: actions/checkout@v2
- name: Safe space - action step
uses: charliegerard/safe-space@master
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
message: "Hello"
toxicity_threshold: 0.7
如果您正在开发自己的操作,您可以通过更改行来测试它
uses: charliegerard/safe-space@master
到
uses: ./
需要注意的一点是,如果您想构建自己的 Github 操作,在使用操作类型issue_comment
和 时pull_request_review
,您需要先将代码推送到主分支(通常称为“master”),然后才能在另一个分支中测试代码是否正常运行。如果您在单独的分支中开发所有内容,则在撰写评论或审核 PR 时将不会触发该操作。
就这样!🎉
潜在的改进
目前,我建议用户在更新恶意评论内容后,手动从机器人中删除该评论。不过,我认为可以在用户编辑时自动执行此操作。当用户编辑评论时,我可以再次运行检查,如果预测该评论是安全的,则自动删除机器人评论,这样用户就不必手动操作了。
文章来源:https://dev.to/devdevcharlie/creating-a-github-action-to-detect-有毒-comments-using-tensorflow-js-13bo