{"repo": "aizhen1412/smartnas", "files": {"scripts/start_smartnas.sh": {"1a2801a5679cd2216c377dc18978fedd78c386cf": {"t": 1785745958.2752016, "n": 2179, "h": "1c4d425d4c7e0f50", "c": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n\nexport SMARTNAS_CONFIG=\"${SMARTNAS_CONFIG:-$ROOT_DIR/config/config.json}\"\nmapfile -t CONFIG_VALUES < <(\n python3 -c 'import json,os; c=json.load(open(os.environ[\"SMARTNAS_CONFIG\"])); [print(c[k]) for k in (\"core_port\",\"agent_port\",\"deepseek_api_base\",\"deepseek_model\",\"data_dir\",\"database_path\",\"deepseek_api_key\")]'\n)\nCORE_PORT=\"${CONFIG_VALUES[0]}\"\nAGENT_PORT=\"${CONFIG_VALUES[1]}\"\nDEEPSEEK_API=\"${CONFIG_VALUES[2]}\"\nDEEPSEEK_MODEL=\"${CONFIG_VALUES[3]}\"\nDATA_DIR=\"${CONFIG_VALUES[4]}\"\nDATABASE_PATH=\"${CONFIG_VALUES[5]}\"\nCONFIG_DEEPSEEK_KEY=\"${CONFIG_VALUES[6]}\"\n[[ \"$DATA_DIR\" = /* ]] || DATA_DIR=\"$ROOT_DIR/$DATA_DIR\"\n[[ \"$DATABASE_PATH\" = /* ]] || DATABASE_PATH=\"$ROOT_DIR/$DATABASE_PATH\"\n\nmkdir -p \"$DATA_DIR\" \"$(dirname \"$DATABASE_PATH\")\"\nif [[ ! -w \"$DATA_DIR\" || ! -w \"$(dirname \"$DATABASE_PATH\")\" ]]; then\n echo \"SmartNAS data directories are not writable: $DATA_DIR or $(dirname \"$DATABASE_PATH\")\" >&2\n exit 1\nfi\n\nif [[ ! -f \"$ROOT_DIR/build/Makefile\" ]]; then\n cmake -S \"$ROOT_DIR\" -B \"$ROOT_DIR/build\"\nfi\n\ncmake --build \"$ROOT_DIR/build\"\n\n\"$ROOT_DIR/build/bin/SmartNAS\" \"$SMARTNAS_CONFIG\" &\nCORE_PID=$!\n\npython3 \"$ROOT_DIR/scripts/agent_service.py\" &\nAGENT_PID=$!\n\ncleanup() {\n kill \"$CORE_PID\" \"$AGENT_PID\" 2>/dev/null || true\n}\ntrap cleanup EXIT INT TERM\n\nsleep 1\nif ! kill -0 \"$CORE_PID\" 2>/dev/null; then\n echo \"SmartNAS Core failed to start.\"\n kill \"$AGENT_PID\" 2>/dev/null || true\n exit 1\nfi\n\nif ! kill -0 \"$AGENT_PID\" 2>/dev/null; then\n echo \"SmartNAS Agent failed to start.\"\n kill \"$CORE_PID\" 2>/dev/null || true\n exit 1\nfi\n\necho \"SmartNAS Core: http://127.0.0.1:$CORE_PORT\"\necho \"SmartNAS Agent: http://127.0.0.1:$AGENT_PORT\"\necho \"LLM provider: DeepSeek\"\necho \"DeepSeek API: $DEEPSEEK_API\"\necho \"DeepSeek model: $DEEPSEEK_MODEL\"\nif [[ -n \"${SMARTNAS_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-$CONFIG_DEEPSEEK_KEY}}\" ]]; then\n echo \"DeepSeek key: configured\"\nelse\n echo \"DeepSeek key: missing (set SMARTNAS_DEEPSEEK_API_KEY or DEEPSEEK_API_KEY)\"\nfi\necho \"Press Ctrl+C to stop both services.\"\n\nwait\n"}, "6137c4f53c41ac9a4651ab2f87252ad1befd0e40": {"t": 1785758264.9656355, "n": 2179, "h": "1c4d425d4c7e0f50", "c": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n\nexport SMARTNAS_CONFIG=\"${SMARTNAS_CONFIG:-$ROOT_DIR/config/config.json}\"\nmapfile -t CONFIG_VALUES < <(\n python3 -c 'import json,os; c=json.load(open(os.environ[\"SMARTNAS_CONFIG\"])); [print(c[k]) for k in (\"core_port\",\"agent_port\",\"deepseek_api_base\",\"deepseek_model\",\"data_dir\",\"database_path\",\"deepseek_api_key\")]'\n)\nCORE_PORT=\"${CONFIG_VALUES[0]}\"\nAGENT_PORT=\"${CONFIG_VALUES[1]}\"\nDEEPSEEK_API=\"${CONFIG_VALUES[2]}\"\nDEEPSEEK_MODEL=\"${CONFIG_VALUES[3]}\"\nDATA_DIR=\"${CONFIG_VALUES[4]}\"\nDATABASE_PATH=\"${CONFIG_VALUES[5]}\"\nCONFIG_DEEPSEEK_KEY=\"${CONFIG_VALUES[6]}\"\n[[ \"$DATA_DIR\" = /* ]] || DATA_DIR=\"$ROOT_DIR/$DATA_DIR\"\n[[ \"$DATABASE_PATH\" = /* ]] || DATABASE_PATH=\"$ROOT_DIR/$DATABASE_PATH\"\n\nmkdir -p \"$DATA_DIR\" \"$(dirname \"$DATABASE_PATH\")\"\nif [[ ! -w \"$DATA_DIR\" || ! -w \"$(dirname \"$DATABASE_PATH\")\" ]]; then\n echo \"SmartNAS data directories are not writable: $DATA_DIR or $(dirname \"$DATABASE_PATH\")\" >&2\n exit 1\nfi\n\nif [[ ! -f \"$ROOT_DIR/build/Makefile\" ]]; then\n cmake -S \"$ROOT_DIR\" -B \"$ROOT_DIR/build\"\nfi\n\ncmake --build \"$ROOT_DIR/build\"\n\n\"$ROOT_DIR/build/bin/SmartNAS\" \"$SMARTNAS_CONFIG\" &\nCORE_PID=$!\n\npython3 \"$ROOT_DIR/scripts/agent_service.py\" &\nAGENT_PID=$!\n\ncleanup() {\n kill \"$CORE_PID\" \"$AGENT_PID\" 2>/dev/null || true\n}\ntrap cleanup EXIT INT TERM\n\nsleep 1\nif ! kill -0 \"$CORE_PID\" 2>/dev/null; then\n echo \"SmartNAS Core failed to start.\"\n kill \"$AGENT_PID\" 2>/dev/null || true\n exit 1\nfi\n\nif ! kill -0 \"$AGENT_PID\" 2>/dev/null; then\n echo \"SmartNAS Agent failed to start.\"\n kill \"$CORE_PID\" 2>/dev/null || true\n exit 1\nfi\n\necho \"SmartNAS Core: http://127.0.0.1:$CORE_PORT\"\necho \"SmartNAS Agent: http://127.0.0.1:$AGENT_PORT\"\necho \"LLM provider: DeepSeek\"\necho \"DeepSeek API: $DEEPSEEK_API\"\necho \"DeepSeek model: $DEEPSEEK_MODEL\"\nif [[ -n \"${SMARTNAS_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-$CONFIG_DEEPSEEK_KEY}}\" ]]; then\n echo \"DeepSeek key: configured\"\nelse\n echo \"DeepSeek key: missing (set SMARTNAS_DEEPSEEK_API_KEY or DEEPSEEK_API_KEY)\"\nfi\necho \"Press Ctrl+C to stop both services.\"\n\nwait\n"}, "ae28c5c82f7abb2ba87647dd6726f75bc0da05bd": {"t": 1785758266.344678, "n": 1702, "h": "b4ec58c548df02ac", "c": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n\nexport SMARTNAS_DEEPSEEK_API_BASE=\"${SMARTNAS_DEEPSEEK_API_BASE:-https://api.deepseek.com}\"\nexport SMARTNAS_DEEPSEEK_MODEL=\"${SMARTNAS_DEEPSEEK_MODEL:-deepseek-chat}\"\nexport SMARTNAS_CORE_API=\"${SMARTNAS_CORE_API:-http://127.0.0.1:8080}\"\n\nmkdir -p \"$ROOT_DIR/var/data\" \"$ROOT_DIR/var/db\"\nif [[ ! -w \"$ROOT_DIR/var/data\" || ! -w \"$ROOT_DIR/var/db\" ]]; then\n echo \"SmartNAS data directories are not writable: $ROOT_DIR/var/data or $ROOT_DIR/var/db\" >&2\n exit 1\nfi\n\nif [[ ! -f \"$ROOT_DIR/build/Makefile\" ]]; then\n cmake -S \"$ROOT_DIR\" -B \"$ROOT_DIR/build\"\nfi\n\ncmake --build \"$ROOT_DIR/build\"\n\n(\n cd \"$ROOT_DIR/build/bin\"\n ./SmartNAS\n) &\nCORE_PID=$!\n\npython3 \"$ROOT_DIR/scripts/agent_service.py\" &\nAGENT_PID=$!\n\ncleanup() {\n kill \"$CORE_PID\" \"$AGENT_PID\" 2>/dev/null || true\n}\ntrap cleanup EXIT INT TERM\n\nsleep 1\nif ! kill -0 \"$CORE_PID\" 2>/dev/null; then\n echo \"SmartNAS Core failed to start.\"\n kill \"$AGENT_PID\" 2>/dev/null || true\n exit 1\nfi\n\nif ! kill -0 \"$AGENT_PID\" 2>/dev/null; then\n echo \"SmartNAS Agent failed to start.\"\n kill \"$CORE_PID\" 2>/dev/null || true\n exit 1\nfi\n\necho \"SmartNAS Core: http://127.0.0.1:8080\"\necho \"SmartNAS Agent: http://127.0.0.1:8081\"\necho \"LLM provider: DeepSeek\"\necho \"DeepSeek API: $SMARTNAS_DEEPSEEK_API_BASE\"\necho \"DeepSeek model: $SMARTNAS_DEEPSEEK_MODEL\"\nif [[ -n \"${SMARTNAS_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-}}\" ]]; then\n echo \"DeepSeek key: configured\"\nelse\n echo \"DeepSeek key: missing (set SMARTNAS_DEEPSEEK_API_KEY or DEEPSEEK_API_KEY)\"\nfi\necho \"Press Ctrl+C to stop both services.\"\n\nwait\n"}, "3d357b3ee317c922fa1ecfbe50b7fcde4a082564": {"t": 1785758267.6211507, "n": 1767, "h": "f0c292f738ce434f", "c": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n\nexport SMARTNAS_DEEPSEEK_API_BASE=\"${SMARTNAS_DEEPSEEK_API_BASE:-https://api.deepseek.com}\"\nexport SMARTNAS_DEEPSEEK_MODEL=\"${SMARTNAS_DEEPSEEK_MODEL:-deepseek-chat}\"\nexport SMARTNAS_MAX_NEW_TOKENS=\"${SMARTNAS_MAX_NEW_TOKENS:-256}\"\nexport SMARTNAS_CORE_API=\"${SMARTNAS_CORE_API:-http://127.0.0.1:8080}\"\n\nmkdir -p \"$ROOT_DIR/var/data\" \"$ROOT_DIR/var/db\"\nif [[ ! -w \"$ROOT_DIR/var/data\" || ! -w \"$ROOT_DIR/var/db\" ]]; then\n echo \"SmartNAS data directories are not writable: $ROOT_DIR/var/data or $ROOT_DIR/var/db\" >&2\n exit 1\nfi\n\nif [[ ! -f \"$ROOT_DIR/build/Makefile\" ]]; then\n cmake -S \"$ROOT_DIR\" -B \"$ROOT_DIR/build\"\nfi\n\ncmake --build \"$ROOT_DIR/build\"\n\n(\n cd \"$ROOT_DIR/build/bin\"\n ./SmartNAS\n) &\nCORE_PID=$!\n\npython3 \"$ROOT_DIR/scripts/agent_service.py\" &\nAGENT_PID=$!\n\ncleanup() {\n kill \"$CORE_PID\" \"$AGENT_PID\" 2>/dev/null || true\n}\ntrap cleanup EXIT INT TERM\n\nsleep 1\nif ! kill -0 \"$CORE_PID\" 2>/dev/null; then\n echo \"SmartNAS Core failed to start.\"\n kill \"$AGENT_PID\" 2>/dev/null || true\n exit 1\nfi\n\nif ! kill -0 \"$AGENT_PID\" 2>/dev/null; then\n echo \"SmartNAS Agent failed to start.\"\n kill \"$CORE_PID\" 2>/dev/null || true\n exit 1\nfi\n\necho \"SmartNAS Core: http://127.0.0.1:8080\"\necho \"SmartNAS Agent: http://127.0.0.1:8081\"\necho \"LLM provider: DeepSeek\"\necho \"DeepSeek API: $SMARTNAS_DEEPSEEK_API_BASE\"\necho \"DeepSeek model: $SMARTNAS_DEEPSEEK_MODEL\"\nif [[ -n \"${SMARTNAS_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-}}\" ]]; then\n echo \"DeepSeek key: configured\"\nelse\n echo \"DeepSeek key: missing (set SMARTNAS_DEEPSEEK_API_KEY or DEEPSEEK_API_KEY)\"\nfi\necho \"Press Ctrl+C to stop both services.\"\n\nwait\n"}, "055c8f825b6e2bdf333b99196669cd8d46979587": {"t": 1785758268.8931334, "n": 1595, "h": "2d5289a228b6f72f", "c": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n\nexport SMARTNAS_DEEPSEEK_API_BASE=\"${SMARTNAS_DEEPSEEK_API_BASE:-https://api.deepseek.com}\"\nexport SMARTNAS_DEEPSEEK_MODEL=\"${SMARTNAS_DEEPSEEK_MODEL:-deepseek-chat}\"\nexport SMARTNAS_MAX_NEW_TOKENS=\"${SMARTNAS_MAX_NEW_TOKENS:-256}\"\nexport SMARTNAS_CORE_API=\"${SMARTNAS_CORE_API:-http://127.0.0.1:8080}\"\n\nif [[ ! -f \"$ROOT_DIR/build/Makefile\" ]]; then\n cmake -S \"$ROOT_DIR\" -B \"$ROOT_DIR/build\"\nfi\n\nif [[ ! -x \"$ROOT_DIR/build/bin/SmartNAS\" ]]; then\n cmake --build \"$ROOT_DIR/build\"\nfi\n\n(\n cd \"$ROOT_DIR/build/bin\"\n ./SmartNAS\n) &\nCORE_PID=$!\n\npython3 \"$ROOT_DIR/scripts/agent_service.py\" &\nAGENT_PID=$!\n\ncleanup() {\n kill \"$CORE_PID\" \"$AGENT_PID\" 2>/dev/null || true\n}\ntrap cleanup EXIT INT TERM\n\nsleep 1\nif ! kill -0 \"$CORE_PID\" 2>/dev/null; then\n echo \"SmartNAS Core failed to start.\"\n kill \"$AGENT_PID\" 2>/dev/null || true\n exit 1\nfi\n\nif ! kill -0 \"$AGENT_PID\" 2>/dev/null; then\n echo \"SmartNAS Agent failed to start.\"\n kill \"$CORE_PID\" 2>/dev/null || true\n exit 1\nfi\n\necho \"SmartNAS Core: http://127.0.0.1:8080\"\necho \"SmartNAS Agent: http://127.0.0.1:8081\"\necho \"LLM provider: DeepSeek\"\necho \"DeepSeek API: $SMARTNAS_DEEPSEEK_API_BASE\"\necho \"DeepSeek model: $SMARTNAS_DEEPSEEK_MODEL\"\nif [[ -n \"${SMARTNAS_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-}}\" ]]; then\n echo \"DeepSeek key: configured\"\nelse\n echo \"DeepSeek key: missing (set SMARTNAS_DEEPSEEK_API_KEY or DEEPSEEK_API_KEY)\"\nfi\necho \"Press Ctrl+C to stop both services.\"\n\nwait\n"}, "0a493bbb7af80e238c96a500c988d086051f9ce5": {"t": 1785758270.470636, "n": 1213, "h": "12fec464dad8da75", "c": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n\nexport SMARTNAS_MODEL_PATH=\"${SMARTNAS_MODEL_PATH:-$ROOT_DIR/models/llm/qwen2.5-7b-instruct-q4_k_m.gguf}\"\nexport SMARTNAS_CORE_API=\"${SMARTNAS_CORE_API:-http://127.0.0.1:8080}\"\n\nif [[ ! -f \"$ROOT_DIR/build/Makefile\" ]]; then\n cmake -S \"$ROOT_DIR\" -B \"$ROOT_DIR/build\"\nfi\n\nif [[ ! -x \"$ROOT_DIR/build/bin/SmartNAS\" ]]; then\n cmake --build \"$ROOT_DIR/build\"\nfi\n\n(\n cd \"$ROOT_DIR/build/bin\"\n ./SmartNAS\n) &\nCORE_PID=$!\n\npython3 \"$ROOT_DIR/scripts/agent_service.py\" &\nAGENT_PID=$!\n\ncleanup() {\n kill \"$CORE_PID\" \"$AGENT_PID\" 2>/dev/null || true\n}\ntrap cleanup EXIT INT TERM\n\nsleep 1\nif ! kill -0 \"$CORE_PID\" 2>/dev/null; then\n echo \"SmartNAS Core failed to start.\"\n kill \"$AGENT_PID\" 2>/dev/null || true\n exit 1\nfi\n\nif ! kill -0 \"$AGENT_PID\" 2>/dev/null; then\n echo \"SmartNAS Agent failed to start.\"\n kill \"$CORE_PID\" 2>/dev/null || true\n exit 1\nfi\n\necho \"SmartNAS Core: http://127.0.0.1:8080\"\necho \"SmartNAS Agent: http://127.0.0.1:8081\"\necho \"LLM backend: transformers\"\necho \"Model path: $SMARTNAS_MODEL_PATH\"\necho \"Press Ctrl+C to stop both services.\"\n\nwait\n"}, "6861d18eeecc3089153d65d9a6994c1ea4df1d4f": {"t": 1785758272.2902687, "n": 1201, "h": "6d7e066094688b70", "c": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n\nexport SMARTNAS_MODEL_PATH=\"${SMARTNAS_MODEL_PATH:-$ROOT_DIR/models/llm/qwen2.5-7b-instruct}\"\nexport SMARTNAS_CORE_API=\"${SMARTNAS_CORE_API:-http://127.0.0.1:8080}\"\n\nif [[ ! -f \"$ROOT_DIR/build/Makefile\" ]]; then\n cmake -S \"$ROOT_DIR\" -B \"$ROOT_DIR/build\"\nfi\n\nif [[ ! -x \"$ROOT_DIR/build/bin/SmartNAS\" ]]; then\n cmake --build \"$ROOT_DIR/build\"\nfi\n\n(\n cd \"$ROOT_DIR/build/bin\"\n ./SmartNAS\n) &\nCORE_PID=$!\n\npython3 \"$ROOT_DIR/scripts/agent_service.py\" &\nAGENT_PID=$!\n\ncleanup() {\n kill \"$CORE_PID\" \"$AGENT_PID\" 2>/dev/null || true\n}\ntrap cleanup EXIT INT TERM\n\nsleep 1\nif ! kill -0 \"$CORE_PID\" 2>/dev/null; then\n echo \"SmartNAS Core failed to start.\"\n kill \"$AGENT_PID\" 2>/dev/null || true\n exit 1\nfi\n\nif ! kill -0 \"$AGENT_PID\" 2>/dev/null; then\n echo \"SmartNAS Agent failed to start.\"\n kill \"$CORE_PID\" 2>/dev/null || true\n exit 1\nfi\n\necho \"SmartNAS Core: http://127.0.0.1:8080\"\necho \"SmartNAS Agent: http://127.0.0.1:8081\"\necho \"LLM backend: transformers\"\necho \"Model path: $SMARTNAS_MODEL_PATH\"\necho \"Press Ctrl+C to stop both services.\"\n\nwait\n"}}, "docs/agent-service.md": {"1a2801a5679cd2216c377dc18978fedd78c386cf": {"t": 1785746410.983145, "n": 5579, "h": "41f891d2800e9f90", "c": "# SmartNAS Agent 功能说明\n\n## 1. 服务定位\n\nSmartNAS Agent 是独立运行的 Python/FastAPI 服务,默认监听 `8081`。它负责:\n\n- 调用 DeepSeek OpenAI-compatible API;\n- 普通对话与 SSE 流式对话;\n- 文件转 Markdown、摘要和标签生成;\n- 单文件全文问答和文件内 RAG 问答;\n- Markdown 分块、向量生成、FAISS 检索和索引持久化;\n- 摘要后台任务、取消、重试、去重与状态持久化;\n- Agent 操作审计。\n\nNAS Core 仍负责 JWT 鉴权、文件存储、下载、元数据和目录操作。Agent 不直接读取 Core 的 SQLite,而是携带用户 JWT 调用 Core HTTP API。\n\n## 2. 启动与依赖\n\n安装依赖:\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r scripts/requirements-agent.txt\n```\n\n单独启动 Agent:\n\n```bash\nexport SMARTNAS_DEEPSEEK_API_KEY=sk-...\npython3 scripts/agent_service.py\n```\n\n也可以同时启动 Core 和 Agent:\n\n```bash\n./scripts/start_smartnas.sh\n```\n\nAgent 默认通过 `http://127.0.0.1:8080` 访问 Core。\n\n## 3. 配置项\n\n默认参数统一存放在 `config/config.json`。下列环境变量用于部署时覆盖对应 JSON 值,优先级为:环境变量 > JSON 配置 > 代码默认值。\n\n| 环境变量 | 默认值 | 用途 |\n| --- | --- | --- |\n| `SMARTNAS_CORE_API` | `http://127.0.0.1:8080` | Core API 地址 |\n| `SMARTNAS_DEEPSEEK_API_KEY` | 空 | DeepSeek API Key,也兼容 `DEEPSEEK_API_KEY` |\n| `SMARTNAS_DEEPSEEK_API_BASE` | `https://api.deepseek.com` | 模型 API 地址 |\n| `SMARTNAS_DEEPSEEK_MODEL` | `deepseek-chat` | 模型名称 |\n| `SMARTNAS_DEEPSEEK_TIMEOUT` | `60` | 模型请求超时秒数 |\n| `SMARTNAS_GENERATION_TEMPERATURE` | `0.7` | 生成温度 |\n| `SMARTNAS_GENERATION_TOP_P` | `0.8` | Top-P |\n| `SMARTNAS_MAX_MARKDOWN_CHARS` | `12000` | 单次摘要输入长度 |\n| `SMARTNAS_SUMMARY_CHUNK_CHARS` | `7000` | 长文摘要分块长度 |\n| `SMARTNAS_FILE_QA_DIRECT_CHARS` | `30000` | 全文问答与 RAG 问答切换阈值 |\n| `SMARTNAS_CACHE_DIR` | `var/cache/markdown` | Markdown 缓存目录 |\n| `SMARTNAS_VECTOR_DIR` | `var/cache/vector` | 向量数据和 FAISS 索引目录 |\n| `SMARTNAS_EMBEDDING_MODEL` | `BAAI/bge-small-zh-v1.5` | Embedding 模型 |\n| `SMARTNAS_RAG_CHUNK_CHARS` | `900` | RAG 文本块长度 |\n| `SMARTNAS_RAG_CHUNK_OVERLAP` | `120` | RAG 分块重叠长度 |\n| `SMARTNAS_RAG_TOP_K` | `6` | 默认召回数量 |\n| `SMARTNAS_RAG_MIN_SCORE` | `0.28` | 最低相似度 |\n| `SMARTNAS_TASK_DB` | `var/cache/agent_tasks.db` | 摘要任务数据库 |\n| `SMARTNAS_AUDIT_LOG` | `var/log/agent_audit.jsonl` | 审计日志路径 |\n| `SMARTNAS_AUDIT_LOG_MAX_BYTES` | `20971520` | 单个审计日志最大字节数 |\n| `SMARTNAS_AUDIT_LOG_BACKUPS` | `10` | 审计日志保留数量 |\n| `SMARTNAS_AUDIT_LOG_FULL_CONTENT` | `0` | 是否记录完整内容 |\n| `SMARTNAS_AGENT_HOST` | `127.0.0.1` | Agent 监听地址 |\n| `SMARTNAS_AGENT_PORT` | `8081` | Agent 监听端口 |\n| `SMARTNAS_SUMMARY_WORKER_COUNT` | `1` | 摘要后台 worker 数 |\n\n## 4. 鉴权与用户隔离\n\n除健康检查外,接口都使用 Core 签发的 JWT:\n\n```http\nAuthorization: Bearer \n```\n\n- Agent 通过 `/api/v1/me` 向 Core 验证身份;\n- 普通聊天上下文按用户隔离;\n- 单文件问答上下文按“用户 + 文件 hash”隔离,并保留最近三轮问答;\n- 向量缓存按用户命名空间隔离;\n- 摘要任务只能由任务所属用户查看、取消或重试。\n\n聊天和单文件问答上下文目前保存在 Agent 内存中,服务重启后会清空。摘要任务与向量索引会持久化。\n\n## 5. HTTP API\n\n### 健康检查\n\n`GET /api/agent/health`\n\n返回模型配置、依赖状态、缓存路径、RAG 参数和支持格式。\n\n### 普通聊天\n\n- `POST /api/agent/chat`:非流式聊天,请求体为 `{\"prompt\":\"...\"}`。\n- `POST /api/agent/chat/stream`:SSE 流式聊天,请求体相同。\n- `POST /api/agent/clear_history`:清空当前用户普通聊天上下文。\n\n流式接口使用以下事件:\n\n```text\ndata: {\"type\":\"delta\",\"content\":\"增量文本\"}\n\ndata: {\"type\":\"done\"}\n\ndata: {\"type\":\"error\",\"message\":\"错误信息\"}\n```\n\n上游 SSE 使用小块读取,并关闭代理转换和缓冲。模型输出中的 Markdown 星号会在 Agent 输出层统一移除。\n\n### 文件摘要与标签\n\n- `POST /api/agent/summarize`:同步摘要,请求体为 `{\"hash\":\"...\",\"force\":false}`。\n- `POST /api/agent/summarize/start`:创建后台摘要任务。\n- `GET /api/agent/summarize/status/{task_id}`:查询任务。\n- `GET /api/agent/summarize/tasks`:列出当前用户任务。\n- `POST /api/agent/summarize/cancel/{task_id}`:取消任务。\n- `POST /api/agent/summarize/retry/{task_id}`:重试失败或取消的任务。\n- `POST /api/agent/summarize/missing`:为缺少摘要的支持文件批量排队;页面登录后会自动调用一次。\n\nWeb UI 在登录后会自动扫描并补齐缺失摘要;文件上传、恢复、重命名和移动成功后,也会静默调用 `summarize/start`。任务在后台完成 Markdown 转换、摘要、标签和向量索引,不阻塞文件操作。\n\n### Markdown 与单文件问答\n\n- `GET /api/agent/markdown/{file_hash}`:返回转换后的 Markdown。\n- `POST /api/agent/file_qa`:非流式单文件问答,请求体为 `{\"hash\":\"...\",\"question\":\"...\"}`。\n- `POST /api/agent/file_qa/stream`:SSE 流式单文件问答,请求体相同。\n\n文件长度不超过 `SMARTNAS_FILE_QA_DIRECT_CHARS` 时,模型接收全文;超过阈值时只召回该文件相关片段。后续问题会携带该文件最近三轮问答,大文件检索还会结合上一个问题处理“它、这个结论”等指代。\n\n`file_qa/stream` 的事件格式与普通流式聊天一致,`done` 事件额外带有 `mode`,值为 `full` 或 `rag`。\n\n### RAG 查询\n\n`POST /api/agent/rag/query`\n\n请求体:\n\n```json\n{\"query\":\"检索问题\",\"top_k\":6,\"file_hash\":\"可选文件 hash\",\"directory\":\"可选目录\"}\n```\n\n返回召回片段、文件名、hash、块序号、综合分数、召回来源和基于片段生成的回答。检索层会融合向量语义召回与本地 SQLite 关键词召回;关键词召回优先使用 FTS5,运行环境不支持 FTS5 时自动退回 `LIKE` 匹配。关键词索引用 `dirty` 状态驱动刷新:摘要索引和显式重建会刷新关键词库,临时补索引会标脏,查询只在索引缺失或标脏时补建。\n\n`GET /api/agent/index/status` 返回当前用户索引 manifest 汇总,包括已记录文件数、状态计数、每个文件的 chunk 数、模型名、更新时间,以及关键词索引的 `dirty`、`indexed_chunks` 和 FTS 可用状态。\n\n索引维护接口:\n\n- `GET /api/agent/index/missing`:扫描当前用户文件,返回支持索引但缺少有效 chunk 的文件。\n- `POST /api/agent/index/rebuild/start`:后台重建索引,请求体为 `{\"hash\":\"可选文件 hash\",\"force\":false,\"include_keyword\":true}`;不传 `hash` 时扫描全部支持文件。\n- `GET /api/agent/index/rebuild/status/{task_id}`:查询索引重建任务。\n- `GET /api/agent/index/rebuild/tasks`:列出当前用户最近索引任务。\n\n## 6. 文档处理与缓存\n\n- MarkItDown 用于 PDF、Office、文本、网页、EPUB、ZIP、Notebook 等格式转换;\n- 图片使用尺寸、EXIF、嵌入元数据和 SVG 文本生成 fallback Markdown;\n- 音视频在无法提取转录时使用媒体元数据;\n- Markdown 按文件 hash 缓存在 `var/cache/markdown`;\n- RAG 块、embedding、FAISS 索引、关键词 SQLite 索引和索引 manifest 缓存在 `var/cache/vector`;\n- 摘要成功后会同时写回 Core 摘要与标签,并更新该用户向量索引;\n- 查询前会对照 Core 当前文件列表,移除已删除文件的块并同步重命名后的文件名。\n\n## 7. 后台任务与审计\n\n摘要任务使用单工作线程执行,避免多个文档转换和本地 embedding 同时挤占内存。相同用户、相同文件的待处理任务会去重。任务状态包括:\n\n- `pending`\n- `running`\n- `cancel_requested`\n- `cancelled`\n- `success`\n- `failed`\n\n任务记录保存在 `var/cache/agent_tasks.db`。Agent 重启时,未完成任务会标记为失败,可通过重试接口重新排队。\n\n审计日志采用轮转 JSONL,记录用户、操作、状态、耗时、线程及结果或错误。默认路径为 `var/log/agent_audit.jsonl`,文件权限会尝试设置为 `0600`。\n\n## 8. 主要代码位置\n\n- Agent 服务与全部 API:`agent/service.py`\n- Agent 配置、请求模型、模型客户端和 SSE:`agent/`\n- 兼容启动入口:`scripts/agent_service.py`\n- Python 依赖:`scripts/requirements-agent.txt`\n- Web 调用与流式渲染:`web/index.html`\n- Core/Agent 边界:`docs/architecture.md`\n"}, "1e4f1b429b0bd6a1eb7914a29d1f5624e8219918": {"t": 1785758265.9726815, "n": 4908, "h": "5a74ae11dd426dc5", "c": "# SmartNAS Agent 功能说明\n\n## 1. 服务定位\n\nSmartNAS Agent 是独立运行的 Python/FastAPI 服务,默认监听 `8081`。它负责:\n\n- 调用 DeepSeek OpenAI-compatible API;\n- 普通对话与 SSE 流式对话;\n- 文件转 Markdown、摘要和标签生成;\n- 单文件全文问答和文件内 RAG 问答;\n- Markdown 分块、向量生成、FAISS 检索和索引持久化;\n- 摘要后台任务、取消、重试、去重与状态持久化;\n- Agent 操作审计。\n\nNAS Core 仍负责 JWT 鉴权、文件存储、下载、元数据和目录操作。Agent 不直接读取 Core 的 SQLite,而是携带用户 JWT 调用 Core HTTP API。\n\n## 2. 启动与依赖\n\n安装依赖:\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r scripts/requirements-agent.txt\n```\n\n单独启动 Agent:\n\n```bash\nexport SMARTNAS_DEEPSEEK_API_KEY=sk-...\npython3 scripts/agent_service.py\n```\n\n也可以同时启动 Core 和 Agent:\n\n```bash\n./scripts/start_smartnas.sh\n```\n\nAgent 默认通过 `http://127.0.0.1:8080` 访问 Core。\n\n## 3. 配置项\n\n默认参数统一存放在 `config/config.json`。下列环境变量用于部署时覆盖对应 JSON 值,优先级为:环境变量 > JSON 配置 > 代码默认值。\n\n| 环境变量 | 默认值 | 用途 |\n| --- | --- | --- |\n| `SMARTNAS_CORE_API` | `http://127.0.0.1:8080` | Core API 地址 |\n| `SMARTNAS_DEEPSEEK_API_KEY` | 空 | DeepSeek API Key,也兼容 `DEEPSEEK_API_KEY` |\n| `SMARTNAS_DEEPSEEK_API_BASE` | `https://api.deepseek.com` | 模型 API 地址 |\n| `SMARTNAS_DEEPSEEK_MODEL` | `deepseek-chat` | 模型名称 |\n| `SMARTNAS_DEEPSEEK_TIMEOUT` | `60` | 模型请求超时秒数 |\n| `SMARTNAS_GENERATION_TEMPERATURE` | `0.7` | 生成温度 |\n| `SMARTNAS_GENERATION_TOP_P` | `0.8` | Top-P |\n| `SMARTNAS_MAX_MARKDOWN_CHARS` | `12000` | 单次摘要输入长度 |\n| `SMARTNAS_SUMMARY_CHUNK_CHARS` | `7000` | 长文摘要分块长度 |\n| `SMARTNAS_FILE_QA_DIRECT_CHARS` | `30000` | 全文问答与 RAG 问答切换阈值 |\n| `SMARTNAS_CACHE_DIR` | `var/cache/markdown` | Markdown 缓存目录 |\n| `SMARTNAS_VECTOR_DIR` | `var/cache/vector` | 向量数据和 FAISS 索引目录 |\n| `SMARTNAS_EMBEDDING_MODEL` | `BAAI/bge-small-zh-v1.5` | Embedding 模型 |\n| `SMARTNAS_RAG_CHUNK_CHARS` | `900` | RAG 文本块长度 |\n| `SMARTNAS_RAG_CHUNK_OVERLAP` | `120` | RAG 分块重叠长度 |\n| `SMARTNAS_RAG_TOP_K` | `6` | 默认召回数量 |\n| `SMARTNAS_RAG_MIN_SCORE` | `0.28` | 最低相似度 |\n| `SMARTNAS_TASK_DB` | `var/cache/agent_tasks.db` | 摘要任务数据库 |\n| `SMARTNAS_AUDIT_LOG` | `var/log/agent_audit.jsonl` | 审计日志路径 |\n| `SMARTNAS_AUDIT_LOG_MAX_BYTES` | `20971520` | 单个审计日志最大字节数 |\n| `SMARTNAS_AUDIT_LOG_BACKUPS` | `10` | 审计日志保留数量 |\n| `SMARTNAS_AUDIT_LOG_FULL_CONTENT` | `0` | 是否记录完整内容 |\n| `SMARTNAS_AGENT_HOST` | `127.0.0.1` | Agent 监听地址 |\n| `SMARTNAS_AGENT_PORT` | `8081` | Agent 监听端口 |\n| `SMARTNAS_SUMMARY_WORKER_COUNT` | `1` | 摘要后台 worker 数 |\n\n## 4. 鉴权与用户隔离\n\n除健康检查外,接口都使用 Core 签发的 JWT:\n\n```http\nAuthorization: Bearer \n```\n\n- Agent 通过 `/api/v1/me` 向 Core 验证身份;\n- 普通聊天上下文按用户隔离;\n- 单文件问答上下文按“用户 + 文件 hash”隔离,并保留最近三轮问答;\n- 向量缓存按用户命名空间隔离;\n- 摘要任务只能由任务所属用户查看、取消或重试。\n\n聊天和单文件问答上下文目前保存在 Agent 内存中,服务重启后会清空。摘要任务与向量索引会持久化。\n\n## 5. HTTP API\n\n### 健康检查\n\n`GET /api/agent/health`\n\n返回模型配置、依赖状态、缓存路径、RAG 参数和支持格式。\n\n### 普通聊天\n\n- `POST /api/agent/chat`:非流式聊天,请求体为 `{\"prompt\":\"...\"}`。\n- `POST /api/agent/chat/stream`:SSE 流式聊天,请求体相同。\n- `POST /api/agent/clear_history`:清空当前用户普通聊天上下文。\n\n流式接口使用以下事件:\n\n```text\ndata: {\"type\":\"delta\",\"content\":\"增量文本\"}\n\ndata: {\"type\":\"done\"}\n\ndata: {\"type\":\"error\",\"message\":\"错误信息\"}\n```\n\n上游 SSE 使用小块读取,并关闭代理转换和缓冲。模型输出中的 Markdown 星号会在 Agent 输出层统一移除。\n\n### 文件摘要与标签\n\n- `POST /api/agent/summarize`:同步摘要,请求体为 `{\"hash\":\"...\",\"force\":false}`。\n- `POST /api/agent/summarize/start`:创建后台摘要任务。\n- `GET /api/agent/summarize/status/{task_id}`:查询任务。\n- `GET /api/agent/summarize/tasks`:列出当前用户任务。\n- `POST /api/agent/summarize/cancel/{task_id}`:取消任务。\n- `POST /api/agent/summarize/retry/{task_id}`:重试失败或取消的任务。\n- `POST /api/agent/summarize/missing`:为缺少摘要的支持文件批量排队,保留作管理 API,页面不再提供手动入口。\n\nWeb UI 在文件上传、恢复、重命名和移动成功后,会静默调用 `summarize/start`。任务在后台完成 Markdown 转换、摘要、标签和向量索引,不阻塞文件操作。\n\n### Markdown 与单文件问答\n\n- `GET /api/agent/markdown/{file_hash}`:返回转换后的 Markdown。\n- `POST /api/agent/file_qa`:非流式单文件问答,请求体为 `{\"hash\":\"...\",\"question\":\"...\"}`。\n- `POST /api/agent/file_qa/stream`:SSE 流式单文件问答,请求体相同。\n\n文件长度不超过 `SMARTNAS_FILE_QA_DIRECT_CHARS` 时,模型接收全文;超过阈值时只召回该文件相关片段。后续问题会携带该文件最近三轮问答,大文件检索还会结合上一个问题处理“它、这个结论”等指代。\n\n`file_qa/stream` 的事件格式与普通流式聊天一致,`done` 事件额外带有 `mode`,值为 `full` 或 `rag`。\n\n### RAG 查询\n\n`POST /api/agent/rag/query`\n\n请求体:\n\n```json\n{\"query\":\"检索问题\",\"top_k\":6}\n```\n\n返回相似片段、文件名、hash、块序号、相似度和基于片段生成的回答。\n\n## 6. 文档处理与缓存\n\n- MarkItDown 用于 PDF、Office、文本、网页、EPUB、ZIP、Notebook 等格式转换;\n- 图片使用尺寸、EXIF、嵌入元数据和 SVG 文本生成 fallback Markdown;\n- 音视频在无法提取转录时使用媒体元数据;\n- Markdown 按文件 hash 缓存在 `var/cache/markdown`;\n- RAG 块、embedding 和 FAISS 索引缓存在 `var/cache/vector`;\n- 摘要成功后会同时写回 Core 摘要与标签,并更新该用户向量索引;\n- 查询前会对照 Core 当前文件列表,移除已删除文件的块并同步重命名后的文件名。\n\n## 7. 后台任务与审计\n\n摘要任务使用单工作线程执行,避免多个文档转换和本地 embedding 同时挤占内存。相同用户、相同文件的待处理任务会去重。任务状态包括:\n\n- `pending`\n- `running`\n- `cancel_requested`\n- `cancelled`\n- `success`\n- `failed`\n\n任务记录保存在 `var/cache/agent_tasks.db`。Agent 重启时,未完成任务会标记为失败,可通过重试接口重新排队。\n\n审计日志采用轮转 JSONL,记录用户、操作、状态、耗时、线程及结果或错误。默认路径为 `var/log/agent_audit.jsonl`,文件权限会尝试设置为 `0600`。\n\n## 8. 主要代码位置\n\n- Agent 服务与全部 API:`agent/service.py`\n- Agent 配置、请求模型、模型客户端和 SSE:`agent/`\n- 兼容启动入口:`scripts/agent_service.py`\n- Python 依赖:`scripts/requirements-agent.txt`\n- Web 调用与流式渲染:`web/index.html`\n- Core/Agent 边界:`docs/agent-decoupling.md`\n"}, "6137c4f53c41ac9a4651ab2f87252ad1befd0e40": {"t": 1785758267.4050243, "n": 4906, "h": "57ee777c0a86d2aa", "c": "# SmartNAS Agent 功能说明\n\n## 1. 服务定位\n\nSmartNAS Agent 是独立运行的 Python/FastAPI 服务,默认监听 `8081`。它负责:\n\n- 调用 DeepSeek OpenAI-compatible API;\n- 普通对话与 SSE 流式对话;\n- 文件转 Markdown、摘要和标签生成;\n- 单文件全文问答和文件内 RAG 问答;\n- Markdown 分块、向量生成、FAISS 检索和索引持久化;\n- 摘要后台任务、取消、重试、去重与状态持久化;\n- Agent 操作审计。\n\nNAS Core 仍负责 JWT 鉴权、文件存储、下载、元数据和目录操作。Agent 不直接读取 Core 的 SQLite,而是携带用户 JWT 调用 Core HTTP API。\n\n## 2. 启动与依赖\n\n安装依赖:\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r scripts/requirements-agent.txt\n```\n\n单独启动 Agent:\n\n```bash\nexport SMARTNAS_DEEPSEEK_API_KEY=sk-...\npython3 scripts/agent_service.py\n```\n\n也可以同时启动 Core 和 Agent:\n\n```bash\n./scripts/start_smartnas.sh\n```\n\nAgent 默认通过 `http://127.0.0.1:8080` 访问 Core。\n\n## 3. 配置项\n\n默认参数统一存放在 `config/config.json`。下列环境变量用于部署时覆盖对应 JSON 值,优先级为:环境变量 > JSON 配置 > 代码默认值。\n\n| 环境变量 | 默认值 | 用途 |\n| --- | --- | --- |\n| `SMARTNAS_CORE_API` | `http://127.0.0.1:8080` | Core API 地址 |\n| `SMARTNAS_DEEPSEEK_API_KEY` | 空 | DeepSeek API Key,也兼容 `DEEPSEEK_API_KEY` |\n| `SMARTNAS_DEEPSEEK_API_BASE` | `https://api.deepseek.com` | 模型 API 地址 |\n| `SMARTNAS_DEEPSEEK_MODEL` | `deepseek-chat` | 模型名称 |\n| `SMARTNAS_DEEPSEEK_TIMEOUT` | `60` | 模型请求超时秒数 |\n| `SMARTNAS_GENERATION_TEMPERATURE` | `0.7` | 生成温度 |\n| `SMARTNAS_GENERATION_TOP_P` | `0.8` | Top-P |\n| `SMARTNAS_MAX_MARKDOWN_CHARS` | `12000` | 单次摘要输入长度 |\n| `SMARTNAS_SUMMARY_CHUNK_CHARS` | `7000` | 长文摘要分块长度 |\n| `SMARTNAS_FILE_QA_DIRECT_CHARS` | `30000` | 全文问答与 RAG 问答切换阈值 |\n| `SMARTNAS_CACHE_DIR` | `var/cache/markdown` | Markdown 缓存目录 |\n| `SMARTNAS_VECTOR_DIR` | `var/cache/vector` | 向量数据和 FAISS 索引目录 |\n| `SMARTNAS_EMBEDDING_MODEL` | `BAAI/bge-small-zh-v1.5` | Embedding 模型 |\n| `SMARTNAS_RAG_CHUNK_CHARS` | `900` | RAG 文本块长度 |\n| `SMARTNAS_RAG_CHUNK_OVERLAP` | `120` | RAG 分块重叠长度 |\n| `SMARTNAS_RAG_TOP_K` | `6` | 默认召回数量 |\n| `SMARTNAS_RAG_MIN_SCORE` | `0.28` | 最低相似度 |\n| `SMARTNAS_TASK_DB` | `var/cache/agent_tasks.db` | 摘要任务数据库 |\n| `SMARTNAS_AUDIT_LOG` | `var/log/agent_audit.jsonl` | 审计日志路径 |\n| `SMARTNAS_AUDIT_LOG_MAX_BYTES` | `20971520` | 单个审计日志最大字节数 |\n| `SMARTNAS_AUDIT_LOG_BACKUPS` | `10` | 审计日志保留数量 |\n| `SMARTNAS_AUDIT_LOG_FULL_CONTENT` | `1` | 是否记录完整内容 |\n| `SMARTNAS_AGENT_HOST` | `0.0.0.0` | Agent 监听地址 |\n| `SMARTNAS_AGENT_PORT` | `8081` | Agent 监听端口 |\n| `SMARTNAS_SUMMARY_WORKER_COUNT` | `1` | 摘要后台 worker 数 |\n\n## 4. 鉴权与用户隔离\n\n除健康检查外,接口都使用 Core 签发的 JWT:\n\n```http\nAuthorization: Bearer \n```\n\n- Agent 通过 `/api/v1/me` 向 Core 验证身份;\n- 普通聊天上下文按用户隔离;\n- 单文件问答上下文按“用户 + 文件 hash”隔离,并保留最近三轮问答;\n- 向量缓存按用户命名空间隔离;\n- 摘要任务只能由任务所属用户查看、取消或重试。\n\n聊天和单文件问答上下文目前保存在 Agent 内存中,服务重启后会清空。摘要任务与向量索引会持久化。\n\n## 5. HTTP API\n\n### 健康检查\n\n`GET /api/agent/health`\n\n返回模型配置、依赖状态、缓存路径、RAG 参数和支持格式。\n\n### 普通聊天\n\n- `POST /api/agent/chat`:非流式聊天,请求体为 `{\"prompt\":\"...\"}`。\n- `POST /api/agent/chat/stream`:SSE 流式聊天,请求体相同。\n- `POST /api/agent/clear_history`:清空当前用户普通聊天上下文。\n\n流式接口使用以下事件:\n\n```text\ndata: {\"type\":\"delta\",\"content\":\"增量文本\"}\n\ndata: {\"type\":\"done\"}\n\ndata: {\"type\":\"error\",\"message\":\"错误信息\"}\n```\n\n上游 SSE 使用小块读取,并关闭代理转换和缓冲。模型输出中的 Markdown 星号会在 Agent 输出层统一移除。\n\n### 文件摘要与标签\n\n- `POST /api/agent/summarize`:同步摘要,请求体为 `{\"hash\":\"...\",\"force\":false}`。\n- `POST /api/agent/summarize/start`:创建后台摘要任务。\n- `GET /api/agent/summarize/status/{task_id}`:查询任务。\n- `GET /api/agent/summarize/tasks`:列出当前用户任务。\n- `POST /api/agent/summarize/cancel/{task_id}`:取消任务。\n- `POST /api/agent/summarize/retry/{task_id}`:重试失败或取消的任务。\n- `POST /api/agent/summarize/missing`:为缺少摘要的支持文件批量排队,保留作管理 API,页面不再提供手动入口。\n\nWeb UI 在文件上传、恢复、重命名和移动成功后,会静默调用 `summarize/start`。任务在后台完成 Markdown 转换、摘要、标签和向量索引,不阻塞文件操作。\n\n### Markdown 与单文件问答\n\n- `GET /api/agent/markdown/{file_hash}`:返回转换后的 Markdown。\n- `POST /api/agent/file_qa`:非流式单文件问答,请求体为 `{\"hash\":\"...\",\"question\":\"...\"}`。\n- `POST /api/agent/file_qa/stream`:SSE 流式单文件问答,请求体相同。\n\n文件长度不超过 `SMARTNAS_FILE_QA_DIRECT_CHARS` 时,模型接收全文;超过阈值时只召回该文件相关片段。后续问题会携带该文件最近三轮问答,大文件检索还会结合上一个问题处理“它、这个结论”等指代。\n\n`file_qa/stream` 的事件格式与普通流式聊天一致,`done` 事件额外带有 `mode`,值为 `full` 或 `rag`。\n\n### RAG 查询\n\n`POST /api/agent/rag/query`\n\n请求体:\n\n```json\n{\"query\":\"检索问题\",\"top_k\":6}\n```\n\n返回相似片段、文件名、hash、块序号、相似度和基于片段生成的回答。\n\n## 6. 文档处理与缓存\n\n- MarkItDown 用于 PDF、Office、文本、网页、EPUB、ZIP、Notebook 等格式转换;\n- 图片使用尺寸、EXIF、嵌入元数据和 SVG 文本生成 fallback Markdown;\n- 音视频在无法提取转录时使用媒体元数据;\n- Markdown 按文件 hash 缓存在 `var/cache/markdown`;\n- RAG 块、embedding 和 FAISS 索引缓存在 `var/cache/vector`;\n- 摘要成功后会同时写回 Core 摘要与标签,并更新该用户向量索引;\n- 查询前会对照 Core 当前文件列表,移除已删除文件的块并同步重命名后的文件名。\n\n## 7. 后台任务与审计\n\n摘要任务使用单工作线程执行,避免多个文档转换和本地 embedding 同时挤占内存。相同用户、相同文件的待处理任务会去重。任务状态包括:\n\n- `pending`\n- `running`\n- `cancel_requested`\n- `cancelled`\n- `success`\n- `failed`\n\n任务记录保存在 `var/cache/agent_tasks.db`。Agent 重启时,未完成任务会标记为失败,可通过重试接口重新排队。\n\n审计日志采用轮转 JSONL,记录用户、操作、状态、耗时、线程及结果或错误。默认路径为 `var/log/agent_audit.jsonl`,文件权限会尝试设置为 `0600`。\n\n## 8. 主要代码位置\n\n- Agent 服务与全部 API:`agent/service.py`\n- Agent 配置、请求模型、模型客户端和 SSE:`agent/`\n- 兼容启动入口:`scripts/agent_service.py`\n- Python 依赖:`scripts/requirements-agent.txt`\n- Web 调用与流式渲染:`web/index.html`\n- Core/Agent 边界:`docs/agent-decoupling.md`\n"}}}}