M2b:导入改造产出可审阅灵胚(启镜物化世谱)与组装管线(符文展开/火候进入 IR),Kernel 打通采样参数链路
This commit is contained in:
1 parent
e6a3b7121f
commit
5f36e3785c
86 files changed
+424
-14455
No files matched your search
@@ -194,6 +194,7 @@ async def activate_embryo(embryo_id: str, owner_scope: str) -> Character:
|
||||
|
||||
session.add(Custodianship(character_id=character.id, custodian_scope=owner_scope))
|
||||
session.add(RuntimeSeat(character_id=character.id, epoch=1))
|
||||
book_entry_count = await _materialize_import_book(session, baseline, character, owner_scope)
|
||||
|
||||
record = build_identity_record(
|
||||
character_id=character.id,
|
||||
@@ -205,6 +206,7 @@ async def activate_embryo(embryo_id: str, owner_scope: str) -> Character:
|
||||
"activated_at": character.activated_at.isoformat(),
|
||||
},
|
||||
runtime_state={"status": "active", "seat_epoch": 1},
|
||||
data_state={"imported_book_entries": book_entry_count},
|
||||
)
|
||||
session.add(record)
|
||||
await session.flush() # 取魂籍 ID
|
||||
@@ -307,3 +309,44 @@ async def _owned_character(
|
||||
if character is None or character.owner_scope != owner_scope:
|
||||
raise AppError("not_found", "人物不存在。")
|
||||
return character
|
||||
|
||||
|
||||
async def _materialize_import_book(
|
||||
session: AsyncSession, baseline: dict, character: Character, owner_scope: str
|
||||
) -> int:
|
||||
"""启镜时把导入卡的 character_book 物化为新人物的世谱书与条目(待审语义随世谱状态治理)。"""
|
||||
from app.models.lorebook import Lorebook, LorebookEntry
|
||||
from app.modules.imports.mapping import character_book_to_entries
|
||||
|
||||
book = (baseline.get("import_card") or {}).get("character_book")
|
||||
entries = character_book_to_entries(book)
|
||||
if not entries:
|
||||
return 0
|
||||
lorebook = Lorebook(
|
||||
character_id=character.id,
|
||||
name=f"{character.name} 的世谱(导入)",
|
||||
description="角色卡 character_book 启镜物化",
|
||||
scope="character",
|
||||
owner_scope=owner_scope,
|
||||
)
|
||||
session.add(lorebook)
|
||||
await session.flush()
|
||||
for spec in entries:
|
||||
session.add(
|
||||
LorebookEntry(
|
||||
lorebook_id=lorebook.id,
|
||||
keys_json=json.dumps(spec["keys"], ensure_ascii=False),
|
||||
content=spec["content"],
|
||||
secondary_keys_json=json.dumps(spec["secondary_keys"], ensure_ascii=False),
|
||||
constant=spec["constant"],
|
||||
selective=spec["selective"],
|
||||
enabled=spec["enabled"],
|
||||
case_sensitive=spec["case_sensitive"],
|
||||
use_regex=spec["use_regex"],
|
||||
position=spec["position"],
|
||||
insertion_order=spec["insertion_order"],
|
||||
priority=spec["priority"],
|
||||
comment=spec["comment"][:500],
|
||||
)
|
||||
)
|
||||
return len(entries)
|
||||
@@ -383,25 +383,80 @@ def _citation_row(node_id: str, data: dict) -> Citation:
|
||||
)
|
||||
|
||||
|
||||
async def build_generation_prompt(question: TreeNode, instructions: str | None) -> str:
|
||||
"""按祖先链组装投影 prompt(M2 形态:祖先路径 + 问题 + 临时指引;记忆/引文随 M3)。"""
|
||||
async def build_generation_prompt(
|
||||
question: TreeNode,
|
||||
instructions: str | None,
|
||||
*,
|
||||
char_name: str | None = None,
|
||||
user_name: str | None = None,
|
||||
) -> str:
|
||||
"""按祖先链组装投影 prompt 并做符文展开({{char}}/{{user}}/…)。
|
||||
|
||||
M2 形态:祖先路径 + 问题 + 临时指引;获准记忆与显式引文随 M3 息壤接入。
|
||||
"""
|
||||
from app.modules.macros.builtins import build_default_registry
|
||||
from app.modules.macros.engine import render
|
||||
from app.modules.macros.types import MacroContext
|
||||
|
||||
ctx = MacroContext(char_name=char_name, user_name=user_name)
|
||||
registry = build_default_registry()
|
||||
|
||||
def expand(text: str) -> str:
|
||||
return render(text, ctx, registry=registry)
|
||||
|
||||
chain = await ancestor_chain(question.id)
|
||||
lines: list[str] = []
|
||||
for n in chain:
|
||||
if n.id == question.id:
|
||||
continue
|
||||
speaker = "用户" if n.node_type == "question" else "人物"
|
||||
lines.append(f"{speaker}:{n.content}")
|
||||
lines.append(f"{speaker}:{expand(n.content)}")
|
||||
transcript = "\n".join(lines)
|
||||
parts = []
|
||||
if transcript:
|
||||
parts.append(f"【共同经历(祖先路径)】\n{transcript}")
|
||||
parts.append(f"【当前问题】\n{question.content}")
|
||||
parts.append(f"【当前问题】\n{expand(question.content)}")
|
||||
if instructions:
|
||||
parts.append(f"【本次指引】\n{instructions}")
|
||||
parts.append(f"【本次指引】\n{expand(instructions)}")
|
||||
return "\n\n".join(parts)
|
||||
|
||||
|
||||
async def _names_for_generation(tree: MirrorTree) -> tuple[str | None, str | None]:
|
||||
"""人物名与执镜者名(符文上下文)。"""
|
||||
from app.models.user import User
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
character = await session.get(Character, tree.character_id)
|
||||
char_name = character.name if character else None
|
||||
user_name = None
|
||||
if character is not None:
|
||||
user = await session.get(User, character.owner_scope)
|
||||
user_name = user.username if user else None
|
||||
return char_name, user_name
|
||||
|
||||
|
||||
async def _generation_overrides(owner_scope: str) -> dict:
|
||||
"""火候(GenerationPreferences):本镜域默认采样参数 → IR 覆写。"""
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.models.governance import GenerationPreferences
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
prefs = await session.get(GenerationPreferences, owner_scope)
|
||||
if prefs is None:
|
||||
# 兼容旧默认 owner_scope 行(历史数据)
|
||||
prefs = (
|
||||
await session.execute(
|
||||
select(GenerationPreferences).where(
|
||||
GenerationPreferences.owner_scope == owner_scope
|
||||
)
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if prefs is None:
|
||||
return {}
|
||||
return {"temperature": prefs.temperature, "max_tokens": prefs.max_output_tokens}
|
||||
|
||||
|
||||
async def spawn_answer(
|
||||
*,
|
||||
question_id: str,
|
||||
@@ -436,13 +491,18 @@ async def spawn_answer(
|
||||
source="mirror_tree",
|
||||
)
|
||||
|
||||
prompt = await build_generation_prompt(question, instructions)
|
||||
char_name, user_name = await _names_for_generation(tree)
|
||||
prompt = await build_generation_prompt(
|
||||
question, instructions, char_name=char_name, user_name=user_name
|
||||
)
|
||||
overrides = await _generation_overrides(owner_scope)
|
||||
run = await kernel.start_foreground_run(
|
||||
character_id=tree.character_id,
|
||||
prompt=prompt,
|
||||
system_contract=None,
|
||||
origin="tree_answer",
|
||||
on_run_final=_make_finalize(tree.id, tree.character_id, node_id),
|
||||
**overrides,
|
||||
)
|
||||
async with AsyncSessionLocal() as session:
|
||||
node = await session.get(TreeNode, node_id)
|
||||
|
||||
Reference in new issue
Block a user