feat(web): 角色群像按定位配比生成(role_mix 缩减版)
- 契约: CharacterGenerateRequest 加 role_mix{定位:数量},model_validator 归一
count=各值之和(每项≥1、总和≤12),缺省退回单一 role+count
- @llm: build_character_gen_context/run_character_gen 加 role_mix 参并注入配比块
(确定性、无时间戳,守 assemble 纯函数);router 穿参
- character-gen.md 加"按配比严格分配 role"纪律 + regen prompt_hashes 金标准
- 前端: sanitizeRoleMix/roleMixTotal + buildCharacterGenerateRequest 第4参 +
useCharacterGen 穿参 + RoleMixEditor + CharacterGenerator 单一/配比双模
This commit is contained in:
@@ -24,7 +24,7 @@ worldbuilder 与 character-gen 都是**独立生成**——不在写章/审稿
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from collections.abc import Sequence
|
||||
from collections.abc import Mapping, Sequence
|
||||
|
||||
import structlog
|
||||
from pydantic import BaseModel
|
||||
@@ -215,6 +215,11 @@ def _render_cards_for_context(cards: Sequence[CharacterCard]) -> str:
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _render_role_mix(role_mix: Mapping[str, int]) -> str:
|
||||
"""把定位配比序列化为清单(保插入序,确定性,无时间戳)。"""
|
||||
return "\n".join(f"- {role}:{n} 个" for role, n in role_mix.items())
|
||||
|
||||
|
||||
def build_character_gen_context(
|
||||
*,
|
||||
brief: str,
|
||||
@@ -223,13 +228,21 @@ def build_character_gen_context(
|
||||
world_context: str,
|
||||
existing_chars: Sequence[CharacterCard],
|
||||
generated_so_far: Sequence[CharacterCard],
|
||||
role_mix: Mapping[str, int] | None = None,
|
||||
) -> str:
|
||||
"""拼装 character-gen 注入文本(含群像防雷同上下文,M5-a)。
|
||||
"""拼装 character-gen 注入文本(含群像防雷同上下文,M5-a;⑦ 群像按定位配比)。
|
||||
|
||||
注入「已有角色」+「本批已生成卡」,要求新卡与二者差异化(避免一群人一个模子)。
|
||||
`role_mix` 在场时改注入定位配比块(各定位产出对应数量、合计等于数量),覆盖单一 `role`。
|
||||
确定性拼接、无时间戳——同输入同输出,便于单测与缓存。
|
||||
"""
|
||||
role_line = f"角色定位:{role}" if role else "角色定位:未指定(由你按需求判定)"
|
||||
if role_mix:
|
||||
role_line = (
|
||||
"角色定位配比(严格按此分配,各定位产出对应数量,合计等于数量):\n"
|
||||
f"{_render_role_mix(role_mix)}"
|
||||
)
|
||||
else:
|
||||
role_line = f"角色定位:{role}" if role else "角色定位:未指定(由你按需求判定)"
|
||||
existing_block = (
|
||||
_render_cards_for_context(existing_chars) if existing_chars else "(暂无已有角色)"
|
||||
)
|
||||
@@ -262,10 +275,12 @@ async def run_character_gen(
|
||||
gateway: GatewayRun,
|
||||
user_id: uuid.UUID,
|
||||
project_id: uuid.UUID,
|
||||
role_mix: Mapping[str, int] | None = None,
|
||||
) -> CharacterGenResult:
|
||||
"""跑角色/群像生成:构防雷同上下文 → `gateway.run` → 返回 `CharacterGenResult`。
|
||||
|
||||
群像防雷同(M5-a):注入「已有角色 + 本批已生成卡」要求差异化(§4.5 / §6.5)。
|
||||
`role_mix`(⑦)在场时按定位配比分配(各定位对应数量、合计等于数量)。
|
||||
独立生成(非并行审):网关失败直接上抛(端点/T5.2 处理),不静默吞。
|
||||
**只产结构化角色卡、不写库**——入库前过 `precheck_generated_cards` 校验、再经
|
||||
入库端点持久化(持久化属 T5.2,不变量 #3)。
|
||||
@@ -277,6 +292,7 @@ async def run_character_gen(
|
||||
world_context=world_context,
|
||||
existing_chars=existing_chars,
|
||||
generated_so_far=generated_so_far,
|
||||
role_mix=role_mix,
|
||||
)
|
||||
req = _build_request(spec, context=context, user_id=user_id, project_id=project_id)
|
||||
resp = await gateway.run(req)
|
||||
|
||||
Reference in New Issue
Block a user