AstroResearch/src/agent/autonomous.rs
Asfmq 49784739fa feat: Agent 全栈升级——模块化重构、Hooks/Skills/Memory/SubAgent/Team 子系统、审计与任务持久化
架构重构:
  - 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 完整项目架构文档
2026-06-17 00:14:02 +08:00

138 lines
4.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 循环结束");
}
}