Content moderation upgrade and migration guide
Migrate from legacy Baidu text moderation or a custom censor URL to the new moderation Plugin system
This guide covers the differences between the legacy content moderation mechanism and the new moderation Plugin system, and shows how to migrate from legacy Baidu text moderation or a custom moderation URL (customCensorURL).
1. Key differences between legacy and new moderation
The new release replaces the built-in text moderation in fastgpt-pro with a standardized moderation Plugin architecture managed by fastgpt-plugin, along with global policy controls and audit logs in the admin console.
| Dimension | Legacy (fastgpt-pro built-in censor) | New (moderation Plugin system) |
|---|---|---|
| Runtime and extensibility | Built into the fastgpt-pro process. Supports only Baidu text moderation or a single custom URL (customCensorURL overrides Baidu when set). | Runs in isolated fastgpt-plugin worker pools. Includes official Plugins for Baidu (baidu), Alibaba Cloud (aliyun), Tencent Cloud (tencent), keyword filtering (keyword), and legacy custom censor compatibility (legacy-custom-censor), plus custom Plugins built with the SDK. |
| Configuration entry | Required changes in two places: 1. Set credentials or URL under censor in the commercial system config2. Set censor: true on every LLM model that needed moderation | The per-model censor flag is deprecated. Configure Plugin parameters, active checkpoints, and handling policies in one place at Admin Console > Content Moderation > Moderation Policy (/admin/moderation/policy). |
| Moderation checkpoints | Checked only before calling an LLM with censor: true (concatenating the System prompt and current user input). Did not support model output or streaming moderation. | Supports three independently configurable global checkpoints: 1. User input ( userInput): Checks current user queries, global Variables, and form inputs before running2. Model input ( modelInput): Checks content sent to the model, including Prompts, Dataset citations, and file text (Chat history is exempt)3. Final output ( finalOutput): Checks text and streaming responses delivered to users (with a configurable streaming check interval) |
| Verdicts and Baidu logic | Only distinguished pass (200) and block (5000). Legacy Baidu moderation blocked content only when data[0].hits[0].words was non-empty, missing violations without matched words. | Standardized four-tier verdicts: pass, block, suspected, and error. The new Baidu Plugin parses conclusionType completely, maps both non-compliant (2) and suspected (3) results accurately, and aggregates risk labels and matched keywords. |
| Suspected risk and service exceptions | No suspected tier; network errors or timeouts silently passed content through (implicit fail-open). | Explicit policy controls: 1. Suspected risk ( suspectedRiskAction): Block and log (default) or Pass and log2. Service exception ( serviceExceptionAction): Allow unmoderated content (default, fail-open) or Block unmoderated content (fail-closed) |
| Audit records and testing | No dedicated moderation records or built-in test runner. | Built-in Moderation test, global Moderation records (/admin/moderation/records), Moderation service logs (/admin/moderation/logs), and per-message moderation details in Chat history. |
2. Migrate from legacy Baidu content moderation
2.1 Configuration mapping
Map the two fields under censor in your legacy commercial configuration to the Baidu Content Security (baidu) Plugin parameters:
| Legacy commercial config field | New baidu Plugin field | Required | Encrypted | Description |
|---|---|---|---|---|
censor.BAIDU_TEXT_CENSOR_CLIENTID | API Key (apiKey) | Yes | Yes | API Key of your Baidu AI Cloud content moderation app |
censor.BAIDU_TEXT_CENSOR_CLIENTSECRET | Secret Key (secretKey) | Yes | Yes | Secret Key of your Baidu AI Cloud content moderation app |
| (Not supported in legacy) | Strategy ID (strategyId) | No | No | Custom moderation strategy ID configured in Baidu AI Cloud; leave blank to use the default strategy |
2.2 Migration steps
- Install or verify the Plugin: Sign in as an administrator, go to Admin Console > Content Moderation > Moderation Policy (
/admin/moderation/policy), and turn on Enable content moderation. If Baidu Content Security is not in the Plugin list, click Add from Marketplace (or Install from local to upload thebaidu.pkgfile in an offline environment). - Configure Baidu credentials: Find Baidu Content Security in the list and click Configure:
- Enter your legacy
BAIDU_TEXT_CENSOR_CLIENTIDin API Key. - Enter your legacy
BAIDU_TEXT_CENSOR_CLIENTSECRETin Secret Key. - Optionally enter a Strategy ID if you use a custom strategy in Baidu AI Cloud.
- Save the configuration. FastGPT automatically runs a connectivity check against Baidu's API before saving.
- Enter your legacy
- Select the active service: Select the radio button next to Baidu Content Security to set it as the Current active service.
- Configure checkpoints and handling policies:
- Moderation checkpoints: User input and Final output are enabled by default. To match legacy input-only behavior, enable User input (and optionally Model input). If you keep Final output enabled, adjust Streaming check interval (default
50characters/check) to balance latency and API usage. - Suspected risk: The new Baidu Plugin maps
conclusionType = 3(suspected) tosuspected, which defaults to Block and log. Switch to Pass and log if you prefer not to block suspected content. - Service exception: Defaults to Allow unmoderated content, matching the legacy fail-open behavior.
- Moderation checkpoints: User input and Final output are enabled by default. To match legacy input-only behavior, enable User input (and optionally Model input). If you keep Final output enabled, adjust Streaming check interval (default
- Test and save: Enter sample text in Moderation test at the bottom of the page, click Start test to verify pass and block behavior, then save the policy.
- Clean up legacy settings: Once verified, remove
censor.BAIDU_TEXT_CENSOR_CLIENTIDandcensor.BAIDU_TEXT_CENSOR_CLIENTSECRETfrom your commercial configuration. Legacycensorflags on individual models are ignored at runtime and do not need to be removed manually.
3. Migrate from a legacy custom censor URL (customCensorURL)
If you previously configured censor.customCensorURL to connect an internal or third-party HTTP moderation service, you have two migration options:
- Option A: Use the official Legacy Custom Moderation (
legacy-custom-censor) Plugin (zero code changes, recommended for quick migration) - Option B: Build a native
moderationPlugin with the SDK (supports risk labels, matched keywords, and suspected verdicts, recommended for deep integration)
3.1 Option A: Use the Legacy Custom Moderation Plugin
The official legacy-custom-censor Plugin adapts the legacy customCensorURL HTTP contract directly so you can connect your existing service without modifying its code.
HTTP contract and verdict mapping
The Plugin sends the following HTTP request to your configured endpoint:
POST <endpoint>
Content-Type: application/json
{"text": "Text to moderate"}Your service must return a JSON object with a top-level boolean success field:
{
"success": true,
"message": "Optional message"
}| Upstream response | New verdict (verdict) | Notes |
|---|---|---|
HTTP 2xx with {"success": true} | pass | Requires success to be the boolean true. |
HTTP 2xx with {"success": false} | block | Requires success to be the boolean false; message is ignored. |
Timeout, network error, non-2xx status, invalid JSON, or non-boolean success (such as "false") | error | Non-boolean values are no longer coerced to pass; the global Service exception policy determines whether to allow or block the request. |
Note: Set
endpointto your actual custom moderation service URL, not the internalfastgpt-proroute/api/common/censor/check(the Plugin does not accept{ "code": 200 }or{ "data": ... }wrappers).
Network and runtime checklist
In the legacy architecture, requests originated from the fastgpt-pro process. In the new architecture, requests originate from fastgpt-plugin (Plugin Server) worker processes. Before migrating, verify that:
- Endpoint reachability: If your legacy
customCensorURLusedlocalhost,127.0.0.1, or an internal container hostname, update it to an address reachable from thefastgpt-plugincontainer or node. - Network allowlist: Confirm that the
fastgpt-pluginruntime allows outbound connections to the target network (for example, viaPOOL_SERVICE_POD_ALLOW_NET) and that your moderation service's firewall or IP allowlist permits traffic fromfastgpt-plugin.
Configuration steps
- Go to Admin Console > Content Moderation > Moderation Policy (
/admin/moderation/policy), turn on Enable content moderation, and install Legacy Custom Moderation (legacy-custom-censor) from the Marketplace or a local.pkgfile. - Click Configure next to the Plugin and fill in:
- Legacy censor URL (
endpoint): Required. Enter your originalcustomCensorURL(must usehttp://orhttps://without embedded username or password credentials). - Request timeout (
timeoutMs): Optional. Integer from100to60000ms, defaulting to10000ms (responses are capped at 64 KiB, with no automatic retries or HTTP redirects).
- Legacy censor URL (
- Save the parameters. FastGPT sends a non-sensitive test phrase to verify that the endpoint returns a valid boolean
successresponse. - Select Legacy Custom Moderation as the Current active service and configure your Moderation checkpoints:
- To preserve legacy fail-open behavior on upstream errors, keep Service exception set to Allow unmoderated content.
- Run a check in Moderation test, save the policy, and remove
censor.customCensorURLfrom your legacy commercial configuration.
3.2 Option B: Build a native moderation Plugin
If your moderation service returns risk categories (such as politics, pornography, contraband, or advertising), matched keywords, confidence scores (0–100), or suspected verdicts, build a native moderation Plugin with @fastgpt-plugin/sdk-factory. Native Plugins display matched keywords and risk labels directly in FastGPT's Moderation records and Chat history details.
1. Scaffold the Plugin
Use @fastgpt-plugin/cli in your Plugin workspace to scaffold a moderation Plugin:
npx @fastgpt-plugin/cli create my-moderation --type moderation2. Define the secret schema and moderation logic
In index.ts, use defineModeration, defineModerationManifest, and defineSecretSchema to declare metadata, configuration fields, and the check handler:
import {
defineModeration,
defineModerationManifest,
defineSecretSchema
} from '@fastgpt-plugin/sdk-factory';
import z from 'zod';
const secretSchema = defineSecretSchema({
endpoint: {
type: z.string().trim().url(),
title: { en: 'Moderation Endpoint', 'zh-CN': '审核接口地址' },
description: {
en: 'HTTP(S) endpoint of your moderation service',
'zh-CN': '自定义审核服务的 HTTP(S) 接口地址'
},
isSecret: false
},
apiKey: {
type: z.string().min(1),
title: { en: 'API Key', 'zh-CN': 'API Key' },
isSecret: true
}
});
export default defineModeration({
manifest: defineModerationManifest({
pluginId: 'my-moderation',
version: '1.0.0',
name: { en: 'Custom Moderation', 'zh-CN': '自定义内容审核' },
description: {
en: 'Custom content moderation plugin',
'zh-CN': '对接内部内容安全审核服务'
},
meta: {
provider: 'my-moderation'
}
}),
secretSchema,
provider: {
name: 'my-moderation',
check: async (input, ctx) => {
try {
const response = await fetch(ctx.secrets.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${ctx.secrets.apiKey}`
},
body: JSON.stringify({ content: input.content }),
signal: AbortSignal.timeout(5000)
});
if (!response.ok) {
return ctx.moderationError(`Upstream HTTP ${response.status}`);
}
const data = (await response.json()) as {
verdict: 'pass' | 'block' | 'suspected';
label?: string;
score?: number;
keywords?: string[];
requestId?: string;
};
if (data.verdict === 'pass') {
return {
verdict: 'pass',
hits: [],
requestId: data.requestId
};
}
return {
verdict: data.verdict,
hits: [
{
label: 'contraband', // Normalized risk label
providerLabel: data.label || 'custom_rule', // Raw upstream label
score: data.score, // Optional confidence score (0-100)
keywords: data.keywords // Optional matched keywords
}
],
requestId: data.requestId
};
} catch (error) {
// Return a structured error result instead of throwing so the host can apply its service exception policy
return ctx.moderationError(
error instanceof Error ? error.message : 'Moderation request failed'
);
}
}
}
});3. Result contract and implementation guidelines
- Four
verdictvalues:pass: Content is compliant.block: Content violates policy and is blocked immediately.suspected: Content is flagged as suspected risk; FastGPT applies the global Suspected risk policy (Block and logorPass and log).error: Upstream timeout, network failure, authentication error, or invalid response. Do not let uncaught exceptions escapecheck—returnctx.moderationError('...')(verdict: 'error'witherrorMessage) so FastGPT applies the configured Service exception policy and records the failure in the moderation service logs.
- Normalized risk labels (
hits[].label): Supported values areporn,politics,terror,contraband,abuse,hate,ad,advertising-law,privacy,flood,self-harm,violence,sexual-minors, andother. - Localized README files:
Provide
README.zh-CN.mdandREADME.en.md(plus optionalREADME.zh-Hant.md) in the Plugin root directory so they are bundled into the.pkgarchive and displayed in the admin console.
4. Debug, package, and install
Run the following commands in the Plugin directory to test locally and build the .pkg package:
# Run a local debug check from the CLI
npx @fastgpt-plugin/cli debug . --run \
--secrets '{"endpoint":"https://example.com/moderate","apiKey":"sk-xxx"}' \
--input '{"content":"Sample text to moderate"}'
# Build, validate, and package into a .pkg archive
pnpm build
npx @fastgpt-plugin/cli check --entry . --output ./dist
pnpm run packAfter packaging, go to Admin Console > Content Moderation > Moderation Policy, click Install from local, and upload the generated .pkg file to configure and enable your Plugin.