初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,118 @@
|
||||
"""大周天内驱力生成测试(_phase_evaluation 子步骤 _generate_drive)。"""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.application.big_cycle import drain_pending_big_cycles, maybe_trigger_big_cycle
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.memory import Memory
|
||||
from app.repositories import drive as drive_repo
|
||||
from app.services.characters import get_active_character_model
|
||||
|
||||
_DRIVE_RESPONSE = (
|
||||
'[{"question":"用户为何频繁提到项目X","source":"memory_gap",'
|
||||
'"expected_value":0.7,"urgency":0.5,"risk_level":"L2",'
|
||||
'"related_entities":["项目X"],'
|
||||
'"subgoals":[{"title":"询问项目X目标","description":"主动询问用户",'
|
||||
'"expected_information_gain":0.6,"interruption_cost":0.3,"risk_level":"L4"}]}]'
|
||||
)
|
||||
|
||||
|
||||
async def _seed_memories(character_id: str, count: int) -> None:
|
||||
async with AsyncSessionLocal() as session:
|
||||
for i in range(count):
|
||||
session.add(
|
||||
Memory(
|
||||
character_id=character_id,
|
||||
type="user",
|
||||
title=f"记忆{i}",
|
||||
content=f"内容{i}",
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_drive_chat(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
async def fake_chat(*_args, **_kwargs):
|
||||
return _DRIVE_RESPONSE
|
||||
|
||||
monkeypatch.setattr("app.application.big_cycle._call_model", fake_chat)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_big_cycle_generates_curiosity_and_subgoal(
|
||||
mock_drive_chat, # noqa: ARG001 - pytest fixture 靠 monkeypatch 副作用生效
|
||||
) -> None:
|
||||
async with AsyncSessionLocal() as session:
|
||||
character = await get_active_character_model(session)
|
||||
await _seed_memories(character.id, 20)
|
||||
|
||||
cycle_id = await maybe_trigger_big_cycle(character.id, trigger="test")
|
||||
assert cycle_id is not None
|
||||
await drain_pending_big_cycles()
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
curiosities, c_total = await drive_repo.list_curiosities(session, character.id)
|
||||
subgoals, s_total = await drive_repo.list_subgoals(session, character.id)
|
||||
|
||||
assert c_total == 1
|
||||
assert curiosities[0].question == "用户为何频繁提到项目X"
|
||||
assert curiosities[0].source == "memory_gap"
|
||||
assert curiosities[0].cycle_id == cycle_id
|
||||
assert s_total == 1
|
||||
assert subgoals[0].status == "candidate"
|
||||
assert subgoals[0].curiosity_question_id == curiosities[0].id
|
||||
assert subgoals[0].title == "询问项目X目标"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_big_cycle_auto_approves_low_risk_subgoal(
|
||||
mock_drive_chat, # noqa: ARG001
|
||||
) -> None:
|
||||
"""低风险次生目标(L0-L2)应在大周天中自动通过并生成 Goal。"""
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.models.governance import Goal
|
||||
|
||||
# 使用低风险的子目标响应
|
||||
_low_risk_response = (
|
||||
'[{"question":"还有哪些未知领域","source":"world_uncertainty",'
|
||||
'"expected_value":0.6,"urgency":0.4,"risk_level":"L1",'
|
||||
'"subgoals":[{"title":"探索未知领域","description":"自动探索",'
|
||||
'"expected_information_gain":0.5,"interruption_cost":0.2,"risk_level":"L1"}]}]'
|
||||
)
|
||||
|
||||
async def fake_low_risk(*_args, **_kwargs):
|
||||
return _low_risk_response
|
||||
|
||||
import app.application.big_cycle as bc
|
||||
|
||||
original = bc._call_model
|
||||
bc._call_model = fake_low_risk
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
character = await get_active_character_model(session)
|
||||
await _seed_memories(character.id, 20)
|
||||
|
||||
cycle_id = await maybe_trigger_big_cycle(character.id, trigger="test")
|
||||
assert cycle_id is not None
|
||||
await drain_pending_big_cycles()
|
||||
|
||||
bc._call_model = original
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
subgoals, s_total = await drive_repo.list_subgoals(session, character.id)
|
||||
assert s_total == 1
|
||||
assert subgoals[0].status in ("approved", "auto_execute_pending"), (
|
||||
f"预期 approved 或 auto_execute_pending,实际={subgoals[0].status}"
|
||||
)
|
||||
|
||||
# 验证自动生成了 Goal
|
||||
async with AsyncSessionLocal() as session:
|
||||
result = await session.execute(
|
||||
select(Goal).where(Goal.character_id == character.id)
|
||||
)
|
||||
goals = result.scalars().all()
|
||||
matching = [g for g in goals if g.title == "探索未知领域"]
|
||||
assert len(matching) == 1
|
||||
assert matching[0].level == "task"
|
||||
Reference in new issue
Block a user