fix(platform): 能力误判根治三层——提取提示词能力判定铁律(按正文模态声明禁照示例);apply能力一致性守卫(描述模态vs capability矛盾代码级纠正t2v→r2v);overrides.model_capability白名单(用户口头纠正能落进规格,禁重提取死循环);check_model_accounting区分failed调用行/充值行非待记账
This commit is contained in:
parent
a24a2320de
commit
b2ecf54f43
@ -136,12 +136,35 @@ def _spec_overlay(base, overrides):
|
||||
"""覆盖项合并:仅允许少量标量覆盖(vendor_name 等),不做大 JSON 搬运。
|
||||
|
||||
overrides 只认白名单键,防止 LLM 借覆盖项塞回编造结构。
|
||||
model_capability(2026-09-06 wan3.0 教训):{vendor_model_id: capability}
|
||||
按模型纠正能力——用户提醒「这是参考生视频」后助手必须有工具路径把纠正
|
||||
落进规格,否则只能重跑 extract,同样提示词同样误判(提醒无效的死循环)。
|
||||
纠正值仍要过能力字典校验(下游 unknown 拒绝落库),不会被塞私货。
|
||||
"""
|
||||
allowed = {'vendor_name', 'base_url', 'protocol', 'doc_url', 'doc_notes'}
|
||||
out = dict(base or {})
|
||||
for k in allowed:
|
||||
if k in (overrides or {}) and str(overrides.get(k) or '').strip():
|
||||
out[k] = str(overrides[k]).strip()
|
||||
mc = (overrides or {}).get('model_capability')
|
||||
if isinstance(mc, str):
|
||||
try:
|
||||
mc = json.loads(mc)
|
||||
except Exception:
|
||||
mc = None
|
||||
if isinstance(mc, dict) and mc:
|
||||
fixed = []
|
||||
models = list(out.get('models') or [])
|
||||
for m in models:
|
||||
vmid = m.get('vendor_model_id') or m.get('name') or ''
|
||||
newcap = str(mc.get(vmid) or '').strip()
|
||||
if vmid and newcap and newcap != m.get('capability'):
|
||||
fixed.append('%s: %s→%s' % (vmid, m.get('capability'), newcap))
|
||||
m['capability'] = newcap
|
||||
out['models'] = models
|
||||
if fixed:
|
||||
out['doc_notes'] = ((out.get('doc_notes') or '') +
|
||||
' [能力已按人工纠正: %s]' % ';'.join(fixed)).strip()
|
||||
return out
|
||||
|
||||
|
||||
@ -335,7 +358,7 @@ _EXTRACT_PROMPT = """你是大模型 API 配置专家。通读下面这份模型
|
||||
"response_format": "响应格式说明(content 字段路径 + usage 字段路径)",
|
||||
"response_example": {"说明": "文档中的响应示例 JSON(或结果字段说明),逐字照抄;无则 null"},
|
||||
"async_steps": [{"purpose": "query|download", "path": "该步骤接口路径", "method": "GET|POST", "request_fields": ["请求字段名列表"], "response_format": "该步骤响应格式说明"}],
|
||||
"models": [{"vendor_model_id": "供应商侧模型ID", "capability": "t2t|t2i|i2t|t2v|i2v|r2v|i2i|embedding|rerank|tts|asr",
|
||||
"models": [{"vendor_model_id": "供应商侧模型ID", "capability": "t2t|t2i|i2t|t2v|i2v|r2v|i2i|embedding|rerank|tts|asr|mm-embedding|mm-rerank",
|
||||
"sync_mode": "sync|async(提交后需轮询查询结果的填 async)",
|
||||
"description": "一句话说明"}],
|
||||
"pricing": {
|
||||
@ -358,6 +381,16 @@ _EXTRACT_PROMPT = """你是大模型 API 配置专家。通读下面这份模型
|
||||
path 照文档逐字抄;同供应商多个模型会复用同一份查询模板,不要因模型而异。
|
||||
- 同步模型 async_steps 填空数组 []。
|
||||
|
||||
能力判定铁律(2026-09-06 wan3.0-video-prime 误判教训——概述明写"四模态全能参考"、
|
||||
输入=音频/图像/文本/视频,却因调用示例只有 prompt 被判成 t2v):
|
||||
- capability 必须以文档**概述正文 + 输入/输出模态声明**为准,禁止以调用示例为准——
|
||||
示例常只演示最简形态(纯文本 prompt),照示例判会把多媒体参考模型误判成纯文本模型。
|
||||
- 判定表(输入模态 → 输出模态 ⇒ 能力):
|
||||
多种媒体输入或正文含「参考/reference/全能参考/多图/多模态输入」⇒ r2v(即使示例只有文本);
|
||||
图像+文本 → 视频 ⇒ i2v;仅文本 → 视频 ⇒ t2v;仅文本 → 图像 ⇒ t2i;图像 → 图像 ⇒ i2i;
|
||||
视频 → 视频 ⇒ v2v 类按字典就近;文本 → 音频 ⇒ tts;音频 → 文本 ⇒ asr。
|
||||
- description 里必须保留正文的模态声明原文关键词(如"支持四模态全能参考"),供下游一致性校验。
|
||||
|
||||
定价规则:
|
||||
- pricing.items 每条必须带 doc_quote(文档原文定价句逐字照抄)——定价只允许来自文档原文
|
||||
- 价格/单位按文档原文记录,禁止自行换算、推导或统一单位
|
||||
@ -411,9 +444,20 @@ async def _h_extract_llm_api_spec(sor, params, ctx):
|
||||
if anchored:
|
||||
out["__anchored__"] = ("规格已锚定到本会话缓存。下一步调用 apply_llm_config 与 "
|
||||
"apply_model_pricing 时传 {\"use_last_extract\": true},"
|
||||
"需改动只传覆盖项(overrides,仅 vendor_name/base_url/"
|
||||
"protocol/doc_url/doc_notes 白名单键)。"
|
||||
"需改动只传覆盖项(overrides 白名单:vendor_name/base_url/"
|
||||
"protocol/doc_url/doc_notes/model_capability)。"
|
||||
"禁止把本规格复制进 spec 参数——复制即编造。")
|
||||
# 能力一致性告警(2026-09-06 wan3.0 教训):提取完当场校验描述模态声明 vs
|
||||
# capability,矛盾信息直接回给助手(用户纠正时传 overrides.model_capability 重 apply)
|
||||
cap_alerts = []
|
||||
for _m in (spec.get("models") or []):
|
||||
_fix, _warn = _capability_guard(_m.get("capability", ""), _m.get("description", ""))
|
||||
if _warn:
|
||||
cap_alerts.append("%s: %s" % (_m.get("vendor_model_id", "?"), _warn))
|
||||
if cap_alerts:
|
||||
out["__capability_alert__"] = ("能力分类与文档描述矛盾(按示例误判的典型信号),"
|
||||
"apply 时会自动纠正,也可用 overrides.model_capability "
|
||||
"人工指定:" + ";".join(cap_alerts))
|
||||
return json.dumps(out, ensure_ascii=False)
|
||||
|
||||
|
||||
@ -426,6 +470,52 @@ def _f(v, default=0.0):
|
||||
return default
|
||||
|
||||
|
||||
# 描述里的媒体输入声明词(能力一致性守卫用)
|
||||
_DESC_MEDIA_WORDS = {
|
||||
'image': ('图像', '图片', '多图'),
|
||||
'video': ('视频',),
|
||||
'audio': ('音频', '声音'),
|
||||
}
|
||||
_DESC_REF_WORDS = ('参考生', '全能参考', '参考图', '参考视频', '参考音频', '四模态', '多模态参考')
|
||||
|
||||
|
||||
def _capability_guard(capability, description):
|
||||
"""描述模态声明 vs capability 一致性守卫(2026-09-06 wan3.0-video-prime 实测:
|
||||
描述自带"支持四模态全能参考"、capability 却是 t2v——提取 LLM 照调用示例判能力,
|
||||
示例只演示最简形态就误判;提示词加铁律后仍需代码级兜底,LLM 不保证遵守)。
|
||||
|
||||
保守策略:只在「纯文本能力(t2v/t2i/t2t)+ 描述含媒体输入/参考声明」这种
|
||||
铁证矛盾时纠正;其他不一致只警告不改(避免误伤描述措辞不规范的正确配置)。
|
||||
返回 (corrected_cap 或 None, warning 或 None)。
|
||||
"""
|
||||
desc = description or ''
|
||||
if not desc or capability not in ('t2v', 't2i', 't2t'):
|
||||
return None, None
|
||||
has_ref = any(w in desc for w in _DESC_REF_WORDS)
|
||||
kinds = [k for k, words in _DESC_MEDIA_WORDS.items() if any(w in desc for w in words)]
|
||||
# 「输出 视频」的"视频"不算输入声明——t2v 描述几乎都带"视频"二字。
|
||||
# 输入声明判据:参考词,或图像/音频词(这两个不会出现在纯文本生成的描述里),
|
||||
# 或"视频"以外多模态组合。
|
||||
input_kinds = [k for k in kinds if k != 'video']
|
||||
if has_ref or len(input_kinds) >= 1 or (has_ref is False and len(kinds) >= 2 and 'video' in kinds and input_kinds):
|
||||
out_word = '视频' if capability == 't2v' else ('图像' if capability == 't2i' else '')
|
||||
if capability == 't2t':
|
||||
return None, ("描述含媒体输入声明(%s)但能力是 t2t——请人工核对能力分类"
|
||||
% '/'.join(kinds))
|
||||
expected = 'r2v' if (has_ref or len(kinds) >= 2) else (
|
||||
'i2v' if 'image' in input_kinds and out_word == '视频' else
|
||||
('i2i' if 'image' in input_kinds and out_word == '图像' else None))
|
||||
if expected and expected != capability:
|
||||
return expected, ("能力自动纠正 %s→%s:描述声明媒体输入(%s%s),"
|
||||
"纯文本能力与之矛盾(提取照示例误判,守卫按正文模态纠正)"
|
||||
% (capability, expected, '/'.join(kinds),
|
||||
',含参考声明' if has_ref else ''))
|
||||
return None, ("描述含媒体输入声明(%s)但能力是 %s——无法确定目标能力,"
|
||||
"请人工核对(可用 overrides.model_capability 纠正)"
|
||||
% ('/'.join(kinds), capability))
|
||||
return None, None
|
||||
|
||||
|
||||
async def _h_apply_llm_config(sor, params, ctx):
|
||||
"""按提取的规格写库:供应商(复用/新建)+ 适配模板 + 模型。
|
||||
|
||||
@ -649,6 +739,15 @@ async def _h_apply_llm_config(sor, params, ctx):
|
||||
cap = m.get("capability") or "t2t"
|
||||
sync_mode = "async" if str(m.get("sync_mode") or "").strip() == "async" else "sync"
|
||||
desc = (m.get("description", "") or "").strip()
|
||||
# 能力一致性守卫(2026-09-06 wan3.0 实测):描述模态声明 vs capability
|
||||
# 铁证矛盾时代码级纠正——提取 LLM 照示例误判,提示词铁律不保证遵守
|
||||
cap_fix, cap_warn = _capability_guard(cap, desc)
|
||||
if cap_fix:
|
||||
tpl_notes.append("%s: %s" % (vmid, cap_warn))
|
||||
cap = cap_fix
|
||||
m["capability"] = cap_fix
|
||||
elif cap_warn:
|
||||
tpl_notes.append("%s: %s" % (vmid, cap_warn))
|
||||
# 出处标注(2026-09-05 用户规则):模型注册表描述字段必须保存文档 URL
|
||||
if doc_url and ("[出处:" + doc_url + "]") not in desc:
|
||||
desc = (desc + " " if desc else "") + "[出处:%s]" % doc_url
|
||||
@ -1731,7 +1830,17 @@ async def _h_check_model_accounting(sor, params, ctx):
|
||||
"模型未映射产品→产品管理导入产线模型;"
|
||||
"定价无匹配档位→核对usages维度值与定价YAML是否一致")
|
||||
elif st == "created":
|
||||
item["verdict"] = "待记账(出账循环60秒一轮,稍后复查;持续不变查记账worker进程)"
|
||||
# m0011 设计:accounting_status 只对 status='ok' 行有意义——
|
||||
# failed 调用行/recharge 充值行挂 created 是被出账循环天然排除(不计费),
|
||||
# 不是「待记账」(2026-09-06 实测:助手把 21 条 failed 行误报成待记账)
|
||||
call_st = row.get("status", "")
|
||||
if call_st == "recharge":
|
||||
item["verdict"] = "充值对账流水(非计费用量,不参与出账)"
|
||||
elif call_st != "ok":
|
||||
item["verdict"] = "调用失败行(不计费,出账循环天然排除)"
|
||||
item["reason"] = (row.get("note") or "")[:200]
|
||||
else:
|
||||
item["verdict"] = "待记账(出账循环60秒一轮,稍后复查;持续不变查记账worker进程)"
|
||||
elif st == "accounted":
|
||||
# 独立复算:用 usages 因子过定价引擎,对比 charge
|
||||
item["verdict"] = "已记账"
|
||||
@ -1813,10 +1922,10 @@ PLATFORM_TOOLS = [
|
||||
),
|
||||
ToolDefinition(
|
||||
name="apply_llm_config",
|
||||
description="按提取的规格写入模型治理配置:供应商/端点/适配模板/模型。幂等。推荐 use_last_extract=true 取本会话锚定规格(禁止把规格复制进 spec——复制即编造);需改动加 overrides(仅 vendor_name/base_url/protocol/doc_url/doc_notes)。仅owner组织角色可用。",
|
||||
description="按提取的规格写入模型治理配置:供应商/端点/适配模板/模型。幂等。推荐 use_last_extract=true 取本会话锚定规格(禁止把规格复制进 spec——复制即编造);需改动加 overrides(vendor_name/base_url/protocol/doc_url/doc_notes/model_capability——用户纠正能力分类时传 model_capability 而非重提取)。仅owner组织角色可用。",
|
||||
parameters={
|
||||
"use_last_extract": "true=用本会话 extract_llm_api_spec 锚定的规格(推荐,会话级隔离)",
|
||||
"overrides": "覆盖项 JSON(白名单:vendor_name/base_url/protocol/doc_url/doc_notes,如用户要求改供应商名)",
|
||||
"overrides": "覆盖项 JSON(白名单:vendor_name/base_url/protocol/doc_url/doc_notes/model_capability。model_capability={模型ID:能力} 如 {\\\"wan3.0-video-prime\\\": \\\"r2v\\\"}——用户纠正能力分类时传它+use_last_extract 重新 apply,不要重提取;模板会按新能力重建)",
|
||||
"spec": "(仅无锚定时的兜底)extract 返回的 JSON 规格原文",
|
||||
},
|
||||
category="platform",
|
||||
@ -1904,6 +2013,11 @@ PLATFORM_PROMPT = """
|
||||
- **定价只允许来自文档原文**(doc_quote 强制),禁止编造/换算/推导
|
||||
- **出处必留**:模型注册表描述字段与定价方案描述字段都必须保存文档 URL
|
||||
- 能力分类不存在 → 先加分类(字典/种子/提示词/端点注释四处同步)再配模型,禁止塞近似分类
|
||||
- 能力分类铁律:按文档正文的输入/输出模态声明判定,禁止照调用示例判(示例常只演示
|
||||
最简形态——wan3.0 实测「四模态全能参考」被示例判成 t2v)。extract 返回
|
||||
__capability_alert__ 说明描述与能力矛盾,apply 会自动纠正;用户口头纠正能力时,
|
||||
传 overrides={"model_capability": {"模型ID": "能力"}} + use_last_extract 重新 apply,
|
||||
禁止重提取(同样的提示词只会同样误判)
|
||||
- 生成类模型媒体铁律:上行统一三数组契约 image_files/audio_files/video_files
|
||||
(字符串或数组均可)经 b64media2url 转公网 URL;下行产物用
|
||||
downloadfile2url(request, url) 落地;apply 返回的 media_audit 警告必须如实转告
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user