refactor: tech debt Wave 1+2 (except Exception 收尾 + core/experts Any 治理) (#10)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details

This commit is contained in:
Fischer 2026-07-01 03:54:53 +08:00
parent 838a05772e
commit a778f816c5
49 changed files with 430 additions and 332 deletions

View File

@ -70,7 +70,9 @@ class InMemoryMessageBus:
for handler in self._subscribers[recipient]:
try:
await handler(message)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — user-defined async handlers can throw arbitrary exceptions
logger.warning(f"Handler error for {recipient}: {e}")
# Check if this is a response to a pending request

View File

@ -62,7 +62,7 @@ class RedisMessageBus:
try:
await redis.xadd(stream_key, {"data": json.dumps(data)})
except Exception as e:
except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError) as e:
logger.error(f"Failed to publish message to {stream_key}: {e}")
raise
@ -103,7 +103,9 @@ class RedisMessageBus:
await redis.xgroup_create(
stream_key, self._consumer_group, id="0", mkstream=True,
)
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — xgroup_create raises ResponseError on BUSYGROUP; redis is optional dep
pass # Group already exists
while True:
@ -126,12 +128,16 @@ class RedisMessageBus:
for handler in self._subscribers.get(agent_name, []):
try:
await handler(message)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — user-defined async handlers can throw arbitrary exceptions
logger.warning(f"Handler error for {agent_name}: {e}")
# Acknowledge message
await redis.xack(stream_key, self._consumer_group, msg_id)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — wraps json parse + handler + xack; multi-op block with diverse failure modes
logger.warning(f"Failed to process message {msg_id}: {e}")
# Move to dead letter after max retries
await self._handle_failed_message(
@ -139,7 +145,7 @@ class RedisMessageBus:
)
except asyncio.CancelledError:
break
except Exception as e:
except Exception as e: # noqa: BLE001 — consumer loop top-level fallback; must keep running on transient errors
logger.error(f"Consumer error for {agent_name}: {e}")
await asyncio.sleep(1)
@ -157,7 +163,7 @@ class RedisMessageBus:
await redis.xadd(dead_key, fields)
await redis.xack(stream_key, self._consumer_group, msg_id)
logger.warning(f"Message {msg_id} moved to dead letter queue")
except Exception as e:
except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError) as e:
logger.error(f"Failed to move message to dead letter: {e}")
async def unsubscribe(self, agent_name: str) -> None:
@ -205,7 +211,7 @@ class RedisMessageBus:
stream_key = self._stream_key(agent_name)
try:
await redis.xadd(stream_key, {"data": json.dumps(data)})
except Exception as e:
except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError) as e:
logger.error(f"Failed to broadcast to {agent_name}: {e}")
# Check pending requests (only for replies)
@ -222,7 +228,7 @@ class RedisMessageBus:
try:
redis = await self._get_redis()
return await redis.ping()
except Exception:
except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError):
return False
@property
@ -257,7 +263,9 @@ def create_message_bus(
)
logger.info(f"MessageBus backend: redis_streams ({redis_url})")
return bus
except Exception as exc:
except asyncio.CancelledError:
raise
except Exception as exc: # noqa: BLE001 — factory fallback to InMemoryMessageBus; must catch import/init errors broadly
logger.warning(
f"Failed to initialise RedisMessageBus ({exc}), "
f"falling back to InMemoryMessageBus"

View File

@ -194,7 +194,7 @@ class RequestPreprocessor:
"""Resolve an explicitly specified skill via @skill:xxx prefix."""
try:
skill = registry.get(skill_name)
except Exception:
except (ValueError, KeyError, TypeError, RuntimeError):
logger.warning(f"Skill '{skill_name}' not found, falling back to REACT")
return SkillRoutingResult(
clean_content=clean_content,

View File

@ -200,7 +200,7 @@ async def resolve_skill_routing(
result.match_method = "explicit"
result.match_confidence = 1.0
logger.info(f"Session {session_id}: using explicit skill '{explicit_skill}'")
except Exception as e:
except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.warning(
f"Session {session_id}: explicit skill '{explicit_skill}' not found: {e}"
)

View File

@ -291,7 +291,7 @@ class SqliteConversationStore:
title_row = await title_cursor.fetchone()
if title_row:
conv.messages.append(ChatMessage(role="user", content=title_row["content"]))
except Exception:
except (aiosqlite.Error, ValueError, KeyError, TypeError):
pass
result.append(conv)
return result
@ -321,7 +321,7 @@ class SqliteConversationStore:
)
await db.execute("DELETE FROM conversations WHERE id = ?", (conversation_id,))
await db.commit()
except Exception:
except (aiosqlite.Error, ValueError, KeyError, RuntimeError):
await db.rollback()
logger.exception("Failed to delete conversation %s; rolled back", conversation_id)
raise
@ -365,6 +365,6 @@ class SqliteConversationStore:
timestamp=self._str_to_dt(row["timestamp"]),
metadata=meta,
)
except Exception as e:
except (aiosqlite.Error, ValueError, KeyError, TypeError, RuntimeError) as e:
logger.warning(f"get_first_user_message failed for {conversation_id}: {e}")
return None

View File

@ -134,7 +134,7 @@ async def _chat_async(
data=data or {},
)
)
except Exception:
except (asyncio.QueueFull, RuntimeError, ConnectionError):
pass # EQ is best-effort; never break CLI flow
# Emit session.started event
@ -177,7 +177,7 @@ async def _chat_async(
elif p.is_file() and p.suffix in (".yaml", ".yml"):
try:
loader.load_from_file(str(p))
except Exception:
except (ValueError, TypeError, KeyError, OSError, RuntimeError):
pass
# Build system prompt — inject memory into system prompt
@ -428,7 +428,9 @@ async def _chat_async(
data={"output": full_content},
)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — CLI main loop top-level; must not crash on user-facing errors
rprint(f"\n[red]Error: {e}[/red]")
# Emit task.failed to EQ (if enabled)
if _emit is not None:
@ -461,7 +463,7 @@ async def _chat_async(
# Archive old daily logs
memory_store.archive_old_dailies(keep_days=2)
except Exception:
except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError):
pass # Daily log generation is best-effort
# Close EventQueue if it was enabled (emit session.ended and close)
@ -473,7 +475,7 @@ async def _chat_async(
session_id=session.session_id,
data={},
)
except Exception:
except (asyncio.QueueFull, RuntimeError, ConnectionError):
pass
eq.close()
@ -502,7 +504,7 @@ def _build_gateway(server_config: "ServerConfig") -> "LLMGateway":
try:
provider = _create_provider(name, pconf)
gateway.register_provider(name, provider)
except Exception as e:
except (ValueError, TypeError, KeyError, RuntimeError, ConnectionError, OSError) as e:
import logging
logging.getLogger(__name__).warning(f"Failed to register LLM provider '{name}': {e}")
@ -603,7 +605,9 @@ def _render_pm_collaboration_event(message: dict) -> bool:
)
)
return True
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — best-effort rendering; must not break orchestration
# ponytail: best-effort 渲染不中断编排,但记录日志便于调试
import logging
@ -782,7 +786,9 @@ async def _execute_team_cli(
elif etype == "user_intervention":
pass # User typed it themselves
# Other events (expert_step, expert_result, expert_joined, etc.) are not rendered
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — best-effort rendering; must not break orchestration
pass # Rendering is best-effort; never break orchestration
team.handoff_transport.register_handler(team.team_channel, _event_handler)
@ -816,7 +822,7 @@ async def _execute_team_cli(
if readable:
try:
line = sys.stdin.readline()
except Exception:
except (OSError, ValueError, RuntimeError):
line = ""
if not line:
break # EOF
@ -846,12 +852,14 @@ async def _execute_team_cli(
)
)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — team execution top-level; must report errors to user without crash
rprint(f"[red]团队执行错误: {e}[/red]")
finally:
try:
await team.dissolve()
except Exception:
except (RuntimeError, asyncio.TimeoutError, ConnectionError):
pass
return True

View File

@ -13,7 +13,7 @@ import logging
import time
from abc import ABC, abstractmethod
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
import redis.asyncio as aioredis
@ -71,7 +71,7 @@ class BaseAgent(ABC):
# 可插拔能力(由子类或配置注入)
self._tools: list["Tool"] = []
self._memory: "Memory | None" = None
self._memory_retriever: Any | None = None
self._memory_retriever: object | None = None
# 外部依赖注入(由 start() 时设置)
self._registry = None
@ -192,7 +192,7 @@ class BaseAgent(ABC):
lines.append(f" - {msg}")
return "\n".join(lines)
def _build_skill_context(self) -> dict[str, Any] | None:
def _build_skill_context(self) -> dict[str, object] | None:
"""从当前技能配置构建 skill_context用于 QualityGate skill_match 校验"""
if not self._skill:
return None
@ -216,17 +216,17 @@ class BaseAgent(ABC):
self._memory = memory
return self
def use_memory_retriever(self, retriever: Any) -> "BaseAgent":
def use_memory_retriever(self, retriever: object) -> "BaseAgent":
"""设置记忆检索器,用于上下文注入"""
self._memory_retriever = retriever
return self
def set_registry(self, registry: Any) -> "BaseAgent":
def set_registry(self, registry: object) -> "BaseAgent":
"""注入注册中心"""
self._registry = registry
return self
def set_dispatcher(self, dispatcher: Any) -> "BaseAgent":
def set_dispatcher(self, dispatcher: object) -> "BaseAgent":
"""注入任务分发器"""
self._dispatcher = dispatcher
return self
@ -489,7 +489,7 @@ class BaseAgent(ABC):
target_agent: str,
task: TaskMessage,
reason: str,
context: dict[str, Any] | None = None,
context: dict[str, object] | None = None,
):
"""将当前任务转交给另一个 Agent"""
if self._redis is None:

View File

