fix(platform): 能力语义判断改LLM裁决(2026-09-08用户定夺:词匹配不当裁决者)
qwen-image-plus实测根因:词匹配守卫把描述"图像生成"的输出词当输入证据, 反复把t2i纠正成i2i,人工override也被改回,死循环。 - 新增_llm_capability_judge:utility模型按能力字典语义裁决(输入→输出模态), 失败不裁决不纠正(宁可保守不让词匹配越权) - extract/apply两处:词匹配只当廉价触发器报可疑,裁决交LLM - 人工override权威:__human_cap_overrides__登记的模型apply守卫一律跳过 - LLM裁决改能力后重新_spec_save锚定(否则apply取回旧值裁决白做)
This commit is contained in:
parent
48735b98ba
commit
7a041cbb18
@ -162,6 +162,12 @@ def _spec_overlay(base, overrides):
|
||||
if vmid and newcap and newcap != m.get('capability'):
|
||||
fixed.append('%s: %s→%s' % (vmid, m.get('capability'), newcap))
|
||||
m['capability'] = newcap
|
||||
# 人工纠正登记(2026-09-08):apply 阶段的能力守卫必须跳过
|
||||
# 这些模型——人工指令是权威,守卫再纠正=把用户的纠正改回去
|
||||
# (qwen-image-plus 实测死循环的机制根源)。
|
||||
hc = dict(out.get('__human_cap_overrides__') or {})
|
||||
hc[vmid] = newcap
|
||||
out['__human_cap_overrides__'] = hc
|
||||
out['models'] = models
|
||||
if fixed:
|
||||
out['doc_notes'] = ((out.get('doc_notes') or '') +
|
||||
@ -449,17 +455,35 @@ async def _h_extract_llm_api_spec(sor, params, ctx):
|
||||
"需改动只传覆盖项(overrides 白名单:vendor_name/base_url/"
|
||||
"protocol/doc_url/doc_notes/model_capability)。"
|
||||
"禁止把本规格复制进 spec 参数——复制即编造。")
|
||||
# 能力一致性告警(2026-09-06 wan3.0 教训):提取完当场校验描述模态声明 vs
|
||||
# capability,矛盾信息直接回给助手(用户纠正时传 overrides.model_capability 重 apply)
|
||||
# 能力一致性告警(2026-09-06 wan3.0 教训;2026-09-08 升级:词匹配只当触发器,
|
||||
# 裁决交 LLM——qwen-image-plus 实测词匹配把输出词当输入证据、t2i 被反复改成 i2i)
|
||||
cap_alerts = []
|
||||
_cap_changed = False
|
||||
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 _warn or _fix:
|
||||
# 词匹配报可疑 → LLM 语义裁决(裁决失败则只告警不纠正)
|
||||
_jfix, _jnote = await _llm_capability_judge(
|
||||
_m.get("capability", ""), _m.get("description", ""),
|
||||
_m.get("vendor_model_id", ""))
|
||||
if _jfix:
|
||||
_m["capability"] = _jfix
|
||||
_cap_changed = True
|
||||
cap_alerts.append("%s: %s(已按 LLM 裁决更新提取规格)"
|
||||
% (_m.get("vendor_model_id", "?"), _jnote))
|
||||
elif _jnote:
|
||||
cap_alerts.append("%s: %s" % (_m.get("vendor_model_id", "?"), _jnote))
|
||||
elif _warn:
|
||||
cap_alerts.append("%s: %s(词匹配可疑,LLM 裁决维持原判)"
|
||||
% (_m.get("vendor_model_id", "?"), _warn))
|
||||
# 裁决改了能力 → 必须重新锚定:_spec_save 在裁决前已存旧值,不重存则
|
||||
# apply(use_last_extract=true) 取回的还是旧能力,LLM 裁决白做(实测根因)。
|
||||
if _cap_changed:
|
||||
_spec_save(ctx, spec)
|
||||
if cap_alerts:
|
||||
out["__capability_alert__"] = ("能力分类与文档描述矛盾(按示例误判的典型信号),"
|
||||
"apply 时会自动纠正,也可用 overrides.model_capability "
|
||||
"人工指定:" + ";".join(cap_alerts))
|
||||
out["__capability_alert__"] = ("能力分类存疑项(LLM 语义裁决结果):"
|
||||
+ ";".join(cap_alerts)
|
||||
+ "。如需人工指定用 overrides.model_capability。")
|
||||
return json.dumps(out, ensure_ascii=False)
|
||||
|
||||
|
||||
@ -481,6 +505,58 @@ _DESC_MEDIA_WORDS = {
|
||||
_DESC_REF_WORDS = ('参考生', '全能参考', '参考图', '参考视频', '参考音频', '四模态', '多模态参考')
|
||||
|
||||
|
||||
async def _llm_capability_judge(capability, description, vendor_model_id=''):
|
||||
"""LLM 语义裁决能力分类(2026-09-08 用户定夺:语义判断必须 LLM 参与,
|
||||
词匹配只能当廉价触发器,不能当裁决者)。
|
||||
|
||||
背景:缺陷2(qwen-image-plus)实测——关键词守卫把描述里「图像生成」的
|
||||
「图像」(输出词)当输入证据,反复把 t2i 纠正成 i2i,人工 override 也被
|
||||
改回去,死循环。词匹配分不清输入/输出语义,LLM 能。
|
||||
|
||||
分工:_capability_guard(词匹配)只报「可疑」;本函数用 utility 廉价模型
|
||||
按能力字典语义裁决。LLM 失败时返回 (None, 原因)——不裁决就不纠正,
|
||||
保持提取 LLM 的原判定(宁可保守也不让词匹配越权)。
|
||||
"""
|
||||
if not (description or '').strip():
|
||||
return None, ''
|
||||
prompt = (
|
||||
'你是模型能力分类裁决器。模型 %s 的文档描述:\n%s\n\n'
|
||||
'当前判定的能力分类:%s\n\n'
|
||||
'能力分类字典(按输入→输出模态判定,描述里的输出媒体词不算输入证据):\n'
|
||||
't2t=文本→文本;i2t=图像→文本;m2t=多媒体→文本;t2i=文本→图像;'
|
||||
'i2i=图像→图像;t2v=文本→视频;i2v=图像→视频;r2v=参考媒体(图/视频/音频组合)→视频;'
|
||||
'tts=文本→语音;asr=语音→文本;embedding=文本→向量;rerank=重排序\n\n'
|
||||
'请判断当前分类是否与描述的输入/输出模态一致。只返回 JSON:'
|
||||
'{"consistent": true/false, "capability": "正确分类(不一致时给出)", '
|
||||
'"reason": "一句话依据"}' % (vendor_model_id or '(未命名)', description[:600],
|
||||
capability or '(空)'))
|
||||
try:
|
||||
from pipeline_service.llm_bridge import llm_call_msgs
|
||||
raw = await llm_call_msgs(
|
||||
[{"role": "user", "content": prompt}],
|
||||
temperature=0, org_id="0", purpose="utility", timeout=60)
|
||||
except Exception as e:
|
||||
return None, 'LLM 裁决调用失败(保持原判定不纠正): %s' % repr(e)[:120]
|
||||
txt = (raw or '').strip()
|
||||
m = re.search(r'\{[\s\S]*\}', txt)
|
||||
if not m:
|
||||
return None, 'LLM 裁决输出非 JSON(保持原判定不纠正): %s' % txt[:120]
|
||||
try:
|
||||
obj = json.loads(m.group(0))
|
||||
except Exception:
|
||||
return None, 'LLM 裁决 JSON 解析失败(保持原判定不纠正)'
|
||||
if not isinstance(obj, dict):
|
||||
return None, ''
|
||||
if obj.get('consistent') is True:
|
||||
return None, ''
|
||||
newcap = str(obj.get('capability') or '').strip().lower()
|
||||
reason = str(obj.get('reason') or '')[:150]
|
||||
if newcap and newcap != (capability or ''):
|
||||
return newcap, 'LLM 语义裁决 %s→%s:%s' % (capability, newcap, reason)
|
||||
return None, ('LLM 裁决认为能力分类存疑但未给出替代(%s),请人工核对'
|
||||
% reason) if reason else None
|
||||
|
||||
|
||||
def _capability_guard(capability, description):
|
||||
"""描述模态声明 vs capability 一致性守卫(2026-09-06 wan3.0-video-prime 实测:
|
||||
描述自带"支持四模态全能参考"、capability 却是 t2v——提取 LLM 照调用示例判能力,
|
||||
@ -590,18 +666,35 @@ async def _h_apply_llm_config(sor, params, ctx):
|
||||
if not models:
|
||||
return "spec.models 为空——文档里没有可配置的模型?"
|
||||
|
||||
# 能力一致性守卫前置(2026-09-06 wan3.0 二次教训):必须在 caps 计算/模板
|
||||
# 生成之前跑——上一版放在模型循环里,纠正 t2v→r2v 时模板已按 t2v 生成,
|
||||
# profile_ids 无 r2v 键,模型更新的 IF 空值保护把旧 t2v 模板原样保留
|
||||
# (实测:template_notes 有纠正记录、库里 cap 仍是 t2v)。
|
||||
# 能力一致性守卫前置(2026-09-06 wan3.0 二次教训;2026-09-08 用户定夺升级):
|
||||
# 必须在 caps 计算/模板生成之前跑——纠正能力后模板按新能力生成,否则
|
||||
# profile_ids 无该能力键、模型更新的 IF 空值保护把旧模板原样保留。
|
||||
# ⚠️ 语义判断必须 LLM 参与:词匹配只当廉价触发器(报可疑),裁决交
|
||||
# _llm_capability_judge;**人工 override 的模型一律跳过守卫**——人工指令是
|
||||
# 权威,守卫把用户纠正改回去正是 qwen-image-plus 死循环的机制根源。
|
||||
cap_notes = []
|
||||
human_caps = spec.get('__human_cap_overrides__') or {}
|
||||
for m in models:
|
||||
vmid = (m.get('vendor_model_id') or m.get('name') or '').strip()
|
||||
if vmid and vmid in human_caps:
|
||||
cap_notes.append("%s: 能力按人工指定 %s(守卫已跳过,不再自动纠正)"
|
||||
% (vmid, human_caps[vmid]))
|
||||
continue
|
||||
_fix, _warn = _capability_guard(
|
||||
m.get("capability") or "", m.get("description") or "")
|
||||
if _fix:
|
||||
m["capability"] = _fix
|
||||
if _warn:
|
||||
cap_notes.append("%s: %s" % (m.get("vendor_model_id") or "?", _warn))
|
||||
if not (_fix or _warn):
|
||||
continue
|
||||
_jfix, _jnote = await _llm_capability_judge(
|
||||
m.get("capability") or "", m.get("description") or "", vmid)
|
||||
if _jfix:
|
||||
m["capability"] = _jfix
|
||||
cap_notes.append("%s: %s" % (vmid or "?", _jnote))
|
||||
elif _jnote:
|
||||
cap_notes.append("%s: %s" % (vmid or "?", _jnote))
|
||||
else:
|
||||
cap_notes.append("%s: 词匹配报可疑但 LLM 裁决维持 %s(%s)"
|
||||
% (vmid or "?", m.get("capability") or "?",
|
||||
(_warn or _fix or '')[:80]))
|
||||
|
||||
# 能力分类校验(2026-09-05 用户定夺的规则):模型能力必须是字典已登记的
|
||||
# 分类;不存在则拒绝落库——先加能力分类(appcodes_kv llm_capability,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user