"""Prompt 模板单元测试""" import pytest # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture def simple_template(): """构造一个简单的 PromptTemplate""" from app.agent_framework.prompts.base_template import PromptSection, PromptTemplate sections = PromptSection( identity="你是一个AI助手", context="品牌:${brand_name}", instructions="请为 ${topic} 生成内容", constraints="字数不超过500字", output_format="输出 JSON 格式", examples="示例:{ 'title': '...' }", ) return PromptTemplate(sections=sections) # --------------------------------------------------------------------------- # PromptTemplate 基本测试 # --------------------------------------------------------------------------- class TestPromptTemplate: def test_render_returns_messages(self, simple_template): """render 输出 list[dict] 格式""" messages = simple_template.render() assert isinstance(messages, list) assert len(messages) >= 1 for msg in messages: assert "role" in msg assert "content" in msg assert msg["role"] in ("system", "user") def test_system_user_message_split(self, simple_template): """system 含 identity+context,user 含 instructions""" messages = simple_template.render(variables={"brand_name": "TestBrand", "topic": "AI"}) roles = [m["role"] for m in messages] assert "system" in roles assert "user" in roles system_msg = next(m for m in messages if m["role"] == "system") user_msg = next(m for m in messages if m["role"] == "user") # identity 和 context 在 system assert "你是一个AI助手" in system_msg["content"] # instructions 在 user assert "生成内容" in user_msg["content"] def test_variable_injection_simple(self, simple_template): """${var} 被替换""" messages = simple_template.render(variables={"brand_name": "MyBrand", "topic": "AI"}) all_content = " ".join(m["content"] for m in messages) assert "MyBrand" in all_content assert "AI" in all_content # 原始占位符不应存在 assert "${brand_name}" not in all_content assert "${topic}" not in all_content def test_variable_injection_nested(self): """${a.b.c} 嵌套路径解析""" from app.agent_framework.prompts.base_template import PromptSection, PromptTemplate sections = PromptSection( instructions="平台:${platform.name},目标:${goal.type}", ) template = PromptTemplate(sections=sections) messages = template.render(variables={ "platform": {"name": "微信公众号"}, "goal": {"type": "品牌曝光"}, }) user_msg = next(m for m in messages if m["role"] == "user") assert "微信公众号" in user_msg["content"] assert "品牌曝光" in user_msg["content"] def test_unresolved_variable_kept(self): """未注入的变量保持 ${var} 原样""" from app.agent_framework.prompts.base_template import PromptSection, PromptTemplate sections = PromptSection( instructions="主题是 ${undefined_var}", ) template = PromptTemplate(sections=sections) messages = template.render(variables={}) user_msg = next(m for m in messages if m["role"] == "user") assert "${undefined_var}" in user_msg["content"] def test_truncation_within_budget(self): """短文本不被截断""" from app.agent_framework.prompts.base_template import PromptSection, PromptTemplate short_context = "简短的上下文内容" sections = PromptSection( identity="身份", context=short_context, ) template = PromptTemplate(sections=sections) messages = template.render(context_budget=3000) system_msg = next(m for m in messages if m["role"] == "system") assert short_context in system_msg["content"] assert "中间内容已省略" not in system_msg["content"] def test_truncation_exceeds_budget(self): """超长文本被智能截断""" from app.agent_framework.prompts.base_template import PromptSection, PromptTemplate # 生成超过 budget 的文本(budget=10 token) long_context = "这是一段非常长的上下文内容,包含大量文字。" * 200 sections = PromptSection( identity="我是AI助手", context=long_context, ) template = PromptTemplate(sections=sections) # 设置很小的 budget 强制触发截断 messages = template.render(context_budget=50) system_msg = next(m for m in messages if m["role"] == "system") # 截断标记应该出现 assert "中间内容已省略" in system_msg["content"] def test_render_with_no_variables(self, simple_template): """不传变量也能正常 render(未解析变量保持原样)""" messages = simple_template.render() assert isinstance(messages, list) assert len(messages) > 0 def test_render_empty_sections(self): """空 sections 不返回空角色 message""" from app.agent_framework.prompts.base_template import PromptSection, PromptTemplate sections = PromptSection(instructions="做些事情") template = PromptTemplate(sections=sections) messages = template.render() # system 内容为空时不应出现 system message roles = [m["role"] for m in messages] assert "user" in roles # 只有 user message(identity/context/constraints 都为空) system_msgs = [m for m in messages if m["role"] == "system"] for sm in system_msgs: assert sm["content"].strip() != "" # --------------------------------------------------------------------------- # 5个 Template 全部能正常 render # --------------------------------------------------------------------------- class TestAllTemplatesRender: def test_topic_selector_template_renders(self): """TOPIC_SELECTOR_TEMPLATE 能正常 render""" from app.agent_framework.prompts import TOPIC_SELECTOR_TEMPLATE messages = TOPIC_SELECTOR_TEMPLATE.render(variables={ "target_keyword": "AI营销", "brand_name": "示例品牌", "target_platform": "微信公众号", }) assert isinstance(messages, list) assert len(messages) > 0 for m in messages: assert "role" in m assert "content" in m def test_content_generator_template_renders(self): """CONTENT_GENERATOR_TEMPLATE 能正常 render""" from app.agent_framework.prompts import CONTENT_GENERATOR_TEMPLATE messages = CONTENT_GENERATOR_TEMPLATE.render(variables={ "topic": "AI发展趋势", "platform": "知乎", }) assert isinstance(messages, list) assert len(messages) > 0 def test_deai_template_renders(self): """DEAI_TEMPLATE 能正常 render""" from app.agent_framework.prompts import DEAI_TEMPLATE messages = DEAI_TEMPLATE.render(variables={ "content": "测试AI生成内容", }) assert isinstance(messages, list) assert len(messages) > 0 def test_geo_optimizer_template_renders(self): """GEO_OPTIMIZER_TEMPLATE 能正常 render""" from app.agent_framework.prompts import GEO_OPTIMIZER_TEMPLATE messages = GEO_OPTIMIZER_TEMPLATE.render(variables={ "content": "待优化的内容", "platform": "小红书", }) assert isinstance(messages, list) assert len(messages) > 0 def test_rule_checker_template_renders(self): """RULE_CHECKER_TEMPLATE 能正常 render""" from app.agent_framework.prompts import RULE_CHECKER_TEMPLATE messages = RULE_CHECKER_TEMPLATE.render(variables={ "content": "待检查的内容", "platform": "微信公众号", }) assert isinstance(messages, list) assert len(messages) > 0 # --------------------------------------------------------------------------- # PromptTemplate._inject 直接测试 # --------------------------------------------------------------------------- class TestPromptTemplateInject: @pytest.fixture def template(self): from app.agent_framework.prompts.base_template import PromptSection, PromptTemplate return PromptTemplate(sections=PromptSection()) def test_inject_simple_var(self, template): """简单变量注入""" result = template._inject("Hello ${name}", {"name": "World"}) assert result == "Hello World" def test_inject_multiple_vars(self, template): """多变量注入""" result = template._inject("${a} and ${b}", {"a": "foo", "b": "bar"}) assert result == "foo and bar" def test_inject_nested_path(self, template): """嵌套路径注入""" result = template._inject("${x.y}", {"x": {"y": "deep_value"}}) assert result == "deep_value" def test_inject_missing_var_kept(self, template): """缺失变量保持原样""" result = template._inject("${missing}", {}) assert result == "${missing}" def test_inject_empty_text(self, template): """空文本原样返回""" result = template._inject("", {"x": "val"}) assert result == "" def test_estimate_tokens_chinese(self, template): """中文字符 token 估算:每字约 1 token""" text = "你好世界" # 4 个中文字符 tokens = template._estimate_tokens(text) assert tokens == 4 def test_estimate_tokens_empty(self, template): """空文本 token 为 0""" assert template._estimate_tokens("") == 0