diff --git a/src/agentkit/core/react.py b/src/agentkit/core/react.py index 8eba539..db9942e 100644 --- a/src/agentkit/core/react.py +++ b/src/agentkit/core/react.py @@ -171,6 +171,11 @@ class ReActEngine: ) -> ReActResult: tools = tools or [] tool_schemas = self._build_tool_schemas(tools) if tools else None + if tool_schemas: + tool_names = [s["function"]["name"] for s in tool_schemas] + logger.info(f"ReActEngine executing with {len(tool_schemas)} tools: {tool_names}") + else: + logger.info("ReActEngine executing with NO tools") # Telemetry: record agent request agent_request_counter().add(1, {"agent.name": agent_name, "agent.type": task_type or "react"}) @@ -478,6 +483,11 @@ class ReActEngine: """ tools = tools or [] tool_schemas = self._build_tool_schemas(tools) if tools else None + if tool_schemas: + tool_names = [s["function"]["name"] for s in tool_schemas] + logger.info(f"ReActEngine executing with {len(tool_schemas)} tools: {tool_names}") + else: + logger.info("ReActEngine executing with NO tools") # 启动轨迹记录 if trace_recorder is not None: diff --git a/src/agentkit/server/routes/portal.py b/src/agentkit/server/routes/portal.py index 7b5f160..a8d2ee3 100644 --- a/src/agentkit/server/routes/portal.py +++ b/src/agentkit/server/routes/portal.py @@ -1,7 +1,3 @@ -"""Portal API routes - unified chat interface with intent routing""" - -from __future__ import annotations - import asyncio import hmac import json @@ -516,51 +512,81 @@ async def portal_websocket(websocket: WebSocket): if not message_text: continue - # Save user message - _conversation_store.add_message(conv.id, "user", message_text) - - # Resolve skill via IntentRouter + # Unified routing via CostAwareRouter (handles Layer 0/1/2) pool = websocket.app.state.agent_pool skill_registry = websocket.app.state.skill_registry + llm_gateway = websocket.app.state.llm_gateway intent_router: IntentRouter = websocket.app.state.intent_router + cost_aware_router = websocket.app.state.cost_aware_router all_skills = skill_registry.list_skills() if not all_skills: await websocket.send_json( - { - "type": "error", - "data": {"message": "No skills available"}, - } + {"type": "error", "data": {"message": "No skills available"}} ) continue - try: - routing_result = await intent_router.route( - {"query": message_text, "sources": sources}, all_skills - ) - await websocket.send_json( - { - "type": "routing", - "skill": routing_result.matched_skill, - "method": routing_result.method, - "confidence": routing_result.confidence, - } - ) + # Get default tools for CostAwareRouter routing (only if default skill exists) + default_tools = [] + default_system_prompt = None + default_agent = pool.get_agent("default") + if default_agent is not None: + default_tools = default_agent.get_tools() + default_system_prompt = default_agent.get_system_prompt() + else: + # Fallback to first available skill's tools + for skill in all_skills: + agent = pool.get_agent(skill.name) + if agent is not None: + default_tools = agent.get_tools() + default_system_prompt = agent.get_system_prompt() + break - skill = skill_registry.get(routing_result.matched_skill) - agent = pool.get_agent(routing_result.matched_skill) - if agent is None: - agent = await pool.create_agent_from_skill(routing_result.matched_skill) - except (ValueError, RuntimeError) as e: - await websocket.send_json( - {"type": "error", "data": {"message": str(e)}} + # Route via CostAwareRouter (Layer 0/1/2) + routing_result = await cost_aware_router.route( + content=message_text, + skill_registry=skill_registry, + intent_router=intent_router, + default_tools=default_tools, + default_system_prompt=default_system_prompt, + default_model="default", + default_agent_name="default", + session_id=conv.id, + transparency="SILENT", + ) + + await websocket.send_json({ + "type": "routing", + "skill": routing_result.agent_name or "default", + "method": routing_result.match_method or "intent", + "confidence": routing_result.match_confidence, + }) + + # Execute based on routing method + if routing_result.match_method in ("greeting", "chat_mode"): + # Zero-cost path: direct LLM call, no ReAct loop + response = await llm_gateway.chat( + messages=[{"role": "user", "content": message_text}], + model="default", + agent_name="default", + task_type="chat", ) + await websocket.send_json({ + "type": "result", + "data": {"status": "completed", "content": response.content}, + }) continue + # General path: agent execution + agent_name = routing_result.agent_name or "default" + agent = pool.get_agent(agent_name) + if agent is None: + agent = await pool.create_agent_from_skill(agent_name) + # Execute via ReAct stream react_config = agent.get_react_config() react_engine = ReActEngine( - llm_gateway=websocket.app.state.llm_gateway, + llm_gateway=llm_gateway, max_steps=react_config["max_steps"], ) diff --git a/tests/unit/test_bus_protocol.py b/tests/unit/test_bus_protocol.py index 0503ee3..d0d4cbf 100644 --- a/tests/unit/test_bus_protocol.py +++ b/tests/unit/test_bus_protocol.py @@ -121,11 +121,11 @@ class TestInMemoryMessageBus: payload={"q": "What is the answer?"}, ) - response = await bus.request(request, timeout=5.0) + response = await bus.request(request, timeout_seconds=5.0) assert response.payload["answer"] == 42 @pytest.mark.asyncio - async def test_request_timeout(self): + async def test_request_timeout_seconds(self): """请求超时后返回 None。""" bus = InMemoryMessageBus() @@ -136,7 +136,7 @@ class TestInMemoryMessageBus: topic="question", ) - result = await bus.request(request, timeout=0.1) + result = await bus.request(request, timeout_seconds=0.1) assert result is None @pytest.mark.asyncio