@ -10,7 +10,7 @@
import json
import logging
import os
from typing import Any, Callable, Coroutine
from typing import Callable, Coroutine
import yaml
@ -39,12 +39,12 @@ class AgentConfig:
task_mode: str = "llm_generate",
supported_tasks: list[str] | None = None,
max_concurrency: int = 1,
input_schema: dict[str, Any] | None = None,
output_schema: dict[str, Any] | None = None,
input_schema: dict[str, object] | None = None,
output_schema: dict[str, object] | None = None,
prompt: dict[str, str] | None = None,
llm: dict[str, Any] | None = None,
llm: dict[str, object] | None = None,
tools: list[str] | None = None,
memory: dict[str, Any] | None = None,
memory: dict[str, object] | None = None,
custom_handler: str | None = None,
):
self.name = name
@ -96,7 +96,7 @@ class AgentConfig:
)
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "AgentConfig":
def from_dict(cls, data: dict[str, object]) -> "AgentConfig":
"""从字典创建配置"""
return cls(
name=data["name"],
@ -128,7 +128,7 @@ class AgentConfig:
)
return cls.from_dict(data)
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
"""序列化为字典"""
d = {
"name": self.name,
@ -197,11 +197,11 @@ class ConfigDrivenAgent(BaseAgent, EvolutionMixin):
self,
config: AgentConfig,
tool_registry: ToolRegistry | None = None,
llm_client: Any = None,
llm_client: object | None = None,
custom_handlers: dict[str, Callable[..., Coroutine]] | None = None,
llm_gateway: Any = None, # NEW v2 param: LLMGateway
llm_gateway: object | None = None, # NEW v2 param: LLMGateway
mcp_servers: dict[str, str] | None = None, # NEW v2 param: MCP server URLs
compressor: Any = None, # CompressionStrategy | None
compressor: object | None = None, # CompressionStrategy | None
):
# v2: If SkillConfig, extract skill info
from agentkit.skills.base import SkillConfig, Skill
@ -310,12 +310,12 @@ class ConfigDrivenAgent(BaseAgent, EvolutionMixin):
logger.info(f"Merged skill tool '{tool.name}' into agent '{self.name}'")
# v2: Register MCP tools if mcp_servers provided
self._mcp_clients: list[Any] = []
self._mcp_clients: list[object] = []
self._mcp_servers: dict[str, str] = mcp_servers or {}
self._mcp_tools_registered = False
# Memory integration: 从 config.memory 自动实例化 MemoryRetriever
self._memory_retriever: Any | None = None
self._memory_retriever: object | None = None
if config.memory:
try:
from agentkit.memory.retriever import MemoryRetriever
@ -903,7 +903,7 @@ class ConfigDrivenAgent(BaseAgent, EvolutionMixin):
)
return await self.handle_task(enhanced_task)
def _wrap_llm_client(self, llm_client: Any):
def _wrap_llm_client(self, llm_client: object):
"""Wrap legacy llm_client into LLMGateway"""
from agentkit.llm.gateway import LLMGateway
from agentkit.llm.protocol import LLMProvider, LLMRequest, LLMResponse, TokenUsage
@ -911,7 +911,7 @@ class ConfigDrivenAgent(BaseAgent, EvolutionMixin):
class ClientProvider(LLMProvider):
"""Adapter: wraps legacy llm_client as an LLMProvider"""
def __init__(self, raw_client: Any):
def __init__(self, raw_client: object):
self._raw_client = raw_client
async def chat(self, request: LLMRequest) -> LLMResponse:

View File

@ -9,7 +9,6 @@ no LLM). See ``docs/plans/2026-06-29-003-feat-agent-wave2-medium-coupling-plan.m
"""
from dataclasses import dataclass
from typing import Any
from agentkit.core.exceptions import (
LLMProviderError,
@ -54,7 +53,7 @@ class EmergencyError:
retryable: bool # whether a user retry might succeed
original_error: str # str(exc) for traceability
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
return {
"error_code": self.error_code,
"message": self.message,

View File

@ -24,7 +24,7 @@ from __future__ import annotations
import json
import logging
from dataclasses import dataclass, field
from typing import Any, Awaitable, Callable, Protocol, runtime_checkable
from typing import Awaitable, Callable, Protocol, runtime_checkable
logger = logging.getLogger(__name__)
@ -37,14 +37,14 @@ class RequestContext:
"""
messages: list[dict[str, str]]
tools: list[Any] = field(default_factory=list)
tools: list[object] = field(default_factory=list)
system_prompt: str | None = None
model: str = "default"
agent_name: str = ""
task_type: str = ""
task_id: str | None = None
# 中间件间共享状态压缩结果、token 用量、循环检测状态等)
metadata: dict[str, Any] = field(default_factory=dict)
metadata: dict[str, object] = field(default_factory=dict)
@runtime_checkable
@ -57,7 +57,7 @@ class Middleware(Protocol):
async def before(self, ctx: RequestContext) -> RequestContext: ...
async def after(self, ctx: RequestContext, result: Any) -> Any: ...
async def after(self, ctx: RequestContext, result: object) -> object: ...
class MiddlewareChain:
@ -81,8 +81,8 @@ class MiddlewareChain:
async def execute(
self,
ctx: RequestContext,
handler: Callable[[RequestContext], Awaitable[Any]],
) -> Any:
handler: Callable[[RequestContext], Awaitable[object]],
) -> object:
"""执行中间件链 + handler。
洋葱模型before 顺序执行 handler after 逆序执行
@ -125,7 +125,7 @@ class SummarizationMiddleware:
after: 无操作压缩在 before 完成
"""
def __init__(self, compressor: Any = None) -> None:
def __init__(self, compressor: object | None = None) -> None:
self._compressor = compressor
async def before(self, ctx: RequestContext) -> RequestContext:
@ -143,7 +143,7 @@ class SummarizationMiddleware:
logger.warning(f"SummarizationMiddleware: compression failed: {e}")
return ctx
async def after(self, ctx: RequestContext, result: Any) -> Any:
async def after(self, ctx: RequestContext, result: object) -> object:
return result
@ -157,7 +157,7 @@ class TokenUsageMiddleware:
async def before(self, ctx: RequestContext) -> RequestContext:
return ctx
async def after(self, ctx: RequestContext, result: Any) -> Any:
async def after(self, ctx: RequestContext, result: object) -> object:
# 从 ReActResult 或类似结构提取 token 用量
# ReActResult 有 total_tokens 属性(非 token_usage
usage = getattr(result, "total_tokens", None)
@ -184,7 +184,7 @@ class LoopDetectionMiddleware:
ctx.metadata["loop_detection_window"] = []
return ctx
async def after(self, ctx: RequestContext, result: Any) -> Any:
async def after(self, ctx: RequestContext, result: object) -> object:
trajectory = getattr(result, "trajectory", None) or []
if len(trajectory) < self._threshold:
return result

View File

