Files
AstroResearch/src/agent/team/inbox.rs
T
fmq 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

111 lines
2.9 KiB
Rust

// src/agent/team/inbox.rs
//
// 团队消息邮箱系统。
// 使用 .team/{session_id}/inbox/{agent_name}.jsonl 作为 append-only 消息文件。
use chrono::Utc;
use serde::{Deserialize, Serialize};
use std::io::Write;
use std::path::{Path, PathBuf};
/// 团队消息类型
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TeamMessageType {
Task,
Result,
Question,
Answer,
Status,
}
impl TeamMessageType {
pub fn as_str(&self) -> &str {
match self {
TeamMessageType::Task => "task",
TeamMessageType::Result => "result",
TeamMessageType::Question => "question",
TeamMessageType::Answer => "answer",
TeamMessageType::Status => "status",
}
}
}
/// 团队消息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TeamMessage {
pub from: String,
pub to: String,
pub content: String,
pub msg_type: TeamMessageType,
pub timestamp: String,
}
impl TeamMessage {
pub fn new(from: &str, to: &str, content: &str, msg_type: TeamMessageType) -> Self {
TeamMessage {
from: from.to_string(),
to: to.to_string(),
content: content.to_string(),
msg_type,
timestamp: Utc::now().to_rfc3339(),
}
}
}
/// 获取团队目录路径
pub fn team_dir(session_id: &str) -> PathBuf {
PathBuf::from(".team").join(session_id)
}
/// 获取指定 agent 的收件箱路径
pub fn inbox_path(team_dir: &Path, agent_name: &str) -> PathBuf {
team_dir.join("inbox").join(format!("{}.jsonl", agent_name))
}
/// 向收件箱追加一条消息
pub fn append_message(team_dir: &Path, agent_name: &str, msg: &TeamMessage) -> std::io::Result<()> {
let inbox = inbox_path(team_dir, agent_name);
if let Some(parent) = inbox.parent() {
std::fs::create_dir_all(parent)?;
}
let line = serde_json::to_string(msg).unwrap_or_default();
let mut file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&inbox)?;
file.write_all(line.as_bytes())?;
file.write_all(b"\n")?;
Ok(())
}
/// 读取并清空收件箱
pub fn drain_inbox(team_dir: &Path, agent_name: &str) -> Vec<TeamMessage> {
let inbox = inbox_path(team_dir, agent_name);
if !inbox.exists() {
return Vec::new();
}
let content = match std::fs::read_to_string(&inbox) {
Ok(c) => c,
Err(_) => return Vec::new(),
};
let messages: Vec<TeamMessage> = content
.lines()
.filter(|l| !l.is_empty())
.filter_map(|l| serde_json::from_str(l).ok())
.collect();
// 清空文件
let _ = std::fs::write(&inbox, "");
messages
}
/// 检查收件箱中是否有未读消息
pub fn has_pending(team_dir: &Path, agent_name: &str) -> bool {
let inbox = inbox_path(team_dir, agent_name);
inbox.exists() && inbox.metadata().map(|m| m.len() > 0).unwrap_or(false)
}