架构重构: - Agent Runtime 由单文件拆为 runtime/ 目录 12 模块(熔断/流式执行/Token预算/文件缓存/权限等) - Agent Tools 由单文件拆为 tools/ 目录 20+ 模块(filesystem/astro/memory/skill/subagent/team 等) - 解析器体系重构(common.rs 836行变更),各解析器同步升级 - Download 服务重构(562行),反爬策略强化 - LLM 客户端重构(266行),流式调用优化 新子系统: - Hooks 生命周期系统(9种事件类型,PreToolUse/PostToolUse 支持输入输出拦截) - Skills 双层加载系统(system-reminder 轻量注入 + LoadSkillTool 按需加载,notify 文件监听热更新) - Memory 项目记忆管理(类型/提取/去重/衰减/保活/选择策略/护栏 7 模块) - SubAgent 上下文隔离子代理运行器(独立 ReAct 循环 + Hook 管道) - Team 多智能体团队协作(文件 inbox 通信、lead/teammate 协调) - TaskBoard DAG 任务依赖管理 - Trajectory 会话轨迹、Terminal 终止信号、Autonomous 自主模式、Background 异步通知 数据库: - agent_tasks 表(DAG 依赖模式,blocked_by JSON 数组) - agent_audit_log 表(工具调用审计:名称/状态/耗时/输出预览) - agent_identity 迁移(消息/审计/任务的 agent_name 归属,agent_team_members 团队注册表) API: - GET /chat/metrics 聚合指标端点 - GET /chat/sessions/:id/audit 会话审计查询 - GET /chat/questions + POST /chat/answer 人机交互问答 工程: - 新增依赖:serde_yaml、notify、glob、walkdir、lru - Skills 目录含 methodology/plotting/presentation 三个初始 SKILL.md - CLAUDE.md 完整项目架构文档
138 lines
4.6 KiB
Rust
138 lines
4.6 KiB
Rust
// src/agent/autonomous.rs
|
||
//
|
||
// 自治研究循环 — 空闲时自动轮询新任务。
|
||
// 参考 learn-claude-code s17 Autonomous Agents。
|
||
//
|
||
// 当 Agent 完成当前回合后,进入 IDLE 阶段:
|
||
// 1. 检查是否有未认领的团队任务
|
||
// 2. 检查批量同步任务队列
|
||
// 3. 检查订阅分类的新论文
|
||
// 发现工作后自动认领并执行。
|
||
|
||
use std::sync::Arc;
|
||
use std::time::Duration;
|
||
use tokio::sync::Notify;
|
||
use tracing::{info, warn};
|
||
|
||
use crate::api::AppState;
|
||
|
||
/// 自治研究配置
|
||
#[derive(Debug, Clone)]
|
||
pub struct AutoResearchConfig {
|
||
/// 是否启用自治模式
|
||
pub enabled: bool,
|
||
/// 订阅的 arXiv 分类
|
||
pub subscribed_categories: Vec<String>,
|
||
/// 最大自治轮次(防止无限循环)
|
||
pub max_autonomous_turns: usize,
|
||
/// IDLE 超时(分钟)
|
||
pub idle_timeout_minutes: u64,
|
||
}
|
||
|
||
impl Default for AutoResearchConfig {
|
||
fn default() -> Self {
|
||
AutoResearchConfig {
|
||
enabled: false,
|
||
subscribed_categories: vec!["astro-ph".to_string()],
|
||
max_autonomous_turns: 5,
|
||
idle_timeout_minutes: 60,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// IDLE 轮询器 — 在 Agent 空闲时检查是否有待处理工作。
|
||
pub struct IdlePoller {
|
||
app_state: Arc<AppState>,
|
||
config: AutoResearchConfig,
|
||
poll_interval: Duration,
|
||
/// 唤醒通知(当外部事件触发时,如新论文到达、任务分配)
|
||
wake_notify: Arc<Notify>,
|
||
}
|
||
|
||
impl IdlePoller {
|
||
pub fn new(app_state: Arc<AppState>, config: AutoResearchConfig) -> Self {
|
||
IdlePoller {
|
||
app_state,
|
||
config,
|
||
poll_interval: Duration::from_secs(30),
|
||
wake_notify: Arc::new(Notify::new()),
|
||
}
|
||
}
|
||
|
||
/// 获取唤醒通知器的 clone(供外部触发)
|
||
pub fn wake_sender(&self) -> Arc<Notify> {
|
||
self.wake_notify.clone()
|
||
}
|
||
|
||
/// 启动 IDLE 循环(应在独立的 tokio::spawn 中运行)
|
||
pub async fn start(self) {
|
||
info!(
|
||
"[IdlePoller] 启动自治轮询 (间隔={:?}, 最大轮次={})",
|
||
self.poll_interval, self.config.max_autonomous_turns
|
||
);
|
||
|
||
let mut autonomous_turns: usize = 0;
|
||
|
||
loop {
|
||
// 等待 poll_interval 或被 notify 唤醒
|
||
tokio::select! {
|
||
_ = tokio::time::sleep(self.poll_interval) => {}
|
||
_ = self.wake_notify.notified() => {
|
||
info!("[IdlePoller] 被外部事件唤醒");
|
||
}
|
||
}
|
||
|
||
if !self.config.enabled {
|
||
continue;
|
||
}
|
||
|
||
if autonomous_turns >= self.config.max_autonomous_turns {
|
||
info!(
|
||
"[IdlePoller] 已达到最大自治轮次 ({}),停止轮询",
|
||
self.config.max_autonomous_turns
|
||
);
|
||
break;
|
||
}
|
||
|
||
// 1. 检查未认领的团队任务
|
||
let task_board = crate::agent::task_board::TaskBoard::new(self.app_state.db.clone());
|
||
match task_board.list_available_tasks(5).await {
|
||
Ok(tasks) if !tasks.is_empty() => {
|
||
for task in tasks {
|
||
if task.can_start {
|
||
info!(
|
||
"[IdlePoller] 发现可认领任务: {} (session={})",
|
||
task.task_id, task.session_id
|
||
);
|
||
if let Ok(true) = task_board
|
||
.claim_task(&task.session_id, &task.task_id, "auto")
|
||
.await
|
||
{
|
||
autonomous_turns += 1;
|
||
// 创建 AgentRuntime 并执行任务
|
||
let runtime = crate::agent::runtime::AgentRuntime::new(
|
||
self.app_state.clone(),
|
||
);
|
||
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
|
||
let _ = runtime
|
||
.run_turn(Some(task.session_id.clone()), &task.content, tx)
|
||
.await;
|
||
}
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
Ok(_) => {}
|
||
Err(e) => {
|
||
warn!("[IdlePoller] 任务查询失败: {}", e);
|
||
}
|
||
}
|
||
|
||
// 2. 检查批量同步状态 (placeholder)
|
||
// 未来: 检查订阅分类的新论文并自动触发同步
|
||
}
|
||
|
||
info!("[IdlePoller] IDLE 循环结束");
|
||
}
|
||
}
|