@ -16,9 +16,9 @@ import asyncio
import logging
import random
import time
from dataclasses import dataclass, field
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Awaitable
from typing import Callable, Awaitable
from agentkit.core.plan_schema import ExecutionPlan, PlanStep, PlanStepStatus
from agentkit.core.protocol import TaskMessage, TaskResult, TaskStatus
@ -42,7 +42,7 @@ class StepExecutionResult:
step_id: str
status: PlanStepStatus
result: dict[str, Any] | None = None
result: dict[str, object] | None = None
error: str | None = None
retry_count: int = 0
duration_ms: float = 0.0
@ -91,7 +91,7 @@ class PlanExecutor:
def __init__(
self,
agent_pool: Any,
agent_pool: object,
max_retries: int = 2,
step_timeout: float = 300.0,
max_parallel: int = 5,
@ -207,7 +207,7 @@ class PlanExecutor:
async def _execute_step_with_retry(
self,
step: PlanStep,
input_data: dict[str, Any],
input_data: dict[str, object],
original_task: TaskMessage,
) -> StepExecutionResult:
"""执行单个步骤,支持重试
@ -281,9 +281,9 @@ class PlanExecutor:
async def _execute_step_once(
self,
step: PlanStep,
input_data: dict[str, Any],
input_data: dict[str, object],
original_task: TaskMessage,
) -> dict[str, Any]:
) -> dict[str, object]:
"""执行单个步骤一次
通过 AgentPool 创建 Agent 执行步骤
@ -463,7 +463,7 @@ class PlanExecutor:
self,
step: PlanStep,
step_results: dict[str, StepExecutionResult],
) -> dict[str, Any]:
) -> dict[str, object]:
"""将依赖步骤的结果注入到当前步骤的输入中
兼容 Orchestrator subtask_results 累积模式
@ -471,7 +471,7 @@ class PlanExecutor:
enriched = dict(step.input_data)
if step.dependencies:
dep_results: dict[str, dict[str, Any]] = {}
dep_results: dict[str, dict[str, object]] = {}
for dep_id in step.dependencies:
if dep_id in step_results:
dep_result = step_results[dep_id]

View File

@ -13,7 +13,7 @@ from collections import Counter, deque
from collections.abc import AsyncGenerator
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Awaitable, Callable
from agentkit.core.exceptions import LLMProviderError, LoopDetectedError, TaskCancelledError, TaskTimeoutError
from agentkit.core.protocol import CancellationToken
@ -43,13 +43,13 @@ class ReActStep:
step: int
action: str # "tool_call" or "final_answer"
tool_name: str | None = None
arguments: dict[str, Any] | None = None
result: Any = None
arguments: dict[str, object] | None = None
result: object = None
content: str | None = None
tokens: int = 0
async def _ensure_async_iterable(obj: Any, label: str = "<obj>"):
async def _ensure_async_iterable(obj: object, label: str = "<obj>"):
"""Defensive helper: ensure the given object is an async iterable.
Guards against the recurring ``'async for' requires an object with
@ -133,7 +133,7 @@ class ReActEvent:
event_type: str # "thinking","token","tool_call","tool_result","confirmation_request","confirmation_result","phase_violation","step","final_answer","final_result","error"
step: int
data: dict[str, Any] = field(default_factory=dict)
data: dict[str, object] = field(default_factory=dict)
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
@ -230,7 +230,7 @@ class ReActEngine:
# step and yields phase_violation ReActEvents. Non-streaming execute()
# simply ignores the accumulator (the error dict returned to the LLM is
# the only signal there).
self._phase_violations: list[dict[str, Any]] = []
self._phase_violations: list[dict[str, object]] = []
def reset(self) -> None:
"""Reset internal state for reuse across conversations.
@ -299,8 +299,8 @@ class ReActEngine:
return False
def _check_phase_permission(
self, tool_name: str, arguments: dict[str, Any]
) -> dict[str, Any] | None:
self, tool_name: str, arguments: dict[str, object]
) -> dict[str, object] | None:
"""Return None if tool is allowed; return a structured error dict if blocked.
The error dict replaces what `_execute_tool` would have returned
@ -351,7 +351,7 @@ class ReActEngine:
return violation
return None
def _check_tool_loop(self, tool_calls: list[Any]) -> str | None:
def _check_tool_loop(self, tool_calls: list[object]) -> str | None:
"""检测重复工具调用模式。
将当前步的工具调用 hash 加入滑动窗口若同一 hash 在窗口内出现
@ -406,10 +406,10 @@ class ReActEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
timeout_seconds: float | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
) -> ReActResult:
"""执行 ReAct 循环
@ -536,7 +536,7 @@ class ReActEngine:
async def _run_loop_and_extract(
self,
**kwargs: Any,
**kwargs: object,
) -> ReActResult:
"""Collect all events from _execute_loop and extract the final ReActResult.
@ -564,9 +564,9 @@ class ReActEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
stream: bool = False,
effective_timeout: float = 0.0,
) -> AsyncGenerator[ReActEvent, None]:
@ -666,7 +666,7 @@ class ReActEngine:
)
# 构建初始消息
conversation: list[dict[str, Any]] = []
conversation: list[dict[str, object]] = []
system_content = self._build_system_message(
stable=system_prompt or "",
volatile=memory_context,
@ -726,7 +726,7 @@ class ReActEngine:
# 流式模式:用 chat_streamyield token events
stream_content_chunks: list[str] = []
stream_usage = None
stream_tool_calls: list[Any] = []
stream_tool_calls: list[object] = []
stream_model = model
# U3/G8: delta_flush 节流 buffer
_flush_buffer: list[str] = []
@ -840,7 +840,7 @@ class ReActEngine:
)
# Act: 记录 assistant 消息(含 tool_calls到对话历史
assistant_msg: dict[str, Any] = {
assistant_msg: dict[str, object] = {
"role": "assistant",
"content": response.content or "",
"tool_calls": [
@ -874,7 +874,7 @@ class ReActEngine:
if i in parallelizable_set
]
all_results: list[Any] = [None] * len(response.tool_calls)
all_results: list[object] = [None] * len(response.tool_calls)
# Execute serial tools first (handles confirmation flow)
for i, tc in serial_calls:
@ -1452,10 +1452,10 @@ class ReActEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
timeout_seconds: float | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
) -> AsyncGenerator[ReActEvent, None]:
"""Execute ReAct loop, yielding ReActEvent objects.
@ -1514,7 +1514,7 @@ class ReActEngine:
volatile: str,
*,
model: str,
) -> str | list[dict[str, Any]] | None:
) -> str | list[dict[str, object]] | None:
"""构建双块结构 system message(stable + volatile)。
- prompt_cache_enable=False 或无 stable+volatile 返回 str( None)
@ -1535,7 +1535,7 @@ class ReActEngine:
provider_name = self._get_provider_name(model)
if provider_name == "anthropic":
blocks: list[dict[str, Any]] = []
blocks: list[dict[str, object]] = []
if stable:
blocks.append(
{
@ -1675,8 +1675,8 @@ class ReActEngine:
@staticmethod
def _build_response_from_stream(
content: str,
tool_calls: list[Any],
usage: Any,
tool_calls: list[object],
usage: object,
model: str,
) -> LLMResponse:
"""Build an LLMResponse from accumulated stream chunks."""
@ -1723,7 +1723,7 @@ class ReActEngine:
async def _build_tool_result_message(
self,
tool_call_id: str,
result: Any,
result: object,
compressor: "CompressionStrategy | None" = None,
tool_name: str | None = None,
) -> dict:
@ -1742,7 +1742,7 @@ class ReActEngine:
}
async def _execute_tool(
self, tool_name: str, arguments: dict[str, Any], tools: list[Tool]
self, tool_name: str, arguments: dict[str, object], tools: list[Tool]
) -> dict:
"""执行工具调用,处理成功和失败情况"""
# U3/G6: phase enforcement — check before dispatch. If the tool is
@ -1788,11 +1788,11 @@ class ReActEngine:
async def _execute_tool_with_confirmation(
self,
tc: Any,
tc: object,
tools: list[Tool],
step: int,
confirmation_handler: Any,
) -> tuple[Any, list[ReActEvent]]:
confirmation_handler: Callable[..., Awaitable[object]] | None,
) -> tuple[object, list[ReActEvent]]:
"""Execute a tool call with confirmation flow support.
Used in the parallel execution path for serial (non-parallelizable) tools
@ -1886,7 +1886,7 @@ class ReActEngine:
return tool_result, events
def _should_execute_parallel(self, tool_calls: list[Any]) -> bool:
def _should_execute_parallel(self, tool_calls: list[object]) -> bool:
"""Determine if tool calls should be executed in parallel.
- parallel_tools=True: always parallel (if >1 tool)
@ -1905,7 +1905,7 @@ class ReActEngine:
return len(parallelizable_indices) > 1
return False
def _get_parallelizable_indices(self, tool_calls: list[Any]) -> list[int]:
def _get_parallelizable_indices(self, tool_calls: list[object]) -> list[int]:
"""Get indices of tool_calls that have _parallelizable=true in arguments.
LLM marks parallelizable tools by including _parallelizable: true
@ -1918,7 +1918,7 @@ class ReActEngine:
indices.append(i)
return indices
def _parse_text_tool_calls(self, content: str) -> list[dict[str, Any]]:
def _parse_text_tool_calls(self, content: str) -> list[dict[str, object]]:
"""从文本中解析工具调用模式
支持格式
@ -1926,7 +1926,7 @@ class ReActEngine:
2. ```tool\n{"name": "...", "arguments": {...}}\n```
3. <tool_use>\n{"name": "...", "arguments": {...}}\n</tool_use>
"""
calls: list[dict[str, Any]] = []
calls: list[dict[str, object]] = []
# 格式 1: Action: tool_name(args)
action_pattern = re.compile(r"Action:\s*(\w+)\((.+?)\)", re.DOTALL)
@ -1999,7 +1999,7 @@ class ReActEngine:
return calls
@staticmethod
def _extract_tool_call_from_malformed(text: str) -> dict[str, Any] | None:
def _extract_tool_call_from_malformed(text: str) -> dict[str, object] | None:
"""从畸形文本中尝试提取工具调用。
处理场景
@ -2066,7 +2066,7 @@ class ReActEngine:
return None
name = name_match.group(1)
arguments: dict[str, Any] = {}
arguments: dict[str, object] = {}
# 提取 "key": "value" 模式
for kv_match in re.finditer(r'"(\w+)"\s*:\s*"([^"]*)"', text):
key = kv_match.group(1)

View File

@ -11,7 +11,7 @@ import logging
import re
import time
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Awaitable, Callable
from agentkit.core.exceptions import LLMProviderError, TaskCancelledError, TaskTimeoutError
from agentkit.core.protocol import CancellationToken
@ -52,7 +52,7 @@ class ReWOOPlanStep:
step_id: int
tool_name: str
arguments: dict[str, Any]
arguments: dict[str, object]
reasoning: str = ""
@ -164,10 +164,10 @@ class ReWOOEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
timeout_seconds: float | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
) -> ReActResult:
"""执行 ReWOO 三阶段流程
@ -241,9 +241,9 @@ class ReWOOEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
) -> ReActResult:
tools = tools or []
tool_schemas = self._build_tool_schemas(tools) if tools else None
@ -350,7 +350,7 @@ class ReWOOEngine:
# 如果计划为空(无需工具),直接让 LLM 回答
if not plan.steps:
llm_messages: list[dict[str, Any]] = []
llm_messages: list[dict[str, object]] = []
if effective_system_prompt:
llm_messages.append({"role": "system", "content": effective_system_prompt})
llm_messages.extend(messages)
@ -399,7 +399,7 @@ class ReWOOEngine:
)
# ── Phase 2: Execution ──
tool_results: list[dict[str, Any]] = []
tool_results: list[dict[str, object]] = []
for plan_step in plan.steps:
# 协作式取消检查
if cancellation_token is not None:
@ -524,10 +524,10 @@ class ReWOOEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
timeout_seconds: float | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
):
"""Execute ReWOO flow, yielding ReActEvent objects.
@ -637,7 +637,7 @@ class ReWOOEngine:
# Empty plan: direct answer
if not plan.steps:
llm_messages: list[dict[str, Any]] = []
llm_messages: list[dict[str, object]] = []
if effective_system_prompt:
llm_messages.append({"role": "system", "content": effective_system_prompt})
llm_messages.extend(messages)
@ -676,7 +676,7 @@ class ReWOOEngine:
return
# ── Phase 2: Execution ──
tool_results: list[dict[str, Any]] = []
tool_results: list[dict[str, object]] = []
for plan_step in plan.steps:
if cancellation_token is not None:
cancellation_token.check()
@ -806,10 +806,10 @@ class ReWOOEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
total_tokens: int = 0,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
):
"""Stream version: try fallback strategies in configured order, yielding events from the first successful one.
@ -902,7 +902,7 @@ class ReWOOEngine:
"reasoning": plan.reasoning,
"steps": [{"step_id": s.step_id, "tool_name": s.tool_name, "arguments": s.arguments, "reasoning": s.reasoning} for s in plan.steps],
})
tool_results: list[dict[str, Any]] = []
tool_results: list[dict[str, object]] = []
for plan_step in plan.steps:
if cancellation_token is not None:
cancellation_token.check()
@ -934,9 +934,9 @@ class ReWOOEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
):
"""Stream: ReAct fallback"""
logger.warning("ReWOO planning failed in stream mode, falling back to ReActEngine")
@ -969,7 +969,7 @@ class ReWOOEngine:
"""Stream: Direct LLM fallback"""
logger.warning("Falling back to direct LLM call in stream mode")
try:
direct_messages: list[dict[str, Any]] = []
direct_messages: list[dict[str, object]] = []
if effective_system_prompt:
direct_messages.append({"role": "system", "content": effective_system_prompt})
direct_messages.extend(messages)
@ -1012,7 +1012,7 @@ class ReWOOEngine:
"reasoning": plan.reasoning,
"steps": [{"step_id": s.step_id, "tool_name": s.tool_name, "arguments": s.arguments, "reasoning": s.reasoning} for s in plan.steps],
})
tool_results: list[dict[str, Any]] = []
tool_results: list[dict[str, object]] = []
for plan_step in plan.steps:
if cancellation_token is not None:
cancellation_token.check()
@ -1043,11 +1043,11 @@ class ReWOOEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
trajectory: list[ReActStep] | None = None,
total_tokens: int = 0,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
) -> ReActResult | None:
"""按配置的 fallback 策略顺序尝试回退,返回第一个成功的结果
@ -1136,7 +1136,7 @@ class ReWOOEngine:
# Execute the simplified plan
trajectory: list[ReActStep] = []
total_tokens = simplified_tokens
tool_results: list[dict[str, Any]] = []
tool_results: list[dict[str, object]] = []
for plan_step in plan.steps:
if cancellation_token is not None:
cancellation_token.check()
@ -1195,9 +1195,9 @@ class ReWOOEngine:
memory_retriever: "MemoryRetriever | None" = None,
task_id: str | None = None,
compressor: "CompressionStrategy | None" = None,
retrieval_config: dict[str, Any] | None = None,
retrieval_config: dict[str, object] | None = None,
cancellation_token: CancellationToken | None = None,
confirmation_handler: Any | None = None,
confirmation_handler: Callable[..., Awaitable[object]] | None = None,
) -> ReActResult | None:
"""ReAct fallback: delegate to ReActEngine"""
logger.warning("ReWOO planning failed, falling back to ReActEngine")
@ -1240,7 +1240,7 @@ class ReWOOEngine:
"""Direct fallback: simple LLM call without tools"""
logger.warning("Falling back to direct LLM call")
try:
direct_messages: list[dict[str, Any]] = []
direct_messages: list[dict[str, object]] = []
if effective_system_prompt:
direct_messages.append({"role": "system", "content": effective_system_prompt})
direct_messages.extend(messages)
@ -1319,7 +1319,7 @@ class ReWOOEngine:
if plan is not None and plan.steps:
trajectory: list[ReActStep] = []
total_tokens = plan_tokens
tool_results: list[dict[str, Any]] = []
tool_results: list[dict[str, object]] = []
for plan_step in plan.steps:
if cancellation_token is not None:
cancellation_token.check()
@ -1396,7 +1396,7 @@ class ReWOOEngine:
tool_descriptions = self._build_tool_descriptions(tools)
# 构建规划消息
planning_messages: list[dict[str, Any]] = [
planning_messages: list[dict[str, object]] = [
{"role": "system", "content": _PLANNING_SYSTEM_PROMPT},
]
@ -1451,7 +1451,7 @@ class ReWOOEngine:
async def _synthesis_phase(
self,
messages: list[dict[str, str]],
tool_results: list[dict[str, Any]],
tool_results: list[dict[str, object]],
model: str,
agent_name: str,
task_type: str,
@ -1468,7 +1468,7 @@ class ReWOOEngine:
cancellation_token.check()
# 构建综合消息
synthesis_messages: list[dict[str, Any]] = [
synthesis_messages: list[dict[str, object]] = [
{"role": "system", "content": _SYNTHESIS_SYSTEM_PROMPT},
]
@ -1600,7 +1600,7 @@ class ReWOOEngine:
return None
async def _execute_tool(
self, tool_name: str, arguments: dict[str, Any], tools: list[Tool]
self, tool_name: str, arguments: dict[str, object], tools: list[Tool]
) -> dict:
"""执行工具调用,处理成功和失败情况"""
tool = self._find_tool(tool_name, tools)

View File

@ -8,6 +8,8 @@ import math
from dataclasses import dataclass
from typing import TYPE_CHECKING
from sqlalchemy.exc import DBAPIError
if TYPE_CHECKING:
from agentkit.evolution.evolution_store import InMemoryEvolutionStore
@ -129,7 +131,7 @@ class ABTester:
sample_count=len(experiment_metrics),
)
logger.info(f"A/B test results persisted for test '{test_id}'")
except Exception as e:
except (DBAPIError, RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to persist A/B test results: {e}")
async def evaluate(self, test_id: str) -> ABTestResult | None:

View File

@ -10,6 +10,7 @@
from __future__ import annotations
import asyncio
import logging
import math
import re
@ -18,6 +19,7 @@ from datetime import datetime, timedelta, timezone
from typing import Any
from sqlalchemy import text
from sqlalchemy.exc import DBAPIError
_SAFE_TABLE_NAME_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
@ -96,7 +98,7 @@ class ExperienceStore:
text = experience.text_for_embedding()
try:
experience.embedding = await self._embedder.embed(text)
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Failed to generate embedding for experience {experience.experience_id}: {e}")
async with self._session_factory() as db:
@ -122,7 +124,7 @@ class ExperienceStore:
f"task_type={experience.task_type} outcome={experience.outcome}"
)
return experience.experience_id
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to record experience: {e}")
raise
@ -149,7 +151,7 @@ class ExperienceStore:
if self._pgvector_enabled and self._embedder:
return await self._search_pgvector(db, query, top_k, task_type, search_multiplier)
return await self._search_client_side(db, query, top_k, task_type, search_multiplier)
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to search experiences: {e}")
return []
@ -343,7 +345,7 @@ class ExperienceStore:
)
)
return metrics_list
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to get metrics: {e}")
return []
@ -375,7 +377,7 @@ class InMemoryExperienceStore:
text = experience.text_for_embedding()
try:
experience.embedding = await self._embedder.embed(text)
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Failed to generate embedding for experience {experience.experience_id}: {e}")
# 存储副本,避免外部修改影响内部状态
@ -411,7 +413,7 @@ class InMemoryExperienceStore:
if self._embedder:
try:
query_embedding = await self._embedder.embed(query)
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Failed to generate query embedding: {e}")
# 筛选候选

View File

@ -13,6 +13,7 @@
from __future__ import annotations
import asyncio
import copy
import logging
import random
@ -292,7 +293,7 @@ class MutationOperator:
model="default",
)
return response.content.strip() or instructions
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"LLM instruction mutation failed: {e}")
# Fallback: simple text mutation (shuffle paragraphs)

View File

@ -8,6 +8,8 @@ from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
from sqlalchemy.exc import DBAPIError
from agentkit.core.protocol import EvolutionEvent, TaskMessage, TaskResult
from agentkit.evolution.ab_tester import ABTestConfig, ABTestResult, ABTester
from agentkit.evolution.evolution_store import EvolutionStore
@ -371,7 +373,7 @@ class EvolutionMixin:
entry.event_id = event_id
break
return True
except Exception as e:
except (DBAPIError, RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to apply evolution change: {e}")
return False
@ -382,7 +384,7 @@ class EvolutionMixin:
try:
return await self._evolution_store.rollback(log_entry.event_id)
except Exception as e:
except (DBAPIError, RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to rollback evolution change: {e}")
return False

View File

@ -3,6 +3,7 @@
通过 LLM 分析执行轨迹生成结构化反思 RuleBasedReflector 提供更深入的洞察
"""
import asyncio
import json
import logging
import re
@ -62,7 +63,7 @@ class LLMReflector:
task_type="reflection",
)
return self._parse_reflection_response(response.content, task, result)
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError, KeyError) as e:
logger.warning(f"LLM reflection failed, returning default: {e}")
return Reflection(
task_id=getattr(task, "task_id", "unknown"),

View File

@ -13,6 +13,7 @@ from typing import Any
from sqlalchemy import select
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy import Column, DateTime, Float, Integer, String, Text, UniqueConstraint
from sqlalchemy.exc import DBAPIError
from sqlalchemy.orm import declarative_base
from agentkit.core.protocol import EvolutionEvent
@ -154,7 +155,7 @@ class PostgreSQLEvolutionStore:
event.event_id = event_id
logger.info(f"Evolution event recorded: {event_id} for agent '{event.agent_name}'")
return event_id
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to record evolution event: {e}")
raise
@ -178,7 +179,7 @@ class PostgreSQLEvolutionStore:
await db.commit()
logger.info(f"Evolution event {event_id} rolled back")
return True
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to rollback evolution event {event_id}: {e}")
return False
@ -218,7 +219,7 @@ class PostgreSQLEvolutionStore:
}
for e in entries
]
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to list evolution events: {e}")
return []
@ -242,7 +243,7 @@ class PostgreSQLEvolutionStore:
db.add(entry)
await db.commit()
return vid
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to record skill version: {e}")
raise
@ -271,7 +272,7 @@ class PostgreSQLEvolutionStore:
}
for e in entries
]
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to list skill versions: {e}")
return []
@ -295,7 +296,7 @@ class PostgreSQLEvolutionStore:
db.add(entry)
await db.commit()
return rid
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to record A/B test result: {e}")
raise
@ -324,6 +325,6 @@ class PostgreSQLEvolutionStore:
}
for e in entries
]
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to get A/B test results: {e}")
return []

