# 从问题到可重跑的研究项目

个人版网页科研智能体：设计、开发与运维指南

面向构建数理理论与大数据研究工具的开发者。目标是帮助一位研究者完成有边界的工作：整理问题与数据，编写并运行计算，修改结果，导出可以继续使用的项目。全文是一份参考设计，不表示 Exobrain 已经实现这些能力。

个人版指每个项目由一位用户拥有。一个公开服务仍可能服务许多个人账号，因此身份校验、用户数据隔离和资源预算不能省略。本文不设计团队角色、多人共同编辑或企业审批流程。

> 先把一项真实研究任务做完整，再把反复出现的步骤沉淀成产品。

## 01 / Persona：两类研究者，一条工作路径 {#personas}

从具体任务定义 persona，比从“科研人员”这个宽泛标签开始更实用。

| 用户 | 带来的材料 | 希望得到的结果 |
| --- | --- | --- |
| 数理理论研究者 | 方程、参数、假设、一小段 Python | 参数扫描、数值解、误差分析、可编辑图表 |
| 数据研究者 | CSV／Parquet、数据字典、一个分析问题 | 数据概览、清洗脚本、统计结果、可重跑报告 |

例如，前者研究一个动力系统在不同参数下的轨迹，后者分析一批观测数据中的分组差异。两者都需要控制假设和参数，而不是只收到一段看似合理的解释。

```mermaid
flowchart TD
  T[数理研究者：方程与参数] --> P[创建个人项目]
  D[数据研究者：数据与问题] --> P
  P --> I[上传文件或编辑 Markdown / Python]
  I --> C[确认目标、输入与资源预算]
  C --> A[Agent 提出计算方案]
  A --> H{用户是否接受方案}
  H -->|修改| C
  H -->|接受| R[运行计算并展示进度]
  R --> V[查看图表、代码、错误与运行记录]
  V --> E{继续修改还是导出}
  E -->|改参数或代码| I
  E -->|导出| X[下载可重跑的研究项目]
```

图中的确认点只放在需要人判断的地方。已授权范围内的普通计算可以连续执行；扩大预算、上传数据到新目的地或覆盖重要产物，应重新确认。错误后保留输入和日志，让用户继续工作。

第一阶段不接湿实验设备，不承诺自动科研发现，也不要求把论文转换成形式化证明。优先交付一个能修改、能重跑的计算任务。

## 02 / 界面：聊天旁边必须有工作对象 {#interface}

建议采用三个区域：项目文件、对话与任务进度、编辑器与结果预览。小屏幕可切换区域，不强塞三个窄栏。

聊天负责解释意图；文件记录实际工作；运行记录说明哪些代码产生了哪些结果。用户手动修改脚本后，下一轮 agent 必须读取当前文件版本，不能只依赖旧聊天上下文。

### assistant-ui 的位置

