feat: mcp-v1.0.3 新增日期解析工具,增加 mcp 使用文档英文版,增强 github 推送的稳定性

This commit is contained in:
sansan
2025-11-26 19:43:01 +08:00
parent 39738cc821
commit c02e5cb55f
9 changed files with 1062 additions and 85 deletions
+229
View File
@@ -6,6 +6,7 @@
import re
from datetime import datetime, timedelta
from typing import Tuple, Dict, Optional
from .errors import InvalidParameterError
@@ -27,6 +28,55 @@ class DateParser:
"yesterday": 1,
}
# 日期范围表达式(用于 resolve_date_range_expression
RANGE_EXPRESSIONS = {
# 中文表达式
"今天": "today",
"昨天": "yesterday",
"本周": "this_week",
"这周": "this_week",
"当前周": "this_week",
"上周": "last_week",
"本月": "this_month",
"这个月": "this_month",
"当前月": "this_month",
"上月": "last_month",
"上个月": "last_month",
"最近3天": "last_3_days",
"近3天": "last_3_days",
"最近7天": "last_7_days",
"近7天": "last_7_days",
"最近一周": "last_7_days",
"过去一周": "last_7_days",
"最近14天": "last_14_days",
"近14天": "last_14_days",
"最近两周": "last_14_days",
"过去两周": "last_14_days",
"最近30天": "last_30_days",
"近30天": "last_30_days",
"最近一个月": "last_30_days",
"过去一个月": "last_30_days",
# 英文表达式
"today": "today",
"yesterday": "yesterday",
"this week": "this_week",
"current week": "this_week",
"last week": "last_week",
"this month": "this_month",
"current month": "this_month",
"last month": "last_month",
"last 3 days": "last_3_days",
"past 3 days": "last_3_days",
"last 7 days": "last_7_days",
"past 7 days": "last_7_days",
"past week": "last_7_days",
"last 14 days": "last_14_days",
"past 14 days": "last_14_days",
"last 30 days": "last_30_days",
"past 30 days": "last_30_days",
"past month": "last_30_days",
}
# 星期映射
WEEKDAY_CN = {
"": 0, "": 1, "": 2, "": 3,
@@ -276,3 +326,182 @@ class DateParser:
f"日期太久远: {date.strftime('%Y-%m-%d')} ({days_ago}天前)",
suggestion=f"请查询{max_days}天内的数据"
)
@staticmethod
def resolve_date_range_expression(expression: str) -> Dict:
"""
将自然语言日期表达式解析为标准日期范围
这是专门为 MCP 工具设计的方法,用于在服务器端解析日期表达式,
避免 AI 模型自己计算日期导致的不一致问题。
Args:
expression: 自然语言日期表达式,支持:
- 单日: "今天", "昨天", "today", "yesterday"
- 本周/上周: "本周", "上周", "this week", "last week"
- 本月/上月: "本月", "上月", "this month", "last month"
- 最近N天: "最近7天", "最近30天", "last 7 days", "last 30 days"
- 动态N天: "最近5天", "last 10 days"
Returns:
解析结果字典:
{
"success": True,
"expression": "本周",
"normalized": "this_week",
"date_range": {
"start": "2025-11-18",
"end": "2025-11-24"
},
"current_date": "2025-11-26",
"description": "本周(周一到周日)"
}
Raises:
InvalidParameterError: 无法识别的日期表达式
Examples:
>>> DateParser.resolve_date_range_expression("本周")
{"success": True, "date_range": {"start": "2025-11-18", "end": "2025-11-24"}, ...}
>>> DateParser.resolve_date_range_expression("最近7天")
{"success": True, "date_range": {"start": "2025-11-20", "end": "2025-11-26"}, ...}
"""
if not expression or not isinstance(expression, str):
raise InvalidParameterError(
"日期表达式不能为空",
suggestion="请提供有效的日期表达式,如:本周、最近7天、last week"
)
expression_lower = expression.strip().lower()
today = datetime.now()
today_str = today.strftime("%Y-%m-%d")
# 1. 尝试匹配预定义表达式
normalized = DateParser.RANGE_EXPRESSIONS.get(expression_lower)
# 2. 尝试匹配动态 "最近N天" / "last N days" 模式
if not normalized:
# 中文: 最近N天
cn_match = re.match(r'最近(\d+)天', expression_lower)
if cn_match:
days = int(cn_match.group(1))
normalized = f"last_{days}_days"
# 英文: last N days
en_match = re.match(r'(?:last|past)\s+(\d+)\s+days?', expression_lower)
if en_match:
days = int(en_match.group(1))
normalized = f"last_{days}_days"
if not normalized:
# 提供支持的表达式列表
supported_cn = ["今天", "昨天", "本周", "上周", "本月", "上月",
"最近7天", "最近30天", "最近N天"]
supported_en = ["today", "yesterday", "this week", "last week",
"this month", "last month", "last 7 days", "last N days"]
raise InvalidParameterError(
f"无法识别的日期表达式: {expression}",
suggestion=f"支持的表达式:\n中文: {', '.join(supported_cn)}\n英文: {', '.join(supported_en)}"
)
# 3. 根据 normalized 类型计算日期范围
start_date, end_date, description = DateParser._calculate_date_range(
normalized, today
)
return {
"success": True,
"expression": expression,
"normalized": normalized,
"date_range": {
"start": start_date.strftime("%Y-%m-%d"),
"end": end_date.strftime("%Y-%m-%d")
},
"current_date": today_str,
"description": description
}
@staticmethod
def _calculate_date_range(
normalized: str,
today: datetime
) -> Tuple[datetime, datetime, str]:
"""
根据标准化的日期类型计算实际日期范围
Args:
normalized: 标准化的日期类型
today: 当前日期
Returns:
(start_date, end_date, description) 元组
"""
# 单日类型
if normalized == "today":
return today, today, "今天"
if normalized == "yesterday":
yesterday = today - timedelta(days=1)
return yesterday, yesterday, "昨天"
# 本周(周一到周日)
if normalized == "this_week":
# 计算本周一
weekday = today.weekday() # 0=周一, 6=周日
start = today - timedelta(days=weekday)
end = start + timedelta(days=6)
# 如果本周还没结束,end 不能超过今天
if end > today:
end = today
return start, end, f"本周(周一到周日,{start.strftime('%m-%d')}{end.strftime('%m-%d')}"
# 上周(上周一到上周日)
if normalized == "last_week":
weekday = today.weekday()
# 本周一
this_monday = today - timedelta(days=weekday)
# 上周一
start = this_monday - timedelta(days=7)
end = start + timedelta(days=6)
return start, end, f"上周({start.strftime('%m-%d')}{end.strftime('%m-%d')}"
# 本月(本月1日到今天)
if normalized == "this_month":
start = today.replace(day=1)
return start, today, f"本月({start.strftime('%m-%d')}{today.strftime('%m-%d')}"
# 上月(上月1日到上月最后一天)
if normalized == "last_month":
# 上月最后一天 = 本月1日 - 1天
first_of_this_month = today.replace(day=1)
end = first_of_this_month - timedelta(days=1)
start = end.replace(day=1)
return start, end, f"上月({start.strftime('%Y-%m-%d')}{end.strftime('%Y-%m-%d')}"
# 最近N天 (last_N_days 格式)
match = re.match(r'last_(\d+)_days', normalized)
if match:
days = int(match.group(1))
start = today - timedelta(days=days - 1) # 包含今天,所以是 days-1
return start, today, f"最近{days}天({start.strftime('%m-%d')}{today.strftime('%m-%d')}"
# 兜底:返回今天
return today, today, "今天(默认)"
@staticmethod
def get_supported_expressions() -> Dict[str, list]:
"""
获取支持的日期表达式列表
Returns:
分类的表达式列表
"""
return {
"单日": ["今天", "昨天", "today", "yesterday"],
"": ["本周", "上周", "this week", "last week"],
"": ["本月", "上月", "this month", "last month"],
"最近N天": ["最近3天", "最近7天", "最近14天", "最近30天",
"last 3 days", "last 7 days", "last 14 days", "last 30 days"],
"动态天数": ["最近N天", "last N days"]
}