10 KiB
第三方风险评估与报告追问接口
当前第三方系统只需调用两个 POST 接口,均一次性返回 JSON:
| 用途 | 路径 | 响应类型 |
|---|---|---|
| 生成报告或重取原报告 | /api/v1/integrations/risk-assessments/report |
application/json |
| 针对原报告继续追问 | /api/v1/integrations/risk-assessments/follow-up |
application/json |
没有单独的创建会话、历史查询、状态查询或流式接口。PLM 不需要管理 conversationId,只需保存报告 requestId。后端第一次追问时自动建立会话,后续同一调用系统、同一报告、同一报告操作人的多次追问沿用该会话。
1. 鉴权与操作人
两个接口均使用系统 HMAC-SHA256 鉴权,不依赖平台用户 JWT。系统密钥只保存在双方后端;PLM 后端必须从真实登录态获取操作人身份,不允许浏览器自行冒用账号。
| 请求头 | 说明 |
|---|---|
Content-Type |
application/json |
X-Client-Id |
已开通的调用系统编号,例如 dieya-plm |
X-Timestamp |
当前 Unix 秒级时间戳,误差不超过 5 分钟 |
X-Nonce |
每次请求使用新的 8~64 位随机串,允许字母、数字、下划线和短横线 |
X-Signature |
HMAC-SHA256 结果,标准 Base64 编码 |
Idempotency-Key |
生成报告时等于 requestId;追问时等于 messageRequestId |
X-Signature-Version |
仅追问接口必须提供,固定为 2 |
生成报告沿用原签名,避免变更已有签名代码:
时间戳 + 换行 + Nonce + 换行 + 原始请求正文
追问的 v2 签名绑定方法、路径和正文,使用真实换行字节(0x0A):
POST + 换行 + /api/v1/integrations/risk-assessments/follow-up + 换行 + 空查询串 + 换行 + 时间戳 + 换行 + Nonce + 换行 + 原始请求正文
追问签名 Python 示例;body 必须与实际发送的 UTF-8 字节完全一致:
import base64
import hashlib
import hmac
import secrets
import time
def sign_follow_up_request(method, path_and_query, body, client_id, client_secret):
timestamp = str(int(time.time()))
nonce = secrets.token_urlsafe(18)
path, _, query = path_and_query.partition("?")
canonical = b"\n".join((
method.upper().encode("ascii"), path.encode("ascii"), query.encode("ascii"),
timestamp.encode("ascii"), nonce.encode("ascii"), body,
))
signature = base64.b64encode(
hmac.new(client_secret.encode("utf-8"), canonical, hashlib.sha256).digest()
).decode("ascii")
return {
"X-Signature-Version": "2", "X-Client-Id": client_id,
"X-Timestamp": timestamp, "X-Nonce": nonce, "X-Signature": signature,
}
operator 使用以下两种身份之一,不能混用:
- 钉钉用户:
{"dingtalkUnionId":"真实UnionId","dingtalkUserId":"真实UserId"},其中 UserId 可省略。 - 外部用户:
{"externalUserId":"superadmin"},需要提前配置该调用系统到平台本地账号的映射。
name、employeeNo 不能单独作为身份依据。账号需启用并拥有 risk.assess 操作权限;每次追问都会重新校验报告归属,不能跨系统、跨用户访问,也不按名字猜测用户。
2. 生成报告
POST /api/v1/integrations/risk-assessments/report
Content-Type: application/json
Idempotency-Key: PLM-REPORT-001
另需提供系统签名请求头。请求正文:
{
"requestId": "PLM-REPORT-001",
"sourceSystem": "dieya-plm",
"operator": {"externalUserId": "superadmin"},
"assessment": {
"subjectId": "PRODUCT-001",
"productName": "保湿面霜",
"productCategory": "面霜/护肤类",
"specification": "50g",
"materialType": "塑料罐",
"stageNew": "产品开发",
"supplier": null,
"factory": null,
"description": "新品包装风险评估",
"asOfDate": "2026-10-28"
}
}
必填字段:requestId、sourceSystem、operator,以及 assessment.subjectId、productCategory、materialType。sourceSystem 必须与 X-Client-Id 一致。其他评估字段可省略:产品名、规格、供应商、加工厂、开发阶段、缺陷等级、描述和截止日期。
requestId为 1~128 位字母、数字、下划线、点、冒号或短横线。stageNew可为需求发现、产品立项、产品开发、量产上市、产品生命管理。defectLevel可为 A、B、C;代表已知信息,不预测新品缺陷等级。asOfDate为 YYYY-MM-DD,不允许未来日期;省略时采用平台当天日期。- 评分仍采用现有七维规则和风险平台本地数据。无法归类或缺少数据时会在报告里说明,不能把默认分理解为通过验证。
成功返回 HTTP 200:
{
"code": 200,
"message": "success",
"data": {
"requestId": "PLM-REPORT-001",
"status": "completed",
"assessmentId": 123,
"score": 62.5,
"level": "moderately_low",
"modelVersion": "database-seven-dimension-v1",
"asOfDate": "2026-10-28",
"reportMarkdown": "# 新品风险评估报告\n\n完整报告正文……"
}
}
示例分数和 ID 仅说明格式。报告正文仍是 Markdown,但放在 JSON 的 data.reportMarkdown 中,HTTP 响应不再是 text/markdown。已集成旧响应的调用方需要改为解析 JSON 后读取该字段。保留 X-Request-Id、X-Assessment-Id、X-Risk-Score、X-Risk-Level、X-Model-Version 响应头,便于现有日志衔接。
同一 sourceSystem + requestId 重复提交同一内容,返回已保存的同一份报告;相同编号换内容返回 409。获取原报告直接重复 POST 原参数,不再提供 GET 查询接口。尚在生成时返回 202 和 Retry-After,等待后重发相同参数,每次使用新的 Nonce 和签名。
3. 对报告追问
POST /api/v1/integrations/risk-assessments/follow-up
Content-Type: application/json
X-Signature-Version: 2
Idempotency-Key: PLM-QUESTION-001
另需提供对应的 HMAC v2 签名请求头。请求正文:
{
"requestId": "PLM-REPORT-001",
"messageRequestId": "PLM-QUESTION-001",
"operator": {"externalUserId": "superadmin"},
"content": "为什么供应商这一项得分较低?"
}
requestId是前一步已成功生成的原报告编号。messageRequestId是本次问题的唯一编号,每个新问题换一个编号;重试同一个问题保持编号及内容不变。operator必须具有原报告的访问权限。content为 1~2000 字符,不能为空或纯空格。- 无需传报告正文、完整聊天历史或会话 ID,后端自行加载。
完成后一次性返回 HTTP 200 JSON,不发送 SSE 或增量文字:
{
"code": 200,
"message": "success",
"data": {
"requestId": "PLM-REPORT-001",
"messageRequestId": "PLM-QUESTION-001",
"status": "completed",
"reply": "根据原报告,供应商维度的得分依据是……"
}
}
继续问“应该怎么改善”,仍使用相同 requestId,换用 messageRequestId=PLM-QUESTION-002 即可。两次问题自动进入同一会话,模型同时获得原报告和近期聊天。
追问会复用现有 Prompt 语义路由,每轮固定提供原始产品资料、报告截止日期和结构化评分;Markdown 正文最多带入 32000 字符,截断时会标记。近期聊天超过窗口后,原报告仍会保留。追问不修改原报告,也不触发重新评分;正式重算应使用新报告编号提交更新后的评估参数。
4. 重试与错误
- 同一个问题重复 POST:返回原答案,不重复插入用户消息,不重复生成已完成的回答。
- 同问题编号更改内容:
409。 - 同一问题正在处理中:
202JSON,data.status=processing、reply=null,携带Retry-After: 3;之后仍重复调用本接口,不需要状态查询接口。 - 同一会话有另一条未完成问题:
409,先用原问题编号完成重试,避免上下文乱序。 - 生成失败或超时:
503JSON,可用原编号和原内容重试。 - 网络断开或服务重启后:重复调用同一接口及原参数;如果旧处理租约仍有效,会先返回
202,过期后重试恢复。 - 签名无效或重放 Nonce:
401;账号未开通、停用或无权限:403;报告不存在或无权访问:404;参数不合法:422;超出频率:429。
处理中的追问示例:
{
"code": 202,
"message": "原问题正在生成,请用相同编号重试",
"data": {
"requestId": "PLM-REPORT-001",
"messageRequestId": "PLM-QUESTION-001",
"status": "processing",
"reply": null
}
}
普通错误统一为 {"code":503,"message":"可阅读的错误说明","data":null}。追问总生成时限默认 240 秒,处理租约 5 分钟。回答正文和完成状态在同一事务中提交;过期旧处理令牌不能覆盖新的回答。
5. 服务端启用与调用脚本
发布前执行迁移并重启后端:
.\.venv\Scripts\python.exe -m alembic upgrade head
新增 integration_conversations 和 integration_conversation_requests 仅用于后端关联会话和幂等,不需要第三方额外调用接口。已有内部用户会话的流式功能不受此处接口精简影响。
RISK_INTEGRATION_CLIENTS={"dieya-plm":"replace-with-at-least-32-random-characters"}
RISK_INTEGRATION_OPERATOR_MAPPINGS={"dieya-plm":{"superadmin":"plm_superadmin"}}
RISK_INTEGRATION_REQUESTS_PER_MINUTE=60
RISK_INTEGRATION_REPORT_TIMEOUT_SECONDS=110
RISK_INTEGRATION_CHAT_TIMEOUT_SECONDS=240
示例密钥必须替换为双方后端保管的独立密钥,至少 32 字符,不能提交真实 .env。默认空 JSON 不允许外部调用。可用 scripts/setup_external_risk.py 开通系统与批准操作人映射。
调用方终端设置 RISK_INTEGRATION_CLIENT_SECRET 后:
# report-request.json 使用第 2 节正文
.\.venv\Scripts\python.exe scripts/call_external_risk_report.py --url http://127.0.0.1:18474/api/v1/integrations/risk-assessments/report --body-file report-request.json --output report.md
# follow-up.json 使用第 3 节正文
.\.venv\Scripts\python.exe scripts/call_external_follow_up.py --url http://127.0.0.1:18474/api/v1/integrations/risk-assessments/follow-up --body-file follow-up.json
生成报告脚本读取 JSON 中的 reportMarkdown 保存为 Markdown 文件;追问脚本直接打印完整 JSON。PLM 后端按同一协议接入即可,HMAC 密钥不下发浏览器。