后端核心变更: - 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 (文献信息渲染)
239 lines
15 KiB
Markdown
239 lines
15 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Build, Lint & Test Commands
|
|
|
|
```bash
|
|
# 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
|
|
```
|
|
|
|
## 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 clients
|
|
- `ads: AdsClient` / `arxiv: ArxivClient` — academic search clients
|
|
- `skill_registry: Arc<RwLock<SkillRegistry>>` — hot-reloaded agent skills
|
|
- `memory_manager: Arc<MemoryManager>` — persistent agent memory (MEMORY.md + decay)
|
|
- `cancelled_runs: Arc<Mutex<HashSet<String>>>` — agent cancellation tokens
|
|
- `harvest_status` / `batch_status` — async batch operation status tracking
|
|
|
|
### Agent System Design
|
|
|
|
The agent (`src/agent/`) implements a **ReAct** (Thought → Action → Observation) loop:
|
|
|
|
1. **`AgentRuntime`** (`runtime/mod.rs`) orchestrates the loop: session create/resume → context build → ReAct loop → finalize
|
|
2. **Streaming**: LLM response is streamed via SSE (`AgentStreamEvent`) — thought, tool_call (with `id`), tool_result (with `tool_call_id`), text_delta, usage, error, done. Tool calls execute in parallel.
|
|
3. **Tools**: Each tool implements `AgentTool` trait (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 when `LLM_VISION_MODEL` set). Plus background tools (bg_task_run, bg_task_check) and team tools (spawn_teammate, send_teammate_message, team_broadcast, check_team_inbox).
|
|
4. **Thinking mode**: `enable_thinking` flag propagates from `AgentChatRequest` → `AgentConfig` → `ToolContext` → `LlmClient::chat_stream`. Only enabled for Qwen/DashScope backends; frontend-controlled via the `thinking` request field.
|
|
5. **Tool call ID tracking**: LLM may not return tool_call IDs — `LlmClient` generates UUID fallbacks. `ToolCall` and `ToolResult` SSE events carry matching IDs for precise frontend pairing.
|
|
6. **ToolContext** (`tools/mod.rs`): Injected into every tool execution — holds `app_state`, `session_id`, `sse_tx` (for intermediate events), `enable_thinking`, `read_file_state` (file cache for dedup), `silent` (sub-agents skip permission prompts).
|
|
7. **Context compression**: Four layers — micro (placeholder replacement), snip (old-message truncation), auto (LLM summarization), aggro_micro (aggressive placeholder). Protected by `CompactionCircuitBreaker`. Transcripts persisted in `agent_messages` table, not filesystem snapshots.
|
|
8. **Skills** (`skills.rs`): Two-layer loading — system-reminder lists names (~20 tokens each), LLM calls `load_skill` to inject full SKILL.md content
|
|
9. **Sub-agents** (`subagent.rs`): `subagent` tool spawns a context-isolated sub-agent with its own ReAct loop. Sub-agent messages (system/user/assistant/tool) are persisted to `agent_messages` with `agent_name` identifier. Returns final summary + activity log. SSE progress forwarded to parent via ToolContext.
|
|
10. **Memory** (`memory/`): File-based persistent memory (MEMORY.md). `MemoryManager` handles extraction from conversation, dedup, recency decay, age-based pruning, and guardrails. Tools: `save_memory`, `load_memory` (auto-injected in system prompt).
|
|
11. **Teams** (`team/`): File-based inbox directory per session for lead/teammate message passing
|
|
12. **Background tasks** (`background.rs`): Slow ops (download, parse) can run async; results inject via `BgNotificationQueue` before 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 functions
|
|
- `assets/` — 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`, `ModeConfig`
|
|
- **`ToolSet`** — `All`, `Allowlist(&[&str])`, or `Except(&[&str])` — constrains which tools are available
|
|
- **`ModeConfig`** — optional overrides for `max_steps`, `enable_thinking`, `tool_timeout_secs`, `permission_profile`
|
|
- **`ModeRegistry`** — holds `&'static AgentMode` references; `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 evaluation
|
|
- `accept_edits` — auto-allow `file_write`/`file_edit` within the working directory
|
|
- `bypass` — 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 rules
|
|
- `AGENT_PERMISSIONS_ASK` — comma-separated ask rules
|
|
- `AGENT_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` (default `false`) — enables automatic memory extraction at session end and during compaction
|
|
- `EXTRACT_MEMORY_THROTTLE_TURNS` (default `3`) — extract every N turns
|
|
- `EXTRACT_MEMORY_MAX_STEPS` (default `3`) — sub-agent max steps for extraction
|
|
|
|
## Code Conventions
|
|
|
|
- Use `anyhow` for application errors, `thiserror` for library-style typed errors
|
|
- Environment variables via `dotenvy` + `std::env::var`, with defaults in `Config::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 `AgentTool` trait; new tools register in `ToolRegistry::new()`
|
|
- Front-end build is triggered by `build.rs` (auto npm install + build when `dashboard/src/` changes)
|