代码质量:
- 新增 .prettierrc + eslint-plugin-prettier,全量格式化所有 TSX/TS/CSS 文件
- npm scripts 新增 format / format:check / lint:fix
Logo 重设计:
- SVG logo 从简单星月改为望远镜+轨道+三角架+星光,favicon 同步更新
Library 服务端分页与多维筛选:
- LibraryQueryParams 支持 q(全局搜索)/status(下载状态)/doctype/author/year/journal/sort_by
- API 返回 {items, total},默认每页 12 条
- 前端新增筛选面板:搜索栏、状态下拉、文献类型、排序、分页控件
Agent 工具输出可视化:
- 新增 SpecialToolRenderers.tsx:query_target 天体参数卡片(含 SIMBAD/VizieR 链接)、search_papers
文献列表(内嵌下载/阅读/星系跳转按钮)
- 系统内部工具(compress_context 等)无错误时自动隐藏,减少时间线噪音
- ToolCallCard 与 Reader/Citation 打通:可在工具输出中直接打开文献或跳转引用星系
.agent 目录统一:
- memory/tool-results/trajectories/images 全部归入 library/.agent/ 子目录
PDF 加载修复:
- BilingualViewer 改用 fetch → blob:// URL 加载 PDF,绕过 iframe SameSite Cookie 限制
16 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build, Lint & Test Commands
# Build (debug)
cargo build
# Build (release with optimizations)
cargo build --release
# Build (release-min profile: size-optimized LTO)
cargo build --profile release-min
# Run (debug, starts server on http://localhost:8000)
cargo run
# Run with Obscura in-process browser (no external binaries needed)
cargo run --features obscura-inprocess
# Run CLI binary
cargo run --bin astroresearch_cli
# Run health check tool
cargo run --bin health_check # read-only scan
cargo run --bin health_check -- --fix # auto-repair
# Lint
cargo clippy
# Format
cargo fmt
# All tests
cargo test
# Unit tests only
cargo test --lib
# Run a specific test
cargo test test_name
# Frontend (cd dashboard first)
npm run dev # HMR dev server on :5173, proxies /api to :8000
npm run build # TypeScript check + Vite production build → dashboard/dist/
npm run lint # ESLint
npm run lint:fix # ESLint with auto-fix
npm run format # Prettier format all source files
npm run format:check # Check formatting without modifying files
Architecture Overview
Stack: Rust Axum backend (port 8000) + React/Vite/TypeScript frontend (port 5173 in dev). In production, the Rust binary serves the pre-built dashboard/dist/ via ServeDir and ServeFile fallback, so there is a single process.
Source Layer Map
src/
├── main.rs # Axum server entry: logging, DB pool, migrations, vec0 table,
│ # client/service construction, route registration, AppState assembly
├── lib.rs # Config struct + from_env() loading from .env
├── api/ # HTTP handlers, AppState, StandardPaper type
│ ├── mod.rs # AppState (shared state), StandardPaper, handlers re-exports
│ ├── agent.rs # SSE chat_agent endpoint, session CRUD, metrics, audit log
│ ├── papers.rs # Search, download, parse, translate, embed, citations, library, export
│ ├── notes.rs # Highlight/note CRUD
│ ├── sync.rs # Meta-sync and asset-batch endpoints
│ ├── targets.rs # Target query/associate/extract, RAG chat, figure chat
│ └── helpers.rs # Shared DB helpers, format conversion, path validation
├── agent/ # ReAct-based research agent (LLM-driven tool-use loop)
│ ├── tools/ # AgentTool trait, ToolRegistry, 25+ tool implementations per domain file
│ ├── runtime/ # ReAct loop core: session, context, streaming, executor, token_budget,
│ │ # permission, permission_profile, checkpoint, circuit_breaker, error_recovery,
│ │ # hardline, partitioner, file_cache, system_prompt, finalize, denial_tracker
│ ├── compact/ # Context compression (micro/auto/manual layers + collapse)
│ ├── memory/ # Persistent memory manager: extraction, dedup, decay, age, guardrails
│ ├── hooks/ # Lifecycle hooks: registry, dispatch, builtins, matcher, traits (PreToolUse/PostToolUse/Stop/etc.)
│ ├── modes/ # Agent session modes: default, deep-research, literature-reader (identity/tools/config presets)
│ ├── skills.rs # SkillRegistry: hot-loads SKILL.md files from skills/ directory
│ ├── subagent.rs # Context-isolated sub-agent runner (subagent tool)
│ ├── team/ # Multi-agent team: file-based inbox, lead/teammate coordination
│ ├── background.rs# BgNotificationQueue for async slow-task (download/parse) notifications
│ ├── task_board.rs# Persistent task board (agent_tasks table)
│ ├── trajectory.rs# Session trajectory recording for audit/debug
│ └── terminal.rs # Escape sequence filter for ANSI-heavy tool outputs
├── clients/ # External API wrappers
│ ├── llm.rs # LlmClient (OpenAI-compatible chat + streaming), EmbeddingClient
│ ├── ads.rs # NASA ADS API
│ ├── arxiv.rs # arXiv Atom XML API
│ └── qiniu.rs # Qiniu cloud storage
├── services/ # Business logic
│ ├── search.rs # Unified cross-source search (ADS + arXiv dedup)
│ ├── download.rs # PDF/HTML download with anti-bot measures and fallback chain
│ ├── parser/ # HTML/PDF → Markdown parsers (A&A, IOP, ar5iv, generic, PDF via MinerU)
│ ├── translation.rs# LLM bilingual translation with Trie-based astronomy glossary
│ ├── rag.rs # Embedding ingest + vector similarity retrieval + LLM answer generation
│ ├── target.rs # Celestial target extraction (IAU name regex) + CDS Sesame lookup
│ ├── chunker.rs # Markdown text chunking for embedding
│ ├── batch/ # Meta-sync (ADS bulk harvest) and asset-batch processing engines
│ ├── query_parser.rs# Advanced search query syntax parser
│ └── logging.rs # Pretty console + rolling file logger
└── bin/
├── health_check.rs # Library consistency checker and auto-repair
├── cli.rs # CLI interface
└── reparse.rs # Re-parse existing library items
AppState — Central Shared State
All handlers access state via Arc<AppState>. Key fields:
db: SqlitePool— SQLite connection pool (5 max connections, foreign keys enforced)llm: LlmClient/embedding: EmbeddingClient— OpenAI-compatible LLM clientsads: AdsClient/arxiv: ArxivClient— academic search clientsskill_registry: Arc<RwLock<SkillRegistry>>— hot-reloaded agent skillsmemory_manager: Arc<MemoryManager>— persistent agent memory (MEMORY.md + decay)cancelled_runs: Arc<Mutex<HashSet<String>>>— agent cancellation tokensharvest_status/batch_status— async batch operation status tracking
Agent System Design
The agent (src/agent/) implements a ReAct (Thought → Action → Observation) loop:
AgentRuntime(runtime/mod.rs) orchestrates the loop: session create/resume → context build → ReAct loop → finalize- Streaming: LLM response is streamed via SSE (
AgentStreamEvent) — thought, tool_call (withid), tool_result (withtool_call_id), text_delta, usage, error, done. Tool calls execute in parallel. - Tools: Each tool implements
AgentTooltrait (name, description, JSON Schema parameters, execute). Core tools: read_file, grep_files, glob_files, run_bash, file_write, file_edit, search_papers, download_paper, parse_paper, get_paper_content, rag_search, query_target, save_note, todo_write, compress_context, load_skill, subagent, ask_user, save_memory, analyze_image (vision model, only whenLLM_VISION_MODELset). Plus background tools (bg_task_run, bg_task_check) and team tools (spawn_teammate, send_teammate_message, team_broadcast, check_team_inbox). - Thinking mode:
enable_thinkingflag propagates fromAgentChatRequest→AgentConfig→ToolContext→LlmClient::chat_stream. Only enabled for Qwen/DashScope backends; frontend-controlled via thethinkingrequest field. - Tool call ID tracking: LLM may not return tool_call IDs —
LlmClientgenerates UUID fallbacks.ToolCallandToolResultSSE events carry matching IDs for precise frontend pairing. - ToolContext (
tools/mod.rs): Injected into every tool execution — holdsapp_state,session_id,sse_tx(for intermediate events),enable_thinking,read_file_state(file cache for dedup),silent(sub-agents skip permission prompts). - Context compression: Four layers — micro (placeholder replacement), snip (old-message truncation), auto (LLM summarization), aggro_micro (aggressive placeholder). Protected by
CompactionCircuitBreaker. Transcripts persisted inagent_messagestable, not filesystem snapshots. - Skills (
skills.rs): Two-layer loading — system-reminder lists names (~20 tokens each), LLM callsload_skillto inject full SKILL.md content - Sub-agents (
subagent.rs):subagenttool spawns a context-isolated sub-agent with its own ReAct loop. Sub-agent messages (system/user/assistant/tool) are persisted toagent_messageswithagent_nameidentifier. Returns final summary + activity log. SSE progress forwarded to parent via ToolContext. - Memory (
memory/): File-based persistent memory (MEMORY.md).MemoryManagerhandles extraction from conversation, dedup, recency decay, age-based pruning, and guardrails. Tools:save_memory,load_memory(auto-injected in system prompt). - Teams (
team/): File-based inbox directory per session for lead/teammate message passing - Background tasks (
background.rs): Slow ops (download, parse) can run async; results inject viaBgNotificationQueuebefore next LLM call
Environment variables for agent tuning: AGENT_TOKEN_SOFT_LIMIT (default 80000) / AGENT_TOKEN_HARD_LIMIT (default 100000).
Database
SQLite via sqlx::sqlite. Migrations in migrations/ are auto-run on startup (sqlx::migrate!("./migrations")). Key tables: papers, citations_references, notes, agent_sessions, agent_messages, agent_tasks, agent_audit_log, paper_chunks_content. Vector embeddings use sqlite-vec (vec_paper_chunks virtual table, auto-registered before any DB connection).
The embedding dimension is controlled by EMBEDDING_DIM env var (default 1536). On dimension mismatch, the vec table and chunk content are dropped and recreated.
Frontend (dashboard/)
React 19 + TypeScript + Vite + Tailwind CSS 4. Organized by technical layer (Type-Based):
pages/— Page-level panel view components (SearchPanel, LibraryPanel, ReaderPanel, CitationPanel, SyncPanel, ResearchAgentPanel, SettingsPanel)components/— Reusable and layout components (sub-folders: agent, reader, sync, layout, dialogs)hooks/— Global and feature-specific custom stateful React Hooks (e.g., useLibrary, useSearch, useNotes)types/— Global TypeScript type definitions (types/index.ts)utils/— Common utility helper functionsassets/— Static assets and global stylesheet styles
Dependencies: react-markdown + rehype-katex + remark-math for Markdown/LaTeX rendering, framer-motion for animations, lucide-react for icons.
Obscura In-Process Browser
The obscura-inprocess feature compiles obscura-browser and obscura-net directly into the binary, eliminating the need for external browser binaries. This is used for bypassing Cloudflare/WAF on PDF download. Enabled via --features obscura-inprocess.
Astronomy Glossary (dictionary.txt)
A 1MB+ bilingual astronomy terminology file loaded at startup into a Trie tree for longest-match glossary construction, used by the translation service to guide LLM translations with domain-accurate term mappings.
Agent Session Modes (src/agent/modes/)
Session-level modes configure the agent's identity, tool access, step limits, thinking, and permissions at creation time. Modes are pure-data constants defined at compile time — adding a new mode requires no logic changes.
Three built-in modes (registered in ModeRegistry::builtins()):
| Mode | ID | Tools | Max Steps | Thinking | Permission Profile |
|---|---|---|---|---|---|
| 通用科研助手 | default |
All (unrestricted) | 8 (default) | user-controlled | none |
| 深度研究 | deep-research |
All | 16 | forced on | research |
| 文献阅读助手 | literature-reader |
Allowlist (read-only + literature) | 6 | forced off | readonly |
Key types in modes/mod.rs:
AgentMode— static definition struct: id, name, description, icon, identity/principles overrides, extra sections,ToolSet,ModeConfigToolSet—All,Allowlist(&[&str]), orExcept(&[&str])— constrains which tools are availableModeConfig— optional overrides formax_steps,enable_thinking,tool_timeout_secs,permission_profileModeRegistry— holds&'static AgentModereferences;get(id)for lookup
The mode is stored in the agent_sessions.mode column (migration 20260624000000_add_session_mode.sql, defaults to 'default'). The frontend ResearchAgentPanel exposes mode selection.
Mode vs Skill: Modes are session-level ("who am I"), Skills are task-level ("how do I do X"). Modes affect initialization; the ReAct loop itself is mode-agnostic.
Permission System (src/agent/runtime/permission.rs)
Tool execution is gated by a priority-ordered rule chain: Deny > Allow > Ask (first match wins). Rules support content-level pattern matching (e.g., run_bash(rm *)).
Permission modes (set via AGENT_PERMISSION_MODE env or mode's permission_profile):
default— full rule chain evaluationaccept_edits— auto-allowfile_write/file_editwithin the working directorybypass— skip all Ask checks (Deny rules still enforced)dont_ask— convert all Ask to Deny
Permission profiles (src/agent/runtime/permission_profile.rs) are named presets loaded by AGENT_PERMISSION_MODE or a mode's permission_profile field. Profiles research and readonly are used by deep-research and literature-reader modes respectively.
Configuration (in .env):
AGENT_PERMISSIONS_DENY— comma-separated deny rules (e.g.,run_bash(rm *),run_bash(sudo *))AGENT_PERMISSIONS_ALLOW— comma-separated allow rulesAGENT_PERMISSIONS_ASK— comma-separated ask rulesAGENT_PERMISSION_MODE— default/accept_edits/bypass/dont_ask
Multi-Tier LLM Configuration
The system supports three LLM tiers with cascade fallback:
| Tier | Env Prefix | Purpose | Fallback |
|---|---|---|---|
| Primary | LLM_ |
Main agent reasoning | — |
| Medium | LLM_MEDIUM_ |
Translation, RAG | Primary LLM config |
| Fast | LLM_FAST_ |
Memory extraction, background tasks | Medium LLM config |
Each tier has _API_KEY, _API_BASE, _MODEL variants. Unset tiers cascade to the next tier down.
Fallback chain: LLM_FALLBACK_CHAIN (comma-separated model names) provides automatic model rotation on repeated 529 errors. LLM_FALLBACK_MODEL is a single backup model tried before the chain.
Vision Model (analyze_image tool)
When LLM_VISION_MODEL env is set, the analyze_image tool (src/agent/tools/astro/analyze_image.rs) is registered. It delegates image analysis to a dedicated vision model, enabling the main agent to use a text-only model while still processing images. Supports local paths (relative to library dir) and HTTP(S) URLs. Results stream via SSE TextDelta events. Also supports LLM_VISION_API_KEY and LLM_VISION_API_BASE (fall back to primary LLM config).
Auto Memory Extraction
Controlled by env vars:
EXTRACT_MEMORY_ENABLED(defaultfalse) — enables automatic memory extraction at session end and during compactionEXTRACT_MEMORY_THROTTLE_TURNS(default3) — extract every N turnsEXTRACT_MEMORY_MAX_STEPS(default3) — sub-agent max steps for extraction
Code Conventions
- Use
anyhowfor application errors,thiserrorfor library-style typed errors - Environment variables via
dotenvy+std::env::var, with defaults inConfig::from_env() - SQL queries use parameterized bindings (
sqlx::query("...").bind(...)) — never string interpolation - API handlers take
State(Arc<AppState>)and return Axum-compatible responses - Agent tools implement
AgentTooltrait; new tools register inToolRegistry::new() - Front-end build is triggered by
build.rs(auto npm install + build whendashboard/src/changes)