- 移除main.py中所有AgentOps相关代码和依赖 - 删除config.py中的AGENTOPS_API_KEY配置 - 简化批处理系统,移除AgentOps会话管理 - 保持原有功能完整,仅移除追踪相关代码 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
697 lines
24 KiB
Python
697 lines
24 KiB
Python
import hashlib
|
||
import asyncio
|
||
import concurrent.futures
|
||
import json
|
||
from typing import Type, Dict, List, Optional, Union, Any, Set
|
||
|
||
from agno.agent import Agent, RunResponse
|
||
from agno.models.deepseek import DeepSeek
|
||
from agno.models.openai import OpenAIChat, OpenAILike
|
||
from agno.storage.agent.sqlite import SqliteAgentStorage
|
||
|
||
# 尝试导入ollama,如果失败则忽略
|
||
try:
|
||
from agno.models.ollama import Ollama
|
||
OLLAMA_AVAILABLE = True
|
||
except ImportError:
|
||
OLLAMA_AVAILABLE = False
|
||
|
||
from agent_system.base.response_model import BaseResponseModel
|
||
from config import LLM_CONFIG
|
||
|
||
class BaseAgent:
|
||
"""基础代理类,封装了 Phidata Agent。
|
||
|
||
此类为不同的 LLM 模型提供统一接口,支持结构化输出、缓存和同步/异步执行。
|
||
|
||
Attributes:
|
||
structured_outputs: 是否返回结构化模型输出
|
||
response_model: 用于结构化响应的 Pydantic 模型
|
||
cache: 可选的响应缓存机制
|
||
agent: 底层的 Phidata Agent 实例
|
||
num_requests: 用于冗余的并行请求数量
|
||
llm_config: LLM 模型的配置
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
model_type: str,
|
||
description: str = "",
|
||
instructions: List[str] = None,
|
||
response_model: Optional[Type[BaseResponseModel]] = None,
|
||
structured_outputs: bool = True,
|
||
storage: Optional[SqliteAgentStorage] = None,
|
||
use_cache: bool = False,
|
||
markdown: bool = True,
|
||
debug_mode: bool = False,
|
||
num_requests: int = 1,
|
||
llm_config: Dict[str, Any] = None,
|
||
**kwargs
|
||
) -> None:
|
||
"""初始化 BaseAgent。
|
||
|
||
Args:
|
||
model_type: 要使用的模型类型(例如 'DeepSeek', 'OpenAIChat')
|
||
description: 代理目的的描述
|
||
instructions: 给代理的指令列表
|
||
response_model: 用于结构化响应的 Pydantic 模型
|
||
structured_outputs: 是否强制结构化输出
|
||
storage: 用于会话历史的可选存储后端
|
||
use_cache: 是否启用响应缓存
|
||
markdown: 是否启用 Markdown 格式化
|
||
debug_mode: 是否启用调试模式
|
||
num_requests: 用于冗余的并行请求数量
|
||
llm_config: LLM 模型的配置字典
|
||
**kwargs: 传递给代理的额外参数
|
||
"""
|
||
# 初始化实例变量
|
||
self.structured_outputs = structured_outputs
|
||
self.response_model = response_model
|
||
self.cache: Optional['Cache'] = Cache() if use_cache else None
|
||
self.agent: Optional[Agent] = None
|
||
self.num_requests = max(1, num_requests) # 确保至少有 1 个请求
|
||
self.llm_config = llm_config or LLM_CONFIG
|
||
|
||
# 安全处理默认空列表
|
||
if instructions is None:
|
||
instructions = []
|
||
|
||
# 使用提供的配置初始化代理
|
||
self._init_agent(
|
||
model_type=model_type,
|
||
description=description,
|
||
instructions=instructions,
|
||
response_model=response_model,
|
||
storage=storage,
|
||
markdown=markdown,
|
||
debug_mode=debug_mode,
|
||
**kwargs
|
||
)
|
||
|
||
def _init_agent(
|
||
self,
|
||
model_type: str,
|
||
description: str = "",
|
||
instructions: List[str] = None,
|
||
response_model: Optional[Type[BaseResponseModel]] = None,
|
||
storage: Optional[SqliteAgentStorage] = None,
|
||
markdown: bool = True,
|
||
debug_mode: bool = False,
|
||
**kwargs
|
||
) -> None:
|
||
"""初始化底层的 Agent 实例。
|
||
|
||
Args:
|
||
model_type: 要使用的模型类型
|
||
description: 代理目的的描述
|
||
instructions: 给代理的指令列表
|
||
response_model: 用于结构化响应的 Pydantic 模型
|
||
storage: 可选的存储后端
|
||
markdown: 是否启用 Markdown 格式化
|
||
debug_mode: 是否启用调试模式
|
||
**kwargs: 额外参数
|
||
|
||
Raises:
|
||
ValueError: 如果模型初始化失败或请求了不支持的模型
|
||
"""
|
||
if instructions is None:
|
||
instructions = []
|
||
|
||
# 定义可用的模型类
|
||
model_classes = {
|
||
"DeepSeek": DeepSeek,
|
||
"OpenAIChat": OpenAIChat,
|
||
"OpenAILike": OpenAILike,
|
||
}
|
||
|
||
# 如果可用则添加 Ollama
|
||
if OLLAMA_AVAILABLE:
|
||
model_classes["Ollama"] = Ollama
|
||
|
||
# 获取模型配置
|
||
model_config = self._get_model_config(model_type)
|
||
|
||
# 验证模型可用性
|
||
self._validate_model_availability(model_config)
|
||
|
||
# 获取模型类和参数
|
||
model_class = model_classes[model_config["class"]]
|
||
model_kwargs = model_config["params"]
|
||
|
||
# 初始化模型
|
||
model = self._create_model_instance(model_class, model_kwargs)
|
||
|
||
# 创建代理 - 不传入response_model以获取原始JSON字符串
|
||
self.agent = Agent(
|
||
model=model,
|
||
description=description,
|
||
instructions=instructions,
|
||
markdown=markdown,
|
||
debug_mode=debug_mode,
|
||
storage=storage,
|
||
**kwargs
|
||
)
|
||
|
||
def _get_model_config(self, model_type: str) -> Dict[str, Any]:
|
||
"""从 llm_config 中获取模型配置。
|
||
|
||
Args:
|
||
model_type: 请求的模型类型
|
||
|
||
Returns:
|
||
模型配置字典
|
||
"""
|
||
llm_keys = self.llm_config.keys()
|
||
|
||
if model_type not in llm_keys:
|
||
# 使用 aliyun 作为回退,并将 model_type 设置为 id
|
||
model_config = self.llm_config["aliyun"].copy()
|
||
model_config["params"]["id"] = model_type
|
||
return model_config
|
||
else:
|
||
return self.llm_config[model_type]
|
||
|
||
def _validate_model_availability(self, model_config: Dict[str, Any]) -> None:
|
||
"""验证请求的模型是否可用。
|
||
|
||
Args:
|
||
model_config: 模型配置字典
|
||
|
||
Raises:
|
||
ValueError: 如果请求了 Ollama 但不可用
|
||
"""
|
||
if model_config["class"] == "Ollama" and not OLLAMA_AVAILABLE:
|
||
raise ValueError(
|
||
"请求了 Ollama 模型,但 ollama 包不可用。"
|
||
"请安装 ollama 或使用不同的模型。"
|
||
)
|
||
|
||
def _create_model_instance(self, model_class: Type, model_kwargs: Dict[str, Any]) -> Any:
|
||
"""创建指定模型类的实例。
|
||
|
||
Args:
|
||
model_class: 要实例化的模型类
|
||
model_kwargs: 模型初始化的关键字参数
|
||
|
||
Returns:
|
||
初始化后的模型实例
|
||
|
||
Raises:
|
||
ValueError: 如果模型初始化失败
|
||
"""
|
||
try:
|
||
return model_class(**model_kwargs)
|
||
except Exception as e:
|
||
raise ValueError(f"模型 {model_class.__name__} 初始化失败: {e}") from e
|
||
|
||
def run(self, prompt: str, **kwargs) -> Union[str, BaseResponseModel]:
|
||
"""执行同步代理运行,支持缓存和结构化输出。
|
||
|
||
Args:
|
||
prompt: 发送给代理的输入提示
|
||
**kwargs: 传递给代理的额外关键字参数
|
||
|
||
Returns:
|
||
字符串响应或结构化的 BaseResponseModel 实例
|
||
|
||
Raises:
|
||
RuntimeError: 如果所有尝试后都无法获得有效响应
|
||
"""
|
||
# 首先检查缓存
|
||
if self.cache and self.cache._check_cache_hit(prompt, **kwargs):
|
||
return self.cache._get_cache()
|
||
|
||
# 根据输出类型获取结果
|
||
if self.structured_outputs:
|
||
result = self._run_structured(prompt, **kwargs)
|
||
else:
|
||
result = self._run_unstructured(prompt, **kwargs)
|
||
|
||
# 如果启用了缓存,则缓存结果
|
||
self._cache_result(result)
|
||
|
||
return result
|
||
|
||
def _run_structured(self, prompt: str, **kwargs) -> BaseResponseModel:
|
||
"""执行结构化输出运行,带重试逻辑。
|
||
|
||
Args:
|
||
prompt: 输入提示
|
||
**kwargs: 额外参数
|
||
|
||
Returns:
|
||
结构化响应模型实例
|
||
|
||
Raises:
|
||
RuntimeError: 如果无法获得有效的结构化响应
|
||
"""
|
||
max_retries = 5
|
||
|
||
for retry_count in range(max_retries):
|
||
result = self._execute_parallel_structured_requests(prompt, **kwargs)
|
||
|
||
if result is not None:
|
||
return result
|
||
|
||
print(f"解析响应的重试尝试 {retry_count + 1}")
|
||
|
||
raise RuntimeError(
|
||
f"在 {max_retries} 次重试尝试后无法获得有效的结构化响应,"
|
||
f"每次尝试并行 {self.num_requests} 个请求。"
|
||
)
|
||
|
||
def _execute_parallel_structured_requests(self, prompt: str, **kwargs) -> Optional[BaseResponseModel]:
|
||
"""执行多个并行的结构化输出请求。
|
||
|
||
Args:
|
||
prompt: 输入提示
|
||
**kwargs: 额外参数
|
||
|
||
Returns:
|
||
第一个有效的结构化响应,如果全部失败则返回 None
|
||
"""
|
||
with concurrent.futures.ThreadPoolExecutor(max_workers=self.num_requests) as executor:
|
||
# 不传入output_class,让agno返回原始字符串
|
||
futures = [
|
||
executor.submit(self.agent.run, prompt, **kwargs)
|
||
for _ in range(self.num_requests)
|
||
]
|
||
|
||
try:
|
||
for future in concurrent.futures.as_completed(futures):
|
||
result = self._process_structured_response(future)
|
||
if result is not None:
|
||
# 取消剩余的 future 并返回结果
|
||
self._cancel_remaining_futures(futures)
|
||
return result
|
||
finally:
|
||
# 确保清理资源
|
||
executor.shutdown(wait=False, cancel_futures=True)
|
||
|
||
return None
|
||
|
||
def _process_structured_response(self, future: concurrent.futures.Future) -> Optional[BaseResponseModel]:
|
||
"""处理单个结构化响应 future。
|
||
|
||
Args:
|
||
future: 要处理的已完成的 future
|
||
|
||
Returns:
|
||
处理后的结构化响应,如果处理失败则返回 None
|
||
"""
|
||
try:
|
||
response: RunResponse = future.result()
|
||
potential_result = response.content
|
||
|
||
# 强制进行手动JSON解析(绕过agno自动解析)
|
||
if isinstance(potential_result, str):
|
||
return self._parse_json_response(potential_result)
|
||
elif isinstance(potential_result, self.response_model):
|
||
# 如果agno已经解析过,直接返回
|
||
return potential_result
|
||
|
||
except Exception as e:
|
||
print(f"代理运行失败: {e}")
|
||
|
||
return None
|
||
|
||
def _parse_json_response(self, response_str: str) -> Optional[BaseResponseModel]:
|
||
"""将 JSON 字符串响应解析为结构化模型。
|
||
|
||
Args:
|
||
response_str: 可能包含 JSON 的字符串响应
|
||
|
||
Returns:
|
||
解析后的模型实例,如果解析失败则返回 None
|
||
"""
|
||
if not response_str or not response_str.strip():
|
||
print(f"空响应字符串,无法解析JSON")
|
||
return None
|
||
|
||
# 清理响应字符串
|
||
cleaned_str = response_str.strip()
|
||
print(f"调试: 原始响应 = {repr(response_str[:200])}...")
|
||
|
||
# 移除可能的代码块标记
|
||
if cleaned_str.startswith('```json'):
|
||
cleaned_str = cleaned_str[7:]
|
||
if cleaned_str.endswith('```'):
|
||
cleaned_str = cleaned_str[:-3]
|
||
|
||
# 尝试提取JSON内容 - 支持嵌套JSON结构
|
||
import re
|
||
|
||
# 首先尝试查找完整的JSON对象(考虑嵌套结构)
|
||
json_str = self._extract_complete_json(cleaned_str)
|
||
if json_str:
|
||
print(f"调试: 提取的完整JSON = {repr(json_str[:200])}...")
|
||
else:
|
||
json_str = cleaned_str.strip()
|
||
print(f"调试: 未找到JSON结构,使用原始内容 = {repr(json_str[:200])}...")
|
||
|
||
try:
|
||
data_dict = json.loads(json_str)
|
||
print(f"调试: 解析成功的字典 = {data_dict}")
|
||
|
||
if self.response_model:
|
||
try:
|
||
result = self.response_model(**data_dict)
|
||
print(f"调试: 成功创建模型实例 = {result}")
|
||
return result
|
||
except Exception as e:
|
||
print(f"无法从字典创建模型实例: {e}")
|
||
return None
|
||
else:
|
||
return data_dict
|
||
|
||
except json.JSONDecodeError as e:
|
||
print(f"JSON解析失败: {e}")
|
||
print(f"尝试解析的内容: {repr(json_str)}")
|
||
return None
|
||
|
||
def _extract_complete_json(self, text: str) -> str:
|
||
"""
|
||
从文本中提取完整的JSON对象,支持嵌套结构
|
||
|
||
Args:
|
||
text: 包含JSON的文本
|
||
|
||
Returns:
|
||
提取的完整JSON字符串,如果未找到则返回None
|
||
"""
|
||
# 查找第一个左大括号
|
||
start_idx = text.find('{')
|
||
if start_idx == -1:
|
||
return None
|
||
|
||
# 使用计数器匹配完整的JSON对象
|
||
brace_count = 0
|
||
in_string = False
|
||
escape_next = False
|
||
|
||
for i, char in enumerate(text[start_idx:], start_idx):
|
||
if escape_next:
|
||
escape_next = False
|
||
continue
|
||
|
||
if char == '\\' and in_string:
|
||
escape_next = True
|
||
continue
|
||
|
||
if char == '"' and not escape_next:
|
||
in_string = not in_string
|
||
continue
|
||
|
||
if not in_string:
|
||
if char == '{':
|
||
brace_count += 1
|
||
elif char == '}':
|
||
brace_count -= 1
|
||
|
||
# 找到匹配的右大括号
|
||
if brace_count == 0:
|
||
return text[start_idx:i+1]
|
||
|
||
# 如果没有找到匹配的大括号,返回从第一个{到末尾
|
||
return text[start_idx:] if brace_count > 0 else None
|
||
|
||
def _run_unstructured(self, prompt: str, **kwargs) -> str:
|
||
"""执行非结构化输出运行。
|
||
|
||
Args:
|
||
prompt: 输入提示
|
||
**kwargs: 额外参数
|
||
|
||
Returns:
|
||
来自第一个完成请求的字符串响应
|
||
|
||
Raises:
|
||
RuntimeError: 如果第一个完成的请求失败
|
||
"""
|
||
with concurrent.futures.ThreadPoolExecutor(max_workers=self.num_requests) as executor:
|
||
futures = [
|
||
executor.submit(self.agent.run, prompt, **kwargs)
|
||
for _ in range(self.num_requests)
|
||
]
|
||
|
||
try:
|
||
# 等待第一个完成
|
||
done, not_done = concurrent.futures.wait(
|
||
futures, return_when=concurrent.futures.FIRST_COMPLETED
|
||
)
|
||
|
||
# 从第一个完成的 future 获取结果
|
||
first_future = next(iter(done))
|
||
try:
|
||
raw_response: RunResponse = first_future.result()
|
||
return raw_response.content
|
||
except Exception as e:
|
||
raise RuntimeError(f"第一个代理运行失败: {e}") from e
|
||
|
||
finally:
|
||
# 取消剩余的 futures
|
||
self._cancel_remaining_futures(futures)
|
||
executor.shutdown(wait=False, cancel_futures=True)
|
||
|
||
def _cancel_remaining_futures(self, futures: List[concurrent.futures.Future]) -> None:
|
||
"""取消所有尚未完成的 futures。
|
||
|
||
Args:
|
||
futures: 要取消的 futures 列表
|
||
"""
|
||
for future in futures:
|
||
if not future.done():
|
||
future.cancel()
|
||
|
||
def _cache_result(self, result: Union[str, BaseResponseModel]) -> None:
|
||
"""如果启用了缓存,则缓存结果。
|
||
|
||
Args:
|
||
result: 要缓存的结果
|
||
"""
|
||
if self.cache and result is not None:
|
||
self.cache._save_cache(result)
|
||
|
||
|
||
async def async_run(self, prompt: str, **kwargs) -> Union[str, BaseResponseModel]:
|
||
"""执行异步代理运行,支持缓存和结构化输出。
|
||
|
||
Args:
|
||
prompt: 发送给代理的输入提示
|
||
**kwargs: 传递给代理的额外关键字参数
|
||
|
||
Returns:
|
||
字符串响应或结构化的 BaseResponseModel 实例
|
||
|
||
Raises:
|
||
RuntimeError: 如果无法获得有效响应
|
||
"""
|
||
# 检查缓存
|
||
if self.cache and self.cache._check_cache_hit(prompt, **kwargs):
|
||
return self.cache._get_cache()
|
||
|
||
# 根据输出类型获取结果
|
||
if self.structured_outputs:
|
||
result = await self._async_run_structured(prompt, **kwargs)
|
||
else:
|
||
result = await self._async_run_unstructured(prompt, **kwargs)
|
||
|
||
# 缓存结果
|
||
self._cache_result(result)
|
||
|
||
return result
|
||
|
||
async def _async_run_structured(self, prompt: str, **kwargs) -> BaseResponseModel:
|
||
"""异步执行结构化输出运行。
|
||
|
||
Args:
|
||
prompt: 输入提示
|
||
**kwargs: 额外参数
|
||
|
||
Returns:
|
||
结构化响应模型实例
|
||
|
||
Raises:
|
||
RuntimeError: 如果无法获得有效的结构化响应
|
||
"""
|
||
tasks = {
|
||
asyncio.create_task(self.agent.arun(prompt, **kwargs))
|
||
for _ in range(self.num_requests)
|
||
}
|
||
|
||
try:
|
||
# 等待第一个完成的任务
|
||
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
||
|
||
while done:
|
||
task = done.pop()
|
||
try:
|
||
response: RunResponse = await task
|
||
result = self._process_async_structured_response(response)
|
||
|
||
if result is not None:
|
||
return result
|
||
|
||
except Exception as e:
|
||
print(f"代理异步运行失败: {e}")
|
||
|
||
# 从任务集中移除已完成的任务
|
||
tasks.discard(task)
|
||
|
||
# 如果没有更多待处理的任务,跳出循环
|
||
if not pending:
|
||
break
|
||
|
||
# 等待下一个任务完成
|
||
done, _ = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
|
||
|
||
raise RuntimeError(f"在 {self.num_requests} 次并行尝试后无法获得有效的结构化响应")
|
||
|
||
finally:
|
||
await self._cancel_remaining_tasks(tasks)
|
||
|
||
async def _async_run_unstructured(self, prompt: str, **kwargs) -> str:
|
||
"""异步执行非结构化输出运行。
|
||
|
||
Args:
|
||
prompt: 输入提示
|
||
**kwargs: 额外参数
|
||
|
||
Returns:
|
||
字符串响应
|
||
|
||
Raises:
|
||
RuntimeError: 如果第一个完成的任务失败
|
||
"""
|
||
tasks = {
|
||
asyncio.create_task(self.agent.arun(prompt, **kwargs))
|
||
for _ in range(self.num_requests)
|
||
}
|
||
|
||
try:
|
||
# 等待第一个完成的任务
|
||
done, _ = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
||
|
||
first_task = done.pop()
|
||
try:
|
||
raw_response: RunResponse = await first_task
|
||
return raw_response.content
|
||
except Exception as e:
|
||
raise RuntimeError(f"第一个代理异步运行失败: {e}") from e
|
||
|
||
finally:
|
||
await self._cancel_remaining_tasks(tasks)
|
||
|
||
def _process_async_structured_response(self, response: RunResponse) -> Optional[BaseResponseModel]:
|
||
"""处理异步结构化响应。
|
||
|
||
Args:
|
||
response: 代理运行响应
|
||
|
||
Returns:
|
||
处理后的结构化响应或 None
|
||
"""
|
||
potential_result = response.content
|
||
|
||
# 直接模型实例
|
||
if isinstance(potential_result, self.response_model):
|
||
return potential_result
|
||
|
||
# 需要解析的字符串响应
|
||
if isinstance(potential_result, str):
|
||
return self._parse_json_response(potential_result)
|
||
|
||
return None
|
||
|
||
async def _cancel_remaining_tasks(self, tasks: Set[asyncio.Task]) -> None:
|
||
"""取消所有剩余的任务。
|
||
|
||
Args:
|
||
tasks: 要取消的任务集合
|
||
"""
|
||
for task in tasks:
|
||
if not task.done():
|
||
task.cancel()
|
||
|
||
# 等待所有任务完成或被取消
|
||
if tasks:
|
||
await asyncio.gather(*tasks, return_exceptions=True)
|
||
|
||
class Cache:
|
||
"""响应缓存类,用于缓存代理响应以提高性能。
|
||
|
||
使用 MD5 哈希生成缓存键,基于提示和参数内容。
|
||
"""
|
||
|
||
def __init__(self) -> None:
|
||
"""初始化缓存。"""
|
||
self.cache: Dict[str, Union[str, BaseResponseModel]] = {}
|
||
self.current_cache_key: Optional[str] = None
|
||
|
||
def _set_cache_key(self, prompt: str, **kwargs) -> None:
|
||
"""为提示和参数生成唯一的缓存键。
|
||
|
||
Args:
|
||
prompt: 输入提示
|
||
**kwargs: 额外参数
|
||
"""
|
||
# 创建结合提示和相关参数的字符串
|
||
cache_str = prompt + str(sorted(kwargs.items()))
|
||
# 生成字符串的哈希值
|
||
self.current_cache_key = hashlib.md5(cache_str.encode('utf-8')).hexdigest()
|
||
|
||
def _check_cache_hit(self, prompt: str, **kwargs) -> bool:
|
||
"""检查当前缓存键是否存在于缓存中。
|
||
|
||
Args:
|
||
prompt: 输入提示
|
||
**kwargs: 额外参数
|
||
|
||
Returns:
|
||
如果缓存命中则返回 True
|
||
"""
|
||
if self.current_cache_key is None:
|
||
self._set_cache_key(prompt, **kwargs)
|
||
return self.current_cache_key in self.cache
|
||
|
||
def _save_cache(self, result: Union[str, BaseResponseModel]) -> None:
|
||
"""将结果保存到缓存中。
|
||
|
||
Args:
|
||
result: 要缓存的结果
|
||
"""
|
||
if self.current_cache_key is not None:
|
||
self.cache[self.current_cache_key] = result
|
||
self.current_cache_key = None
|
||
|
||
def _get_cache(self) -> Union[str, BaseResponseModel]:
|
||
"""从缓存中获取结果。
|
||
|
||
Returns:
|
||
缓存的结果
|
||
|
||
Raises:
|
||
KeyError: 如果缓存键不存在
|
||
"""
|
||
if self.current_cache_key is None:
|
||
raise ValueError("未设置缓存键")
|
||
|
||
if self.current_cache_key not in self.cache:
|
||
raise KeyError(f"缓存键 '{self.current_cache_key}' 不存在")
|
||
|
||
result = self.cache[self.current_cache_key]
|
||
self.current_cache_key = None
|
||
return result
|
||
|
||
def clear(self) -> None:
|
||
"""清空所有缓存。"""
|
||
self.cache.clear()
|
||
self.current_cache_key = None
|
||
|
||
def size(self) -> int:
|
||
"""获取缓存中的项目数量。
|
||
|
||
Returns:
|
||
缓存项目数量
|
||
"""
|
||
return len(self.cache) |