View File

@ -151,7 +151,7 @@ class PitfallDetector:
task_type=task_type,
)
return results
except Exception as e:
except (RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to search experiences for pitfall detection: {e}")
return []

View File

@ -10,6 +10,7 @@
- LLMPromptOptimizer: 基于 LLM 分析反思结果生成改进指令
"""
import asyncio
import logging
from dataclasses import dataclass, field
from typing import Any
@ -202,7 +203,7 @@ class LLMPromptOptimizer:
"""
try:
optimized_instruction = await self._llm_optimize_instruction(module, trace, reflection)
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"LLM prompt optimization failed, falling back to bootstrap: {e}")
return await self._bootstrap.optimize(module)

View File

@ -8,6 +8,7 @@
from __future__ import annotations
import asyncio
import json
import logging
import re
@ -79,7 +80,7 @@ class RiskGuardLearner:
top_k=top_k,
task_type=skill_name,
)
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError, KeyError) as e:
logger.warning(f"RiskGuardLearner: failed to search experiences: {e}")
return []
@ -95,7 +96,7 @@ class RiskGuardLearner:
# 2. 构建 LLM prompt
try:
prompt = self._build_prompt(failures)
except Exception as e:
except (ValueError, KeyError, RuntimeError) as e:
logger.warning(f"RiskGuardLearner: failed to build prompt: {e}")
return []
@ -117,14 +118,14 @@ class RiskGuardLearner:
agent_name="risk_guard_learner",
task_type="risk_guard_learning",
)
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"RiskGuardLearner: LLM call failed: {e}")
return []
# 4. 解析响应
try:
return self._parse_response(response.content, source_ids)
except Exception as e:
except (ValueError, KeyError, TypeError) as e:
logger.warning(f"RiskGuardLearner: failed to parse response: {e}")
return []

View File

@ -9,7 +9,7 @@ import asyncio
import json
import logging
import re
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from .expert import Expert
from .plan import PhaseStatus, PlanPhase, TeamPlan
@ -28,7 +28,7 @@ class DebateRunnerMixin:
_phase_semaphore: asyncio.Semaphore
MAX_DEBATE_ROUNDS: int
async def _execute_debate_phase(self, phase: PlanPhase, plan: TeamPlan) -> dict[str, Any]:
async def _execute_debate_phase(self, phase: PlanPhase, plan: TeamPlan) -> dict[str, object]:
"""Execute a DEBATE phase: Lead-facilitated structured debate (5 stages).
Parse config Lead opens experts argue in parallel rounds Lead
summarizes Lead adjudicates write conclusion to workspace."""
@ -86,7 +86,7 @@ class DebateRunnerMixin:
)
# Debate history for context (Lead opening + expert arguments + Lead summaries)
history: list[dict[str, Any]] = [
history: list[dict[str, object]] = [
{"expert": lead.config.name, "content": opening, "round": 0, "role": "moderator"}
]
@ -103,7 +103,7 @@ class DebateRunnerMixin:
break
# Experts argue in parallel (with concurrency limit)
async def _bounded_debate(e: Any) -> str:
async def _bounded_debate(e: object) -> str:
async with self._phase_semaphore:
return await self._generate_debate_argument(e, topic, history, round_num)
@ -234,7 +234,7 @@ class DebateRunnerMixin:
return f"辩论主题:{topic}。请各位专家发表看法。"
async def _generate_debate_argument(
self, expert: Expert, topic: str, history: list[dict[str, Any]], round_num: int
self, expert: Expert, topic: str, history: list[dict[str, object]], round_num: int
) -> str:
"""Generate an expert's debate argument for the current round."""
gateway = self._get_llm_gateway(expert)
@ -269,7 +269,7 @@ class DebateRunnerMixin:
return response.content.strip()
async def _generate_debate_summary(
self, lead: Expert, topic: str, history: list[dict[str, Any]], round_num: int
self, lead: Expert, topic: str, history: list[dict[str, object]], round_num: int
) -> str:
"""Generate Lead's summary of the current debate round."""
gateway = self._get_llm_gateway(lead)
@ -307,8 +307,8 @@ class DebateRunnerMixin:
return f"[第 {round_num} 轮辩论完成,小结生成失败]"
async def _generate_debate_verdict(
self, lead: Expert, topic: str, history: list[dict[str, Any]]
) -> dict[str, Any]:
self, lead: Expert, topic: str, history: list[dict[str, object]]
) -> dict[str, object]:
"""Generate Lead's final verdict for the debate."""
gateway = self._get_llm_gateway(lead)
if not gateway:
@ -371,7 +371,7 @@ class DebateRunnerMixin:
"conclusion": f"辩论主题:{topic}。裁决生成失败,建议参考辩论历史自行判断。",
}
def _format_debate_history(self, history: list[dict[str, Any]]) -> str:
def _format_debate_history(self, history: list[dict[str, object]]) -> str:
"""Format debate history as readable text for LLM prompts."""
if not history:
return ""

View File

@ -6,7 +6,7 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from .expert import Expert
from .plan import PhaseStatus, PhaseType, PlanPhase, TeamPlan
@ -23,7 +23,7 @@ class DivergenceDetectorMixin:
# Shared state provided by TeamOrchestrator (annotations only)
_team: ExpertTeam
_debate_count: int
_checkpoint: Any
_checkpoint: object
MAX_DEBATES: int
async def _maybe_add_plan_review_debate(self, lead: Expert, plan: TeamPlan, task: str) -> None:

View File

