| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- from unittest.mock import MagicMock, patch
- from hello_agents.tools import ToolResponse, ToolStatus
- from app.agent.real_god import RealGodAgent, _StepSearchToolAdapter
- def test_real_god_builds_react_agent_with_registered_stepsearch_tool():
- registry = MagicMock()
- react_agent = MagicMock()
- with patch.object(RealGodAgent, "_supports_persona_search", return_value=True), patch(
- "app.agent.real_god.ToolRegistry", return_value=registry
- ), patch(
- "app.agent.real_god.ReActAgent", return_value=react_agent
- ) as react_class, patch("app.agent.real_god.create_helloagents_llm"), patch(
- "app.agent.real_god.create_helloagents_config"
- ):
- result = RealGodAgent(max_steps=4)._build_agent("system")
- assert result is react_agent
- tool = registry.register_tool.call_args.args[0]
- assert isinstance(tool, _StepSearchToolAdapter)
- react_class.assert_called_once()
- assert react_class.call_args.kwargs["tool_registry"] is registry
- assert react_class.call_args.kwargs["max_steps"] == 4
- def test_real_god_rejects_non_stepfun_provider():
- registry = MagicMock()
- with patch.object(RealGodAgent, "_supports_persona_search", return_value=False), patch(
- "app.agent.real_god.ToolRegistry", return_value=registry
- ):
- try:
- RealGodAgent()._build_agent("system")
- except RuntimeError as exc:
- assert "StepFun" in str(exc)
- else:
- raise AssertionError("non-StepFun provider should be rejected")
- registry.register_tool.assert_not_called()
- def test_stepsearch_adapter_exposes_structured_helloagents_tool():
- backend = MagicMock()
- backend.search.return_value = "search result"
- tool = _StepSearchToolAdapter(backend)
- response = tool.run({"query": "Ada Lovelace"})
- assert response.status is ToolStatus.SUCCESS
- assert response.data["provider"] == "stepsearch"
- backend.search.assert_called_once_with("Ada Lovelace")
- def test_real_god_run_emits_valid_result_events():
- framework_agent = MagicMock()
- framework_agent.run.return_value = '{"name":"Ada Lovelace","title":"Mathematician","bio":"Bio","theories":["1","2","3","4","5","6","7"],"stance":"Analytical","system_prompt":"I am Ada."}'
- agent = RealGodAgent()
- with patch.object(agent, "_build_agent", return_value=framework_agent), patch.object(
- agent, "_matches_user_request", return_value=True
- ):
- events = list(agent.run("create one", n=1))
- assert [event["type"] for event in events] == ["count", "thought_start", "progress", "result"]
- assert events[-1]["content"][0]["name"] == "Ada Lovelace"
- def test_real_god_repairs_truncated_provider_json_once():
- framework_agent = MagicMock()
- framework_agent.run.side_effect = ["{\"name\": \"Ada", "{\"name\": \"Ada Lovelace\", \"title\": \"Math\", \"bio\": \"Bio\", \"theories\": [\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\"], \"stance\": \"Analytical\", \"system_prompt\": \"I am Ada.\"}"]
- agent = RealGodAgent()
- with patch.object(agent, "_build_agent", return_value=framework_agent), patch.object(
- agent, "_matches_user_request", return_value=True
- ):
- events = list(agent.run("create one", n=1))
- assert [event["type"] for event in events] == ["count", "thought_start", "progress", "result"]
- assert framework_agent.run.call_count == 2
- assert events[-1]["content"][0]["name"] == "Ada Lovelace"
- def test_real_god_retries_with_fresh_agent_when_persona_is_off_topic():
- wrong_agent = MagicMock()
- wrong_agent.run.return_value = '{"name":"Marie Curie","title":"Scientist","bio":"Bio","theories":["1","2","3","4","5","6","7"],"stance":"Science","system_prompt":"I am Marie."}'
- corrected_agent = MagicMock()
- corrected_agent.run.return_value = '{"name":"Edsger Dijkstra","title":"Computer Scientist","bio":"Bio","theories":["1","2","3","4","5","6","7"],"stance":"Structured programming","system_prompt":"I am Dijkstra."}'
- agent = RealGodAgent()
- with patch.object(agent, "_build_agent", side_effect=[wrong_agent, corrected_agent]), patch.object(
- agent, "_matches_user_request", side_effect=[False, True]
- ):
- events = list(agent.run("Create Edsger Dijkstra", n=1))
- assert events[-1]["content"][0]["name"] == "Edsger Dijkstra"
- assert "Marie Curie" in corrected_agent.run.call_args.args[0]
- def test_multi_persona_generation_pins_each_agent_to_requested_position():
- first_agent = MagicMock()
- first_agent.run.return_value = '{"name":"Teacher A","title":"Innovator","bio":"Bio","theories":["1","2","3","4","5","6","7"],"stance":"Support","system_prompt":"I support adoption."}'
- second_agent = MagicMock()
- second_agent.run.return_value = '{"name":"Teacher B","title":"Ethicist","bio":"Bio","theories":["1","2","3","4","5","6","7"],"stance":"Cautious","system_prompt":"I focus on risks."}'
- agent = RealGodAgent()
- with patch.object(agent, "_build_agent", side_effect=[first_agent, second_agent]), patch.object(
- agent, "_matches_user_request", return_value=True
- ) as alignment:
- events = list(agent.run("第一位支持课堂 AI;第二位强调学术诚信风险", n=2))
- results = [event["content"][0]["name"] for event in events if event["type"] == "result"]
- assert results == ["Teacher A", "Teacher B"]
- assert "第 1 个描述" in first_agent.run.call_args.args[0]
- assert "第 2 个描述" in second_agent.run.call_args.args[0]
- assert "当前只校验第 1 位" in alignment.call_args_list[0].args[0]
- assert "当前只校验第 2 位" in alignment.call_args_list[1].args[0]
- assert "Teacher A" in alignment.call_args_list[1].args[0]
- def test_explicit_named_person_is_checked_before_model_alignment():
- with patch("app.agent.real_god.run_simple_agent") as completion:
- assert RealGodAgent._matches_user_request(
- "创建哈利波特",
- {"name": "阿斯特拉·韦斯莱-布莱克"},
- ) is False
- completion.assert_not_called()
- def test_explicit_named_person_allows_name_punctuation_variants():
- with patch("app.agent.real_god.run_simple_agent", return_value="YES"):
- assert RealGodAgent._matches_user_request(
- "创建阿不思邓布利多",
- {"name": "阿不思·邓布利多"},
- ) is True
- def test_explicit_named_person_allows_translated_name_when_bio_confirms_identity():
- with patch("app.agent.real_god.run_simple_agent", return_value="YES"):
- assert RealGodAgent._matches_user_request(
- "创建哈利波特",
- {
- "name": "Harry Potter",
- "bio": "哈利·波特是霍格沃茨格兰芬多学院的学生。",
- },
- ) is True
- def test_topic_request_does_not_force_persona_name():
- assert RealGodAgent._explicit_requested_name("创建一位算法专家") is None
- assert RealGodAgent._explicit_requested_name("生成一个科学家角色") is None
- def test_long_named_person_prompt_forces_single_person_without_model_counting():
- prompt = "请联网核实并创建真实人物王佑镁,作为教育伦理讨论角色。必须生成王佑镁本人。"
- agent = RealGodAgent()
- with patch("app.agent.real_god.run_simple_agent") as completion:
- assert agent._explicit_requested_name(prompt) == "王佑镁"
- assert agent._get_persona_count(prompt) == 1
- completion.assert_not_called()
- def test_stepsearch_adapter_converts_provider_errors():
- backend = MagicMock()
- backend.search.side_effect = RuntimeError("offline")
- response = _StepSearchToolAdapter(backend).run({"query": "Ada Lovelace"})
- assert isinstance(response, ToolResponse)
- assert response.status is ToolStatus.ERROR
- assert response.error_info["code"] == "SEARCH_FAILED"
|