feat: 高德地图工具 + 多轮 bug 修复

新增:
- 高德地图 6 个内置工具: amap_poi_search, amap_geocode, amap_reverse_geocode, amap_route_plan, amap_travel_plan, amap_map_link
- CLI 子命令: ias tool amap (poi-search/geocode/route-plan/travel-plan/map-link)
- 工具优先级指南: build_capability_guide() 含 amap > weather > web_search 优先级
- DEFAULT_SYSTEM_PROMPT 内置工具优先级说明(不再依赖环境变量)

Bug 修复:
1. (HIGH) call_capability 参数契约修复 — 新增 unpack_call_params() 统一解包 prompt 字段
2. (MEDIUM) 天气 hours=0 不再调用逐时 API
3. (MEDIUM) 长期记忆 daemon 双写缓存+DB,无 DB 不丢失
4. (HIGH) 审批流程状态语义修复 — handle_reply 返回 ApprovalDecision,拒绝/过期不再当作通过
5. (HIGH) 审批有效期基于 expires_at 时间戳而非 receiver drop
6. (MEDIUM) 摘要保存改为 current_user_id 与读取一致
7. (MEDIUM) worker 审批通过后删除孤立 tool result,仅保留 approved_tool 状态放行

架构:
- daemon + worker IPC 架构 (Unix Domain Socket)
- build_capability_guide 共享函数消除 main.rs/worker.rs 重复
This commit is contained in:
2026-06-03 17:01:04 +08:00
parent e379e498b5
commit b2fe054eaf
23 changed files with 4367 additions and 263 deletions
+246 -28
View File
@@ -2,8 +2,14 @@ use clap::{Parser, Subcommand};
/// iAs — 微信 AI 智能助手
///
/// 支持扫码登录、长轮询收发消息、AI 自动回复(DeepSeek/LM Studio)、
/// 工具调用(天气/搜索/邮件/Shell/定时任务等)、Token 用量统计。
/// 支持扫码登录、长轮询收发消息、AI 自动回复(DeepSeek)、
/// 内置工具(天气/搜索/备忘录/日期时间等)、Token 用量统计。
///
/// 快速开始:
/// ias login 扫码登录微信
/// ias listen --llm 启动 AI 自动回复
/// ias tool weather 北京 查询天气
/// ias tool search "最新新闻" 联网搜索
#[derive(Parser, Debug)]
#[command(name = "ias", version, about, long_about = None)]
pub struct Cli {
@@ -15,8 +21,12 @@ pub struct Cli {
pub enum Commands {
/// 扫码登录微信
///
/// 在终端显示二维码,扫描后完成登录。
/// 在终端显示登录二维码,扫描后完成登录。
/// 认证信息存入 PostgreSQL 或本地文件。
///
/// 示例:
/// ias login
/// ias login --timeout 600
Login {
/// 登录超时秒数(默认 480s)
#[arg(long, default_value = "480")]
@@ -26,62 +36,55 @@ pub enum Commands {
/// 监听微信消息(长轮询)
///
/// 启动微信消息监听,接收并处理消息。
/// 使用 --llm 启用 AI 自动回复,--echo 原样回显。
///
/// 示例:
/// ias listen # 仅监听,不做回复
/// ias listen --llm # AI 自动回复
/// ias listen --echo # 原样回显
Listen {
/// 启用 AI 自动回复DeepSeek/LM Studio
#[arg(long, help = "启用 AI 自动回复")]
/// 启用 AI 自动回复
#[arg(long)]
llm: bool,
/// 仅回显消息(不调用 AI
#[arg(long, help = "原样回显收到的消息")]
/// 原样回显收到的消息
#[arg(long)]
echo: bool,
},
/// 发送文本消息
///
/// 向指定微信用户发送文本。
///
/// 示例:
/// ias send --to <user_id> --text "你好"
/// ias send --to wxid_xxx --text "你好"
Send {
/// 目标用户 ID
#[arg(long, help = "微信用户 ID")]
#[arg(long)]
to: String,
/// 消息内容
#[arg(long, help = "文本消息内容")]
#[arg(long)]
text: String,
/// 上下文令牌
#[arg(long, help = "微信上下文令牌")]
#[arg(long)]
context_token: Option<String>,
},
/// 查看当前登录状态
///
/// 显示已登录的账号、Token 前缀、API 地址及存储方式。
Whoami,
/// 查看 LLM Token 使用统计
///
/// 查询 LLM 调用的 Token 消耗、缓存命中率
/// 默认查询最近 7 天,支持时间范围和模型过滤。
/// 默认查询最近 7 天
///
/// 示例:
/// ias usage
/// ias usage --since 2026-06-01T00:00:00Z
/// ias usage --model deepseek-v4-flash
/// ias usage --since 2026-06-01T00:00:00Z --model deepseek-v4-flash
Usage {
/// 起始时间(RFC3339 格式,默认 7 天前
#[arg(long, help = "起始时间(如 2026-06-01T00:00:00Z")]
/// 起始时间(RFC3339 格式)
#[arg(long)]
since: Option<String>,
/// 结束时间(RFC3339 格式,默认现在
/// 结束时间(RFC3339 格式)
#[arg(long)]
until: Option<String>,
@@ -90,13 +93,228 @@ pub enum Commands {
model: Option<String>,
},
/// 后台服务模式
/// 后台服务模式(等同 listen --llm + 文件日志)
///
/// 等同 listen --llm,同时日志写入 ~/.ias/logs/ 滚动文件
/// 配合 systemd 实现开机自启和进程守护。
/// 配合 systemd 实现开机自启
///
/// 示例:
/// ias service # 前台运行
/// sudo systemctl enable ias --now # systemd 管理
/// ias service
Service,
/// 守护进程模式(daemon + worker 分离架构)
///
/// Daemon 持有 WeChat 长连接和数据库,每条消息 spawn 独立 worker。
/// Worker 代码更新后编译即可,下一条消息自动用新版本。
///
/// 示例:
/// ias daemon
/// ias daemon --sock /tmp/ias.sock
Daemon {
/// Unix Domain Socket 路径
#[arg(long, default_value = "/tmp/ias_daemon.sock")]
sock: String,
},
/// Worker 进程(由 daemon 自动 spawn,也可手动测试)
///
/// 示例:
/// ias worker --sock /tmp/ias_daemon.sock
Worker {
/// Unix Domain Socket 路径
#[arg(long, default_value = "/tmp/ias_daemon.sock")]
sock: String,
},
/// 调用内置工具(无需登录,独立运行)
///
/// 可直接在终端调用天气、搜索、备忘录、日期时间、高德地图等工具。
#[command(
subcommand,
after_help = "示例:\n ias tool datetime\n ias tool weather 北京\n ias tool weather 上海 --days 7\n ias tool search \"Rust 2024 edition\"\n ias tool fetch https://example.com/article\n ias tool memos list\n ias tool memos add \"明天下午2点开会\"\n ias tool memos delete 3\n ias tool amap poi-search --keywords 肯德基 --city 北京\n ias tool amap geocode --address 西直门\n ias tool amap route-plan --type walking --origin 116.397,39.909 --destination 116.427,39.903\n ias tool amap travel-plan --city 北京 --interests 景点,美食 --route-type walking"
)]
Tool(ToolCommand),
}
// ─── 工具子命令 ───
#[derive(Subcommand, Debug, Clone)]
pub enum ToolCommand {
/// 获取当前日期时间(北京时间 UTC+8)
Datetime,
/// 管理备忘录
#[command(subcommand)]
Memos(MemosAction),
/// 查询指定城市的天气
///
/// 示例:
/// ias tool weather 北京
/// ias tool weather 上海 --days 7
Weather {
/// 城市名称
location: String,
/// 查询天数(1-7,默认 3
#[arg(long, default_value = "3")]
days: u32,
},
/// 联网搜索最新信息(需配置 TAVILY_API_KEY
///
/// 示例:
/// ias tool search "最新 AI 新闻"
/// ias tool search "Rust async" --max-results 10 --no-answer
/// ias tool search "比特币" --topic finance --time-range week
Search {
/// 搜索关键词
query: String,
/// 最大结果数(默认 5
#[arg(long, default_value = "5")]
max_results: u32,
/// 不包含 AI 生成的摘要
#[arg(long)]
no_answer: bool,
/// 搜索主题: general / news / finance
#[arg(long)]
topic: Option<String>,
/// 发布时间过滤: day / week / month / year
#[arg(long)]
time_range: Option<String>,
},
/// 抓取网页并提取正文内容
Fetch {
/// 网页 URL
url: String,
},
/// 高德地图工具(POI搜索、地理编码、路径规划、旅游规划)
#[command(subcommand)]
Amap(AmapAction),
}
#[derive(Subcommand, Debug, Clone)]
pub enum AmapAction {
/// POI(地点)搜索
PoiSearch {
/// 搜索关键词
#[arg(long)]
keywords: String,
/// 城市名称
#[arg(long)]
city: Option<String>,
/// 中心点坐标(经度,纬度),用于周边搜索
#[arg(long)]
location: Option<String>,
/// 搜索半径(米),默认 1000
#[arg(long)]
radius: Option<u32>,
/// 页码,默认 1
#[arg(long, default_value = "1")]
page: u32,
/// 每页数量,默认 10
#[arg(long, default_value = "10")]
offset: u32,
},
/// 地理编码:地址 → 坐标
Geocode {
/// 地址名称
#[arg(long)]
address: String,
/// 城市名称(可选)
#[arg(long)]
city: Option<String>,
},
/// 逆地理编码:坐标 → 地址
ReverseGeocode {
/// 坐标(经度,纬度)
#[arg(long)]
location: String,
},
/// 路径规划
RoutePlan {
/// 出行方式:walking/driving/riding/transit
#[arg(long)]
r#type: String,
/// 起点坐标(经度,纬度)
#[arg(long)]
origin: String,
/// 终点坐标(经度,纬度)
#[arg(long)]
destination: String,
/// 城市名称(公交方式必填)
#[arg(long)]
city: Option<String>,
/// 途经点(驾车可选,多个用;分隔)
#[arg(long)]
waypoints: Option<String>,
/// 策略(驾车默认10躲避拥堵,公交默认0最快捷)
#[arg(long)]
strategy: Option<u32>,
},
/// 智能旅游规划
TravelPlan {
/// 城市名称
#[arg(long)]
city: String,
/// 兴趣点关键词(逗号分隔),如 景点,美食,酒店
#[arg(long, default_value = "景点,美食")]
interests: String,
/// 路线类型:walking/driving/riding/transit
#[arg(long, default_value = "walking")]
route_type: String,
},
/// 生成地图可视化链接
MapLink {
/// 地图数据 JSON 数组
#[arg(long)]
data: String,
},
}
#[derive(Subcommand, Debug, Clone)]
pub enum MemosAction {
/// 列出所有备忘录
List,
/// 添加备忘录
///
/// 示例:
/// ias tool memos add "明天下午2点开会"
Add {
/// 备忘录内容
content: String,
},
/// 删除指定 ID 的备忘录
///
/// 示例:
/// ias tool memos delete 3
Delete {
/// 备忘录 ID
id: i64,
},
}