@ -9,7 +9,7 @@ import asyncio
import copy
import logging
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from agentkit.core.config_driven import ConfigDrivenAgent
from agentkit.core.protocol import TaskMessage, TaskResult, TaskStatus
@ -37,7 +37,7 @@ class PhaseExecutorMixin:
# U4: State offloading helpers — keep memory lean for long-horizon runs.
_OFFLOAD_SUMMARY_LIMIT = 500
def _offload_result(self, content: str, ref_key: str) -> dict[str, Any]:
def _offload_result(self, content: str, ref_key: str) -> dict[str, object]:
"""Create an offloaded result: summary in memory, full content in workspace."""
if not isinstance(content, str):
content = str(content) if content is not None else ""
@ -64,17 +64,17 @@ class PhaseExecutorMixin:
logger.warning(f"Failed to read offloaded output '{ref_key}': {e}")
return content
async def _execute_phase(self, phase: PlanPhase, plan: TeamPlan) -> dict[str, Any]:
async def _execute_phase(self, phase: PlanPhase, plan: TeamPlan) -> dict[str, object]:
"""Execute a single phase, dispatching by phase_type."""
if phase.phase_type == PhaseType.DEBATE:
return await self._execute_debate_phase(phase, plan)
return await self._execute_execution_phase(phase, plan)
async def _execute_execution_phase(self, phase: PlanPhase, plan: TeamPlan) -> dict[str, Any]:
async def _execute_execution_phase(self, phase: PlanPhase, plan: TeamPlan) -> dict[str, object]:
"""Execute a standard EXECUTION phase. Split into 3 sub-methods (U2, KTD3 isolation)."""
expert, agent, lead = await self._prepare_phase_context(phase, plan)
last_error: str | None = None
result: dict[str, Any] | None = None
result: dict[str, object] | None = None
try:
# U3: 返工循环 — 最多 MAX_REWORKS + 1 次1 次初始 + MAX_REWORKS 次返工)
@ -135,11 +135,11 @@ class PhaseExecutorMixin:
self,
expert: Expert,
phase: PlanPhase,
dependency_outputs: dict[str, Any],
dependency_outputs: dict[str, object],
collaboration_outputs: dict[str, str],
) -> TaskMessage:
"""Build TaskMessage for execution with context isolation."""
input_data: dict[str, Any] = {
input_data: dict[str, object] = {
"task": phase.task_description,
"team_id": self._team.team_id,
"phase_id": phase.id,
@ -180,12 +180,12 @@ class PhaseExecutorMixin:
lead: Expert,
phase: PlanPhase,
plan: TeamPlan,
) -> tuple[dict[str, Any], str | None, bool, str, bool]:
) -> tuple[dict[str, object], str | None, bool, str, bool]:
"""Run one rework iteration: read deps, build input, execute, review. Returns
(result, last_error, passed, feedback, degraded). Raises RuntimeError on retry
exhaustion."""
# 每次迭代重新读取依赖输出(前置阶段可能在返工期间完成)
dependency_outputs: dict[str, Any] = {}
dependency_outputs: dict[str, object] = {}
for dep_id in phase.depends_on:
dep_phase = plan.get_phase(dep_id)
if dep_phase and dep_phase.status == PhaseStatus.COMPLETED and dep_phase.result:
@ -216,7 +216,7 @@ class PhaseExecutorMixin:
# 执行专家任务带重试MAX_RETRIES 处理瞬时失败)
last_error: str | None = None
result: dict[str, Any] | None = None
result: dict[str, object] | None = None
for attempt in range(self.MAX_RETRIES + 1):
try:
task_result: TaskResult = await agent.execute(task_msg)
@ -265,7 +265,7 @@ class PhaseExecutorMixin:
lead: Expert,
phase: PlanPhase,
plan: TeamPlan,
result: dict[str, Any],
result: dict[str, object],
passed: bool,
feedback: str,
degraded: bool = False,

View File

@ -10,7 +10,6 @@ import json
import logging
import re
from dataclasses import dataclass
from typing import Any
from agentkit.core.exceptions import LLMProviderError
@ -45,7 +44,7 @@ class ReviewGateMixin:
"""Mixin: Lead 验收阶段输出质量 + 解析风险标记。由 TeamOrchestrator 组合。"""
async def _review_phase_output(
self, lead: Expert, phase: PlanPhase, result: dict[str, Any]
self, lead: Expert, phase: PlanPhase, result: dict[str, object]
) -> ReviewResult:
"""Lead 验收阶段输出质量。
@ -93,7 +92,7 @@ class ReviewGateMixin:
)
# P2: 优先尝试直接解析整个响应为 JSON避免贪婪正则匹配过多
review: dict[str, Any] | None = None
review: dict[str, object] | None = None
try:
review = json.loads(response.content)
except (json.JSONDecodeError, TypeError):

View File

@ -6,7 +6,7 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from agentkit.orchestrator.rollback import RollbackExecutor
@ -27,7 +27,7 @@ class RollbackHandlerMixin:
_rollback_timeout: float
async def _mark_dependents_failed(
self, failed_phase_id: str, plan: TeamPlan, phase_results: dict[str, dict[str, Any]]
self, failed_phase_id: str, plan: TeamPlan, phase_results: dict[str, dict[str, object]]
) -> None:
"""Mark all phases that depend on the failed phase as FAILED."""
for ph in plan.phases:

View File

@ -7,7 +7,7 @@ from __future__ import annotations
import logging
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from agentkit.core.protocol import TaskMessage, TaskResult
@ -29,7 +29,7 @@ class SynthesizerMixin:
async def _synthesize_results(
self, lead: Expert, task: str, completed_phases: list[PlanPhase]
) -> dict[str, Any]:
) -> dict[str, object]:
"""Lead Expert synthesizes results using BEST strategy.
The Lead Expert evaluates all completed phase results and produces
@ -114,8 +114,8 @@ class SynthesizerMixin:
self,
task: str,
plan: TeamPlan,
phase_results: dict[str, dict[str, Any]],
) -> dict[str, Any]:
phase_results: dict[str, dict[str, object]],
) -> dict[str, object]:
"""Fallback to single agent mode when pipeline execution fails.
Uses the lead expert (or first active expert) to complete the original task.
@ -128,7 +128,7 @@ class SynthesizerMixin:
active = self._team.active_experts
expert = active[0] if active else None
fallback_result: dict[str, Any] | None = None
fallback_result: dict[str, object] | None = None
if expert:
try:
task_msg = TaskMessage(

View File

@ -16,7 +16,6 @@ import enum
import logging
import time
import uuid
from typing import Any
from .config import ExpertConfig
from .expert import Expert
@ -72,7 +71,7 @@ class BoardTeam:
# Discussion state
self._topic: str = ""
self._history: list[dict[str, Any]] = []
self._history: list[dict[str, object]] = []
self._current_round: int = 0
self._max_rounds: int = max_rounds
self._user_interventions: list[str] = [] # Pending user messages
@ -125,7 +124,7 @@ class BoardTeam:
return self._max_rounds
@property
def history(self) -> list[dict[str, Any]]:
def history(self) -> list[dict[str, object]]:
return self._history.copy()
def get_expert(self, name: str) -> Expert | None:
@ -254,7 +253,7 @@ class BoardTeam:
return "\n\n---\n\n".join(lines)
async def compress_history(self, moderator: Expert, llm_gateway: Any) -> None:
async def compress_history(self, moderator: Expert, llm_gateway: object) -> None:
"""Compress discussion history when it exceeds token threshold.
The moderator summarizes each round's key points, replacing
@ -291,7 +290,7 @@ class BoardTeam:
# Parse compressed history back to entries
# This is a best-effort compression; if parsing fails, keep original
new_history: list[dict[str, Any]] = []
new_history: list[dict[str, object]] = []
current_round = 0
for line in compressed.split("\n"):
line = line.strip()

View File

