8.0 KiB
阿美巴内容判断引擎文章发布 API
版本:V1
站点身份:human
公开栏目:认知观察
生产站点:https://www.ameiba.cn
本文档供“阿美巴内容判断引擎”调用传统官网发布接口。网站只接收人工确认终稿、保存并安全排版,不调用 AI 二次修改正文。
接口配置
base_url=https://www.ameiba.cn
health_path=/api/internal/content/v1/health
upsert_path=/api/internal/content/v1/articles/upsert
auth_type=Bearer
token_environment_name=CONTENT_INGEST_TOKEN
site_profile=human
public_url_rule=https://www.ameiba.cn/insights/{slug}
所有请求必须使用 HTTPS,并在请求头携带:
Authorization: Bearer <CONTENT_INGEST_TOKEN>
发布密钥只保存在双方服务端环境变量中,不得写入前端、Git、请求日志或普通业务日志。
1. 健康检查
GET /api/internal/content/v1/health
Authorization: Bearer <CONTENT_INGEST_TOKEN>
成功响应,HTTP 200:
{
"status": "ok",
"service": "amaba-human-website",
"site_profile": "human",
"api_version": "v1"
}
2. 新增或更新文章
POST /api/internal/content/v1/articles/upsert
Authorization: Bearer <CONTENT_INGEST_TOKEN>
Content-Type: application/json
Idempotency-Key: <source_task_id>:<source_version_id>:human
请求示例:
{
"request_id": "01JEXAMPLE",
"source_system": "amaba-content-core",
"source_task_id": "69900421-8edd-4fb3-9bc6-f656348a416c",
"source_version_id": "af6a2665-f8f3-4f16-b99e-14372a311bf8",
"source_version_number": 2,
"content_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"site_profile": "human",
"publish_mode": "publish",
"article": {
"title": "文章标题",
"body_plain": "第一自然段。\n第二自然段。",
"summary": "列表页摘要",
"slug": "example-slug",
"primary_keyword": "企业AI进化",
"keywords": ["企业AI", "组织进化"],
"content_type": "industry",
"direction": "行业观察",
"target_audience": "企业经营者与管理者",
"author_name": "阿美巴",
"cover_image_url": null,
"canonical_url": null
},
"quality": {
"human_confirmed": true,
"confirmed_at": "2026-09-11T10:00:00+08:00",
"prompt_version": "AmAba-Audit-V1.2",
"judgment_version": "AmAba-Judgment-V1.1",
"model": "deepseek",
"remaining_issue_levels": ["P3"]
},
"provenance": {
"knowledge_refs": [
{"id": "knowledge-id", "name": "知识名称", "version": "1.0"}
]
},
"semantic": {
"entities": ["阿美巴", "企业AI进化"],
"questions": [],
"facts": [],
"suggested_schema_type": "Article"
}
}
首次创建成功返回 HTTP 201:
{
"success": true,
"publication_id": "art-网站文章ID",
"status": "published",
"url": "https://www.ameiba.cn/insights/example-slug",
"revision": 1,
"content_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"published_at": "2026-09-11T10:01:20.000Z"
}
同一来源任务的新确认版本会更新原文章,保留原 publication_id 和原公开 URL,revision 加 1,返回 HTTP 200。相同幂等键的安全重试也返回 HTTP 200。
字段和校验规则
source_system固定为amaba-content-core。site_profile固定为human。quality.human_confirmed必须严格为true。source_version_number必须是正整数;更新时必须高于网站当前版本,防止旧版本倒序覆盖。content_hash必须为 64 位 SHA-256 十六进制字符串。publish_mode支持publish和draft。article.title最长 200 字符。article.body_plain最长 200,000 字符。article.summary最长 1,000 字符。- 关键词、问题、实体、事实、知识引用等数组最多 30 项。
slug可省略;提供时仅允许小写字母、数字和连字符。cover_image_url和canonical_url仅接受 HTTP/HTTPS URL。- 接口默认按来源 IP 每分钟限制 30 次,可用
CONTENT_INGEST_RATE_LIMIT调整。
正文安全处理
body_plain 被视为不可信纯文本。服务端会:
- 统一换行符。
- 按单换行拆分自然段并忽略空行。
- 转义
& < > " '等 HTML 特殊字符。 - 每个自然段生成独立
<p>存入content_html。 - 同时原样保存规范化后的
body_plain。
前端使用 React 文本节点按自然段渲染,不使用 dangerouslySetInnerHTML,因此正文中的 HTML 或 JavaScript 不会执行。
幂等和文章唯一性
标准幂等键:
<source_task_id>:<source_version_id>:human
规则:
UNIQUE(idempotency_key)防止同一版本重复发布。UNIQUE(source_system, source_task_id, site_profile)保证同一内容任务始终对应同一网站文章和公开 URL。- 每个发布过的幂等键同时写入
article_ingest_requests,因此旧版本请求在新版本发布后重试仍可正确识别。 - 相同幂等键和相同哈希:返回原
publication_id与 URL,不写入重复文章。 - 相同幂等键但哈希不同:返回 HTTP 409,不静默覆盖。
- 新
source_version_id和新幂等键:更新原文章,保留 slug,revision + 1。
数据库增量迁移
现有 MySQL articles 表增量增加:
source_system, source_task_id, source_version_id, source_version_number,
site_profile, idempotency_key, content_hash, body_plain, revision,
primary_keyword, keywords_json, content_type, direction, target_audience,
author_name, human_confirmed, confirmed_at, request_id, canonical_url,
quality_json, provenance_json, semantic_json
新增关联表 article_ingest_requests 保存历史幂等键、文章 ID、版本 ID、哈希和当次 revision。迁移位于 cms/server/src/db.js 的现有启动迁移流程中,CMS 正常启动时通过 SHOW COLUMNS、ALTER TABLE ADD COLUMN 和 CREATE TABLE IF NOT EXISTS 执行;不会删除、清空或重建现有文章表。
错误响应
统一格式:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "article.title不能为空",
"retryable": false
}
}
| HTTP | code | 是否建议自动重试 | 说明 |
|---|---|---|---|
| 400 | VALIDATION_ERROR |
否 | 请求字段或幂等键格式错误 |
| 401 | UNAUTHORIZED |
否 | 缺少或错误 Token |
| 403 | HUMAN_CONFIRMATION_REQUIRED |
否 | 不是人工确认终稿 |
| 403 | SOURCE_NOT_ALLOWED / SITE_PROFILE_MISMATCH |
否 | 来源系统或站点身份错误 |
| 409 | CONTENT_CONFLICT |
否,需人工检查 | 幂等哈希、slug 或版本冲突 |
| 413 | CONTENT_TOO_LARGE |
否 | 正文或元数据超过限制 |
| 429 | RATE_LIMITED |
是 | 请求频率超过限制 |
| 500 | INTERNAL_ERROR |
是 | 网站内部错误 |
| 503 | SERVICE_UNAVAILABLE |
是 | 生产环境未配置发布密钥或服务暂不可用 |
curl 示例
健康检查:
curl -H "Authorization: Bearer $CONTENT_INGEST_TOKEN" \
https://www.ameiba.cn/api/internal/content/v1/health
发布文章:
curl -X POST \
-H "Authorization: Bearer $CONTENT_INGEST_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 69900421-8edd-4fb3-9bc6-f656348a416c:af6a2665-f8f3-4f16-b99e-14372a311bf8:human" \
--data @article-publish.json \
https://www.ameiba.cn/api/internal/content/v1/articles/upsert
环境变量
CONTENT_INGEST_TOKEN=双方服务端共享的高强度随机密钥
CONTENT_INGEST_RATE_LIMIT=30
PUBLIC_SITE_URL=https://www.ameiba.cn
仓库中的 cms/server/.env.example 只提供空值名称。真实值写入服务器的 /var/www/ameiba-cn-new/cms/server/.env,不得提交 Git。
本地验证
cd cms/server
npm install
npm test
cd ../..
npm install
npm run build
当前验收结果:内部发布接口自动化测试 12 项通过;前端 TypeScript 与 Vite 生产构建通过。自动化测试覆盖 Token、健康检查、字段校验、人工确认、站点身份、首次创建、幂等重试、哈希冲突、同 URL 更新、revision 增长、自然段转换与 XSS 转义。