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'):
|
if vmid and newcap and newcap != m.get('capability'):
|
||||||
fixed.append('%s: %s→%s' % (vmid, m.get('capability'), newcap))
|
fixed.append('%s: %s→%s' % (vmid, m.get('capability'), newcap))
|
||||||
m['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
|
out['models'] = models
|
||||||
if fixed:
|
if fixed:
|
||||||
out['doc_notes'] = ((out.get('doc_notes') or '') +
|
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/"
|
"需改动只传覆盖项(overrides 白名单:vendor_name/base_url/"
|
||||||
"protocol/doc_url/doc_notes/model_capability)。"
|
"protocol/doc_url/doc_notes/model_capability)。"
|
||||||
"禁止把本规格复制进 spec 参数——复制即编造。")
|
"禁止把本规格复制进 spec 参数——复制即编造。")
|
||||||
# 能力一致性告警(2026-09-06 wan3.0 教训):提取完当场校验描述模态声明 vs
|
# 能力一致性告警(2026-09-06 wan3.0 教训;2026-09-08 升级:词匹配只当触发器,
|
||||||
# capability,矛盾信息直接回给助手(用户纠正时传 overrides.model_capability 重 apply)
|
# 裁决交 LLM——qwen-image-plus 实测词匹配把输出词当输入证据、t2i 被反复改成 i2i)
|
||||||
cap_alerts = []
|
cap_alerts = []
|
||||||
|
_cap_changed = False
|
||||||
for _m in (spec.get("models") or []):
|
for _m in (spec.get("models") or []):
|
||||||
_fix, _warn = _capability_guard(_m.get("capability", ""), _m.get("description", ""))
|
_fix, _warn = _capability_guard(_m.get("capability", ""), _m.get("description", ""))
|
||||||
if _warn:
|
if _warn or _fix:
|
||||||
cap_alerts.append("%s: %s" % (_m.get("vendor_model_id", "?"), _warn))
|
# 词匹配报可疑 → 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:
|
if cap_alerts:
|
||||||
out["__capability_alert__"] = ("能力分类与文档描述矛盾(按示例误判的典型信号),"
|
out["__capability_alert__"] = ("能力分类存疑项(LLM 语义裁决结果):"
|
||||||
"apply 时会自动纠正,也可用 overrides.model_capability "
|
+ ";".join(cap_alerts)
|
||||||
"人工指定:" + ";".join(cap_alerts))
|
+ "。如需人工指定用 overrides.model_capability。")
|
||||||
return json.dumps(out, ensure_ascii=False)
|
return json.dumps(out, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
@ -481,6 +505,58 @@ _DESC_MEDIA_WORDS = {
|
|||||||
_DESC_REF_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):
|
def _capability_guard(capability, description):
|
||||||
"""描述模态声明 vs capability 一致性守卫(2026-09-06 wan3.0-video-prime 实测:
|
"""描述模态声明 vs capability 一致性守卫(2026-09-06 wan3.0-video-prime 实测:
|
||||||
描述自带"支持四模态全能参考"、capability 却是 t2v——提取 LLM 照调用示例判能力,
|
描述自带"支持四模态全能参考"、capability 却是 t2v——提取 LLM 照调用示例判能力,
|
||||||
@ -590,18 +666,35 @@ async def _h_apply_llm_config(sor, params, ctx):
|
|||||||
if not models:
|
if not models:
|
||||||
return "spec.models 为空——文档里没有可配置的模型?"
|
return "spec.models 为空——文档里没有可配置的模型?"
|
||||||
|
|
||||||
# 能力一致性守卫前置(2026-09-06 wan3.0 二次教训):必须在 caps 计算/模板
|
# 能力一致性守卫前置(2026-09-06 wan3.0 二次教训;2026-09-08 用户定夺升级):
|
||||||
# 生成之前跑——上一版放在模型循环里,纠正 t2v→r2v 时模板已按 t2v 生成,
|
# 必须在 caps 计算/模板生成之前跑——纠正能力后模板按新能力生成,否则
|
||||||
# profile_ids 无 r2v 键,模型更新的 IF 空值保护把旧 t2v 模板原样保留
|
# profile_ids 无该能力键、模型更新的 IF 空值保护把旧模板原样保留。
|
||||||
# (实测:template_notes 有纠正记录、库里 cap 仍是 t2v)。
|
# ⚠️ 语义判断必须 LLM 参与:词匹配只当廉价触发器(报可疑),裁决交
|
||||||
|
# _llm_capability_judge;**人工 override 的模型一律跳过守卫**——人工指令是
|
||||||
|
# 权威,守卫把用户纠正改回去正是 qwen-image-plus 死循环的机制根源。
|
||||||
cap_notes = []
|
cap_notes = []
|
||||||
|
human_caps = spec.get('__human_cap_overrides__') or {}
|
||||||
for m in models:
|
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(
|
_fix, _warn = _capability_guard(
|
||||||
m.get("capability") or "", m.get("description") or "")
|
m.get("capability") or "", m.get("description") or "")
|
||||||
if _fix:
|
if not (_fix or _warn):
|
||||||
m["capability"] = _fix
|
continue
|
||||||
if _warn:
|
_jfix, _jnote = await _llm_capability_judge(
|
||||||
cap_notes.append("%s: %s" % (m.get("vendor_model_id") or "?", _warn))
|
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 用户定夺的规则):模型能力必须是字典已登记的
|
# 能力分类校验(2026-09-05 用户定夺的规则):模型能力必须是字典已登记的
|
||||||
# 分类;不存在则拒绝落库——先加能力分类(appcodes_kv llm_capability,
|
# 分类;不存在则拒绝落库——先加能力分类(appcodes_kv llm_capability,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user