@ -19,7 +19,6 @@ from __future__ import annotations
import asyncio
import logging
from typing import Any
from .expert import Expert
from .board import BoardTeam, BoardStatus
@ -43,7 +42,7 @@ class BoardOrchestrator:
def __init__(self, team: BoardTeam) -> None:
self._team = team
async def execute(self, topic: str) -> dict[str, Any]:
async def execute(self, topic: str) -> dict[str, object]:
"""Execute a board meeting discussion.
Flow:
@ -351,7 +350,7 @@ class BoardOrchestrator:
logger.warning(f"Moderator summary generation failed: {e}")
return f"[第 {round} 轮讨论完成,主持人小结生成失败]"
async def _generate_final_conclusion(self, moderator: Expert, topic: str) -> dict[str, Any]:
async def _generate_final_conclusion(self, moderator: Expert, topic: str) -> dict[str, object]:
"""Generate moderator's final conclusion.
The moderator gives:
@ -428,7 +427,7 @@ class BoardOrchestrator:
"dissent_points": [],
}
async def _generate_fallback_conclusion(self, moderator: Expert, topic: str) -> dict[str, Any]:
async def _generate_fallback_conclusion(self, moderator: Expert, topic: str) -> dict[str, object]:
"""Generate a fallback conclusion when execution fails.
Uses existing discussion history to provide a basic summary.
@ -491,7 +490,7 @@ class BoardOrchestrator:
return True
return False
def _get_llm_gateway(self, expert: Expert | None = None) -> Any:
def _get_llm_gateway(self, expert: Expert | None = None) -> object:
"""Get LLM gateway from the given expert or the moderator's agent.
Falls back to other active experts if the primary target has no gateway.
@ -509,7 +508,7 @@ class BoardOrchestrator:
return gateway
return None
async def _broadcast_event(self, event_type: str, data: dict[str, Any]) -> None:
async def _broadcast_event(self, event_type: str, data: dict[str, object]) -> None:
"""Broadcast a board event to the team channel.
Events are emitted via handoff_transport for WebSocket relay.

View File

@ -3,7 +3,6 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from agentkit.core.config_driven import AgentConfig
@ -24,12 +23,12 @@ class ExpertConfig(AgentConfig):
task_mode: str = "llm_generate",
supported_tasks: list[str] | None = None,
max_concurrency: int = 1,
input_schema: dict[str, Any] | None = None,
output_schema: dict[str, Any] | None = None,
input_schema: dict[str, object] | None = None,
output_schema: dict[str, object] | None = None,
prompt: dict[str, str] | None = None,
llm: dict[str, Any] | None = None,
llm: dict[str, object] | None = None,
tools: list[str] | None = None,
memory: dict[str, Any] | None = None,
memory: dict[str, object] | None = None,
custom_handler: str | None = None,
# Expert 专属字段
persona: str = "",
@ -70,7 +69,7 @@ class ExpertConfig(AgentConfig):
self.decision_framework = decision_framework
@classmethod
def from_dict(cls, data: dict[str, Any]) -> ExpertConfig:
def from_dict(cls, data: dict[str, object]) -> ExpertConfig:
"""从字典创建配置"""
return cls(
name=data["name"],
@ -98,7 +97,7 @@ class ExpertConfig(AgentConfig):
decision_framework=data.get("decision_framework", ""),
)
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
"""序列化为字典,包含 Expert 专属字段"""
d = super().to_dict()
d["persona"] = self.persona
@ -125,7 +124,7 @@ class ExpertTemplate:
is_builtin: bool = False
description: str = ""
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
"""序列化为字典"""
return {
"name": self.name,
@ -135,7 +134,7 @@ class ExpertTemplate:
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> ExpertTemplate:
def from_dict(cls, data: dict[str, object]) -> ExpertTemplate:
"""从字典创建模板"""
config_data = data["config"]
config = ExpertConfig.from_dict(config_data)

View File

@ -14,7 +14,6 @@ import asyncio
import json
import logging
import re
from typing import Any
from agentkit.core.exceptions import LLMProviderError
from agentkit.llm.gateway import LLMGateway
@ -72,7 +71,7 @@ class TeamOrchestrator(
self,
team: ExpertTeam,
max_concurrent_phases: int | None = None,
checkpoint: Any = None,
checkpoint: object | None = None,
workspace_root: str | None = None,
rollback_timeout: float | None = None,
) -> None:
@ -95,7 +94,7 @@ class TeamOrchestrator(
self._workspace_root = workspace_root
self._rollback_timeout = rollback_timeout or self.DEFAULT_ROLLBACK_TIMEOUT
async def execute(self, task: str) -> dict[str, Any]:
async def execute(self, task: str) -> dict[str, object]:
"""Execute a task in pipeline mode. Lead decomposes → topological sort →
execute layers (parallel within layer) synthesize. Returns dict with
status/result/phase_results/plan."""
@ -175,7 +174,7 @@ class TeamOrchestrator(
# 4. Set EXECUTING status, execute phases
self._team.set_status(TeamStatus.EXECUTING)
phase_results: dict[str, dict[str, Any]] = {}
phase_results: dict[str, dict[str, object]] = {}
return await self._run_pipeline(lead, plan, phase_results, task)
@ -183,9 +182,9 @@ class TeamOrchestrator(
self,
lead: Expert,
plan: TeamPlan,
phase_results: dict[str, dict[str, Any]],
phase_results: dict[str, dict[str, object]],
task: str,
) -> dict[str, Any]:
) -> dict[str, object]:
"""Execute the pipeline loop: run pending phases, synthesize, return result.
Shared by execute() and resume(). phase_results may be pre-populated
@ -220,7 +219,7 @@ class TeamOrchestrator(
break
# Execute all phases in this layer in parallel (with concurrency limit)
async def _bounded_phase(ph: PlanPhase) -> dict[str, Any]:
async def _bounded_phase(ph: PlanPhase) -> dict[str, object]:
async with self._phase_semaphore:
return await self._execute_phase(ph, plan)
@ -335,7 +334,7 @@ class TeamOrchestrator(
await self._broadcast_event("team_dissolved", {"team_id": self._team.team_id})
return await self._fallback_to_single_agent(task, plan, phase_results)
async def resume(self, plan_id: str) -> dict[str, Any]:
async def resume(self, plan_id: str) -> dict[str, object]:
"""Resume from last checkpoint: load plan, restore completed/failed phases,
continue via _run_pipeline. Returns same dict shape as execute()."""
if self._checkpoint is None:
@ -362,7 +361,7 @@ class TeamOrchestrator(
# 3. Load checkpoints, mark completed phases
checkpoints = await self._checkpoint.list_checkpoints(plan_id)
phase_results: dict[str, dict[str, Any]] = {}
phase_results: dict[str, dict[str, object]] = {}
completed_phase_ids: set[str] = set()
failed_phase_ids: set[str] = set()
@ -492,7 +491,7 @@ class TeamOrchestrator(
# First pass: create phases with IDs, build name->id mapping
name_to_id: dict[str, str] = {}
raw_phases: list[dict[str, Any]] = []
raw_phases: list[dict[str, object]] = []
for item in items:
if not isinstance(item, dict):
@ -584,7 +583,7 @@ class TeamOrchestrator(
return gateway
return None
async def _broadcast_event(self, event_type: str, data: dict[str, Any]) -> None:
async def _broadcast_event(self, event_type: str, data: dict[str, object]) -> None:
"""Broadcast an orchestration event to the team channel via handoff_transport."""
if self._team.handoff_transport:
try:

View File

@ -15,7 +15,6 @@ from __future__ import annotations
import enum
import uuid
from dataclasses import dataclass, field
from typing import Any
class MergeStrategy(str, enum.Enum):
@ -82,9 +81,9 @@ class SubTask:
description: str = ""
assigned_expert: str = ""
status: SubTaskStatus = SubTaskStatus.PENDING
result: dict[str, Any] | None = None
result: dict[str, object] | None = None
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
"""序列化为字典"""
return {
"id": self.id,
@ -95,7 +94,7 @@ class SubTask:
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> SubTask:
def from_dict(cls, data: dict[str, object]) -> SubTask:
"""从字典创建 SubTask"""
return cls(
id=data.get("id", str(uuid.uuid4())),
@ -124,7 +123,7 @@ class CollaborationContract:
content_description: str = ""
status: str = "pending"
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
"""序列化为字典"""
return {
"from_expert": self.from_expert,
@ -134,7 +133,7 @@ class CollaborationContract:
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> CollaborationContract:
def from_dict(cls, data: dict[str, object]) -> CollaborationContract:
"""从字典创建 CollaborationContract"""
return cls(
from_expert=data.get("from_expert", ""),
@ -176,9 +175,9 @@ class PlanPhase:
task_description: str = ""
depends_on: list[str] = field(default_factory=list)
status: PhaseStatus = PhaseStatus.PENDING
result: dict[str, Any] | None = None
result: dict[str, object] | None = None
phase_type: PhaseType = PhaseType.EXECUTION
debate_config: dict[str, Any] | None = None
debate_config: dict[str, object] | None = None
collaboration_contracts: list[CollaborationContract] = field(default_factory=list)
rework_count: int = 0
review_feedback: str | None = None
@ -188,7 +187,7 @@ class PlanPhase:
validation_command: str | None = None
rollback_command: str | None = None
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
"""序列化为字典"""
# Serialize result to string to match frontend ITeamPlanPhase.result type
result_str: str | None = None
@ -197,7 +196,7 @@ class PlanPhase:
result_str = self.result.get("content", str(self.result))
else:
result_str = str(self.result)
out: dict[str, Any] = {
out: dict[str, object] = {
"id": self.id,
"name": self.name,
"assigned_expert": self.assigned_expert,
@ -219,7 +218,7 @@ class PlanPhase:
return out
@classmethod
def from_dict(cls, data: dict[str, Any]) -> PlanPhase:
def from_dict(cls, data: dict[str, object]) -> PlanPhase:
"""从字典创建 PlanPhase"""
contracts_data = data.get("collaboration_contracts", [])
if not isinstance(contracts_data, list):
@ -272,7 +271,7 @@ class TeamPlan:
status: PlanStatus = PlanStatus.DRAFT
lead_expert: str = ""
def to_dict(self) -> dict[str, Any]:
def to_dict(self) -> dict[str, object]:
"""序列化为字典"""
return {
"id": self.id,
@ -284,7 +283,7 @@ class TeamPlan:
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> TeamPlan:
def from_dict(cls, data: dict[str, object]) -> TeamPlan:
"""从字典创建 TeamPlan"""
subtasks = [SubTask.from_dict(st) for st in data.get("subtasks", [])]
phases = [PlanPhase.from_dict(ph) for ph in data.get("phases", [])]
@ -307,7 +306,7 @@ class TeamPlan:
return None
def update_subtask_status(
self, subtask_id: str, status: SubTaskStatus, result: dict[str, Any] | None = None
self, subtask_id: str, status: SubTaskStatus, result: dict[str, object] | None = None
) -> None:
"""更新子任务状态和可选的结果"""
st = self.get_subtask(subtask_id)
@ -343,7 +342,7 @@ class TeamPlan:
return None
def update_phase_status(
self, phase_id: str, status: PhaseStatus, result: dict[str, Any] | None = None
self, phase_id: str, status: PhaseStatus, result: dict[str, object] | None = None
) -> None:
"""更新阶段状态和可选的结果"""
ph = self.get_phase(phase_id)

View File

@ -17,7 +17,6 @@ import enum
import logging
import time
import uuid
from typing import Any
from .config import ExpertConfig
from .expert import Expert
@ -63,7 +62,7 @@ class ExpertTeam:
workspace: SharedWorkspace | None = None,
pool: AgentPool | None = None,
template_registry: ExpertTemplateRegistry | None = None,
redis_client: Any = None,
redis_client: object | None = None,
):
self.team_id = team_id or str(uuid.uuid4())
# U4: Accept redis_client for SharedWorkspace state offloading.

View File

@ -6,6 +6,7 @@
from __future__ import annotations
import asyncio
import logging
import httpx
@ -83,7 +84,7 @@ class ConfluenceAdapter(KBAdapter):
resp = await client.get("/rest/api/user/current")
self._authenticated = resp.status_code == 200
return self._authenticated
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence auth error: {e}")
self._authenticated = False
return False
@ -139,7 +140,7 @@ class ConfluenceAdapter(KBAdapter):
f"Confluence search HTTP error: {e.response.status_code}{e.response.text[:200]}"
)
return []
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence search error: {e}")
return []
@ -170,7 +171,7 @@ class ConfluenceAdapter(KBAdapter):
"version": page.get("version", {}).get("number", 0),
},
)
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence get_document error: {e}")
return None
@ -202,7 +203,7 @@ class ConfluenceAdapter(KBAdapter):
)
]
)
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence list_sources error: {e}")
return [
SourceInfo(
@ -218,5 +219,7 @@ class ConfluenceAdapter(KBAdapter):
try:
resp = await client.get("/rest/api/space", params={"limit": 1})
return resp.status_code == 200
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常都返回 False
return False

View File

@ -6,6 +6,7 @@
from __future__ import annotations
import asyncio
import logging
import time
from typing import TypeAlias
@ -101,7 +102,7 @@ class FeishuKBAdapter(KBAdapter):
else:
logger.error(f"Feishu auth failed: code={data.get('code')}, msg={data.get('msg')}")
return None
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu auth error: {e}")
return None
@ -166,7 +167,7 @@ class FeishuKBAdapter(KBAdapter):
f"Feishu search HTTP error: {e.response.status_code}{e.response.text[:200]}"
)
return []
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu search error: {e}")
return []
@ -199,7 +200,7 @@ class FeishuKBAdapter(KBAdapter):
"obj_type": node.get("obj_type", ""),
},
)
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu get_document error: {e}")
return None
@ -241,7 +242,7 @@ class FeishuKBAdapter(KBAdapter):
)
]
)
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu list_sources error: {e}")
return [
SourceInfo(
@ -256,5 +257,7 @@ class FeishuKBAdapter(KBAdapter):
try:
token = await self._get_access_token()
return token is not None
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常都返回 False
return False

View File

@ -6,6 +6,7 @@
from __future__ import annotations
import asyncio
import logging
import httpx
@ -138,7 +139,7 @@ class GenericHTTPAdapter(KBAdapter):
f"GenericHTTP search HTTP error: {e.response.status_code}{e.response.text[:200]}"
)
return []
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP search error: {e}")
return []
@ -179,7 +180,7 @@ class GenericHTTPAdapter(KBAdapter):
f"GenericHTTP ingest HTTP error: {e.response.status_code}{e.response.text[:200]}"
)
return []
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP ingest error: {e}")
return []
@ -194,7 +195,7 @@ class GenericHTTPAdapter(KBAdapter):
if resp.status_code in (200, 204):
return True
return False
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP delete_by_id error: {e}")
return False
@ -216,7 +217,7 @@ class GenericHTTPAdapter(KBAdapter):
source_id=data.get("source_id", self._source_id),
metadata=data.get("metadata", {}),
)
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP get_document error: {e}")
return None
@ -254,7 +255,9 @@ class GenericHTTPAdapter(KBAdapter):
)
]
)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — list_sources 设计意图:任何异常回退到默认信息源
logger.debug(f"GenericHTTP list_sources error (endpoint may not exist): {e}")
return [
@ -272,14 +275,18 @@ class GenericHTTPAdapter(KBAdapter):
resp = await client.get("/health")
if resp.status_code == 200:
return True
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常继续尝试 fallback
pass
# Fallback: try the base endpoint
try:
resp = await client.get("/")
return resp.status_code in (200, 401, 403)
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常都返回 False
return False
async def authenticate(self) -> bool:
@ -289,6 +296,8 @@ class GenericHTTPAdapter(KBAdapter):
"""
try:
self._authenticated = await self.health_check()
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — authenticate 设计意图:任何异常都返回 False
self._authenticated = False
return self._authenticated

View File

@ -6,6 +6,7 @@
from __future__ import annotations
import asyncio
import hashlib
import logging
from dataclasses import dataclass
@ -199,7 +200,7 @@ class ContextualChunker:
)
context = response.content.strip()
return context
except Exception as e:
except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Context generation failed for chunk: {e}")
return ""

View File

@ -9,6 +9,8 @@ from __future__ import annotations
import io
import logging
import uuid
import zipfile
import asyncio
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
@ -194,7 +196,9 @@ class DocumentLoader:
return text, meta
except ImportError:
pass
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"PyMuPDF parsing failed for {filename}: {e}")
# 尝试 pdfplumber
@ -217,7 +221,9 @@ class DocumentLoader:
return text, meta
except ImportError:
pass
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"pdfplumber parsing failed for {filename}: {e}")
# 回退到纯文本
@ -264,7 +270,9 @@ class DocumentLoader:
except ImportError:
logger.warning(f"python-docx not available for {filename}, falling back to text")
return self._parse_text(content, filename)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"python-docx parsing failed for {filename}: {e}")
return self._parse_text(content, filename)
@ -333,7 +341,9 @@ class DocumentLoader:
except ImportError:
logger.warning(f"openpyxl not available for {filename}, falling back to text")
return self._parse_text(content, filename)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"openpyxl parsing failed for {filename}: {e}")
return self._parse_text(content, filename)
@ -407,7 +417,9 @@ class DocumentLoader:
except ImportError:
logger.warning(f"BeautifulSoup not available for {filename}, falling back to text")
return self._parse_text(content, filename)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"BeautifulSoup parsing failed for {filename}: {e}")
return self._parse_text(content, filename)

View File

@ -8,9 +8,7 @@ import os
import time
from abc import ABC, abstractmethod
from collections import OrderedDict
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import httpx
logger = logging.getLogger(__name__)
@ -108,8 +106,6 @@ class OpenAIEmbedder(Embedder):
def _get_client(self) -> httpx.AsyncClient:
"""Lazily create and reuse a single httpx.AsyncClient."""
if self._client is None:
import httpx
self._client = httpx.AsyncClient(timeout=30.0)
return self._client
@ -153,7 +149,7 @@ class OpenAIEmbedder(Embedder):
self._cache.put(text, embedding)
return embedding
except Exception as e:
except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"OpenAI embedding failed: {e}")
raise

View File

@ -9,6 +9,7 @@ from datetime import datetime, timezone
from typing import TYPE_CHECKING
from sqlalchemy import text
from sqlalchemy.exc import DBAPIError
from agentkit.memory.base import Memory, MemoryItem, MetadataDict
from agentkit.memory.embedder import Embedder
@ -94,7 +95,7 @@ class EpisodicMemory(Memory):
)
db.add(entry)
await db.commit()
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to store episodic memory: {e}")
raise
@ -111,7 +112,7 @@ class EpisodicMemory(Memory):
if self._pgvector_enabled:
return await self._retrieve_pgvector(db, query_embedding)
return await self._retrieve_client_side(db, query_embedding)
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to retrieve episodic memory: {e}")
return None
@ -223,7 +224,7 @@ class EpisodicMemory(Memory):
if self._pgvector_enabled and self._embedder:
return await self._search_pgvector(db, query, top_k, filters, search_multiplier)
return await self._search_client_side(db, query, top_k, filters, search_multiplier)
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to search episodic memory: {e}")
return []
@ -405,7 +406,7 @@ class EpisodicMemory(Memory):
await db.execute(stmt)
await db.commit()
return True
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to delete episodic memory: {e}")
return False

View File

@ -147,7 +147,7 @@ class HttpRAGService:
except httpx.RequestError as e:
logger.error(f"RAG search request error: {e}")
return []
except Exception as e:
except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.error(f"RAG search unexpected error: {e}")
return []
@ -237,7 +237,7 @@ class HttpRAGService:
except httpx.RequestError as e:
logger.error(f"RAG enhanced_search request error for KB {kb_id}: {e}")
raise
except Exception as e:
except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.error(f"RAG enhanced_search unexpected error for KB {kb_id}: {e}")
raise
@ -302,7 +302,7 @@ class HttpRAGService:
except httpx.HTTPStatusError as e:
logger.error(f"RAG ingest HTTP error: {e.response.status_code}")
return None
except Exception as e:
except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.error(f"RAG ingest error: {e}")
return None
@ -312,7 +312,7 @@ class HttpRAGService:
try:
resp = await client.get("/bases")
return resp.status_code in (200, 401) # 401 = 服务在但需认证
except Exception:
except (httpx.HTTPError, ValueError, KeyError, TypeError):
return False
async def close(self) -> None:

View File

@ -14,6 +14,8 @@ import re
from datetime import datetime, timezone
from typing import TYPE_CHECKING, TypeAlias
from sqlalchemy.exc import DBAPIError
from agentkit.memory.chunking import Chunk, StructuralChunker, TextChunker
from agentkit.memory.document_loader import Document as LoaderDocument
from agentkit.memory.embedder import Embedder
@ -108,7 +110,7 @@ class LocalRAGService:
await self._store_chunks(doc, chunks)
ingested_ids.append(doc.doc_id)
logger.info(f"Ingested document '{doc.title}' with {len(chunks)} chunks")
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to ingest document '{doc.title}': {e}")
return ingested_ids
@ -130,7 +132,7 @@ class LocalRAGService:
if self._pgvector_enabled:
return await self._query_pgvector(db, query_embedding, top_k)
return await self._query_client_side(db, query_embedding, top_k)
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to query knowledge base: {e}")
return []
@ -151,7 +153,7 @@ class LocalRAGService:
await db.execute(sql, {"doc_id": id})
await db.commit()
return True
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to delete document {id}: {e}")
return False
@ -190,7 +192,7 @@ class LocalRAGService:
)
)
return sources
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to list sources: {e}")
return []
@ -202,7 +204,7 @@ class LocalRAGService:
await db.execute(sql_text(f"SELECT 1 FROM {self._table_name} LIMIT 1"))
return True
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Health check failed: {e}")
return False
@ -268,7 +270,7 @@ class LocalRAGService:
await db.execute(sql, params_list)
await db.commit()
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback()
logger.error(f"Failed to store chunks for document '{doc.title}': {e}")
raise
@ -455,7 +457,7 @@ class InMemoryLocalRAGService:
}
ingested_ids.append(doc.doc_id)
logger.info(f"Ingested document '{doc.title}' with {len(chunks)} chunks")
except Exception as e:
except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to ingest document '{doc.title}': {e}")
return ingested_ids

View File

@ -378,7 +378,7 @@ class MemoryStore:
try:
new_prompt = self.refresh_system_prompt()
self._on_change(new_prompt)
except Exception:
except (OSError, ValueError, RuntimeError, TypeError):
import logging
logging.getLogger(__name__).warning("Memory notify_change failed", exc_info=True)

View File

@ -392,5 +392,5 @@ class RetrieveKnowledgeTool(Tool):
}
)
return {"query": query, "results": results, "call_count": self._call_count}
except Exception as e:
except (ConnectionError, RuntimeError, ValueError, KeyError, TypeError) as e:
return {"error": str(e), "results": []}

View File

@ -13,6 +13,12 @@ import logging
import time
from typing import Any, Protocol, runtime_checkable
# redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义(降级到 fallback
try:
from redis.exceptions import RedisError as _RedisError
except ImportError:
_RedisError = Exception
logger = logging.getLogger(__name__)
@ -157,7 +163,7 @@ class RedisCascadeStateStore:
pipe.expire(key, self._session_ttl)
results = pipe.execute()
return results[0]
except Exception as e:
except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade increment failed: {e}")
self._degrade_to_fallback()
if self._fallback is not None:
@ -171,7 +177,7 @@ class RedisCascadeStateStore:
r = self._get_sync_redis()
val = r.get(f"{self.INTER_PREFIX}{session_id}")
return int(val) if val is not None else 0
except Exception as e:
except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade get failed: {e}")
if self._fallback is not None:
return self._fallback.get_interaction(session_id)
@ -188,7 +194,7 @@ class RedisCascadeStateStore:
pipe.set(key, depth)
pipe.expire(key, self._session_ttl)
pipe.execute()
except Exception as e:
except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade set_depth failed: {e}")
self._degrade_to_fallback()
if self._fallback is not None:
@ -201,7 +207,7 @@ class RedisCascadeStateStore:
r = self._get_sync_redis()
val = r.get(f"{self.DEPTH_PREFIX}{session_id}")
return int(val) if val is not None else 0
except Exception as e:
except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade get_depth failed: {e}")
if self._fallback is not None:
return self._fallback.get_depth(session_id)
@ -217,7 +223,7 @@ class RedisCascadeStateStore:
pipe.delete(f"{self.INTER_PREFIX}{session_id}")
pipe.delete(f"{self.DEPTH_PREFIX}{session_id}")
pipe.execute()
except Exception as e:
except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade reset failed: {e}")
self._degrade_to_fallback()
if self._fallback is not None:

View File

@ -81,7 +81,7 @@ def _build_llm_gateway(config: ServerConfig) -> LLMGateway:
backend=config.usage_store.get("backend", "memory"),
redis_url=config.usage_store.get("redis_url", "redis://localhost:6379"),
)
except Exception as e:
except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError) as e:
logger.warning(f"Failed to initialize usage store: {e}, using in-memory")
gateway = LLMGateway(config=config.llm_config, usage_store=usage_store)
@ -93,7 +93,7 @@ def _build_llm_gateway(config: ServerConfig) -> LLMGateway:
try:
provider = _create_provider(name, pconf)
gateway.register_provider(name, provider)
except Exception as e:
except (ValueError, TypeError, KeyError, RuntimeError, ConnectionError, OSError) as e:
logger.warning(f"Failed to register LLM provider '{name}': {e}")
return gateway
@ -222,7 +222,7 @@ async def lifespan(app: FastAPI):
if agent is not None:
agent._system_prompt = new_prompt
updated += 1
except Exception:
except (AttributeError, TypeError, ValueError):
logger.warning(
f"Failed to update system prompt for agent '{agent_name}'", exc_info=True
)
@ -283,7 +283,9 @@ async def lifespan(app: FastAPI):
agent._tool_registry.register(DocumentTool(service=doc_service))
app.state.document_service = doc_service
logger.info("DocumentTool registered with word/excel/pdf renderers")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — DocumentTool init + registry; diverse failure modes
logger.exception("Failed to register DocumentTool")
# Override system prompt with memory-injected version + available tools
@ -296,7 +298,9 @@ async def lifespan(app: FastAPI):
)
logger.info("GUI mode: created default chat agent with memory + tools")
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — GUI mode agent creation; multi-step init with diverse failures
logger.warning(f"GUI mode: failed to create default agent: {e}")
# Load skills from config and register into SkillRegistry
@ -310,7 +314,7 @@ async def lifespan(app: FastAPI):
for tool in agent._tool_registry.list_tools():
try:
tool_registry.register(tool)
except Exception:
except (ValueError, KeyError, RuntimeError):
pass # Already registered
# Load skills from configured paths
@ -331,11 +335,13 @@ async def lifespan(app: FastAPI):
try:
loader.load_from_file(str(p))
logger.info(f"GUI mode: loaded skill from {p}")
except Exception as se:
except (ValueError, TypeError, KeyError, OSError, RuntimeError) as se:
logger.warning(f"GUI mode: failed to load skill from {p}: {se}")
logger.info(f"GUI mode: {len(skill_registry.list_skills())} skills registered")
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — GUI mode skills loading top-level; multi-step init
logger.warning(f"GUI mode: failed to load skills: {e}")
elif os.environ.get("AGENTKIT_GUI_MODE"):
# Agent already exists (e.g. from config), still ensure memory store is available
@ -369,7 +375,7 @@ async def lifespan(app: FastAPI):
if agent is not None:
agent._system_prompt = new_prompt
updated += 1
except Exception:
except (AttributeError, TypeError, ValueError):
logger.warning(
f"Failed to update system prompt for agent '{agent_name}'",
exc_info=True,
@ -416,7 +422,9 @@ async def lifespan(app: FastAPI):
app.state.expert_template_registry = expert_registry
if total_loaded:
logger.info(f"Total {total_loaded} ExpertTemplates registered for @board mode")
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — ExpertTemplate registry loading; multi-step init
logger.warning(f"Failed to load ExpertTemplates: {e}")
# Ensure app.state.expert_template_registry always exists (empty registry)
from agentkit.experts.registry import ExpertTemplateRegistry
@ -468,7 +476,7 @@ async def lifespan(app: FastAPI):
_row = await _cur.fetchone()
if _row is not None:
default_cal_user_id = str(_row["id"])
except Exception:
except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError):
logger.debug("Could not resolve default user_id for CalendarTool", exc_info=True)
calendar_tool = CalendarTool(
@ -492,7 +500,7 @@ async def lifespan(app: FastAPI):
if default_agent is not None:
try:
default_agent._tool_registry.register(calendar_tool)
except Exception:
except (ValueError, KeyError, RuntimeError, AttributeError):
# ponytail: log at debug — CalendarTool double-registration
# is expected on reload, but silent pass hides real errors.
logger.debug("CalendarTool already registered or registration failed", exc_info=True)
@ -512,12 +520,18 @@ async def lifespan(app: FastAPI):
"CalendarTool attached to default agent (tools=%d)",
len(default_agent._tool_registry.list_tools()),
)
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — CalendarTool attach; multi-step with attribute access
logger.exception("Failed to attach CalendarTool to default agent")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — CalendarTool registration; multi-step init
logger.exception("Failed to register CalendarTool")
logger.info("Calendar subsystem initialized (service + reminder scheduler)")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — calendar subsystem top-level init; must not block app startup
logger.exception("Failed to initialize calendar subsystem — calendar API unavailable")
# Bitable subsystem: init DB, service, internal token (KTD11).
@ -529,7 +543,9 @@ async def lifespan(app: FastAPI):
app.state.bitable_service = BitableService(db=bitable_db)
app.state.bitable_internal_token = os.environ.get("AGENTKIT_BITABLE_INTERNAL_TOKEN")
logger.info("Bitable subsystem initialized")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — bitable subsystem top-level init; includes import + DB setup
logger.exception("Failed to initialize bitable subsystem")
# RAG platform subsystem (P1): wire KBStore, RetrievalEngine, HitProcessor,
@ -552,7 +568,7 @@ async def lifespan(app: FastAPI):
from agentkit.rag_platform.store import ensure_tables
await ensure_tables(rag_database_url)
except Exception:
except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError):
logger.exception("Failed to ensure rag_platform tables")
# KBStore — KB/Document persistence
@ -561,7 +577,9 @@ async def lifespan(app: FastAPI):
app.state.kb_store = KBStore(session_factory=kb_session_factory)
logger.info("KBStore initialized")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — KBStore init includes import + DB setup
logger.exception("Failed to initialize KBStore")
# RetrievalEngine — requires llama_index PGVectorStore
@ -576,9 +594,13 @@ async def lifespan(app: FastAPI):
session_factory=kb_session_factory,
)
logger.info("RetrievalEngine initialized (pgvector ready)")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — RetrievalEngine init includes import + pgvector setup
logger.exception("Failed to initialize RetrievalEngine — vector search unavailable")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — rag_platform top-level init
logger.exception("Failed to initialize rag_platform DB components")
# HitProcessor — LLM-based answer generation from retrieval results
@ -587,7 +609,9 @@ async def lifespan(app: FastAPI):
app.state.hit_processor = HitProcessor(llm_gateway=app.state.llm_gateway)
logger.info("HitProcessor initialized")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — HitProcessor init includes import
logger.exception("Failed to initialize HitProcessor")
# TaskManager — async vectorize/batch-index tasks
@ -598,7 +622,9 @@ async def lifespan(app: FastAPI):
app.state.task_manager = TaskManager()
logger.info("TaskManager initialized (degraded mode: InMemoryTaskStore)")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — TaskManager init includes import
logger.exception("Failed to initialize TaskManager")
# KBSettingsStore — per-KB retrieval/hit-processing defaults (process singleton)
@ -607,7 +633,9 @@ async def lifespan(app: FastAPI):
app.state.kb_settings_store = get_settings_store()
logger.info("KBSettingsStore initialized")
except Exception:
except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — KBSettingsStore init includes import
logger.exception("Failed to initialize KBSettingsStore")
yield
@ -618,7 +646,7 @@ async def lifespan(app: FastAPI):
from agentkit.bitable.db import close_bitable_db
await close_bitable_db()
except Exception:
except (RuntimeError, asyncio.TimeoutError, ConnectionError, OSError):
pass
# Stop MCP servers
if mcp_manager is not None:
@ -660,7 +688,7 @@ async def lifespan(app: FastAPI):
from agentkit.server.routes.channels import close_all_adapters
await close_all_adapters()
except Exception:
except (RuntimeError, asyncio.TimeoutError, ConnectionError, OSError):
logger.debug("close_all_adapters 异常已忽略")
@ -706,7 +734,7 @@ def _on_config_change(app: FastAPI, config: ServerConfig) -> None:
if hasattr(app.state, "agent_pool") and app.state.agent_pool is not None:
app.state.agent_pool._llm_gateway = new_gateway
logger.info(f"LLM Gateway reloaded (config v{current_version})")
except Exception as e:
except (ValueError, TypeError, KeyError, RuntimeError, ConnectionError, OSError) as e:
logger.error(f"Failed to reload LLM Gateway: {e}")
# Reload skills if skill paths changed
@ -730,13 +758,15 @@ def _on_config_change(app: FastAPI, config: ServerConfig) -> None:
elif p.is_file() and p.suffix in (".yaml", ".yml"):
try:
loader.load_from_file(str(p))
except Exception:
except (ValueError, TypeError, KeyError, OSError, RuntimeError):
pass
app.state.skill_registry = new_skill_registry
if hasattr(app.state, "agent_pool") and app.state.agent_pool is not None:
app.state.agent_pool._skill_registry = new_skill_registry
logger.info(f"Skills reloaded (config v{current_version})")
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — skills reload top-level; multi-step init
logger.error(f"Failed to reload skills: {e}")
# Update config version on all agents
@ -856,7 +886,7 @@ def create_app(
from agentkit.server.middleware import _load_client_keys
client_keys = _load_client_keys()
except Exception as e:
except (ValueError, TypeError, KeyError, OSError, RuntimeError) as e:
logger.warning(f"Failed to load client keys for AuthMiddleware: {e}")
app.add_middleware(
@ -983,7 +1013,7 @@ def create_app(
try:
ts_config = {**ts_config, **_json.loads(ts_env)}
except Exception:
except (ValueError, TypeError):
pass
task_store = create_task_store(
backend=ts_config.get("backend", "memory"),
@ -1039,7 +1069,9 @@ def create_app(
db_path=evo_conf.get("db_path", "~/.agentkit/evolution.db"),
database_url=evo_conf.get("database_url"),
)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — evolution store init includes import + DB setup
logger.warning(f"Failed to initialize evolution store: {e}")
app.state.evolution_store = None
else:
@ -1056,7 +1088,9 @@ def create_app(
redis_url=cs_conf.get("redis_url", "redis://localhost:6379"),
session_ttl=cs_conf.get("session_ttl", 86400),
)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — cascade state store init includes import + setup
logger.warning(f"Failed to initialize cascade state store: {e}")
app.state.cascade_state_store = None
else:
@ -1138,7 +1172,7 @@ def create_app(
try:
epi_session_factory = create_episodic_session_factory(database_url)
epi_model = EpisodeModel
except Exception as db_err:
except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError, ImportError) as db_err:
import logging as _log
_log.getLogger(__name__).warning(
@ -1155,7 +1189,9 @@ def create_app(
pgvector_enabled=epi_conf.get("pgvector_enabled", True),
table_name=epi_conf.get("table_name", "episodic_memories"),
)
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — episodic memory init; multi-step with embedder + DB
import logging
logging.getLogger(__name__).warning(
@ -1174,7 +1210,9 @@ def create_app(
retrieve_tool = memory_retriever.create_retrieve_tool()
if retrieve_tool:
app.state.retrieve_knowledge_tool = retrieve_tool
except Exception as e:
except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — memory components top-level init; multi-step with imports + DB
import logging
logging.getLogger(__name__).warning(f"Failed to initialize memory components: {e}")

View File

@ -7,6 +7,12 @@ import logging
import os
from typing import Any, Protocol, runtime_checkable
# redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义
try:
from redis.exceptions import RedisError as _RedisError
except ImportError:
_RedisError = Exception
from agentkit.session.models import Message, Session, SessionStatus
logger = logging.getLogger(__name__)
@ -207,7 +213,7 @@ class RedisSessionStore:
try:
redis = await self._get_redis()
return await redis.ping()
except Exception:
except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError):
return False
@ -283,7 +289,7 @@ class FileSessionStore:
session = Session.from_dict(data["session"])
if agent_name is None or session.agent_name == agent_name:
sessions.append(session)
except Exception:
except (ValueError, KeyError, TypeError, OSError):
continue
sessions.sort(key=lambda s: s.updated_at, reverse=True)
return sessions[:limit]
@ -343,7 +349,7 @@ def create_session_store(
store = RedisSessionStore(redis_url=redis_url, ttl_seconds=ttl_seconds)
logger.info(f"SessionStore backend: redis")
return store
except Exception as exc:
except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError) as exc:
logger.warning(f"Failed to initialise RedisSessionStore ({exc}), falling back to InMemorySessionStore")
store = InMemorySessionStore()

View File

@ -56,7 +56,7 @@ class SkillLoader:
try:
skill = self._load_skill_from_file(yaml_path)
skills.append(skill)
except Exception as e:
except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Skipping invalid YAML file '{yaml_path}': {e}")
# 加载 SKILL.md 文件
@ -66,7 +66,7 @@ class SkillLoader:
try:
skill = self.load_from_skill_md(md_path)
skills.append(skill)
except Exception as e:
except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Skipping invalid SKILL.md file '{md_path}': {e}")
return skills
@ -144,7 +144,7 @@ class SkillLoader:
from importlib.metadata import entry_points as _entry_points
eps = _entry_points().get(group_name, [])
except Exception as e:
except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Failed to discover entry_points for group '{group_name}': {e}")
return skills
@ -192,7 +192,7 @@ class SkillLoader:
logger.info(
f"Loaded skill '{skill.name}' v{skill.version} from entry_point '{ep.name}'"
)
except Exception as e:
except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Failed to load skill from entry_point '{ep.name}': {e}")
return skills
@ -208,6 +208,6 @@ class SkillLoader:
tool = self._tool_registry.get(tool_name)
tools.append(tool)
logger.info(f"Bound tool '{tool_name}' to skill '{config.name}'")
except Exception as e:
except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Failed to bind tool '{tool_name}' to skill '{config.name}': {e}")
return tools