diff --git a/src/agentkit/core/config_driven.py b/src/agentkit/core/config_driven.py index 0d461aa..0206e5e 100644 --- a/src/agentkit/core/config_driven.py +++ b/src/agentkit/core/config_driven.py @@ -1358,7 +1358,14 @@ class ConfigDrivenAgent(BaseAgent, EvolutionMixin): return gateway async def _handle_llm_generate(self, task: TaskMessage) -> dict: - """LLM 生成模式:渲染 Prompt → 调用 LLM → 解析输出""" + """LLM 生成模式:渲染 Prompt → 调用 LLM → 解析输出 + + ponytail: 优先使用 ``_llm_gateway``(v2 路径,``AgentPool`` 注入的), + 仅在 gateway 缺失时回退到 ``_llm_client``(legacy 字段)。原实现只检查 + ``_llm_client``,导致 ``AgentPool`` 创建的 expert/board agent 在 + ``_llm_client is None`` 但 ``_llm_gateway`` 已注入时错误降级为 + ``llm_generate_no_client``。 + """ if not self._prompt_template: raise ConfigValidationError( agent_name=self.name, @@ -1371,7 +1378,19 @@ class ConfigDrivenAgent(BaseAgent, EvolutionMixin): variables["task_type"] = task.task_type messages = self._prompt_template.render(variables=variables) - # 调用 LLM + # v2 路径:_llm_gateway 优先(AgentPool 仅注入 gateway,不设 _llm_client) + if self._llm_gateway is not None: + llm_params = self._config.llm.copy() if self._config.llm else {} + model = llm_params.get("model", "default") + response = await self._llm_gateway.chat( + messages=messages, + model=model, + agent_name=self.name, + task_type=task.task_type, + ) + return self._parse_llm_response(response.content) + + # 兼容旧路径:_llm_client(legacy) if self._llm_client is None: # 无 LLM 客户端时返回渲染后的 Prompt(降级模式) return { diff --git a/tests/unit/test_config_driven.py b/tests/unit/test_config_driven.py index a0ed6ad..409b263 100644 --- a/tests/unit/test_config_driven.py +++ b/tests/unit/test_config_driven.py @@ -144,6 +144,52 @@ class TestConfigDrivenAgent: assert result["mode"] == "llm_generate_no_client" assert len(result["messages"]) == 2 # system + user + async def test_llm_generate_with_gateway_only(self): + """仅注入 LLMGateway(_llm_client=None)时应正常调用 LLM。 + + 复现:AgentPool.create_agent() 只注入 llm_gateway,不设 _llm_client。 + 原实现错误降级为 llm_generate_no_client,导致私董会专家返回占位文本。 + """ + + class _StubResponse: + def __init__(self, content: str) -> None: + self.content = content + + class _StubGateway: + def __init__(self) -> None: + self.calls: list[dict] = [] + + async def chat(self, *, messages, model, agent_name, task_type, **kwargs): + self.calls.append( + { + "messages": messages, + "model": model, + "agent_name": agent_name, + "task_type": task_type, + } + ) + return _StubResponse(json.dumps({"title": "FromGateway", "content": "OK"})) + + gateway = _StubGateway() + config = AgentConfig.from_dict(_sample_llm_config()) + agent = ConfigDrivenAgent(config=config, llm_gateway=gateway) + + # 验证前置条件:_llm_client 必须为 None,模拟 AgentPool 注入路径 + assert agent._llm_client is None + assert agent._llm_gateway is gateway + + task = _make_task() + result = await agent.handle_task(task) + + # 不应再走 no_client 降级 + assert "mode" not in result or result.get("mode") != "llm_generate_no_client" + assert result["title"] == "FromGateway" + assert result["content"] == "OK" + # 验证 gateway.chat 被实际调用 + assert len(gateway.calls) == 1 + assert gateway.calls[0]["agent_name"] == config.name + assert gateway.calls[0]["model"] == "gpt-4" + async def test_llm_generate_with_client(self): """有 LLM 客户端时调用 LLM 并解析结果"""