refactor: 全栈架构重构与质量硬化——API 错误统一、工具域重组、安全加固、前端组件化

后端核心变更:
  - API 层: 新增 AppError 枚举统一错误类型,替代散落的 (StatusCode, String)
  - Agent 工具域: 重组为 astro/system/ 和 astro/research/ 两级域,新增 ProcessPaperTool 流水线工具
  - 安全: 新增 SSRF 双层防护 (同步字符串级 + 异步 DNS 解析级),覆盖 IPv4/IPv6 私网段
  - 弱密码检测: 扩展弱密码列表并增加最小长度检查
  - LLM 客户端: 新增 ChatCompleter/Embedder trait,支持依赖注入与批量向量化 embed_batch
  - 批量处理: AssetBatch 从串行改为 Semaphore 并发池 (BATCH_CONCURRENCY=3)
  - 分块器: 重写为三阶段结构化管线 (章节解析→短节合并→带标题路径子块)
  - RAG: embedding 计算移出事务,RetrievalResult 新增 headings/section_index 字段
  - 检索: ADS/arXiv 并行检索 (tokio::join!),去重改用 HashSet,本地库回填批量 IN 查询
  - 天体查询: Sesame API 升级到 v4,新增视差误差/自行/视向速度/多波段测光字段
  - 迁移: 14 个增量文件合并为单一 init.sql,支持 sqlx::migrate! 内存库集成测试
  - 测试: circuit_breaker/hooks/task_board/session/memory/streaming_executor 新增修正 15+ 测试

  前端架构重构:
  - 目录重组: features/ → pages/ + components/ + hooks/ 三层分离
  - App.tsx 从 1181 行压缩至 ~174 行 (逻辑抽入 9 个自定义 Hook)
  - Agent 面板拆分为 AgentSessionSidebar/AgentMessageList/AgentInputArea 子组件
  - 新增 GlobalDialog/PaperDetailModal/UncachedPaperModal 通用对话框组件
  - 工具函数抽取: celestial.ts (天体坐标格式), paper.tsx (文献信息渲染)
This commit is contained in:
fmq
2026-06-25 23:45:37 +08:00
parent b11b8ad015
commit 5db4cc5998
131 changed files with 13934 additions and 7887 deletions
@@ -0,0 +1,73 @@
// src/agent/tools/astro/research/metadata.rs
// GetPaperMetadataTool — 查询文献完整元数据
use async_trait::async_trait;
use serde_json::json;
use tracing::info;
use crate::agent::tools::{AgentTool, ToolContext, ToolOutput};
pub struct GetPaperMetadataTool;
#[async_trait]
impl AgentTool for GetPaperMetadataTool {
fn name(&self) -> &str {
"get_paper_metadata"
}
fn description(&self) -> &str {
"获取指定文献的完整元数据信息(包括完整标题、所有作者、出版期刊、关键字、引用数、完整摘要等)。\
适用于:需要查看某篇文献的详细信息、阅读完整摘要以评估文献相关性。"
}
fn parameters(&self) -> serde_json::Value {
json!({
"type": "object",
"properties": {
"bibcode": {
"type": "string",
"description": "文献的唯一标识符,支持 ADS Bibcode(如 '2024ApJ...960..123A')、DOI 或 arXiv ID"
}
},
"required": ["bibcode"]
})
}
fn group(&self) -> &str {
"as:research"
}
fn is_concurrency_safe(&self, _args: &serde_json::Value) -> bool {
true
}
fn is_readonly(&self) -> bool {
true
}
async fn execute(&self, args: serde_json::Value, ctx: &ToolContext) -> ToolOutput {
let bibcode = match args.get("bibcode").and_then(|b| b.as_str()) {
Some(b) => b.to_string(),
None => return ToolOutput::error("缺少必需参数 'bibcode'"),
};
info!("[GetPaperMetadata] 获取文献元数据: {}", bibcode);
let state = &ctx.app_state;
match crate::api::helpers::get_paper_from_db(&state.db, &state.config.library_dir, &bibcode)
.await
{
Ok(paper) => {
let content = format!(
"文献元数据 [{}]\n 标题: {}\n 作者: {}\n 年份: {}\n 期刊: {}\n 关键字: {}\n DOI: {}\n arXiv ID: {}\n 引用数: {}\n 参考文献数: {}\n 文献类型: {}\n 已下载: {}\n 已解析为 Markdown: {}\n 摘要:\n{}",
paper.bibcode, paper.title, paper.authors.join(", "), paper.year,
paper.pub_journal, paper.keywords.join(", "), paper.doi, paper.arxiv_id,
paper.citation_count, paper.reference_count, paper.doctype,
paper.is_downloaded, paper.has_markdown, paper.abstract_text
);
ToolOutput::success(content, json!(paper))
}
Err(e) => ToolOutput::error(format!("获取文献 {} 元数据失败: {}", bibcode, e)),
}
}
}