ZAIA Research Web 是一个研究报告生成 Web 工具:用户在浏览器里提交研究主题、模式、重点和章节规划,服务端生成报告 bundle,并导出 HTML、Markdown 和 PDF。
项目源自 Fat-Jan/deep-research 的结构化研究思路,但重心已经变成可交互、可下载、可验收的 Web 产品闭环。
- Web 表单入口:外部用户可以直接在 HTML 页面提交研究任务。
- 多格式交付:同一份报告会生成 HTML 子页面、Markdown 文档和 PDF 文档。
- 报告库:页面右侧展示历史报告,可直接查看和下载。
- 模型降级:支持主备 key、模型链 fallback、SSE 响应兼容和模板兜底。
- PDF 导出:优先使用 Chrome / Chromium 打印 PDF,
pdfkit作为兜底。 - 深度验收:内置
npm run verify:deep,检查报告落盘、长度、PDF 和异常内容。
- 先把研究任务拆成结构化输入
- 用统一的工作流服务生成报告骨架
- 一次写出多种交付格式
- 提供报告浏览与文件下载入口
当前版本重点完成这条闭环:
- 外部用户打开 HTML 页面
- 提交研究主题、模式、重点和章节规划
- 服务端生成一份研究报告 bundle
- 输出同名的 HTML 子页面、Markdown 文档、PDF 文档
- 在页面右侧报告库中直接查看和下载
src/ 前端页面
server/index.js Express 服务入口
server/report-service.js 研究编排、模型调用与导出逻辑(待模块化重构)
data/reports/ 生成后的报告产物
.ops/validation/ 深度报告验收脚本与样例请求
ARCHITECTURE.md 架构、部署路线与验收口径
UPGRADE_PLAN.md 功能升级路线图(三期规划)
REFACTOR_PLAN.md 模块化重构任务清单(阶段零,优先执行)
scripts/sample-request.json 接口样例请求
当前项目处于基础可用阶段,完整升级路线见 UPGRADE_PLAN.md:
-
阶段零(1-2 天):模块化重构,详见
REFACTOR_PLAN.md- 拆分
report-service.js(1766 行 → 8-10 个模块) - 集成 Grok API 搜索(四层降级:Grok high → low → wwwneo → LLM 内置)
- 集成 image2 生图(报告封面图)
- 拆分
-
第一期(3-4 周):增强单体架构
- WebSocket 流式进度反馈
- 分阶段报告生成(Plan → Search → Synthesize → Refine)
- 报告历史管理(SQLite)
- 前端界面重构
-
第二期(5-6 周):异步任务与扩展性
- 异步任务队列(BullMQ + Redis)
- 多报告合并、AI 生成追问
- 报告质量评分
-
第三期(可选):Cloudflare 无服务器重构
下一步行动:执行 REFACTOR_PLAN.md 完成模块化重构(可分配给其他 LLM)。
npm install
npm run server启动后访问:
http://localhost:4173
npm run dev:full前端使用 Vite 热更新,后端使用 Express 提供 API 与静态文件。
标准:默认使用deepseek-v4-pro,优先保证写作稳定和成稿质量。深度:默认使用grok-4.20-multi-agent-high,允许更长内容,并在兼容端支持时带回联网整理来源。
相比原项目的多种模式,这里刻意简化为两档。对外部用户来说,最常见的区别只有“快速成稿”和“需要更多研究展开”,不必把内部工作流拆得太碎。
curl http://localhost:4173/api/healthcurl http://localhost:4173/api/configcurl -X POST http://localhost:4173/api/reports \
-H 'Content-Type: application/json' \
--data @scripts/sample-request.json成功后,产物会写入:
data/reports/<slug>/
其中包含:
*.html*.md*.pdf
深度模式现在是主验收面。设置真实模型端点和 key 后运行:
npm run verify:deep这个命令会构建前端、启动 Express 服务、创建一份深度模式报告,并检查 Markdown / HTML / PDF 是否真实落盘、报告长度是否达到深度阈值、PDF 是否可识别,以及产物中是否混入破损 JSON、代码围栏、假来源或异常标题层级。
设置以下环境变量后,报告生成会优先调用兼容 OpenAI 的聊天接口;如果某个 key 额度不足或不可用,会自动尝试下一个 key。全部模型调用失败时才回退到模板生成,但仍输出 HTML / MD / PDF。
也可以用逗号一次性传入多个 key:
export LLM_API_KEYS='主 key,备用 key'当前实测情况:
/v1/models可返回模型列表- 写作模型优先使用
deepseek-v4-pro,备用可选glm-5.1,快写可选deepseek-v4-flash - 搜索整理模型优先使用
grok-4.20-multi-agent-high,备用可选grok-4.20-multi-agent-low - 首轮生成可用
LLM_MODELS_STANDARD/LLM_MODELS_DEEP指定模型链;HTTP 错误、空内容、JSON/结构不完整都会继续尝试下一个模型 - 二阶段扩写现在支持模型级降级:默认顺序为当前模式模型 -> 标准写作模型 ->
glm-5.1->deepseek-v4-flash;每个模型内部仍会轮换 key 池 - 如需避开某个容易
429的搜索模型,可用LLM_MODEL_EXPAND把扩写优先切到写作模型,或用LLM_MODELS_EXPAND指定完整逗号列表 - 深度验收支持长等待和心跳:
LLM_REQUEST_TIMEOUT_MS控制单次模型请求超时,VERIFY_DEEP_MAX_SECONDS控制整次验收上限,VERIFY_DEEP_HEARTBEAT_SECONDS控制心跳间隔 grok-4.20-multi-agent-*即使请求stream:false,也可能返回 SSEdata:分块;服务端已兼容这类响应gpt-5.5-chat当前会返回all accounts failed,grok-4.3-high当前实测会遇到上游429,暂不作为默认模型
导出链路现在统一为 Markdown -> 清洗后的 HTML -> Chrome/Chromium 打印 PDF,HTML 子页面与 PDF 使用同一份排版 CSS,减少 Markdown、HTML、PDF 三份产物互相漂移的问题。
服务端会优先使用本机 Chrome/Chromium,可通过 CHROME_PATH 或 CHROMIUM_PATH 指定可执行文件。未找到浏览器或渲染失败时才回退到 pdfkit,并优先加载 macOS 本机可用的 Arial Unicode.ttf 降低中文乱码风险。
当前阶段优先部署到 OpenDeploy。理由是项目已经是单个 Express 服务,运行时会写 data/reports/,并依赖 Chrome/Chromium 生成 PDF;OpenDeploy 只需要 Node 服务、运行时环境变量和持久卷即可贴近现状上线。
OpenDeploy 部署口径:
- Build:
npm ci && npm run build - Start:
npm start - Port:读取
PORT - Runtime env:
LLM_BASE_URL、LLM_API_KEYS或LLM_API_KEY/LLM_API_KEY_BACKUP、LLM_MODEL_STANDARD、LLM_MODEL_DEEP、LLM_REQUEST_TIMEOUT_MS,以及可选LLM_MODELS_STANDARD/LLM_MODELS_DEEP/LLM_MODEL_EXPAND/LLM_MODELS_EXPAND/LLM_MAX_TOKENS/CHROME_PATH - Storage:生产环境给
data/挂持久卷,避免重启或重新部署后丢失报告产物
本地 OpenDeploy 预检已经通过:opendeploy preflight . --json 与 opendeploy deploy plan . --review --json 均为 ready,无阻塞项。
Cloudflare 更适合作为二期重构目标。若走 Cloudflare,应拆成静态前端、Worker API、R2 产物存储、D1/KV 报告索引、Queues/Workflows 深度任务,以及 Cloudflare Browser Rendering 或外部 browserless PDF 服务。
- 已实现:HTML 页面调用、REST API、报告落盘、多格式导出、报告库展示、真实 LLM 接入、主备 key 自动切换、首轮/二阶段模型降级、深度验收心跳、SSE 响应兼容、模型 JSON 修复/回退、统一 HTML/PDF 排版导出、深度模式验收脚本
- 暂未接入:独立抓取器、来源去重校验、异步任务队列、在线预览级 PDF 增强
如果后续要继续升级,最自然的下一步是补上独立抓取器,把深度模式中的“搜索结果”与“正文写作”完全拆开。