tencent-cloud 腾讯云服务
腾讯云内容安全审核服务集成,支持文本和图片审核
概述
@mono/tencent-cloud 是腾讯云服务集成包,提供文本和图片内容安全审核功能。基于腾讯云官方 SDK 封装,提供简洁易用的 API 和完整的 TypeScript 类型支持。
为什么需要内容审核?
在用户生成内容(UGC)的应用中,内容审核是必不可少的功能:
- 合规要求: 符合国家法律法规,避免违规内容
- 用户体验: 过滤垃圾信息、广告、恶意内容
- 品牌保护: 防止不良内容影响品牌形象
- 自动化: 减少人工审核成本,提高效率
功能特性
文本内容审核
- ✅ 违规内容检测(政治、色情、暴力、广告等)
- ✅ 批量审核支持
- ✅ 自定义业务类型
- ✅ 违规关键词提取
- ✅ 审核建议(通过/拦截/人工审核)
图片内容审核
- ✅ 多种检测方式(文件路径、URL)
- ✅ 违规图片识别
- ✅ OCR 文字识别
- ✅ 物体检测
- ✅ 人脸识别
- ✅ 自定义库匹配
快速开始
配置环境变量
在 .env.local 中配置腾讯云密钥:
TENCENT_CLOUD_SECRET_ID=your-secret-id
TENCENT_CLOUD_SECRET_KEY=your-secret-key
TENCENT_CLOUD_REGION=ap-shanghai # 可选,默认 ap-shanghai获取腾讯云密钥
文本审核示例
import { createTencentTextModerationClientFromEnv } from "@mono/tencent-cloud";
// 从环境变量创建客户端
const client = createTencentTextModerationClientFromEnv();
// 审核文本内容
const result = await client.moderateText("用户评论内容");
if (result.suggestion === "Pass") {
console.log("✅ 内容安全");
} else if (result.suggestion === "Block") {
console.log("❌ 内容违规:", result.label);
console.log("违规关键词:", result.keywords);
} else {
console.log("⚠️ 需要人工审核");
}图片审核示例
import { createTencentImageModerationClientFromEnv } from "@mono/tencent-cloud";
const client = createTencentImageModerationClientFromEnv();
// 方式 1: 通过 URL 审核
const result = await client.moderateImage({
fileUrl: "https://example.com/image.jpg",
});
// 方式 2: 通过文件路径审核
const result = await client.moderateImage({
filePath: "/path/to/image.jpg",
});
if (result.suggestion === "Pass") {
console.log("✅ 图片安全");
} else {
console.log("❌ 图片违规:", result.label);
console.log("详细结果:", result.labelResults);
}实战场景
场景 1: 用户评论审核
在用户发表评论时自动审核内容:
import { createTencentTextModerationClientFromEnv } from "@mono/tencent-cloud";
import { prisma } from "@/lib/prisma";
const moderationClient = createTencentTextModerationClientFromEnv();
export async function createComment(userId: string, content: string) {
// 1. 审核内容
const moderation = await moderationClient.moderateText(content);
// 2. 根据审核结果决定状态
if (moderation.suggestion === "Block") {
throw new Error("评论内容违规,无法发布");
}
// 3. 创建评论
const comment = await prisma.comment.create({
data: {
userId,
content,
// 可疑内容标记为待审核
status: moderation.suggestion === "Review" ? "pending" : "approved",
// 保存审核信息用于分析
moderationLabel: moderation.label,
moderationScore: moderation.score,
},
});
return comment;
}场景 2: 用户头像审核
上传头像时检查图片内容:
import { createTencentImageModerationClientFromEnv } from "@mono/tencent-cloud";
const moderationClient = createTencentImageModerationClientFromEnv();
export async function validateAvatar(imageUrl: string) {
const result = await moderationClient.moderateImage({
fileUrl: imageUrl,
bizType: "avatar", // 自定义业务类型
});
// 检查是否违规
if (result.suggestion === "Block") {
return {
valid: false,
reason: `图片包含违规内容: ${result.label}`,
};
}
// 检查是否包含文字(头像不应包含文字)
if (result.ocrResults.length > 0) {
const hasText = result.ocrResults.some(
(ocr) => ocr.Text && ocr.Text.length > 5
);
if (hasText) {
return {
valid: false,
reason: "头像不应包含过多文字",
};
}
}
return { valid: true };
}场景 3: 批量内容审核
定期审核历史内容:
import { createTencentTextModerationClientFromEnv } from "@mono/tencent-cloud";
import { prisma } from "@/lib/prisma";
const moderationClient = createTencentTextModerationClientFromEnv();
export async function auditPendingComments() {
// 1. 获取待审核评论
const comments = await prisma.comment.findMany({
where: { status: "pending" },
take: 100,
});
// 2. 批量审核
const contents = comments.map((c) => c.content);
const results = await moderationClient.moderateTexts(contents);
// 3. 更新状态
const updates = comments.map((comment, index) => {
const result = results[index];
return prisma.comment.update({
where: { id: comment.id },
data: {
status: result.suggestion === "Pass" ? "approved" : "rejected",
moderationLabel: result.label,
moderationScore: result.score,
},
});
});
await Promise.all(updates);
return {
total: comments.length,
approved: results.filter((r) => r.suggestion === "Pass").length,
rejected: results.filter((r) => r.suggestion === "Block").length,
};
}场景 4: API 路由集成
创建审核 API 端点:
// app/api/moderation/text/route.ts
import { createTencentTextModerationClientFromEnv } from "@mono/tencent-cloud";
import { NextResponse } from "next/server";
const client = createTencentTextModerationClientFromEnv();
export async function POST(request: Request) {
try {
const { content } = await request.json();
if (!content || typeof content !== "string") {
return NextResponse.json(
{ error: "Invalid content" },
{ status: 400 }
);
}
const result = await client.moderateText(content);
return NextResponse.json({
suggestion: result.suggestion,
label: result.label,
score: result.score,
keywords: result.keywords,
});
} catch (error) {
console.error("Moderation error:", error);
return NextResponse.json(
{ error: "Moderation failed" },
{ status: 500 }
);
}
}场景 5: 实时审核 Hook
创建 React Hook 用于客户端实时审核:
// hooks/use-content-moderation.ts
"use client";
import { useState } from "react";
export function useContentModeration() {
const [isChecking, setIsChecking] = useState(false);
const checkContent = async (content: string) => {
setIsChecking(true);
try {
const response = await fetch("/api/moderation/text", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content }),
});
const result = await response.json();
return result;
} finally {
setIsChecking(false);
}
};
return { checkContent, isChecking };
}
// 使用示例
function CommentForm() {
const { checkContent, isChecking } = useContentModeration();
const handleSubmit = async (content: string) => {
const result = await checkContent(content);
if (result.suggestion === "Block") {
alert(`内容违规: ${result.label}`);
return;
}
// 提交评论
await submitComment(content);
};
return (
<form onSubmit={(e) => {
e.preventDefault();
handleSubmit(e.currentTarget.content.value);
}}>
<textarea name="content" />
<button disabled={isChecking}>
{isChecking ? "检查中..." : "提交"}
</button>
</form>
);
}场景 6: 降级策略
审核服务不可用时的降级处理:
import { createTencentTextModerationClientFromEnv } from "@mono/tencent-cloud";
const client = createTencentTextModerationClientFromEnv();
// 本地关键词黑名单(降级方案)
const bannedKeywords = ["违禁词1", "违禁词2"];
export async function moderateWithFallback(content: string) {
try {
// 尝试使用腾讯云审核
const result = await client.moderateText(content);
return {
suggestion: result.suggestion,
source: "tencent-cloud",
};
} catch (error) {
console.error("审核服务不可用,使用本地规则:", error);
// 降级到本地关键词过滤
const hasViolation = bannedKeywords.some((keyword) =>
content.includes(keyword)
);
return {
suggestion: hasViolation ? "Block" : "Pass",
source: "local-fallback",
};
}
}API 参考
文本审核
moderateText(content, bizType?)
审核单个文本内容。
参数:
content(string): 要检测的文本内容bizType(string, 可选): 业务类型
返回:
{
suggestion: "Pass" | "Block" | "Review";
label: string; // 违规标签
score: number; // 违规分数 (0-100)
keywords: string[]; // 违规关键词
subLabel: string; // 子标签
}moderateTexts(contents, bizType?)
批量审核多个文本。
参数:
contents(string[]): 文本数组bizType(string, 可选): 业务类型
返回: Promise<TextModerationResult[]>
isTextSafe(content, bizType?)
判断文本是否安全。
返回: Promise<boolean>
图片审核
moderateImage(options)
审核图片内容。
参数:
{
filePath?: string; // 本地文件路径
fileUrl?: string; // 图片 URL
dataId?: string; // 数据 ID
bizType?: string; // 业务类型
type?: string; // 图片类型
interval?: number; // 截帧间隔(视频)
maxFrames?: number; // 最大截帧数(视频)
}返回:
{
suggestion: "Pass" | "Block" | "Review";
label: string;
subLabel: string;
score: number;
labelResults: Array<{...}>; // 标签检测结果
objectResults: Array<{...}>; // 物体检测结果
ocrResults: Array<{...}>; // OCR 识别结果
libResults: Array<{...}>; // 自定义库结果
recognitionResults: Array<{...}>; // 人脸识别结果
}isImageSafe(options)
判断图片是否安全。
返回: Promise<boolean>
审核结果说明
审核建议 (suggestion)
- Pass: 内容安全,可以放行
- Block: 内容违规,建议拦截
- Review: 内容可疑,建议人工审核
违规标签 (label)
常见标签包括:
Normal: 正常内容Porn: 色情内容Abuse: 谩骂内容Ad: 广告内容Illegal: 违法内容Spam: 垃圾信息Political: 政治敏感Terrorism: 暴恐内容
违规分数 (score)
- 范围: 0-100
- 分数越高,违规可能性越大
- 通常 > 80 建议拦截
最佳实践
1. 异步审核
对于非关键路径,使用异步审核:
// 先保存,后审核
const comment = await prisma.comment.create({
data: { content, status: "pending" },
});
// 异步审核
moderateComment(comment.id).catch(console.error);2. 缓存审核结果
对于相同内容,缓存审核结果:
import { createHash } from "crypto";
const cache = new Map<string, TextModerationResult>();
async function moderateWithCache(content: string) {
const hash = createHash("md5").update(content).digest("hex");
if (cache.has(hash)) {
return cache.get(hash)!;
}
const result = await client.moderateText(content);
cache.set(hash, result);
return result;
}3. 记录审核日志
记录审核结果用于分析:
await prisma.moderationLog.create({
data: {
userId,
content,
suggestion: result.suggestion,
label: result.label,
score: result.score,
keywords: result.keywords,
},
});4. 分级处理
根据违规程度采取不同措施:
if (result.score > 90) {
// 严重违规:直接拒绝
throw new Error("内容严重违规");
} else if (result.score > 70) {
// 中度违规:标记待审核
status = "pending";
} else if (result.score > 50) {
// 轻度违规:发出警告
warnings.push("内容可能不当");
}5. 选择合适的地域
根据业务所在地选择最近的地域:
// 华南地区
const client = createTencentTextModerationClientFromEnv("ap-guangzhou");
// 华东地区
const client = createTencentTextModerationClientFromEnv("ap-shanghai");
// 华北地区
const client = createTencentTextModerationClientFromEnv("ap-beijing");支持的地域
| 地域 | Region | 说明 |
|---|---|---|
| 华东(上海) | ap-shanghai | 默认地域 |
| 华南(广州) | ap-guangzhou | 华南地区 |
| 华北(北京) | ap-beijing | 华北地区 |
| 西南(成都) | ap-chengdu | 西南地区 |
| 西南(重庆) | ap-chongqing | 西南地区 |
错误处理
认证错误
try {
const client = createTencentTextModerationClientFromEnv();
} catch (error) {
// Error: "TENCENT_CLOUD_SECRET_ID and TENCENT_CLOUD_SECRET_KEY environment variables are required"
console.error("配置错误:", error);
}审核失败
try {
const result = await client.moderateText(content);
} catch (error) {
// Error: "Text moderation failed: [具体错误]"
console.error("审核失败:", error);
// 实施降级策略
}参数错误
try {
await client.moderateImage({});
} catch (error) {
// Error: "Either filePath or fileUrl must be provided"
console.error("参数错误:", error);
}性能优化
批量处理
// ✅ 推荐: 批量审核
const results = await client.moderateTexts(contents);
// ❌ 避免: 逐个审核
for (const content of contents) {
await client.moderateText(content);
}并发控制
import pLimit from "p-limit";
const limit = pLimit(5); // 最多 5 个并发
const results = await Promise.all(
contents.map((content) =>
limit(() => client.moderateText(content))
)
);故障排查
问题 1: 认证失败
症状: AuthFailure 错误
解决方案:
- 检查 SecretId 和 SecretKey 是否正确
- 确认账号已开通内容安全服务
- 检查 API 密钥权限
问题 2: 地域不支持
症状: UnsupportedRegion 错误
解决方案:
- 检查地域代码是否正确
- 使用默认地域
ap-shanghai - 查看 支持的地域列表
问题 3: 请求频率限制
症状: RequestLimitExceeded 错误
解决方案:
- 实现请求限流
- 使用批量审核接口
- 联系腾讯云提升配额
问题 4: 图片审核失败
症状: 图片审核返回错误
解决方案:
- 检查图片格式(支持 JPG、PNG、BMP、GIF、WEBP)
- 确认图片大小不超过 5MB
- 检查图片 URL 是否可访问
- 确认文件路径是否正确
相关资源
相关包
@mono/content-moderation- 内容审核抽象层tencentcloud-sdk-nodejs- 腾讯云官方 SDK