feat: 将成长档案调整为近期实修回顾

This commit is contained in:
2026-08-03 12:25:51 +08:00
parent d2e08ab36c
commit 7747334ea4
29 changed files with 566 additions and 344 deletions

View File

@@ -37,7 +37,15 @@ def test_agent_debug_can_simulate_user_growth_profile_context():
status=1,
)
)
db.add(UserGrowthProfile(user_id=1, profile_text="用户在表达障碍主题上反复出现身体紧绷。"))
db.add(
UserGrowthProfile(
user_id=1,
schema_version=2,
recent_review="最近谈到表达时不知道怎么回到当下。",
current_focus="表达时先看见当下",
profile_text="旧版画像不得注入",
)
)
db.commit()
result = asyncio.run(
@@ -53,10 +61,11 @@ def test_agent_debug_can_simulate_user_growth_profile_context():
)
rendered = "\n".join(item["content"] for item in result.messages)
assert "[长期成长档案]" in rendered
assert "表达障碍" in rendered
assert "[近期实修回顾]" in rendered
assert "表达时先看见当下" in rendered
assert "旧版画像不得注入" not in rendered
assert result.tool_trace[0]["tool"] == "load_debug_user_context"
assert result.tool_trace[0]["response"]["growthProfileUsed"] is True
assert result.tool_trace[0]["response"]["recentPracticeReviewUsed"] is True
assert "[当前产品权益]" in rendered
@@ -88,7 +97,13 @@ def test_agent_debug_loads_selected_topic_history_summary_and_permissions():
allow_share_draft=0,
status=1,
),
UserGrowthProfile(user_id=1, profile_text="用户在表达时容易身体紧绷。"),
UserGrowthProfile(
user_id=1,
schema_version=2,
recent_review="最近谈到面对领导时不敢表达。",
current_focus="面对领导时的当下表达",
profile_text="",
),
ChatMessage(
id=100,
session_id=20,
@@ -109,6 +124,7 @@ def test_agent_debug_loads_selected_topic_history_summary_and_permissions():
topic_session_id=30,
user_id=1,
summary="学员正在观察面对权威时的紧绷。",
main_events="面对领导时如何表达",
emotions="害怕",
body_feelings="心口紧",
status="success",
@@ -134,6 +150,9 @@ def test_agent_debug_loads_selected_topic_history_summary_and_permissions():
rendered = "\n".join(item["content"] for item in result.messages)
assert "[当前对话主题]" in rendered
assert "[所选主题摘要]" in rendered
assert "本次关注:面对领导时如何表达" in rendered
assert "情绪:害怕" not in rendered
assert "身体感受:心口紧" not in rendered
assert "我又不敢说话了" in rendered
assert "我想继续刚才的主题" in rendered
assert "老师求助卡:可由学员主动生成" in rendered

View File

@@ -72,7 +72,15 @@ def test_finish_topic_enqueues_summary_and_worker_updates_growth_profile_for_ena
assert completed.status == "success"
assert completed.summary
profile = db.query(UserGrowthProfile).filter_by(user_id=1).one()
assert "最近主题" in profile.profile_text
assert profile.schema_version == 2
assert "本次主要谈到" in (profile.recent_review or "")
assert profile.current_focus == "我做阴影人格会抗拒,身体发紧"
assert profile.profile_text == ""
assert profile.common_emotions is None
assert profile.body_patterns is None
assert profile.homework_done is None
assert profile.effective_homework is None
assert profile.recent_progress is None
assert db.query(UserGrowthProfile).filter_by(user_id=1).count() == 1
assert db.query(GrowthProfileRevision).filter_by(user_id=1).count() == 1
@@ -167,11 +175,21 @@ def test_stale_topic_settlement_is_recovered_after_restart():
assert "自动恢复" in (summary.error_message or "")
def test_prompt_can_include_growth_profile_context_without_replacing_knowledge_context():
def test_prompt_includes_only_v2_recent_review_without_replacing_knowledge_context():
with _db() as db:
user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
db.add(user)
db.add(UserGrowthProfile(user_id=1, profile_text="用户最近反复在表达障碍和身体紧绷之间观察。"))
db.add(
UserGrowthProfile(
user_id=1,
schema_version=2,
recent_review="最近谈到面对领导时不敢表达。",
current_focus="面对领导时如何回到当下",
profile_text="旧画像不应进入提示词",
common_emotions="紧张",
body_patterns="身体紧绷",
)
)
db.commit()
growth_context = GrowthProfileService.prompt_context(db, user)
@@ -183,24 +201,45 @@ def test_prompt_can_include_growth_profile_context_without_replacing_knowledge_c
)
content = "\n".join(item["content"] for item in messages)
assert "[长期成长档案]" in content
assert "表达障碍" in content
assert "[近期实修回顾]" in content
assert "面对领导时如何回到当下" in content
assert "旧画像不应进入提示词" not in content
assert "身体紧绷" not in content
assert "常见情绪" not in content
assert "[本轮可靠知识上下文]" in content
def test_legacy_growth_profile_is_not_injected_before_v2_rebuild():
with _db() as db:
user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
db.add_all(
[
user,
UserGrowthProfile(
user_id=1,
schema_version=1,
profile_text="旧版长期人格画像",
common_emotions="焦虑",
),
]
)
db.commit()
assert GrowthProfileService.prompt_context(db, user) is None
def test_summary_json_parser_ignores_model_reasoning_and_uses_final_json():
raw = """
<think>
先分析一下,并给出一个未完成草稿:
```json
{"profileText": "不应采用的草稿"}
{"reviewText": "不应采用的草稿"}
```
</think>
```json
{
"profileText": "只保留最终成长档案",
"commonEmotions": ["紧张"],
"recentProgress": ["开始观察身体感受"]
"reviewText": "只保留最终近期回顾",
"currentFocus": ["当下正在谈到的问题"]
}
```
"""
@@ -208,5 +247,5 @@ def test_summary_json_parser_ignores_model_reasoning_and_uses_final_json():
parsed = _parse_summary_json(raw)
assert parsed is not None
assert parsed["profileText"] == "只保留最终成长档案"
assert parsed["reviewText"] == "只保留最终近期回顾"
assert "think" not in str(parsed).lower()

