fix(core): prefer _llm_gateway over _llm_client in _handle_llm_generate
AgentPool.create_agent() injects only llm_gateway (v2 path) into ConfigDrivenAgent and never sets the legacy _llm_client field. The previous _handle_llm_generate implementation gated the entire LLM call on `self._llm_client is None`, so every expert/board agent created via the pool silently fell through to the `llm_generate_no_client` placeholder — which is why the private board moderator/experts reported "LLM 不可用" even when the gateway and providers were healthy. Mirror the same precedence used by `_handle_direct`: prefer `_llm_gateway` (with agent_name/task_type for usage tracking), fall back to `_llm_client` for backward compatibility, and only return the no-client placeholder when both are unavailable. Add a unit test that constructs an agent with `llm_gateway` only (_llm_client is None) and asserts the gateway.chat path is taken.
This commit is contained in:
parent
8063f4efe0
commit
d0fe6611c7
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 并解析结果"""
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue