feat(platform): apply_llm_config自动产品映射(2026-09-08用户需求:添加模型时如有产品模块自动映射)

根治记账failed"模型未映射到产品"不靠人工事后补:
- _sync_model_products:apply落库后调env.import_categories_and_products
  (resource_module=pipeline_llm)增量导入所有active模型为产品(幂等:已映射按
  resource_ref_id+org_id跳过,顺带自愈其他漏映射模型)
- 父类别复用已有pipeline_llm根类别"大模型消费"(防类别树分裂)
- 产品模块未装→跳过不阻断(模型注册已成功,映射是下游增值步骤);失败如实记
  product_mapping.note
- check_model_accounting hint与工作流prompt同步:未映射→重跑apply触发自动映射
This commit is contained in:
yumoqing 2026-09-08 13:58:50 +08:00
parent a9cac99c52
commit e5902a4fd4

View File

@ -735,6 +735,61 @@ async def _write_model_skill(sor, vmid, m, spec, profile_ids):
logger.info("模型配套技能已写入: %s", path) logger.info("模型配套技能已写入: %s", path)
# pipeline_llm 产品类别根名(首次自动建;已存在则复用,防类别树分裂)
_LLM_PRODUCT_ROOT_NAME = "大模型消费"
_LLM_ROOT_CAT_ID = "7S4za5hOEJqBGXb2f-NmK" # 现有根类别 id(实测),新建时兜底用它
async def _sync_model_products(sor, org_id="0"):
"""模型上线后自动产品映射(2026-09-08 用户需求:添加模型时如有产品模块自动映射)。
根治记账 failed「模型未映射到产品」:apply 落库模型后,调产品模块的
import_categories_and_products(resource_module='pipeline_llm') 增量导入
所有 active 模型为产品(幂等:已映射的按 resource_ref_id+org_id 跳过,
顺带自愈其他漏映射模型)。
产品模块未装(env 无 import_categories_and_products)→ 返回跳过说明,
不阻断 apply(模型注册本身已成功,产品映射是下游增值步骤)。
返回 (ok, note)。
"""
from ahserver.serverenv import ServerEnv
env = ServerEnv()
import_fn = getattr(env, "import_categories_and_products", None)
if not callable(import_fn):
return True, "产品模块(product_management)未加载,跳过自动产品映射(模型已注册成功,可后续在产品管理手动导入产线模型)"
# 定父类别:复用已有 pipeline_llm 根类别(防类别树分裂);没有则新建根
parent_cat = _LLM_ROOT_CAT_ID
try:
recs = await sor.sqlExe(
"SELECT id FROM product_category WHERE resource_module='pipeline_llm' "
"AND parent_id IS NULL AND org_id=${o}$ LIMIT 1", {"o": org_id})
await sor.sqlExe("COMMIT", {})
if recs:
parent_cat = getattr(recs[0], "id", "") or _LLM_ROOT_CAT_ID
else:
# 查名字兜底(parent_id 可能存 '0' 而非 NULL)
recs2 = await sor.sqlExe(
"SELECT id FROM product_category WHERE name=${n}$ AND org_id=${o}$ LIMIT 1",
{"n": _LLM_PRODUCT_ROOT_NAME, "o": org_id})
await sor.sqlExe("COMMIT", {})
if recs2:
parent_cat = getattr(recs2[0], "id", "") or _LLM_ROOT_CAT_ID
except Exception as e:
logger.warning("_sync_model_products 父类别解析失败(用默认根): %s", e)
try:
result = await import_fn(
resource_module="pipeline_llm", org_id=org_id,
parent_category_id=parent_cat, user_id="system")
except Exception as e:
return False, "自动产品映射调用失败(模型已注册,请手动在产品管理导入产线模型):%s" % repr(e)[:150]
if not result or not result.get("success"):
err = (result or {}).get("error", "导入无返回") if isinstance(result, dict) else "导入无返回"
return False, "自动产品映射未成功(模型已注册,请手动导入):%s" % str(err)[:150]
return True, "自动产品映射完成:%s" % str((result or {}).get("message", ""))[:150]
async def _h_apply_llm_config(sor, params, ctx): async def _h_apply_llm_config(sor, params, ctx):
"""按提取的规格写库:供应商(复用/新建)+ 适配模板 + 模型。 """按提取的规格写库:供应商(复用/新建)+ 适配模板 + 模型。
@ -1123,6 +1178,10 @@ async def _h_apply_llm_config(sor, params, ctx):
except Exception as e: except Exception as e:
skill_notes.append("%s: 配套技能生成失败(%s)——完备性判断将按能力语义降级" skill_notes.append("%s: 配套技能生成失败(%s)——完备性判断将按能力语义降级"
% (vmid, str(e)[:80])) % (vmid, str(e)[:80]))
# 6. 自动产品映射(2026-09-08 用户需求:添加模型时如有产品模块自动做产品映射)。
# 根治记账 failed「模型未映射到产品」——不靠人工事后补。产品模块未装则
# 跳过不阻断(模型注册已成功);映射幂等(已映射跳过+自愈其他漏映射模型)。
prod_ok, prod_note = await _sync_model_products(sor, org_id="0")
return json.dumps({ return json.dumps({
"vendor": vendor_action, "profiles": profile_ids, "vendor": vendor_action, "profiles": profile_ids,
"models_created": created, "models_updated": updated, "models_skipped": skipped, "models_created": created, "models_updated": updated, "models_skipped": skipped,
@ -1131,6 +1190,7 @@ async def _h_apply_llm_config(sor, params, ctx):
"template_notes": tpl_notes, "template_notes": tpl_notes,
"account_notes": account_notes, "account_notes": account_notes,
"skill_notes": skill_notes, "skill_notes": skill_notes,
"product_mapping": {"ok": prod_ok, "note": prod_note},
"runtime_note": rt_note, "runtime_note": rt_note,
"media_audit": audit_note, "media_audit": audit_note,
}, ensure_ascii=False) }, ensure_ascii=False)
@ -2385,7 +2445,9 @@ async def _h_check_model_accounting(sor, params, ctx):
item["verdict"] = "记账失败" item["verdict"] = "记账失败"
item["reason"] = (row.get("note") or "")[:200] item["reason"] = (row.get("note") or "")[:200]
item["hint"] = ("常见处置:模型未挂ppid→apply_model_pricing;" item["hint"] = ("常见处置:模型未挂ppid→apply_model_pricing;"
"模型未映射产品→产品管理导入产线模型;" "模型未映射产品→apply_llm_config 已自动映射(2026-09-08),"
"仍报未映射说明自动映射失败(看 apply 返回 product_mapping.note,"
"多为产品模块未加载),重跑一次 apply 或告知用户;"
"定价无匹配档位→核对usages维度值与定价YAML是否一致") "定价无匹配档位→核对usages维度值与定价YAML是否一致")
elif st == "created": elif st == "created":
# 2026-09-06 用户定夺:只有 status='SUCCEEDED' 的行才写 accounting_status, # 2026-09-06 用户定夺:只有 status='SUCCEEDED' 的行才写 accounting_status,
@ -2711,7 +2773,9 @@ PLATFORM_PROMPT = """
- failed → 按 reason/hint 处置;需要核实库内数据(产品映射/定价挂接/流水明细) - failed → 按 reason/hint 处置;需要核实库内数据(产品映射/定价挂接/流水明细)
一律用 `db_query` 只读查询,**禁止用 run_command 手搓连库脚本**(沙箱 /tmp 不跨 一律用 `db_query` 只读查询,**禁止用 run_command 手搓连库脚本**(沙箱 /tmp 不跨
命令持久、平台目录只读、输出双截断——手搓必陷多轮试错)。 命令持久、平台目录只读、输出双截断——手搓必陷多轮试错)。
未映射产品→告知用户在产品管理导入产线模型(人工步骤,如实转告即止,不要自行深挖) 产品映射由 apply_llm_config 自动完成(2026-09-08 起)——若仍报「未映射到产品」,
回看 apply 返回的 product_mapping.note(多为产品模块未加载),重跑一次
apply_llm_config(幂等)触发自动映射,仍失败才如实告知用户。
9. 汇报:配置/定价/测试/记账四段结果 + 出处 URL + 遗留事项(如模板需人工核对项)。 9. 汇报:配置/定价/测试/记账四段结果 + 出处 URL + 遗留事项(如模板需人工核对项)。
用户发现问题后按其说明修改,再重新走对应环节(全链幂等,重跑安全)。 用户发现问题后按其说明修改,再重新走对应环节(全链幂等,重跑安全)。