[assistant-ui 的 ExternalStoreRuntime](https://www.assistant-ui.com/docs/runtimes/custom/external-store)允许应用保留自己的消息状态，通过适配器连接聊天组件。对于已有 Python 后端、已有消息流的 Exobrain，这是比整体重写更小的试验范围。

先接通发送、流式输出、停止、失败重试、文件引用与工具结果，再考虑更丰富的交互。项目文件和执行结果仍由应用维护。使用 LangGraph 库并不自动具备其服务端 API，不能假设任意 LangGraph UI 适配器都可直接接入。

### 编辑与接受改动

Markdown 与 Python 编辑器可以分别使用成熟的文本编辑组件；初期无需实现完整 IDE。agent 输出候选文件，界面显示差异，用户按文档接受或拒绝。记录候选文件基于哪个旧版本；如果用户已经改过原文件，就提示冲突。

一次变更同时修改脚本和参数时，可以整批接受。恢复旧版本应创建一次新的版本记录，避免抹掉后续修改。逐行接受、实时共同编辑和任意 Notebook 内核，都可以后置。

### 事件协议先于漂亮气泡

建议消息流至少区分 message、tool_started、tool_finished、artifact_created、run_status、error。每条持久事件有 run_id 与递增 seq。SSE 重连时按 seq 补发，前端去重；断开浏览器连接不等于取消计算。取消必须走独立接口，并传到 worker 与执行环境。

## 03 / 架构：把网页服务和计算分开 {#architecture}

一个可运营的最小系统可以有五部分：网页、API、后台 worker、数据库、对象存储。模型和隔离计算环境通过供应商适配层接入。

```mermaid
flowchart TD
  U[浏览器：assistant-ui / 编辑器 / 图表] -->|HTTPS / SSE| API[API：身份、项目、任务、事件]
  API --> DB[(PostgreSQL：项目 / 版本 / 任务 / 事件)]
  API --> Q[持久任务队列]
  Q --> W[Worker：LangGraph 编排]
  W --> DB
  W --> L[模型适配器 → LLM 服务]
  W --> S[Runner 适配器 → 隔离 CPU 沙箱]
  W --> B[批任务适配器 → GPU / 大内存计算]
  S -->|限定输入与输出| O[(对象存储：数据 / 脚本 / 产物)]
  B -->|分区数据与结果| O
  API -->|授权上传与下载| O
  W -->|任务状态与资源统计| M[日志 / 指标 / 告警]
  J[定期清理与超时回收] --> S
  J --> B
  J --> DB
```

队列是逻辑职责，不一定要新增 Redis。低并发时可以用 PostgreSQL 任务表配合租约和安全领取机制；任务规模扩大后再换专门队列。API 与 worker 可以共用代码仓库，但长时间计算不应占住网页请求。

执行器只获得当前任务允许的文件与短时访问凭据。数据库连接、完整对象存储密钥和模型供应商管理密钥留在控制服务中。个人版项目也要按登录用户校验所有文件、任务和下载请求。

### 最小接口

| 接口示意 | 职责 |
| --- | --- |
| POST /projects | 创建个人项目 |
| POST /projects/:id/uploads | 校验归属，签发限定对象的上传地址 |
| POST /projects/:id/runs | 冻结输入版本、记录预算，返回 run_id |
| GET /runs/:id/events?after=seq | 获取持久事件并接续 SSE |
| POST /runs/:id/cancel | 请求取消，直到执行器确认后进入终态 |
| POST /artifacts/:id/versions | 带 base_version 保存新版本，拒绝静默覆盖 |
| GET /runs/:id/export | 生成下载包或返回异步打包任务 |

这些是设计契约，不是现有 Exobrain API。创建运行带幂等键，避免页面重试意外启动两份昂贵计算。

## 04 / ER：文件、聊天和运行分别保存 {#data-model}

聊天是解释与交互记录。Artifact 是逻辑文件；ArtifactVersion 是它的一份不可变内容。Run 则是一轮实际执行，必须引用具体输入版本，而不是一个会继续变化的文件名。

```mermaid
erDiagram
  USER ||--o{ PROJECT : owns
  PROJECT ||--o{ THREAD : contains
  THREAD ||--o{ MESSAGE : contains
  PROJECT ||--o{ ARTIFACT : contains
  ARTIFACT ||--|{ ARTIFACT_VERSION : has
  THREAD ||--o{ RUN : starts
  RUN ||--o{ RUN_EVENT : emits
  RUN ||--o{ RUN_ARTIFACT : records
  ARTIFACT_VERSION ||--o{ RUN_ARTIFACT : referenced_by
  USER {
    uuid id PK
  }
  PROJECT {
    uuid id PK
    uuid owner_id FK
    string title
  }
  THREAD {
    uuid id PK
    uuid project_id FK
  }
  MESSAGE {
    uuid id PK
    uuid thread_id FK
    json content_parts
  }
  ARTIFACT {
    uuid id PK
    uuid project_id FK
    string logical_path
  }
  ARTIFACT_VERSION {
    uuid id PK
    uuid artifact_id FK
    string object_key
    string content_hash
  }
  RUN {
    uuid id PK
    uuid thread_id FK
    string status
    json execution_spec
  }
  RUN_EVENT {
    uuid run_id FK
    int seq
    json payload
  }
  RUN_ARTIFACT {
    uuid run_id FK
    uuid version_id FK
    string role
  }
```

约束比字段数量重要：RUN_EVENT 的 (run_id, seq) 唯一；RUN_ARTIFACT 的 role 区分 input、output 与 log；同一关联里的运行与文件必须属于同一个项目。Artifact 的逻辑路径在项目内唯一。跨用户访问必须在服务端拒绝，不能依赖前端隐藏按钮。

execution_spec 保存环境镜像摘要、模型配置、提示词版本、参数、种子、资源上限和供应商任务标识。大型数据、日志正文与图像放对象存储，数据库只存索引和引用。聊天摘要用于节省上下文，但不能代替运行证据。

这里没有 team、membership 或企业角色表。未来是否引入这些对象，取决于真实需求。

## 05 / LangGraph：围绕一次有边界的运行 {#workflow}

图的状态应是可序列化的任务数据，例如 project_id、run_id、input_versions、task_kind、plan、budget、attempt、job_id、outputs 与 error。不要把数据库连接或供应商密钥放进 checkpoint。

```mermaid
flowchart TD
  A[START：读取项目与输入版本] --> B{信息是否足够}
  B -->|否| C[请求澄清 / interrupt]
  C -->|用户补充后 resume| A
  B -->|是| D{需要指定资料吗}
  D -->|已收录| E[按需检索]
  D -->|不需要| F[生成回答或计算方案]
  D -->|未收录| G[说明来源缺口]
  G --> C
  E --> F
  F --> H{是否需要执行}
  H -->|否| Z[保存回复与引用 / END]
  H -->|是| I[校验工具、参数、预算与能力]
  I --> J{需要用户批准吗}
  J -->|是| K[interrupt：等待批准]
  K -->|批准| L[幂等提交计算任务]
  K -->|拒绝| X[取消 / END]
  J -->|已授权| L
  L --> M[持久化 job_id / 等待结果]
  M --> N{执行结果}
  N -->|成功| O[检查输出并保存产物]
  N -->|可修复且有剩余预算| P[增加 attempt / 修正代码]
  P --> I
  N -->|不可恢复或预算耗尽| Y[保存错误与部分产物 / END]
  O --> Z
```

这是建议流程，不是现有 Exobrain graph 的导出。计算任务返回后还需检查空文件、NaN、数据列、样本数、单位或数值收敛等；程序退出码为零只是其中一个信号。

[LangGraph persistence](https://docs.langchain.com/oss/python/langgraph/persistence)支持保存运行状态，[interrupts](https://docs.langchain.com/oss/python/langgraph/interrupts)用于暂停等待外部输入。生产环境需要持久化 checkpointer 和稳定的 thread_id；调用 compile 本身不会自动完成部署与恢复设计。

人工暂停应发生在产生副作用之前。恢复可能重跑节点，因此提交计算、创建文件版本等操作要用 run_id 与步骤标识去重。checkpoint 中保存远程 job_id，重启后查询原任务，避免重新创建。

不要同时让托管 agent 与自己的 graph 都无限重试同一件事。选择一个层负责迭代与预算控制，另一层通过明确的任务契约接入。

## 06 / 简单选型：先选职责，再选品牌 {#choices}

### LLM：一主一备，按任务选能力

| 用途 | 起步选择 | 关注点 |
| --- | --- | --- |
| 任务分类、来源判断 | 轻量、低延迟模型，简单规则先行 | 输出结构稳定，错误时能澄清 |
| 方案与代码生成 | 工具调用和代码能力较好的主模型 | 当前任务完成率、修改次数、延迟与总成本 |
| 难题处理 | 明确触发后升级模型或思考预算 | 上限可控，不把每轮聊天都升级 |
| 检索向量 | 与语言、领域和索引匹配的 embedding | 模型变化需要兼容或重建索引 |

起步只接一个实际可用的模型供应商，记录模型标识和配置；确有稳定性需求再加备选。OpenAI、方舟或百炼都可以是候选，选择取决于可用地区、数据要求、工具调用兼容性和自己的任务样例，不在本文给出永久排名。

大模型处理数据字典、抽样、聚合结果与必要文件片段。不要把整张大表塞进上下文。模型输出的工具参数由服务端校验，不能由提示词替代执行边界。

### UI、存储与编排的建议起点

| 层 | 建议起点 | 暂缓 |
| --- | --- | --- |
| Web UI | React + assistant-ui 适配现有消息流 | 重写整个 workspace |
| 编辑 | Markdown / Python 文本编辑与文件级 diff | 逐行合并、完整 IDE |
| 后端 | Python API + 独立 worker | 多个语言重复实现同一工具 |
| 编排 | 有分支与暂停需求时采用 LangGraph | 为每个函数都创建 agent |
| 数据 | PostgreSQL + S3 兼容对象存储 | 大文件写数据库或 Git |
| 检索 | 只有明确资料需求才建索引 | 对所有问题强制 RAG |

### 执行环境：microVM 是隔离选项

[Vercel Sandbox](https://vercel.com/docs/sandbox)官方说明使用 Firecracker microVM；[E2B](https://docs.e2b.dev/)提供可编程沙箱；[Railway Sandboxes](https://docs.railway.com/sandboxes)提供隔离执行环境。部署在 Railway 的 API 可以通过供应商 SDK 调用外部执行服务，不必与网页同一个进程或机器。

选择前用自己的 Python 依赖和文件任务验证：启动时间、内存、绝对超时、网络策略、文件回传、失败清理、地区与计费。不要仅凭“沙箱”二字认定隔离技术相同；百炼 Managed Agents 文档描述的是云端容器环境，也不能自动当作 microVM。

个人产品通常没有必要自己运维 Firecracker 宿主集群。优先使用成熟服务，但把 create、execute、status、cancel、collect、destroy 收敛到自己的小接口，保存可导出的文件与运行记录。

## 07 / 科学环境与大数据：运行前知道能力 {#compute}

### 依赖分层，避免一个镜像包办所有任务

SciencePro 在研究者提供的对话中自述预装了大量 Python 包。该清单是单次会话的自述，本文未独立核验版本或数量。它提醒我们：包数量不等于任务能力，更不等于 GPU 可用。

建议起步准备三个环境规格，按任务选择并固定版本：

- **基础 CPU：** SymPy、NumPy、SciPy、pandas、matplotlib，完成符号、数值与小中型数据分析。
- **数据分析：** 按需要加入 DuckDB、Arrow／Parquet、Polars，优先列裁剪、过滤、分批与磁盘执行。
- **深度学习：** 按任务选择 PyTorch 或其他框架，区分 CPU 与 GPU 环境，避免默认装齐所有大型框架。

这些是包类别建议，不是已验证的兼容版本组合。发布镜像前运行依赖解析、导入测试和代表性计算，记录锁文件与镜像摘要。不能假设数百个包在同一环境中长期兼容。

### 没有 GPU，深度学习库仍然可以工作

PyTorch、TensorFlow 可以执行 CPU 运算、小模型推理与小规模训练。GPU 主要改变可承担的工作量和速度。nvidia-smi 失败可能来自工具缺失、驱动或设备不可见；它不说明所有 GPU 后端、其他运行规格或整个平台都不存在 GPU。

更好的产品设计是由受信任的运行器发布一份经过探测的能力清单。以下字段和值仅为示意：

```json
{
  "environment": "science-cpu-v1",
  "python": "pinned-by-image",
  "accelerator": "cpu",
  "memory_mb": 4096,
  "max_runtime_seconds": 300,
  "internet": "restricted",
  "packages_manifest": "sha256:...",
  "tools": { "python": true, "lean": false }
}
```

硬件分配与框架能否使用硬件要分别检查，例如 PyTorch 的 CUDA 可用性、实际的小算子运行和驱动兼容性。未知状态就写 unknown，不由 LLM 编造。

研究者需要知道自己获准使用的 CPU／GPU、内存和工具状态，不需要宿主内部地址或密钥。提供受限诊断工具比靠系统提示词禁止谈论配置更可靠。一次对话愿意回答环境问题，也不能据此判定发生了越权或沙箱逃逸。

### 大数据需要移动计算，而不是把数据塞进聊天

先读取 schema、行数估计、缺失率和小样本，再生成计算计划。数据以分区 Parquet 等形式保留在对象存储；计算尽可能靠近数据。对超过单机内存的任务，使用磁盘执行、分块处理或提交外部批任务，避免直接全量加载到 pandas。

长时 GPU／大内存任务走单独的批任务接口，返回 job_id，让用户离线后任务仍可查询。普通聊天沙箱不能因为装了 torch 就承担大模型训练。上传、下载、解压与输出均有大小限制；数据传输、对象存储和失败重试也计入成本。

## 08 / 开发与运维：让失败可恢复 {#operations}

从一个垂直闭环开始：上传一个小数据集 → 生成并编辑脚本 → 在 CPU 环境运行 → 展示结果 → 修改参数重跑 → 导出。先不要同时建设 GPU 平台、团队协作和自动论文写作。

### 运行状态是产品的一部分

建议状态包括 queued、running、waiting_user、succeeded、failed、cancelled、timed_out。cancel_requested 可以作为过渡状态；只有远端停止确认后才显示已取消。worker 用心跳和租约表明自己仍在处理；租约失效后先核对远端 job，再决定恢复或终止。

| 失效场景 | 产品行为与工程处理 |
| --- | --- |
| SSE 断开 | 显示重新连接，按事件序号补发，不重复创建任务 |
| worker 重启 | 从持久状态恢复，查询已有远端 job |
| 代码出错 | 保存 stderr 和脚本版本，有限次数修复后交给用户 |
| 超时或预算耗尽 | 停止新调用，取消执行器，保存可用的部分产物 |
| 产物回传失败 | 保留暂存引用，在限定期限内重试收集 |
| 沙箱遗留 | 定期回收器按任务终态与绝对期限清理 |

### 最小可观测性

结构化日志统一携带 request_id、project_id、run_id、step 与 provider_job_id。记录队列等待、模型调用时长、计算时间、token 用量、退出码和产物大小。默认不记录完整私有数据、密钥或未经筛选的提示词。

告警先覆盖队列积压、异常失败率、超时任务、孤立沙箱与预算耗尽。诊断时应能从一条失败运行追到模型、执行器和文件版本；采用哪一种追踪平台是后续选择。

### 发布前要验证的完整路径

用少量固定任务覆盖正常计算、语法错误、依赖缺失、超时、用户取消、浏览器重连与 worker 重启。对有明确答案的数值任务设置容差，对数据分析检查样本数与列类型。它们验证系统行为，不宣称自动认证科学结论。

开发与生产分开配置；数据库迁移保持兼容；提示词、模型配置和镜像版本可以回退。备份数据库和重要对象，并实际测试恢复。成本预算同时限制模型、运行时长、并发、存储与外部传输，不能只统计 token。

默认关闭任意外网访问，确有需要时按目的地授权；依赖尽量在镜像构建阶段安装。代码执行不携带应用密钥，下载结果按文件类型安全展示。模型可以提议动作，服务端负责决定动作能否执行。

## 09 / 托管平台与自研：保留有用的产品层 {#build-or-buy}

通用聊天、文件处理、代码执行和 agent 循环，越来越适合购买或复用。仅仅把这些功能再包装一遍，产品的替代风险很高。

截至 2026-09-21，OpenAI 官方文档说明 Assistants API 已在 2026-08-26 下线，新接入应使用 Responses API。[迁移说明](https://developers.openai.com/api/docs/assistants/migration)

百炼 Managed Agents 官方文档列出会话状态、云端容器、工具执行和持久事件；方舟也提供 Managed Agents 及相关能力。平台已经覆盖的基础设施，不必为了“拥有技术栈”重新造一遍。[百炼官方文档](https://help.aliyun.com/zh/model-studio/managed-agents-introduction)、[方舟官方产品入口](https://www.volcengine.com/)

| 优先购买或复用 | 值得自己掌握 |
| --- | --- |
| 模型、常规工具循环、沙箱、对象存储、基础聊天组件 | 用户任务定义与科研工作流程 |
| 通用检索与文件处理基础设施 | 输入数据、假设、参数和结果的对应关系 |
| 托管计算与任务执行 | 可编辑、可重跑、可导出的项目契约 |
| 通用运行监控能力 | 领域检查、故障解释与用户工作衔接 |

版本管理和可复现性也不是天然独有优势。是否值得做成产品，取决于具体用户是否因此少走步骤、减少手工修正，并愿意继续使用。

接近用户的实施工作，可以帮助发现这些重复需求：先用现成平台完成真实任务，再把重复出现的导入、计算、编辑与导出步骤做成小产品。若每次需求都完全不同，适合继续作为实施服务；若稳定流程反复出现，再扩大产品投入。

## 10 / 可移植项目的最小形态 {#portable-project}

```text
research-project/
  README.md                 # 问题、入口与重跑方法
  assumptions.md            # 假设、单位和适用范围
  analysis.py               # 当前分析脚本
  parameters.json           # 参数和随机种子
  environment.lock          # 依赖与环境版本
  manifest.json             # 文件哈希、来源、许可与版本
  data/README.md            # 数据取得方式；大数据可保留引用
  runs/run-001/
    execution.json          # 输入版本、环境、预算与退出状态
    stdout.txt
    figures/
    results.parquet
```

大文件引用必须说明访问方法、版本与校验方式，不导出长期访问密钥。受限制的数据可能不能公开，也可能无法由其他人直接取得；导出时应明确这些条件。

同一脚本重跑与让 agent 重新生成脚本不同。记录种子、环境与参数有助于重跑，但远程模型、外部数据及数值后端的变化仍可能改变结果。

**这个产品的完成标准，是用户能带走自己的研究工作，并继续修改和运行。**

[Exobrain 产品设计笔记](/docs/agentic-science) · [打开 Exobrain](https://emergence.science/exobrain)
