M2b:导入改造产出可审阅灵胚(启镜物化世谱)与组装管线(符文展开/火候进入 IR),Kernel 打通采样参数链路

This commit is contained in:
chenxin committed 2026-08-21 23:43:29 +08:00
1 parent e6a3b7121f
commit 5f36e3785c
86 files changed
+424 -14455

No files matched your search

@@ -1,59 +0,0 @@
"""道体注入与完整性校验测试。"""
from __future__ import annotations
import pytest
from app.application.chat.send_message import _contract_system_messages
from app.modules.runtime.contract import RuntimeContract
from app.modules.runtime.integrity import validate_daoti_integrity
def _contract(**kw: object) -> RuntimeContract:
base: dict[str, object] = {
"character_id": "c",
"session_id": "s",
"run_id": "r",
"model_hint": "m",
"trace_id": "t",
}
base.update(kw)
return RuntimeContract(**base)
def test_system_prompt_injected() -> None:
msgs = _contract_system_messages(_contract(system_prompt="特殊系统指令"))
assert "特殊系统指令" in msgs
def test_mission_injected() -> None:
msgs = _contract_system_messages(_contract(mission="守护用户"))
assert any("守护用户" in m for m in msgs)
def test_safety_guidelines_precede_safety_constraints() -> None:
c = _contract(safety_guidelines="道体边界", safety_constraints=["司命约束"])
msgs = _contract_system_messages(c)
assert msgs.index("行为边界:\n道体边界") < msgs.index("安全约束:\n- 司命约束")
def test_integrity_pass_when_consistent() -> None:
c = _contract(mission="M", safety_guidelines="S")
validate_daoti_integrity(c, mission="M", safety_guidelines="S") # 不抛即通过
def test_integrity_raises_on_mission_tamper() -> None:
c = _contract(mission="M")
with pytest.raises(ValueError):
validate_daoti_integrity(c, mission="被篡改")
def test_integrity_raises_on_safety_tamper() -> None:
c = _contract(safety_guidelines="S")
with pytest.raises(ValueError):
validate_daoti_integrity(c, safety_guidelines="被篡改")
def test_integrity_skips_none_fields() -> None:
# 道体字段未设(None)时跳过校验
validate_daoti_integrity(_contract(), mission=None, safety_guidelines=None)
@@ -1,48 +0,0 @@
"""灵核情绪状态 system prompt 注入测试(设计 §11.5)。
验证 ``_contract_system_messages`` 把 ``emotion_state`` 注入到正确位置
(personality_summary 后、memory_context 前),且 emotion_state=None 时不注入。
"""
from __future__ import annotations
from app.application.chat.send_message import _contract_system_messages
from app.modules.runtime.contract import RuntimeContract
def _contract(**overrides) -> RuntimeContract:
base = {
"character_id": "c1",
"session_id": "s1",
"run_id": "r1",
"model_hint": "m",
"trace_id": "t",
}
base.update(overrides)
return RuntimeContract(**base)
def test_emotion_state_injected_between_personality_and_memory() -> None:
contract = _contract(
personality_summary="人格X",
emotion_state="[当前情绪] 感动(强度 70%)",
memory_context="记忆Y",
)
messages = _contract_system_messages(contract)
personality_idx = next(i for i, m in enumerate(messages) if "人格X" in m)
emotion_idx = next(i for i, m in enumerate(messages) if "感动" in m)
memory_idx = next(i for i, m in enumerate(messages) if "记忆Y" in m)
assert personality_idx < emotion_idx < memory_idx
def test_emotion_state_omitted_when_none() -> None:
contract = _contract(personality_summary="人格X", memory_context="记忆Y")
messages = _contract_system_messages(contract)
assert all("当前情绪" not in m for m in messages)
def test_emotion_state_included_without_memory() -> None:
"""emotion_state 独立于 memory_context 存在。"""
contract = _contract(emotion_state="[当前情绪] 关切(强度 50%)")
messages = _contract_system_messages(contract)
assert any("关切" in m for m in messages)
@@ -1,41 +0,0 @@
"""世谱注入位置测试:before/after 相对 personality_summary。"""
from __future__ import annotations
from app.application.chat.send_message import _contract_system_messages
from app.modules.runtime.contract import RuntimeContract
def _contract(**kw: object) -> RuntimeContract:
base: dict[str, object] = {
"character_id": "c",
"session_id": "s",
"run_id": "r",
"model_hint": "m",
"trace_id": "t",
}
base.update(kw)
return RuntimeContract(**base)
def test_lorebook_before_precedes_personality() -> None:
c = _contract(personality_summary="人格X", lorebook_before="世界设定前")
msgs = _contract_system_messages(c)
assert msgs.index("世界设定前") < msgs.index("当前人格:\n人格X")
def test_lorebook_after_follows_personality() -> None:
c = _contract(personality_summary="人格X", lorebook_after="世界设定后")
msgs = _contract_system_messages(c)
assert msgs.index("当前人格:\n人格X") < msgs.index("世界设定后")
def test_no_lorebook_when_none() -> None:
msgs = _contract_system_messages(_contract())
assert not any("世界设定" in m for m in msgs)
def test_lorebook_without_personality_still_injects() -> None:
c = _contract(lorebook_before="前", lorebook_after="后")
msgs = _contract_system_messages(c)
assert "前" in msgs and "后" in msgs
@@ -1,8 +0,0 @@
"""测试 send_message 中的目标自动注入(集成验证)。"""
def test_auto_persist_session_goal_is_callable():
"""验证 auto_persist_session_goal 函数可导入且可调用。"""
from app.services.goal_persist import auto_persist_session_goal
assert callable(auto_persist_session_goal)
@@ -1,179 +0,0 @@
"""对话 temporal_context 注入测试(辰正 §8.1)。"""
from __future__ import annotations
from collections.abc import AsyncGenerator
from typing import Any
import pytest
from httpx import ASGITransport, AsyncClient
from app.application.agent.runtime import AgentTurnResult
from app.application.chat.send_message import _contract_system_messages
from app.main import app
from app.modules.runtime.contract import RuntimeContract
def _make_contract(temporal: str | None) -> RuntimeContract:
return RuntimeContract(
character_id="char_x",
session_id="sess_x",
run_id="run_x",
model_hint="default",
system_instructions=["base"],
personality_summary="人格X",
memory_context="记忆Y",
temporal_context=temporal,
trace_id="trace_x",
)
def test_contract_system_messages_includes_temporal_context():
messages = _contract_system_messages(_make_contract("现在是子时·深夜(01:42)。"))
assert any("子时·深夜" in m for m in messages)
def test_contract_system_messages_no_temporal_context_when_none():
messages = _contract_system_messages(_make_contract(None))
assert all("子时" not in m for m in messages)
def test_contract_system_messages_temporal_after_memory_before_safety():
"""注入位置:记忆之后、安全约束之前(设计 §8.1)。"""
contract = RuntimeContract(
character_id="c",
session_id="s",
run_id="r",
model_hint="m",
system_instructions=["base"],
memory_context="MEMORY_MARKER",
temporal_context="TEMPORAL_MARKER",
safety_constraints=["SAFETY_MARKER"],
trace_id="t",
)
messages = _contract_system_messages(contract)
joined = "\n".join(messages)
assert joined.index("MEMORY_MARKER") < joined.index("TEMPORAL_MARKER")
assert joined.index("TEMPORAL_MARKER") < joined.index("SAFETY_MARKER")
# ---------------------------------------------------------------------------
# §12 降级路径守护(设计 §12:辰正永不阻塞对话)
# 1. event_store=None → 跳过辰正调用,temporal_context 保持 None,对话照常完成
# 2. compute_live_snapshot raise → except 捕获,temporal_context 保持 None,对话不阻塞
# ---------------------------------------------------------------------------
async def _create_session(client: AsyncClient) -> str:
resp = await client.post("/api/v1/chat/sessions", json={"title": "辰正降级"})
assert resp.status_code == 200
return str(resp.json()["session_id"])
@pytest.fixture
async def temporal_client(
auth_headers: dict[str, str], monkeypatch: pytest.MonkeyPatch
) -> AsyncGenerator[AsyncClient, None]:
"""HTTP client:mock run_agent_turn,并通过 messages 捕获最终 system 消息用于断言。
复用 test_send_message_agent.py 的 mock 边界(只 mock run_agent_turn,跑真实 send_message
全链路),让降级路径在生产代码路径中被真实驱动。
"""
captured: dict[str, Any] = {}
async def capture_agent_turn(*, messages: Any, **_kwargs: object) -> AgentTurnResult:
captured["messages"] = messages
return AgentTurnResult(content="好的。", reasoning="")
monkeypatch.setattr("app.application.chat.send_message.run_agent_turn", capture_agent_turn)
transport = ASGITransport(app=app)
async with AsyncClient(
transport=transport, base_url="http://test", headers=auth_headers
) as http_client:
http_client._temporal_captured = captured # type: ignore[attr-defined]
yield http_client
def _system_text(messages: list[Any]) -> str:
return "\n".join(str(m.content) for m in messages if getattr(m, "role", "") == "system")
@pytest.mark.asyncio
async def test_temporal_context_skipped_when_event_store_is_none(
temporal_client: AsyncClient, monkeypatch: pytest.MonkeyPatch
) -> None:
"""§12 降径 1:event_store=None 时跳过辰正调用,对话照常完成且不注入 temporal_context。"""
from app.application.chat.send_message import drain_pending_streams
# 降径 1:明确将 event_store 置 None(route 经 getattr 默认也是 None,此处显式断言降径)
# 测试 app.state 默认无 event_store 属性,monkeypatch.setattr 对对象属性可在不存在时
# 创建并在 teardown 时删除(恢复"无属性"原状)。
monkeypatch.setattr(app.state, "event_store", None, raising=False)
# spy:若被调用即标记(不应发生)
called: dict[str, bool] = {"hit": False}
import app.services.time_sense as time_sense_mod
real_compute = time_sense_mod.compute_live_snapshot
async def spy_compute(*args: Any, **kwargs: Any) -> Any:
called["hit"] = True
return await real_compute(*args, **kwargs)
monkeypatch.setattr(time_sense_mod, "compute_live_snapshot", spy_compute)
session_id = await _create_session(temporal_client)
sent = await temporal_client.post(
f"/api/v1/chat/sessions/{session_id}/messages",
json={"content": "你好", "modality": "text"},
)
assert sent.status_code == 200
await drain_pending_streams()
# 真实行为断言:辰正未被调用
assert called["hit"] is False
# 对话完成:run_agent_turn 被正常调用且 system 消息中无辰正上下文标记
captured = temporal_client._temporal_captured # type: ignore[attr-defined]
sys_text = _system_text(captured["messages"])
assert "今日命中" not in sys_text # render_temporal_context 的稳定首行尾块标记
@pytest.mark.asyncio
async def test_temporal_context_swallowed_when_compute_raises(
temporal_client: AsyncClient, monkeypatch: pytest.MonkeyPatch
) -> None:
"""§12 降径 2:compute_live_snapshot raise 时 except 捕获,对话不阻塞且 temporal_context=None。"""
async def boom(*_args: Any, **_kwargs: Any) -> Any:
raise RuntimeError("辰正服务故障(模拟)")
monkeypatch.setattr("app.services.time_sense.compute_live_snapshot", boom)
from app.application.chat.send_message import drain_pending_streams
session_id = await _create_session(temporal_client)
sent = await temporal_client.post(
f"/api/v1/chat/sessions/{session_id}/messages",
json={"content": "你好", "modality": "text"},
)
# 真实行为断言:异常被 except 吞掉,HTTP 仍 200,对话照常完成
assert sent.status_code == 200
run_id = str(sent.json()["run_id"])
await drain_pending_streams()
# 对话成功落库:run 进入终态 completed(而非 failed)
from sqlalchemy import select
from app.db.session import AsyncSessionLocal
from app.models.runtime import Run
async with AsyncSessionLocal() as db:
run = (await db.execute(select(Run).where(Run.id == run_id))).scalar_one()
assert run.status == "completed"
# temporal_context 未注入:system 消息中无辰正上下文标记
captured = temporal_client._temporal_captured # type: ignore[attr-defined]
sys_text = _system_text(captured["messages"])
assert "子时" not in sys_text and "时态" not in sys_text