View File

@@ -54,6 +54,8 @@ def test_generate_help_card_from_topic_summary_and_mark_copied():
assert "给老师的求助卡" in card.content
assert "不会自动发送给老师" in card.content
assert "阴影人格练习步骤是否正确" in card.content
assert "情绪 / 身体感受" not in card.content
assert "已经尝试过或被建议的功课" not in card.content
assert db.get(TopicSession, 1).help_card_generated == 1
assert db.query(TeacherHelpCard).count() == 1
@@ -61,4 +63,3 @@ def test_generate_help_card_from_topic_summary_and_mark_copied():
assert copied.copied == 1
assert copied.copied_at is not None

View File

@@ -14,6 +14,7 @@ from app.models.growth import PeriodicReport, TopicSummary, UserGrowthProfile
from app.models.user import User
from app.services.periodic_report_service import PeriodicReportService, periodic_report_dict
from app.services.periodic_report_worker import PeriodicReportWorker, scheduled_period
from app.services.model_service import ModelCompletion
from app.services.tracked_generation_service import TrackedGenerationService
@@ -51,7 +52,11 @@ def test_generate_periodic_report_from_topic_summaries():
report = PeriodicReportService.generate_for_user(db, user=user, report_type="weekly", period_start=now - timedelta(days=7), period_end=now)
assert report.status == "success"
assert report.schema_version == 2
assert report.content
assert "长期成长档案" not in report.content
assert "推荐功课:" not in report.content
assert "情绪:害怕" not in report.content
data = periodic_report_dict(report)
assert data["sourceSummaryIds"] == [1]
assert data["sourceTopicIds"] == [1]
@@ -71,6 +76,68 @@ def test_generate_empty_periodic_report_when_no_summaries():
assert periodic_report_dict(report)["sourceSummaryIds"] == []
def test_periodic_report_strips_model_reasoning_before_saving(monkeypatch):
with _db() as db:
now = _now()
user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
session = ChatSession(id=1, user_id=1, title="当下", message_count=1, last_message_at=now, is_deleted=0)
topic = TopicSession(id=1, user_id=1, chat_session_id=1, title="当下", core_question="我想先看当下", status="completed")
summary = TopicSummary(
id=1,
user_id=1,
topic_session_id=1,
summary="最近谈到想先看当下。",
generated_at=now - timedelta(days=1),
)
db.add_all([user, session, topic, summary])
db.commit()
monkeypatch.setattr(
TrackedGenerationService,
"generate",
staticmethod(
lambda *_args, **_kwargs: ModelCompletion(
answer="<think>内部分析与旧画像标签</think>\n## 本周期谈到的主题\n\n最近谈到想先看当下。",
model_id=1,
model_name="test-model",
input_token=1,
output_token=1,
)
),
)
report = PeriodicReportService.generate_for_user(
db,
user=user,
report_type="weekly",
period_start=now - timedelta(days=7),
period_end=now,
)
assert report.content.startswith("## 本周期谈到的主题")
assert "内部分析" not in report.content
assert "<think>" not in report.content
def test_legacy_periodic_reports_are_hidden_until_regenerated():
with _db() as db:
now = _now()
user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
legacy = PeriodicReport(
user_id=1,
schema_version=1,
report_type="weekly",
period_start=now - timedelta(days=7),
period_end=now,
title="旧版成长报告",
content="常见情绪、身体模式、有效功课和近期变化",
status="success",
)
db.add_all([user, legacy])
db.commit()
assert PeriodicReportService.list_user_reports(db, user_id=1) == []
def test_async_report_job_is_durable_and_idempotent():
with _db() as db:
now = _now()

View File

@@ -53,7 +53,10 @@ def test_generate_share_draft_from_topic_summary_and_mark_copied():
assert "实修分享稿草稿" in draft.content
assert "系统不会自动发送到任何群" in draft.content
assert "不代表已经彻底解决" in draft.content
assert "不代表结论" in draft.content
assert "情绪和身体反应" not in draft.content
assert "做了什么功课" not in draft.content
assert "当下的一点变化" not in draft.content
assert "我看见自己不敢表达" in draft.content
assert db.get(TopicSession, 1).share_draft_generated == 1
assert db.query(ShareDraft).count() == 1